Jaki jest najczystszy sposób na tymczasowe wyłączenie efektów przejścia CSS?

209

Mam element DOM z zastosowanymi niektórymi / wszystkimi następującymi efektami:

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

Piszę wtyczkę jQuery, która zmienia rozmiar tego elementu, muszę tymczasowo wyłączyć te efekty, aby móc płynnie zmieniać rozmiar.

Jaki jest najbardziej elegancki sposób tymczasowego wyłączenia tych efektów (a następnie ich ponownego włączenia), biorąc pod uwagę, że można je zastosować od rodziców lub w ogóle nie można zastosować.

Sam Saffron
źródło
1
Wygląda to obiecująco: ricostacruz.com/jquery.transit . Ma licencję MIT, więc możesz ją włączyć lub przestudiować, aby zobaczyć, jak to robi.
Robert Harvey
Wszystkie odpowiedzi dodać do .notransitionklasy. Rozsądniej byłoby zrobić to na odwrót, tj. Celować za #elem.yestransitionpomocą css i usunąć tę klasę. To usuwa nieprzyjemne *s w twoim css.
marcellothearcane

Odpowiedzi:

484

Krótka odpowiedź

Użyj tego CSS:

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

Plus ten JS (bez 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

Lub ten JS z jQuery ...

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

... lub równoważny kod przy użyciu dowolnej innej biblioteki lub frameworka, z którym pracujesz.

Wyjaśnienie

To właściwie dość subtelny problem.

Po pierwsze, prawdopodobnie chcesz utworzyć klasę „notransition”, którą możesz zastosować do elementów, aby ustawić ich *-transitionatrybuty CSS none. Na przykład:

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

(Drobny na bok - zauważ brak -ms-transitiontam. Nie potrzebujesz go. Pierwszą wersją Internet Explorera do obsługi przejść w ogóle była IE 10, która obsługiwała je bez prefiksu).

Ale to tylko styl i to jest łatwe. Kiedy spróbujesz skorzystać z tej klasy, wpadniesz w pułapkę. Pułapka polega na tym, że taki kod nie będzie działał w sposób, jakiego można naiwnie oczekiwać:

// 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')

Naiwnie możesz pomyśleć, że zmiana wysokości nie będzie animowana, ponieważ dzieje się tak, gdy stosowana jest klasa „notransition”. Jednak w rzeczywistości będzie animowany, przynajmniej we wszystkich nowoczesnych przeglądarkach, które wypróbowałem. Problem polega na tym, że przeglądarka buforuje zmiany stylu, które musi wprowadzić, aż JavaScript zakończy wykonywanie, a następnie dokonuje wszystkich zmian w jednym przepływie. W wyniku tego następuje ponowne wlanie, w którym nie ma zmiany siatki określającej, czy przejścia są włączone, ale występuje zmiana wysokości netto. W związku z tym animuje zmianę wysokości.

Można pomyśleć, że rozsądnym i czystym sposobem na obejście tego byłoby ominięcie usunięcia klasy „notransition” w czasie 1 ms, jak poniżej:

// 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);

ale to też nie działa niezawodnie. Nie udało mi się złamać powyższego kodu w przeglądarkach WebKit, ale w Firefoksie (zarówno na powolnych, jak i szybkich komputerach) czasami (pozornie przypadkowo) dostajesz takie samo zachowanie jak naiwne podejście. Wydaje mi się, że powodem jest to, że wykonanie JavaScript może być na tyle powolne, że funkcja limitu czasu czeka na wykonanie do czasu, gdy przeglądarka jest bezczynna, i w przeciwnym razie pomyślałaby o oportunistycznym przepełnieniu, a jeśli tak się stanie, Firefox wykonuje funkcję w kolejce przed ponownym zalaniem.

Jedynym rozwiązaniem, jakie znalazłem dla tego problemu, jest wymuszenie ponownego wlewania elementu, opróżnienie dokonanych w nim zmian CSS, przed usunięciem klasy „notransition”. Można to zrobić na wiele sposobów - zobacz tutaj . Najbliższą „standardową” metodą jest odczytanie offsetHeightwłaściwości elementu.

Jednym z faktycznie działających rozwiązań jest

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

Oto skrzypce JS, które ilustrują trzy możliwe podejścia, które tu opisałem (zarówno jedno udane, jak i dwa nieudane): http://jsfiddle.net/2uVAA/131/

Mark Amery
źródło
8
Odpowiedź Excelente. Dobra robota i doceniam, odkąd wpadłem na ten sam problem. Co jeśli chciałbym usunąć tylko jeden rodzaj przejścia?
rafaelmorais
2
Twoje rozwiązanie jest słuszne, ale dla tych, którzy szukają dodatkowych informacji: stackoverflow.com/a/31862081/1026
Nickolay
2
Moll moll na bok, IE10 była pierwsza stabilna wersja IE do statku z przejściami bez prefiksów. Wersje przedpremierowe, z wyjątkiem wersji zapoznawczej, wymagały przedrostków -ms- do przejścia, wraz z mnóstwem innych rzeczy. Podejrzewam, że jest to jeden z powodów, dla których dzisiaj pojawia się prefiks -ms-. Innym, o wiele bardziej prawdopodobnym, oczywiście powodem jest zwykły kult ładunku, który wszyscy znamy i uwielbiamy na temat prefiksów dostawców.
BoltClock
1
Interesujące jest to, że po prostu sprawdzenie odsunięcia wysokości elementu uruchamia ponowny przepływ. Chciałbym to wiedzieć rok temu, kiedy waliłem głową w ścianę z tym samym problemem. Czy nadal jest to uważane za najbardziej idealny sposób radzenia sobie z tym?
Brett84c,
2
Wydaje się, że sztuczka reflow nie działa podczas animacji elementów SVG, które nie mają atrybutu offsetHeight; setTimout działa.
piotr_cz
19

Zalecałbym wyłączenie animacji zgodnie z sugestią DaneSoul, ale uczynienie przełącznika globalnym:

/*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; 
} 

.notransitionmożna następnie zastosować do bodyelementu, skutecznie zastępując dowolną animację przejścia na stronie:

$('body').toggleClass('notransition');
ov
źródło
2
W rzeczywistości jest to idealna opcja imo. Zwłaszcza * bardzo pomaga, upewniając się, że żadna z animacji podrzędnych nie zostanie uruchomiona. Dziękuję, głosowałem.
SchizoDuckie,
To właściwa odpowiedź w sytuacjach, gdy chcesz zrobić wszystko naraz. Na przykład chcę wyłączyć przejścia podczas obracania na telefonie komórkowym i jest to idealne do tego.
Ben Lachman
Skończyło się na tym, że użyłem tego w angularjs, w nieco skomplikowanym scenariuszu, ale na Boga, było to bardzo pomocne po wielu dniach walenia głową.
Aditya, poseł
2
Pod względem wydajności nie mogę oczekiwać, że będzie to dobry pomysł. „Och, po prostu zastosuj to do WSZYSTKIEGO na stronie, co ułatwia rzeczy!”
Lodewijk
1
Powinny prawdopodobnie wybrać zarówno „.notransition”, jak i „.notransition *”, aby były w pełni skuteczne.
Nathan
19

Dodaj dodatkową klasę CSS, która blokuje przejście, a następnie usuń ją, aby powrócić do poprzedniego stanu. Dzięki temu zarówno kod CSS, jak i kod JQuery są krótkie, proste i zrozumiałe.

CSS :

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

!important dodano, aby mieć pewność, że ta reguła będzie miała większą „wagę”, ponieważ ID jest zwykle bardziej szczegółowe niż klasa.

JQuery :

$('#elem').addClass('notransition'); // to remove transition
$('#elem').removeClass('notransition'); // to return to previouse transition
DaneSoul
źródło
5
-1, ponieważ nie wystarczało to dla mnie do rozwiązania problemu w Chrome lub Firefox - jeśli mam JavaScript, który dodaje klasę, wprowadza zmiany i usuwa klasę, czasem zmiany nadal są animowane. Spędziłem ostatnią godzinę lub dwie szczegółowo badając ten problem i opublikuję odpowiedź później ze skrzypcami pokazującymi przypadki, w których naiwne podejście zawodzi, oraz schludny hack, który naprawia rozwiązanie tutaj, wymuszając przepływ między wprowadzaniem zmian a usuwaniem klasa „notransition”.
Mark Amery
9

Dla czystego roztworu JS (bez klas CSS), po prostu ustawić transitionsię 'none'. Aby przywrócić przejście określone w CSS, ustaw transitionpusty ciąg.

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

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

Jeśli korzystasz z prefiksów dostawców, musisz je również ustawić.

elem.style.webkitTransition = 'none'
Thomas Higginbotham
źródło
8

Za pomocą tego kodu css możesz wyłączyć animację, przejścia, przekształcanie wszystkich elementów na stronie

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);
Ali
źródło
1
Po pierwsze, to jest niesamowite, dziękuję! Po drugie, należy ustawić także inne właściwości; patrz github.com/japgolly/test-state/blob/master/util/shared/src/test/…
Golly
@Golly Te inne właściwości mogą mieć wpływ na ostateczny projekt
jonperl
0

Myślę, że możesz utworzyć osobną klasę css, której możesz użyć w następujących przypadkach:

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

Następnie w jQuery przełączałbyś klasę tak:

$('#<your-element>').addClass('disable-transition');
Kod cyklonowy
źródło
tak, myślę, że to najlepsze podejście, w rzeczywistości wtyczka może wstrzyknąć ten blok css na stronę
Sam Saffron
3
Czy na pewno nie! Ważna linijka KLASY zastąpi ID 1?
DaneSoul
0

Jeśli potrzebujesz prostego rozwiązania eliminującego jquery, aby zapobiec wszystkim przejściom:

  1. Dodaj ten CSS:
body.no-transition * {
  transition: none !important;
}
  1. A potem w twoim 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)
mrm
źródło
-1

Jeśli chcesz usunąć przejścia, transformacje i animacje CSS z bieżącej strony, możesz po prostu wykonać ten mały skrypt, który napisałem (w konsoli przeglądarki):

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);

Do załadowania tego pliku css używa vanillaJS . Oto także repozytorium github na wypadek, gdybyś chciał użyć go w kontekście skrobaka (Ruby-Selenium): remove-CSS-animations-repo

Tomasz
źródło
-3

robi

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

w twoich js zabij to?

oczywiście powtórz dla każdego.

Chris
źródło
1
potem musisz go zresetować po fakcie, więc musisz go przechowywać, co doprowadziłoby do sporej ilości płyty kotła
Sam Saffron
-4

Miałbym klasę w twoim CSS w ten sposób:

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

a następnie w jQuery:

$('#elem').addClass('no-transition'); //will disable it
$('#elem').removeClass('no-transition'); //will enable it
Moin Zaman
źródło
1
Czy na pewno nie! Ważna linijka KLASY zastąpi ID 1?
DaneSoul
1
Tak, ponieważ teraz celujesz w # elem.no-przejście, które jest bardziej szczegółowe niż tylko identyfikator
Moin Zaman
3
@MoinZaman Erm, ale nie zostały skierowane #elem.no-transitionw CSS, właśnie kierowane .no-transition. Być może chciałeś napisać #elem.no-transitionw swoim CSS?
Mark Amery