原文转自:http://blog.jobbole.com/56243/css
本篇文章主要讲述CSS3 transition规范和在不一样浏览器之间的使用差别,关于具体解决方法或如何规避问题的意见能够参考另外一篇很是有见地的文章,“All You Need to Know About CSS Transitions”。Alex MacCaw讲述的是关于实现特定的效果,而我要谈的是技术背景,主要讨论在使用CSS过渡的过程当中所未预料到的问题。html
结构 (HTML),表现(CSS),以及行为(JavaScript)相分离并非什么新鲜的事情,然而 CSS 能跨越这个界限而且能够在短时间内获得实际的应用,这还真的是一个彻底不一样的讨论话题。jquery
几周前,我开发一个 JavaScript 模块,在可以使用 CSS 过渡的条件下,JavaScript 端又没法获取到实现过渡的方式。实际遇到的问题是这二者根本没有办法同步,通过屡次的测试后,我只能放弃。而个人测试结果正是本文所讲述的。css3
首先,咱们要说一下getcomputedstyle(),是一种用 JavaScript 返回浏览器渲染CSS的属性值的方法。 这个方法能够查看“DOM Level 2: getComputedStyle()”和“CSS Level 2: Computed Values”。算法
这对于像 font-size 这样的属性, 经过一个参数即可以转换为像素值。 但对于能够缩写的属性值,例如 margin ,一些浏览器则返回为空。再就是那些同一属性的不一样属性值,例如 font-weight 的值 bold 和700。WebKit也有一个小bug,它会从伪对象中提取出属性值。api
这里所讲述的浏览器之间的差别是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 来发现问题。异步
一、指定过渡
CSS3 transitions 规范定义了如下四个 CSS 属性:编辑器
过渡属性
transition-property 是用来指定当元素其中一个属性改变时执行 transition 效果。系统默认值是 all,这意味着浏览器可以以动画形式呈现全部的可过渡属性(transition-duration持续时间超过0s),该属性支持单个值或以逗号隔开的多个值列表(跟其余全部transition-*属性同样)。ide
规范规定,一个浏览器应该接受并保存任何它不能识别的属性。所以,下面的例子中将会看到持续2秒的 padding 过渡:
1
2
|
transition-property:foobar,padding;
transition-duration:
1
s,
2
s;
|
不一样于规范的是,上面的状况在 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的过渡。
1
2
|
.example{
padding
:
1px
;transition-property:padding;transition-duration:
1
s;}
.example:hover{
padding
:
10px
;}
|
以上 CSS 将在不一样浏览器下触发不一样的 TransitionEnd:
Gecko,Trident,Presto:
padding-top,padding-right,padding-bottom,padding-left
WebKit:
padding
1
2
|
.example {
padding
:
1px
;transition-property:
all
, padding;transition-duration:
1
s;}
.example:hover{
padding
:
10px
;}
|
以上 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,其余的均可以支持。
4 过渡属性的优先级
transition-property 规范容许屡次过渡单个属性,若是单个属性在“过渡属性”中的值被屡次指定,过渡将经过持续时间,延迟和时间曲线给出的值来实现。所以,咱们能够实现 padding 过渡持续1秒,padding-left 过渡持续2秒; 或使用 transition-property: all 来定义默认属性类型并重置特定属性。
在 Firefox 和 IE 浏览器上,这些都没有任何问题。 但 opera下会混淆优先顺序。它认为 padding-left 比padding 和 all 更加具体,而不是简单地使用最后一个属性。
最大的问题是WebKit。若是一个属性被屡次指定,它将进行屡次过渡。 若是想让WebKit崩溃,尝试用transition-duration :0.1秒运行transition-property: padding, padding-left,WebKit将至少执行两次过渡。但更有意思的是,TransitionEnd能够进行上百次的单一过渡。
5 auto的转变
CSS 属性中的 auto 值可以自适应宽度,若是块级元素设置了width: auto,那么就会继承父级的宽度。有时你须要从 width: auto 改变到一个具体宽度,而且须要过渡那个改变。固然本规范并无强制或否认 auto 值可用于过渡。
Firefox,IE 和 Opera 没法从 or 值过渡到 auto 值。 IE 下有 z-index 有一点点例外,但仅此而已。 另外一方面,WebKit 可以从and 过渡到几乎能够接受 auto 值的任意CSS 属性。WebKit 不太喜欢 clip;由于这个属性,它只会引起 TransitionEnd 过渡,而过渡期间不会产生或显示任何中间值或状态。
对于其余属性,如 width 和 height,WebKit 下会有一些差别。若是 width: auto 过渡为 300px 的宽度,而后再过渡成 100px,那么过渡不会从 300 缩至100 像素。它会从 0 增长到 100 像素。
关于完整的兼容性列表,能够查看“CSS Animatable Properties.”
6 隐式过渡
隐式过渡发生在当一个属性的改变引发另外一个属性被过渡的时候, 或者当你想改变一个父级元素中的属性, 会致使子元素不管是继承过渡或附属属性的过渡。font-size: 18px, padding: 2em—–padding 会被计算为 2 × font-size, em 就是36像素。
有各类各样的相对值类型:, , em, rem, vh, vw等等。使用一个相对值,如 padding: 2em,让浏览器从新计算属性的 getComputedValue(),每次应变量(如font-size)都会发生改变。因为计算样式改变,将反过来致使 padding 的过渡。这种过渡被定义为“隐式过渡”,由于padding属性值没有被修改。
大多数浏览器会实现这种隐式过渡。除了 IE 10,只对 line-height 属性执行隐式过渡。除了 vertical-align 外,Webkit 能够针对其余全部属性执行隐式过渡。除了字体相对属性值,还有宽度相对属性值(一般为),相对属性值(如vh和vw),默认初始值(Opera中的column-gap: 1em),还有“currentColor”。全部这些都有可能会,也可能不会引发隐式过渡。
在 Firefox 中, 当继承和附属属性执行过渡,但他们的 transition-duration 或 transition-delay 并无随着过渡, 这些隐式过渡就会变得特别有趣。 而 Webkit 和 Opera 执行过渡时会颇有视觉感,但 Firefox 会稍显错乱。在IE中不会轻易执行隐式过渡。
另外,别忘了继承, DOM 元素的 font-size 将会由其子元素继承,只要不被覆盖,就可能引发隐式过渡。
7 转换和伪元素
伪元素(:before和:after),在 CSS2 中已经有了介绍. 若是不熟悉能够查看 “Learning to Use the :before and :after Pseudo-Elements in CSS”。 虽然 CSS3 中定义了额外的伪元素(::alternate,::outside),可是他们(到目前为止)还并无被支持。所以全部 CSS 动画属性也应该是伪元素的动画属性。
Firefox 和 IE 10 能够在伪元素上实现属性过渡. 而 Opera,Chrome 和 Safari 则不会。 WebKit 从2013年一月起也开始支持。
伪元素的过渡会致使内容自身产生一些新问题,由于在生成内容时 TransitionEnd 过渡根本尚未结束。 在某一时间段内,他们理应在主元素上被触发,并经过 TransitionEnd.pseudoElement 提供伪元素,但即使是“CSS动画过渡”的“过渡事件”部分,编写者的方案也并无指定哪个最合适。
咱们想要改变 content 属性值,所以IE 8将在特殊状况下(好比:hover状态)将会从新渲染该元素。结果代表,对老的IE版本进行兼容会影响到全部其余浏览器的效率。因此, 当试图在伪元素上进行属性过渡时,要确保 content 的值不会被改变。
若是主元素没有运行:hover状态,那么 IE 10 将不针对伪元素“:hover”执行过渡。
1
2
|
.some-selector:before{
content
:
"hello"
;
color
:
red
;transition:
all
1
s linear
0
s;}
.some-selector:hover:before{
color
:
green
;}
|
在 IE10 下,:before在 mouseover 的时候,:hover 是必定要定义的。
这个问题在于不是必定要求你定义主元素:hover 状态。而是若是没有定义,IE 10 会将:hover解释为:active。更奇怪的是,:active状态甚至会在 mouseup 后继续持续,而当你再次点击就会取消。
8 背景标签
在编辑标签时,IE 10 是惟一可对背景或前景响应的浏览器,若是标签变为背景后,虽然它会完成正在执行的过渡,但它不会执行新的过渡。IE 10 将等到标签变为前景后再执行新过渡。幸运的是,IE 10 已经支持页面的可见性 API,容许开发人员应对这种操做行为。
9 隐藏元素
对于隐藏的元素,过渡是不会被执行的,由于大多数浏览器都明确认为没有必要在一个看不见的元素里运行过渡。然而,也有特例,在 Opera 下不管元素隐藏与否它都将执行过渡。
10 过渡以前,DOM树是否加载完毕
当文档脱离解析模式时,DOMContentLoaded 被触发,若是你在使用 jquery,那么应该了解jQuery.ready(),过渡能够在这以前运行。
11 渲染差别
这个问题我以前已经说过了, 本文就是基于个人测试结果进行阐述的。测试是自动运行的,但事实证实,仍是发现了不少问题。
当时要实现从渐变到渐变的背景过渡是不可能的,但能够实现从渐变到纯色的过渡。若是渐变正在进行中,从白色到目标颜色的过渡即将开始,在过渡启动时,会看到白色在快速闪动。目前全部的浏览器中均可以察觉到这一点。
不过Firefox 彷佛是用不一样的算法来渲染图像的,以代表它们执行了动画过渡(见实例)。很显然,在动画过渡时, Gecko 并无呈现好的效果。若是 transform: scale() 足够低,这种状况将发生。
Firefox 不会从 a:visited 到 a:hover 过程当中过渡动画,反之亦然。 但它会从 a:visited 直接跳到 a:link,而后过渡到 a:hover 状态, 你能够在这个例子中看到,这是在 Mozilla Developer Network”Privacy and the :visited Selector”中提到的。然而 IE 10 与 Chrome,Safari 和 Opera 浏览器同样,会从a:link到a:visited实现过渡。
若是子元素的 position 改变时, Firefox 不会触发元素的属性, 而 WebKit,Opera 和 IE 10 则会触发。
12 对规范的建议
看完了整个规范并对全部功能进行了测试以后,以为若是能进行如下优化将会更好:
13 使用delay
使用延时,能够很好的解决无心的鼠标悬停形成的样式变化,如同setTimeout()。
14 总结(可参考前面谈到过的实例)