Пример добавления анимации к mFilter2

В ответ на вопрос по анимированию работы фильтров mFilter2, выкладываю пример решения данной задачи (демо).



Вначале нам необходимо реализовать визуальный эффект самой анимации (css):

@-webkit-keyframes FadeIn {
	0% {
		opacity: 1;
		-webkit-transform: scale(1) translate3d(1px, 0, 0);
		transform: scale(1) translate3d(1px, 0, 0);
	}
	100% {
		opacity: 0;
		-webkit-transform: scale(.8) translate3d(0, 0, 0);
		transform: scale(.8) translate3d(0, 0, 0);
	}
}
@keyframes FadeIn {
	0% {
		opacity: 1;
		-webkit-transform: scale(1) translate3d(1px, 0, 0);
		transform: scale(1) translate3d(1px, 0, 0);
	}
	100% {
		opacity: 0;
		-webkit-transform: scale(.8) translate3d(0, 0, 0);
		transform: scale(.8) translate3d(0, 0, 0);
	}
}
@-webkit-keyframes FadeOut {
	0% {
		opacity: 0;
		-webkit-transform: scale(.8) translate3d(1px, 0, 0);
		transform: scale(.8) translate3d(1px, 0, 0);
	}
	100% {
		opacity: 1;
		-webkit-transform: scale(1) translate3d(0, 0, 0);
		transform: scale(1) translate3d(0, 0, 0);
	}
}
@keyframes FadeOut {
	0% {
		opacity: 0;
		-webkit-transform: scale(.8) translate3d(1px, 0, 0);
		transform: scale(.8) translate3d(1px, 0, 0);
	}
	100% {
		opacity: 1;
		-webkit-transform: scale(1) translate3d(0, 0, 0);
		transform: scale(1) translate3d(0, 0, 0);
	}
}

После чего привязать анимацию к определенным классам, которые будут попеременно навешиваться на контейнер с результатами:

body .container > .rows.fade-in > .item {
	-webkit-animation: FadeIn cubic-bezier(.55, .08, .06, .97) .5s;
	animation: FadeIn cubic-bezier(.55, .08, .06, .97) .5s;
	-webkit-animation-iteration-count: 1;
	animation-iteration-count: 1;
	-webkit-transform-origin: center top;
	-ms-transform-origin: center top;
	transform-origin: center top;
	-webkit-animation-fill-mode: forwards;
	animation-fill-mode: forwards;
}
body .container > .rows.fade-out > .item {
	-webkit-animation: FadeOut cubic-bezier(.55, .08, .06, .97) .5s;
	animation: FadeOut cubic-bezier(.55, .08, .06, .97) .5s;
	-webkit-animation-iteration-count: 1;
	animation-iteration-count: 1;
	-webkit-transform-origin: center top;
	-ms-transform-origin: center top;
	transform-origin: center top;
	-webkit-animation-fill-mode: forwards;
	animation-fill-mode: forwards;
	visibility: visible !important;
}


Теперь осталось только научить mFilter2 накладывать классы к обертке результатов в нужный момент.
Для этого переопределим родные функции загрузки результатов:

$(document).ready(function() {
	//вынесем параметр времени последней анимации в глобальную переменную, так как в противном случае при повторном запросе (например, при быстром переключении фильтров) не будет учитываться то, закончилась ли предыдущая анимация или нет.
	var last_animation_time = 0;

	//расширяем набор передаваемых значений в функцию, на случай, если потребуется отключить анимацию для определенных событий
	//(например подгрузка результатов скроллом)
	mSearch2.load = function (params, callback, animation) {
		if (!params || !Object.keys(params).length) {
			params = this.getFilters();
		}
		params.action = 'filter';
		params.pageId = mse2Config.pageId;

		this.beforeLoad();
		params.key = mse2Config.key;
		
		//по-умолчанию анимация всегда будет срабатывать, если ее не запретить
		var effects = '';

		if (animation == 'no-animation') {
			//теперь для отключения анимации достаточно передать в функцию mSearch2.load третий параметр 'no-animation'
			effects = 'no-animation';
		}
		else {
			last_animation_time = $.now();
			//если название ваших классов отличается от приведенных выше, то здесь их необходимо заменить
			$(this.options['results']).removeClass('fade-out').addClass('fade-in');
		}
		
		$.post(mse2Config.actionUrl, params, function(response) {
			mSearch2.afterLoad();		

			if (response.success) {
				mSearch2.Message.success(response.message);
				mSearch2.pagination.html(response.data['pagination']);
				
				//В моем случае анимация появления/скрытия отличаются друг от друга, поэтому необходимо накладывать последующий эффект только после окончания предыдущего для избежания "мельтешения".	

				//Мы, конечно, могли бы просто затормозить процесс добавления результатов на длину времени анимации, но в таком случае пользователю придется дольше ждать, если фильтры отработают быстрее самой анимации. Так что считаем разницу:
				var animation_delay = 0;
				if (effects != 'no-animation') {
					var animation_time_left;
					
					animation_time_left = $.now() - last_animation_time;
					//если время воспроизведения вашей анимации отличается, то здесь необходимо указать актуальное значение
					if (animation_time_left >= 500) {
						animation_delay = 0;
					}
					else {
						animation_delay = 500 - animation_time_left;
					}
				}
				
				setTimeout(function(){
					mSearch2.results.html(response.data['results']);
				
					if (effects != 'no-animation') {
						$(mSearch2.options['results']).addClass('fade-out').removeClass('fade-in');
					}
					
					//добавим запрет на переключение фильтров до момента завершения анимации появления
					setTimeout(function(){
						$(mSearch2.options.filters + ' .' + mSearch2.options.disabled_class).prop('disabled', false).removeClass(mSearch2.options.disabled_class);
					}, 500);
				}, animation_delay);
				
				mSearch2.setTotal(response.data.total);
				if (callback && $.isFunction(callback)) {
					callback.call(this, response, params);
				}
				mSearch2.setSuggestions(response.data.suggestions);
				mSearch2.setEmptyFieldsets();
				if (response.data.log) {
					$('.mFilterLog').html(response.data.log);
				}
				$(document).trigger('mse2_load', response);
			}
			else {
				mSearch2.Message.error(response.message);
			}
		}, 'json');
		
	};
	
	//Так как мы вынесли процесс обновления доступности фильтров в наш таймер, необходимо убрать аналогичный фукнционал из afterLoad (там же уберем и эффект полупрозрачности):
	
	mSearch2.beforeLoad = function() {
		$(this.options.pagination_link).addClass(this.options.active_class);
		this.filters.find('input, select').prop('disabled', true).addClass(this.options.disabled_class);
	};
	
	mSearch2.afterLoad = function() {

	};
});

Готово! Можно наслаждаться результатом..)
Максим Кузнецов
28 марта 2017, 10:30
modx.pro
13
2 134
+13

Комментарии: 6

Василий Наумкин
28 марта 2017, 15:07
2
+5
Гораздо лучше и проще переопределить функцию из своего скрипта, загруженного позже оригинального:

Например:
$(document).ready(function() {
	mSearch2.load = function (params, callback, animation) {
		// ...
	};
})
Тогда и основной скрипт не нужно будет переименовывать и заморачиваться при обновлениях с добавлением исправлений.
    Максим Кузнецов
    28 марта 2017, 15:18
    0
    Спасибо за совет, переписал.)
      Максим
      28 марта 2017, 19:07
      0
      а можно вопрос. Этот файл откуда брать?
      $(document).ready(function() {
      	//вынесем параметр времени последней анимации в глобальную переменную, так как в противном случае при повторном запросе (например, при быстром переключении фильтров) не будет учитываться то, закончилась ли предыдущая анимация или нет.
      	var last_animation_time = 0;
      
      	//расширяем набор передаваемых значений в функцию, на случай, если потребуется отключить анимацию для определенных событий
      	//(например подгрузка результатов скроллом)
      	mSearch2.load = function (params, callback, animation) {
      		if (!params || !Object.keys(params).length) {
      			params = this.getFilters();
      		}
      		params.action = 'filter';
      		params.pageId = mse2Config.pageId;
      
      		this.beforeLoad();
      		params.key = mse2Config.key;
      		
      		//по-умолчанию анимация всегда будет срабатывать, если ее не запретить
      		var effects = '';
      
      		if (animation == 'no-animation') {
      			//теперь для отключения анимации достаточно передать в функцию mSearch2.load третий параметр 'no-animation'
      			effects = 'no-animation';
      		}
      		else {
      			last_animation_time = $.now();
      			//если название ваших классов отличается от приведенных выше, то здесь их необходимо заменить
      			$(this.options['results']).removeClass('fade-out').addClass('fade-in');
      		}
      		
      		$.post(mse2Config.actionUrl, params, function(response) {
      			mSearch2.afterLoad();		
      
      			if (response.success) {
      				mSearch2.Message.success(response.message);
      				mSearch2.pagination.html(response.data['pagination']);
      				
      				//В моем случае анимация появления/скрытия отличаются друг от друга, поэтому необходимо накладывать последующий эффект только после окончания предыдущего для избежания "мельтешения".	
      
      				//Мы, конечно, могли бы просто затормозить процесс добавления результатов на длину времени анимации, но в таком случае пользователю придется дольше ждать, если фильтры отработают быстрее самой анимации. Так что считаем разницу:
      				var animation_delay = 0;
      				if (effects != 'no-animation') {
      					var animation_time_left;
      					
      					animation_time_left = $.now() - last_animation_time;
      					//если время воспроизведения вашей анимации отличается, то здесь необходимо указать актуальное значение
      					if (animation_time_left >= 500) {
      						animation_delay = 0;
      					}
      					else {
      						animation_delay = 500 - animation_time_left;
      					}
      				}
      				
      				setTimeout(function(){
      					mSearch2.results.html(response.data['results']);
      				
      					if (effects != 'no-animation') {
      						$(mSearch2.options['results']).addClass('fade-out').removeClass('fade-in');
      					}
      					
      					//добавим запрет на переключение фильтров до момента завершения анимации появления
      					setTimeout(function(){
      						$(mSearch2.options.filters + ' .' + mSearch2.options.disabled_class).prop('disabled', false).removeClass(mSearch2.options.disabled_class);
      					}, 500);
      				}, animation_delay);
      				
      				mSearch2.setTotal(response.data.total);
      				if (callback && $.isFunction(callback)) {
      					callback.call(this, response, params);
      				}
      				mSearch2.setSuggestions(response.data.suggestions);
      				mSearch2.setEmptyFieldsets();
      				if (response.data.log) {
      					$('.mFilterLog').html(response.data.log);
      				}
      				$(document).trigger('mse2_load', response);
      			}
      			else {
      				mSearch2.Message.error(response.message);
      			}
      		}, 'json');
      		
      	};
      	
      	//Так как мы вынесли процесс обновления доступности фильтров в наш таймер, необходимо убрать аналогичный фукнционал из afterLoad (там же уберем и эффект полупрозрачности):
      	
      	mSearch2.beforeLoad = function() {
      		$(this.options.pagination_link).addClass(this.options.active_class);
      		this.filters.find('input, select').prop('disabled', true).addClass(this.options.disabled_class);
      	};
      	
      	mSearch2.afterLoad = function() {
      
      	};
      });
        Максим Кузнецов
        28 марта 2017, 19:10
        +1
        Это вставить где-нибудь внизу страницы в
        <script type="text/javascript">
        	//сюда
        </script>
        (после подключенного jQuery)
          Это сообщение было удалено
            Василий Наумкин
            29 марта 2017, 17:29
            +1
            Завязывай уже эту Санта-Барбару.

            Еще один такой коммент — и отключу нафиг, надоело.
    man
    man
    06 апреля 2017, 13:29
    0
    Эмм, а не подскажите как вы меняете url)?
    Все нашел, всем спасибо!
      Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
      7