本篇文章主要讲述CSS3 transition规范和在不同浏览器之间的使用差异,关于具体解决方法或如何规避问题的意见可以参考另一篇非常有见地的文章,“All You Need to Know About CSS Transitions”。Alex MacCaw讲述的是关于实现特定的效果,而我要谈的是技术背景,主要讨论在使用CSS过渡的过程中所未预料到的问题。
结构 (HTML),表现(CSS),以及行为(JavaScript)相分离并不是什么新鲜的事情,然而 CSS 能跨越这个界限并且可以在短期内得到实际的应用,这还真的是一个完全不同的讨论话题。
几周前,我开发一个 JavaScript 模块,在能够使用 CSS 过渡的条件下,JavaScript 端又无法获取到实现过渡的方式。实际遇到的问题是这两者根本没有办法同步,经过多次的测试后,我只能放弃。而我的测试结果正是本文所讲述的。
首先,我们要说一下getcomputedstyle(),是一种用 JavaScript 返回浏览器渲染CSS的属性值的方法。 这个方法可以查看“DOM Level 2: getComputedStyle()”和“CSS Level 2: Computed Values”。
这对于像 font-size 这样的属性, 通过一个参数便可以转换为像素值。 但对于可以缩写的属性值,例如 margin ,一些浏览器则返回为空。再就是那些同一属性的不同属性值,例如 font-weight 的值 bold 和700。WebKit也有一个小bug,它会从伪对象中提取出属性值。
这里所讲述的浏览器之间的差异是2013年1月在使用 Firefox18(Gecko),Opera 12.12 (Presto), Internet Explorer10(Trident),Safari 浏览器6.0.2(WebKit),Chrome 23(WebKit) 以及 Gecko 和 WebKit的 Nightly build channels。
事不宜迟,让我们来一起看一下规范与实际情况的差异,为了方便,我省略了各浏览器的前缀。在文中我通过创建一个 CSS3 Transitions Test Suite 来发现问题。
1、指定过渡
CSS3 transitions 规范定义了以下四个 CSS 属性:
transition-propertytransition-durationtransition-delaytransition-timing-function
过渡属性
transition-property 是用来指定当元素其中一个属性改变时执行 transition 效果。系统默认值是 all,这意味着浏览器能够以动画形式呈现所有的可过渡属性(transition-duration持续时间超过0s),该属性支持单个值或以逗号隔开的多个值列表(跟其他所有transition-*属性一样)。
规范规定,一个浏览器应该接受并保存任何它不能识别的属性。因此,下面的例子中将会看到持续2秒的 padding 过渡:<font face="inherit">transition-property:foobar,padding;
transition-duration:1s,2s;</font>复制代码不同于规范的是,上面的情况在 WebKit 下会解析为 transition-property: all。 而 Firefox 和 Opera 会解析为 transition-property: all, padding.
过渡持续时间
transition-duration 属性规定了一个过渡从初始状态到目标状态的持续时间。它接受以秒或毫秒的值(例如,2.3S和2300ms都是指2.3秒)。
尽管规范明确规定了过渡值必须为正数,但 Opera 仍接受-5S的值,至少对于getComputedStyle()来说是这样的。虽然规范中并没有限制属性值的大小,但 Opera 和 IE 不接受低于10ms的值。而 WebKit 在 getComputedStyle()执行中有个小bug,例如:返回值0.009999999776482582s会取代0.01s。
过渡延迟时间
transition-delay 属性规定了在执行一个过渡之前的等待时间,同样使用值。Delay 可以是负值,但这会导致动画无法平滑过渡。
IE 和 Opera 不接受 transition-duration 在-10ms和10ms之间的值。WebKit 的 floating point 也会在这儿出现。
transition-timing-function 属性规定了过渡效果的时间曲线。包括cubic-bezier(x1, y1, x2, y2), step(, start|end),和预先定义的 cubic-bezier 曲线关键词,linear, ease, ease-in, ease-out和ease-in-out。在使用 LEA Verou 特有的 cubic-bezier 曲线编辑器时,cubic-bezier 背后的公式就变得不再重要。尽管 cubic-bezier 曲线会平滑过渡,但是step()函数会在一个固定的间隔跳到下一个值。这样便会产生逐帧动画的效果;如“Pure CSS3 Typing Animation With steps()”。
linear 的计算值通常表示为 cubic-bezier(0, 0, 1, 1)—— WebKit除外。但 WebKit 仍然会返回 cubic-bezier(0.25, 0.1, 0.25, 1),而不是 ease。规范规定 X 值的必须介于0和1之间,y 值可以超过该范围,而WebKit 允许 X 超过此范围,而 Android 浏览器(4.0版本)却混淆了x和y的范围。
2 过渡完成
我前面已经提到了 CSS 过渡异步运行的问题。规范提及了 TransitionEnd 事件允许 JavaScript 与已完成的过渡同步进行。但可恶的是该规范对此并没具体阐述。事实上,它只是简单地说明单个事件会因为已完成过渡的属性而被终止。
规范指出缩写属性(如padding)应为包括其在内的所有属性(padding-top,padding-right,等等)实现过渡,它并没有说哪个属性应该在 TransitionEnd 事件中被具体命名。然而即使过渡被定义为缩写属性(如padding),Gecko,Trident 和 Presto 对于普通书写的子属性(如padding-top)同样可以实现过渡,而 WebKit 则会阻止过渡。 如果你指定 transition-property: padding,WebKit 会为 padding 执行过渡, 但 transition-property: all 这样就会针对 padding-left 执行新的过渡。而当 padding 正执行过渡时, iPhone 6.0.1 的 Safari 浏览器在也可以执行 font-size 和 line-height的过渡。<font face="inherit">.example{padding:1px;transition-property:padding;transition-duration:1s;}
.example:hover{padding:10px;}</font>复制代码以上 CSS 将在不同浏览器下触发不同的 TransitionEnd:
Gecko,Trident,Presto:
padding-top,padding-right,padding-bottom,padding-left
WebKit:
padding<font face="inherit">.example {padding: 1px;transition-property: all, padding;transition-duration:1s;}
.example:hover{padding:10px;}</font>复制代码以上 CSS 将在不同浏览器下触发不同的TransitionEnd:
Gecko,Trident,Presto,WebKit:
padding-top,padding-right,padding-bottom,padding-left
Safari 6.0.1 on iPhone:
padding-top, padding-right, padding-bottom, padding-left, font-size, line-height
你可以指定负值 transition-delay 来“快速实现”转换。但是transition-duration: 1s; transition-delay: -1s; 在 Gecko 和 WebKit 下执行转换并会立即跳转至目标值。而Trident 和 Presto 将不会触发任何事件。
WebKit在 getComputedStyle() 上遇到的浮点问题也同样存在于 TransitionEnd.elapsedTime 中,所有的浏览器如此。 Math.round(event.elapsedTime * 1000) / 1000 可辅助修复。
WebKit 和 IE 浏览器下执行 background-position,会触发对 background-position-x 和 background-position-y 的 TransitionEnd,而不是 background-position 的TransitionEnd。
所以,即使你知道过渡正在执行,你也不能依赖已有的 TransitionEnd.propertyName。尽管你可以编写大量的 JavaScript 来弥补,但在没有对每一个属性进行恰当性能检测的情况下,即使你采用最新方法也将无法实现。
3 过渡属性
规范列出了浏览器支持动画过渡的一些CSS属性。当然也包括CSS2.1的属性。还有一些可以动态变化的新属性,如 Flexible Box Layout。
该属性数值类型非常重要。margin-top 接受和值,但根据可过渡CSS属性列表,只有是可实现动画效果。但这并不能让浏览器开发商避开值实现过渡。然而,word-spacing 属性除外。该属性包括值,但没有浏览器能以动画形式显示。
撇开 TransitionEnd 事件,如果在过渡发生的指定时间内,getComputedStyle()值从A变到B,该属性就会从值A过渡为值B。如果没有执行,例如“CSS属性值发生变化”,那么也许应该仔细核查下DOM。setTimeout()的解析度还不够好以达到快速过渡(小于几百毫秒的持续时间),这时候requestAnimationFrame()就是你的帮手。在重绘前会提醒你,并提供了一些中间值供参考。除了opera,其他的都可以支持。