Какой самый чистый способ временно отключить эффекты перехода CSS?

210

У меня есть элемент DOM с некоторыми / всеми из следующих эффектов:

#elem {
  -webkit-transition: height 0.4s ease;
  -moz-transition: height 0.4s ease;
  -o-transition: height 0.4s ease;
  transition: height 0.4s ease;
}

Я пишу плагин jQuery, который изменяет размеры этого элемента, мне нужно временно отключить эти эффекты, чтобы я мог плавно изменить его размер.

Каков самый элегантный способ временно отключить эти эффекты (а затем повторно включить их), учитывая, что они могут применяться от родителей или не применяться вообще.

Сэм Шафран
источник
1
Это выглядит многообещающе: ricostacruz.com/jquery.transit . Это лицензия MIT, так что вы можете включить ее или изучить, как он это делает.
Роберт Харви
Все ответы добавить в .notransitionкласс. Было бы разумнее сделать это наоборот, то есть нацелиться #elem.yestransitionна css и удалить этот класс. Это устраняет наличие неприятных *s в вашем css.
марта

Ответы:

484

Короткий ответ

Используйте этот CSS:

.notransition {
  -webkit-transition: none !important;
  -moz-transition: none !important;
  -o-transition: none !important;
  transition: none !important;
}

Плюс либо этот JS (без jQuery) ...

someElement.classList.add('notransition'); // Disable transitions
doWhateverCssChangesYouWant(someElement);
someElement.offsetHeight; // Trigger a reflow, flushing the CSS changes
someElement.classList.remove('notransition'); // Re-enable transitions

Или этот JS с JQuery ...

$someElement.addClass('notransition'); // Disable transitions
doWhateverCssChangesYouWant($someElement);
$someElement[0].offsetHeight; // Trigger a reflow, flushing the CSS changes
$someElement.removeClass('notransition'); // Re-enable transitions

... или эквивалентный код с использованием любой другой библиотеки или фреймворка, с которым вы работаете.

объяснение

Это на самом деле довольно тонкая проблема.

Во-первых, вы, вероятно, захотите создать класс «notransition», который вы можете применить к элементам для установки их *-transitionатрибутов CSS none. Например:

.notransition {
  -webkit-transition: none !important;
  -moz-transition: none !important;
  -o-transition: none !important;
  transition: none !important;
}

(Незначительно, обратите внимание на отсутствие -ms-transitionтам. Вам это не нужно. Первой версией Internet Explorer, которая вообще поддерживала переходы, был IE 10, который поддерживал их без префикса.)

Но это просто стиль, и это легко. Когда вы попытаетесь использовать этот класс, вы попадете в ловушку. Ловушка в том, что такой код не будет работать так, как вы могли бы наивно ожидать:

// Don't do things this way! It doesn't work!
someElement.classList.add('notransition')
someElement.style.height = '50px' // just an example; could be any CSS change
someElement.classList.remove('notransition')

Наивно, вы можете подумать, что изменение высоты не будет анимированным, потому что это происходит, когда применяется класс notransition. В действительности, однако, это будет анимированным, по крайней мере, во всех современных браузерах, которые я пробовал. Проблема заключается в том, что браузер кэширует изменения стиля, которые необходимо внести до завершения выполнения JavaScript, а затем вносит все изменения в одном перекомпоновании. В результате он выполняет перекомпоновку, где нет чистого изменения того, разрешены ли переходы, но есть чистое изменение высоты. Следовательно, он оживляет изменение высоты.

Вы могли бы подумать, что разумным и чистым способом обойти это было бы завершение удаления класса «notransition» за 1 мс, например:

// Don't do things this way! It STILL doesn't work!
someElement.classList.add('notransition')
someElement.style.height = '50px' // just an example; could be any CSS change
setTimeout(function () {someElement.classList.remove('notransition')}, 1);

но это также не работает надежно. Я не смог сделать приведенный выше разрыв кода в браузерах WebKit, но в Firefox (как на медленных, так и на быстрых машинах) вы иногда (казалось бы, наугад) будете вести себя так же, как и при использовании наивного подхода. Я предполагаю, что причина этого заключается в том, что выполнение JavaScript может быть достаточно медленным, чтобы функция тайм-аута ожидала выполнения к тому моменту, когда браузер бездействует, и в противном случае могла бы подумать о выполнении оппортунистического перекомпоновки, и если этот сценарий произойдет, Firefox выполняет функцию из очереди до перекомпоновки.

Единственное решение, которое я нашел для этой проблемы, состоит в том, чтобы принудительно перекомпоновать элемент, сбрасывая изменения CSS, внесенные в него, перед удалением класса notransition. Существуют различные способы сделать это - посмотрите здесь для некоторых. Наиболее близким к «стандартному» способу сделать это является чтение offsetHeightсвойства элемента.

Одно из решений, которое действительно работает,

someElement.classList.add('notransition'); // Disable transitions
doWhateverCssChangesYouWant(someElement);
someElement.offsetHeight; // Trigger a reflow, flushing the CSS changes
someElement.classList.remove('notransition'); // Re-enable transitions

Вот сценарий JS, который иллюстрирует три возможных подхода, которые я описал здесь (как один успешный, так и два неудачных): http://jsfiddle.net/2uVAA/131/

Марк Эмери
источник
8
Превосходный ответ. Хорошая работа, и ценится, так как я придумал ту же проблему. Что если я хочу удалить только один тип перехода?
rafaelmorais
2
Ваше решение правильное, но для тех, кто ищет дополнительную справочную информацию: stackoverflow.com/a/31862081/1026
Николай
2
Незначительно незначительное в стороне, IE10 была первой стабильной версией IE, которая поставлялась с нефиксированными переходами. В предварительных версиях, за исключением Release Preview, требовался префикс -ms- для переходов, а также множество других вещей. Я подозреваю, что это одна из причин появления префикса -ms- сегодня. Другой, гораздо более вероятной причиной, конечно же, является обычный культ груза, который мы все знаем и любим в отношении префиксов поставщиков.
BoltClock
1
Интересно, что простая проверка высоты смещения элемента запускает перекомпоновку. Жаль, что я знал это год назад, когда я бился головой об стену с этой же проблемой. Это все еще считается самым идеальным способом справиться с этим?
Brett84c
2
Трюк с перекомпоновкой не работает при анимации SVG-элементов, которые не имеют атрибута offsetHeight; setTimout работает.
piotr_cz
19

Я бы выступил за отключение анимации в соответствии с предложением DaneSoul, но сделав переключение глобальным:

/*kill the transitions on any descendant elements of .notransition*/
.notransition * { 
  -webkit-transition: none !important; 
  -moz-transition: none !important; 
  -o-transition: none !important; 
  -ms-transition: none !important; 
  transition: none !important; 
} 

.notransitionзатем может быть применен к bodyэлементу, эффективно перекрывая любую анимацию перехода на странице:

$('body').toggleClass('notransition');
OV
источник
2
На самом деле это идеальный вариант ИМО. Особенно * очень помогает, убедившись, что ни одна из вспомогательных анимаций не запущена. Спасибо, проголосовал.
SchizoDuckie
Это правильный ответ для ситуаций, когда вы просто хотите сделать все сразу. Например, я хочу отключить переходы во время вращения на мобильном телефоне, и это идеально подходит для этого.
Бен Лахман
Я закончил тем, что использовал это в angularjs, в несколько сложном сценарии, но, черт возьми, это было так полезно после нескольких дней стука моей головы.
Адитья депутат
2
В плане производительности я не могу ожидать, что это будет хорошей идеей. «О, просто примените это ко ВСЕМУ на странице, это делает вещи проще!»
Lodewijk
1
Вероятно, следует выбрать «.notransition» и «.notransition *» для полной эффективности.
Натан
19

Добавьте дополнительный класс CSS, который блокирует переход, а затем удалите его, чтобы вернуться в предыдущее состояние. Это делает код CSS и JQuery коротким, простым и понятным.

CSS :

.notransition {
  -webkit-transition: none !important;
  -moz-transition: none !important;
  -o-transition: none !important;
  -ms-transition: none !important;
  transition: none !important;
}

!important было добавлено, чтобы быть уверенным, что это правило будет иметь больший «вес», потому что идентификатор обычно более специфичен, чем класс.

JQuery :

$('#elem').addClass('notransition'); // to remove transition
$('#elem').removeClass('notransition'); // to return to previouse transition
DaneSoul
источник
5
-1, потому что этого было недостаточно, чтобы решить эту проблему для меня в Chrome или Firefox - если у меня есть Javascript, который добавляет класс, вносит изменения и удаляет класс, иногда изменения по- прежнему анимируются. Я провел последние час или два, изучая эту проблему более подробно, и позже опубликую ответ с скриптами, показывающими случаи, когда наивный подход терпит неудачу, плюс аккуратный хак, который исправляет решение здесь, заставляя перекомпоновку между внесением изменений и удалением класс «notransition».
Марк Эмери
9

Для чистого решения JS (без CSS-классов) просто установите transitionв 'none'. Чтобы восстановить переход, указанный в CSS, установите transitionпустую строку.

// Remove the transition
elem.style.transition = 'none';

// Restore the transition
elem.style.transition = '';

Если вы используете префиксы вендоров, вам тоже нужно их установить.

elem.style.webkitTransition = 'none'
Томас Хиггинботам
источник
8

Вы можете отключить анимацию, переход, trasforms для всех элементов на странице с помощью этого кода CSS

var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '* {' +
'/*CSS transitions*/' +
' -o-transition-property: none !important;' +
' -moz-transition-property: none !important;' +
' -ms-transition-property: none !important;' +
' -webkit-transition-property: none !important;' +
'  transition-property: none !important;' +
'/*CSS transforms*/' +
'  -o-transform: none !important;' +
' -moz-transform: none !important;' +
'   -ms-transform: none !important;' +
'  -webkit-transform: none !important;' +
'   transform: none !important;' +
'  /*CSS animations*/' +
'   -webkit-animation: none !important;' +
'   -moz-animation: none !important;' +
'   -o-animation: none !important;' +
'   -ms-animation: none !important;' +
'   animation: none !important;}';
   document.getElementsByTagName('head')[0].appendChild(style);
Али
источник
1
Во-первых, это здорово, спасибо! Во-вторых, есть также некоторые другие свойства, которые должны быть установлены; см. github.com/japgolly/test-state/blob/master/util/shared/src/test/…
Golly
@Golly Эти другие свойства могут повлиять на окончательный дизайн
jonperl
0

Я думаю, что вы могли бы создать отдельный класс CSS, который вы можете использовать в этих случаях:

.disable-transition {
  -webkit-transition: none;
  -moz-transition: none;
  -o-transition: color 0 ease-in;
  -ms-transition: none;
  transition: none;
}

Тогда в jQuery вы бы переключили класс следующим образом:

$('#<your-element>').addClass('disable-transition');
Cyclonecode
источник
да, я думаю, что это лучший подход, на самом деле плагин может добавить этот блок CSS на страницу
Сэм Саффрон
3
Вы уверены, что без! Важной линейки CLASS переопределит ID один?
DaneSoul
0

Если вам нужно простое решение без jquery для предотвращения всех переходов:

  1. Добавьте этот CSS:
body.no-transition * {
  transition: none !important;
}
  1. И тогда в вашем JS:
document.body.classList.add("no-transition");

// do your work, and then either immediately remove the class:

document.body.classList.remove("no-transition");

// or, if browser rendering takes longer and you need to wait until a paint or two:

setTimeout(() => document.body.classList.remove("no-transition"), 1);

// (try changing 1 to a larger value if the transition is still applying)
мили относительного движения
источник
-1

Если вы хотите удалить CSS-переходы, преобразования и анимации с текущей веб-страницы, вы можете просто выполнить этот небольшой скрипт, который я написал (в консоли браузера):

let filePath = "https://dl.dropboxusercontent.com/s/ep1nzckmvgjq7jr/remove_transitions_from_page.css";
let html = `<link rel="stylesheet" type="text/css" href="${filePath}">`;
document.querySelector("html > head").insertAdjacentHTML("beforeend", html);

Он использует vanillaJS для загрузки этого css-файла . Здесь также есть репозиторий github на случай, если вы захотите использовать его в контексте скребка (Ruby-Selenium): remove-CSS-animations-repo

Томас
источник
-3

делает

$('#elem').css('-webkit-transition','none !important'); 

в твоих js это убить?

очевидно, повторить для каждого.

Крис
источник
1
затем вам нужно сбросить его после факта, поэтому вам нужно хранить его, что приведет к достаточному количеству котельной плиты
Сэм Саффрон
-4

Я хотел бы иметь класс в вашем CSS, как это:

.no-transition { 
  -webkit-transition: none;
  -moz-transition: none;
  -o-transition: none;
  -ms-transition: none;
  transition: none;
}

а затем в вашем JQuery:

$('#elem').addClass('no-transition'); //will disable it
$('#elem').removeClass('no-transition'); //will enable it
Моин Заман
источник
1
Вы уверены, что без! Важной линейки CLASS переопределит ID один?
DaneSoul
1
Да, так и будет, потому что теперь вы нацеливаетесь на # elem.no-transition, который является более конкретным, чем просто идентификатор
Мойн Заман
3
@MoinZaman Эээ, но вы не настроили таргетинг #elem.no-transitionна свой CSS, вы просто нацелились .no-transition. Возможно, вы хотели написать #elem.no-transitionв своем CSS?
Марк Амери