CSS Transitions applied even if activated after state change?

五迷三道 提交于 2019-12-24 11:34:33

问题


I have a jQuery plugin that moves an element on the screen, but has an "animate" toggle to show a slide transition or not. In attempting to use CSS Transitions rather than Javascript transitions for the change, I ran across this, and I'm not sure if it's a bug/quirk, or I'm just doing it wrong:

var $item = $('#myItem');
if (!animate) {
  $item.removeClass('csstransitions'); // Class that has "transition:left 0.25s ease-out" 
  $('#myItem').css('left', '300px'); // Move the element
  $('#myItem').addClass('csstransitions'); // Re-apply transitions class
}

When done this way, where the css change happens while the transitions class is not applied to the element, but is applied immediately after, some browsers (Chrome and Safari, in my testing) still apply the CSS transition, when by my logic, it should just snap to the new location.

See this in action in this jsFiddle; In Chrome or Safari, click the "No Delay" button, and see that it does still animate the position of the box, while the "Delay" button (which uses a timeout set for one millisecond later) doesn't animate the CSS change.

As indicated in the jsFiddle, I'm having to use a setTimeout call (setTimeout(function() { $el.addClass('csstransition'); }, 1);) to get the proper behavior in Chrome and Safari. Is this just because CSS transitions are bleeding edge, or am I doing something wrong, and there's a simpler way to temporarily "turn of" the transitions?

EDIT: Noticed this question is similar, and while the answer on that one is to just separate the two calls, the question remains of "why do we (web developers) need to separate those two calls?" Is this the method we should be using to toggle CSS transitions?


回答1:


I would vote for quirk, or implementation difference.

Before transitions, it really didn't matter which order styles were applied, because in practicality, order didn't matter, just specificity. But with transitions, an element of time delay was added into styles, which is the crux of the issue.

Not knowing how any of the browsers apply styles, I could guess that Safari and Chrome do some optimizations to not have to re-flow the page after every style update. Instead, they probably wait for particular intervals or events to do the updates, such as at the end of code blocks.

Some of the differences are detailed here:
http://taligarsiel.com/Projects/howbrowserswork1.htm

Although, I don't know if this specific issue is covered.

As demonstration, another way you could handle this is to have 2 click handlers:

$('button#nodelay').on('click', function() {
    var $el = $('#square');
    $el.removeClass('csstransition');
    $el.css('left', '100px');
}).on('click', function() {
    $el.addClass('csstransition');
});

This basically divides the two updates into separate code blocks, much like the setTimeout method.

Also, as transitions are still draft standard, I wouldn't depend on any of this behavior to stay consistent, and there are quirks. (I ran into an issue where transitioning left and top at the same time didn't work in all browsers).

Edit

To further explain, if the browser renders all CSS as soon as it is added to the DOM you get this flow:

  1. CSS left: 300px added to DOM element
  2. Style rendered: Browser checks to see if there is a transition on the element. If so, animate, if not, apply immediately. In this case, there is not a transition (yet), so no animation occurs.
  3. CSS transition: left 2s ease-out added to DOM element
  4. Style rendered: No rendering change, transition applies to future left changes.

However, if the browser optimizes the rendering of CSS, by grouping at the end of code blocks (or something of the sort), you get this:

  1. CSS left: 300px added to DOM element
  2. CSS transition: left 2s ease-out added to DOM element
  3. Rendering point reached (end of code block, etc), all styles rendered:
  4. When left is applied, the browser checks to see if there is a transition on the element. If so, animate, if not, apply immediately. In this case, there is a transition, so the animation occurs.

So, the length of the animation, and the time you wait is irrelevant. What is important is that the left: 300px gets rendered before the transition is applied. Currently, in WebKit, this means applying the transition style in a separate, later code block than the left style. This is accomplished by all of the answers suggested setTimeout (event with 0 delay), separate click handler (if applied second), or a function callback.

Here is another way that works:

$('button#nodelay').on('click', function() {
    var $el = $('#square');
    $el.removeClass('csstransition');
    $el.css('left', '100px').css('left'); // <-- This (or .show() even)
    $el.addClass('csstransition');
});

This works, because you are forcing the browser to stop and evaluate the CSS so that you can get the value of left (although you could put any valid css attribute in the second .css() call). In this case, it applies all of the CSS and forces the element(s) to re-render.



来源:https://stackoverflow.com/questions/9774329/css-transitions-applied-even-if-activated-after-state-change

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!