当转换后的CSS属性被前后更改时,尤其是当下一个属性更改在上一个转换完成之前开始时,结果是整体效果的外观和感觉非常不稳定(因为下一个转换停止了上一个,开始得很慢)。在属性快速更改期间删除转换(或将其临时设置为0s)可能不够平滑,也可能不够平滑。我真正想要的是一种从一个过渡到另一个过渡的良好方式,尤其是在一个过渡还没有完全结束,另一个开始的情况下。
用例:当属性通过JavaScript快速更改时
假设某个元素正在通过鼠标或触摸进行拖动。浏览器在方便的时候触发这些鼠标/触摸事件,而且浏览器并不总是及时触发这些事件。通常情况下,它们确实可以顺利发射,但有时两个事件发射之间可能会有明显的延迟。生成的效果可以是急动的,也可以是起伏的,这取决于元素上是否设置了过渡(请参见困境)。理想情况下,过渡应该能够平滑整体效果,如果只有CSS属性告诉浏览器以与任何当前动画过渡相同的速度继续后续过渡,而不是在开始下一个过渡之前停止当前过渡。不过,我还没有遇到这样的房产。
具体示例
举个例子,一个元素在屏幕上移动(为了简单起见)。假设有一个按钮触发元素在屏幕上指定的开始和结束位置之间移动,其"动画"通过transition: 1s
处理,然后添加一个类,通过transform属性更改元素的位置。瞧,元素从开始到结束都是"动画化的"。现在假设您希望用户也能够在元素的起始位置和结束位置之间拖动或滑动元素。当然,这将通过一些JavaScript来处理(目前,可能直到HTML6——因为HTML5draggable
属性无论如何都不适用于大多数触摸屏/移动设备)。
因此,您应该为鼠标/触摸事件设置事件侦听器,并通过mousedown
/mousemove
或touchstart
/touchmove
处理可移动元素的转换,为每次移动设置相关的转换值。然后,当他们松开鼠标或触摸(mouseup
或touchend
)时,您可以通过删除mousemove
或touchmove
事件应用的动态样式,并添加一个类来完成将元素移动到其结束位置的操作(如果需要,也可以将其移回其开始位置),如果满足某些条件,则可以添加该类来将元素移动至其结束位置。
困境
如果在拖动元素时没有动态地将转换设置为0s,那么每次更改转换的属性时,如果上一个转换仍在进行,它就会停止在其轨道上,而新的转换则从停止点开始,从而产生剧烈的抖动效果。
如果在拖动元素时动态地将转换设置为0s,那么当两次事件触发之间存在显著延迟,并且结果是位置发生显著变化时,元素只是立即跳到新位置,而不是平滑地转换到新位置。
解决方案尝试
我已经考虑了几种可能的解决方案,但它们并不完全理想。
第一种是观察事件(或其报告的坐标)之间的显著延迟,并通过将动态设置的转换属性从0更改为将转换动画化的属性来做出相应的反应,但这可能会被下一个事件触发几乎立即取消,该事件会将动态转换属性更改回0,或者更改元素的位置,其中动态转换仍从上一个转换设置,导致该转换停止,下一个从当前位置开始。它还为JavaScript代码增加了额外的开销,如果有更好的方法,我不喜欢。
另一种是将transition-timing-function: cubic-bezier(0,1,0,1)
用于后续转换,这会导致后续转换开始得更快,但结束得也更快,这并不一定是可取的。此外,这种方法仍然有一点奇怪的感觉,因为当转换的属性变化非常快时,会出现明显的停顿。至少这是我在Chrome的经验。当转换属性的更改速度不那么快时(例如,在屏幕上拖动元素的速度较慢),它看起来更平滑。
还有一个问题,发布的唯一解决方案也未能使整体效果顺利。该解决方案具有与我上面提到的相同的断断续续的缺陷,即对以更高持续时间1s转换的属性进行快速更改。
如果有一个转换的CSS属性,告诉浏览器以相同的速度继续到下一个转换,而不停止其中一个,也不从静止状态开始另一个,那么会简单得多。然而,我还没有找到这样的房产。
如果还不存在这样的性质,有没有更好的方法来消除过渡性质的快速火灾变化?
这里有一些非常简单的演示代码,不幸的是,它太简单了,无法展示我所说的滞后事件触发,但至少这是一个起点。如果我没有得到像样的回应,我会尝试制作一个更高级的例子,利用更多的计算能力,使事件触发滞后更加明显。
CSS转换在从JavaScript调用时非常痛苦,尤其是在需要立即启动的情况下。如果在开始动画之前不必为转换CSS属性设置新值,那么它们的工作效果很好,但当您这样做时,最终会出现同步和时间问题。就我个人而言,我必须先设置转换值,然后在setTimeout()回调中设置要转换的实际CSS,以确保转换值首先生效,这会增加一些延迟和跳过
浏览器供应商正在寻找更好的方法:http://updates.html5rocks.com/2014/05/Web-Animations---element-animate-is-now-in-Chrome-36.但目前只有Chrome支持它
同时,如果您需要启动一个新的动画来响应非常动态的用户输入,我建议使用requestAnimationFrame
。它的性能远远好于旧的JavaScript动画方法(包括jQuery),因为它与浏览器的实际刷新周期同步。在上面的例子中,您可以为初始动画使用转换,但当用户用自己的滑动动作打断它时,您可以终止它,并使用requestAnimationFrame切换到JavaScript解决方案。
(对它的无前缀支持始于IE10、FF23、Ch6和Safari 6.1。对于较旧的浏览器,有一个垫片将恢复为跳跃的setTimeouts,但移动在其他情况下会起作用。)
简而言之:您似乎希望能够使用javascript快速更改转换,但对实现的结果不满意?
这是一个经典的例子:
- 过于出血
- 错误任务的错误解决方案
无论哪种方式,您的解决方案都从其他地方开始,并且可能涉及到不以您现在的方式使用转换。
听起来你可能需要看看这个链接:http://www.elated.com/articles/drag-and-drop-with-jquery-your-essential-guide/
我相信您将不得不依靠jQuery来处理"节流"动画,以防止它们踩到对方的脚。
尝试这样的方法,jQuery将等待一个动画完成,然后再启动下一个动画。这是假设.slideMeLeft和.slideMeDown具有动画样式。一旦slideMeLeft的动画完成,就会添加slideMeDown类,该类将拥有自己的类。
$('#animatedElement').addClass('slideMeLeft');
$('#animatedElement').one(
'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend',
$('#animatedElement').addClass('slideMeDown');
);