CSS 3学习——transition 过渡

以下内容根据官方规范翻译以及自己的理解整理。

1.介绍

这篇文档介绍能够实现隐式过渡的CSS新特性。文档中介绍的CSS新特性描述了CSS属性的值如何在给定的时间内平滑地从一个值变为另一个值。

2.过渡transitions

通常情况下,当CSS属性发生改变时,对应元素的CSS属性值立即从旧值变为新值,渲染结果也是立即更新。这部分介绍一种方法,可以通过使用新的CSS属性指定过渡的过程。这些新属性让元素以平滑动画的形式逐渐地从旧状态过渡到新状态。

比如,假设一个元素的left属性和background-color属性被设置了1s的过渡:位置从左到右,背景颜色从红色到蓝色(其他的过渡参数为默认值)。下面的图展示了这个元素的过渡过程。

left和background-color的过渡

过渡是一个视觉效果。被设置了过渡的属性的值逐渐地从旧值变为新值,因此如果一个脚本查询了正处在过渡过程中的元素的属性值,那么它将得到一个表示当前动画状态的中间值。

只有可动画的CSS属性才能设置过渡,具体看Properties from CSS,了解哪些属性是可动画属性。

给一个属性设置过渡要使用几个新的CSS属性。比如:

例1

div {
  transition-property: opacity;
  transition-duration: 2s;
}

上面这个例子在opacity属性上定义了一个过渡(transition)。如果一个新值被指定给opacity,那么旧值就会用两秒钟的时间逐渐地变为新值。

transition的每个分属性都可指定一个以逗号分隔的值列表,允许指定多个属性的过渡,每个过渡对应不同的属性。在下面的例子中,每个过渡按照其在列表中的顺序匹配参数。

例2

div {
  transition-property: opacity, left;
  transition-duration: 2s, 4s;
}

这个例子中opacity属性对应的过渡时间为2s,left属性对应的过渡时间为4s。

在下面的例子中,transition的分属性的值列表长度不一样长。当过渡(transitions)开始时,transition-property属性的列表长度决定了其他分属性的值列表的条目数。值列表的匹配从第一个值开始,最后多余的值被忽略。如果有分属性的以逗号分隔的值不足以匹配transition-property属性的值列表,那么用户代理必须重复该分属性的值列表,以使他的值列表足以匹配transition-property属性的值列表。这些值的截断或重复不会影响最终值的计算。注意:这类似于背景样式的相关属性,transition-property属性类似于background-image属性。

例3

div {
  transition-property: opacity, left, top, width;
  transition-duration: 2s, 1s;
}

上面的例子中定义了opacity属性过渡的持续时间为2s;left属性过渡的持续时间为1s;top属性过渡的持续时间为2s;width属性过渡的持续时间为1s。

注意:开发者可以利用过渡创建动态变化地内容,而动态变化地内容可能导致某些用户的癫痫发作。

2.1. transition-property 属性

值:none | <single-transition-property> #;

其中<single-transition-property> = all | <custom-ident>;

默认值:all

不可继承

适用于:all elements, ::before and ::after pseudo elements。

none

  表示没用属性会过渡

all

  表示所有可动画的属性都会表现出过渡动画。

如果transition-property的值列表中有标识符不是一个可识别的属性名或者不是一个可动画(animatable)的属性,浏览器在具体实现时必须仍旧从transition-duration属性, transition-delay属性和 transition-timing-function属性的值列表的第一项开始匹配。也就是说,不可识别的属性名或不可动画的属性必须仍然占据他们在值列表项中的位置以参与值的匹配。

例4

div {
  transition-property: opacity, nothing, width;
  transition-duration: 2s, 3s, 4s;
}

这个例子中opacity属性匹配到2s;nothing是不可识别的属性名,仍然匹配到3s;width匹配到4s。

<single-transition-property>产生的<custom-ident>要排除关键字“none”,另外也要排除其他非法的关键字。也就是说,关键字“none”,“inherit”,“initial”是不允许出现在值列表中的,使用了上述关键字的值列表将是语法无效的(语法无效,即整个transition-property无效。无效后,浏览器用了默认值all。其他分属性同理)。

对于关键字“all”或者简写速记属性,浏览器在实现时必须给速记简写属性的所有可动画的分属性添加过渡,匹配transition-duration,transition-delay,和 transition-timing-function参数时使用速记简写属性匹配。

如果一个属性在transition-property属性中指定了多次(不管是用它自身的属性名还是用“all”关键字,还是用包含它的简写属性),那么使用其在transition-property值列表中最后一个位置匹配到的参数进行过渡。

注意:all关键字与简写速记属性以相似的方式起作用,所以说,all关键字可以看做所有可动画的属性的简写形式。

2.2. transition-duration 属性

transition-duration属性定义一次过渡所花费的时长(过渡时间)。

取值: <time> #

以s或ms为单位。

默认值:0s

不可继承

适用于:all elements, ::before and ::after pseudo elements

这个属性指定过渡从旧值变为新值所花费的时间。默认值是“0s”,表示

旧值立即变为新值,没有动画效果。如果是负值,则整个transition-duration无效,使用默认值。

2.3. transition-timing-function属性

transition-timing-function属性描述在过渡期间中间值如何计算。它允许在过渡期间改变过渡的速度,这些效应称为缓动函数。不管那种情况,一个可提供平滑曲线的数学函数可以被使用。

Timing functions可以被指定为stepping function(步进函数)或者cubic Bézier curve(三次贝塞尔曲线)。指定的这个timing function把当前经过的过渡时间的百分数当做他的输入值,输出从开始值到结束值的百分比。至于如何使用这个输出结果,参见interpolation rules

Stepping function用一个数字(相当于总的步数或分成的区间数)把作用域(过渡时间)分成相同大小的区间。每经过一个区间,就向最终的目标状态前进相等的一步。这个函数也可以指定输出百分比的变化是发生在区间的起始点还是发生在区间的结束点(in other words, if 0% on the input percentage is the point of initial change,实在看不懂这句啥意思)。

我的理解:start表示区间结束点的状态(关键帧)插入到区间开始点(值立即变为区间结束点对应的值);end表示区间结束点的状态(关键帧)出现在他原本应该出现的位置。所以start立即发生,end延迟一个区间的时间发生。

 

Step timing functions

一个三次贝塞尔曲线由4个控制点定义:P0,P1,P2,P3(如下图)。P0和P3通常设置为(0,0)和(1,1)。transition-timing-function属性可以用来指定点P1和P2的值。可以使用预设的关键字(后面内容中会列举),或者也可以用cubic-bezier()函数。在cubic-bezier()函数中,P1和P2都用一个X值和一个Y值指定。

Bézier Timing Function Control Points

transition-timing-function属性

取值:<single-transition-timing-function> #;

<single-transition-timing-function> = ease | linear | ease-in | ease-out | ease-in-out | step-start | step-end | steps(<integer>[, [ start | end ] ]?) | cubic-bezier(<number>, <number>, <number>, <number>)

默认值:ease

适用于:all elements, ::before and ::after pseudo elements

Timing functions可以用下面的值定义:

关键字:

ease

    与ease-in-out有点类似,刚开始速度急剧增加,到接近中间点时速度开始降低,相当于cubic-bezier(0.25, 0.1, 0.25, 1)。

linear

   从开始状态匀速地过渡到最终状态,相当于cubic-bezier(0, 0, 1, 1)。

ease-in

   刚开始过渡速度很慢,然后逐步加速直到到达最终状态,同时动画立即停止相当于cubic-bezier(0.42, 0, 1, 1)。

ease-out

    过渡动画刚开始速度很快,然后逐步减速,直到过渡到最终状态,相当于cubic-bezier(0, 0, 0.58, 1)。

ease-in-out

    从开始到中间点类似于ease-in,从中间点到最终状态类似于ease-out,相当于cubic-bezier(0.42, 0, 0.58, 1)。

step-start

    相当于steps(1, start)。使用这个值时,元素立即从开始状态跳转到最终状态,然后在最终状态停留transition-duration设置的时间。

step-end

    相当于steps(1, end)。使用这个值时,元素先在其初始状态停留transition-duration设置的时间,然后再直接跳转到最终状态。

函数:

steps()

语法:steps(number_of_steps, direction)

numbers of steps必须是一个大于0的正整数,表示步数或等分的区间数。

direction可以取“start”或“end”中的一个,表示值的改变发生在区间中的哪一点。如果省略,则作为 “end”处理。

cubic-bezier()

语法:cubic-bezier(<number>, <number>, <number>, <number>)

指定一个三次贝塞尔曲线。前两个值是点P1的横坐标和纵坐标,后两个值是点P2的横坐标和纵坐标,(x1,y1, x2,y2)。横坐标必须在[0, 1]区间内否则无效,纵坐标可以超出这个区间范围。如果指定一个无效的三次贝塞尔曲线,则整个transition-timing-function属性无效,浏览器使用默认值。

2.3.1. 序列化timing function

使用在[CSSOM]中定义的常用的串行化模式序列化Timing function,另外还要加上以下要求:

l  序列化之前,ease, linear, ease-in, ease-out,和ease-in-out这些关键字值不转换为等价的cubic-bezier()函数。
l  步进时间函数(Step timing functions),不管用的是steps()还是关键字step-start或step-end都按以下规则序列化:

如果值的改变发生在结束点(相当于steps()函数的第二个参数为end),按照steps(<integer>)序列化
否则的话按照steps(<integer>, start)序列化。

2.4. transition-delay属性

transition-delay属性定义了过渡什么时候开始。当一个过渡被应用时,通过该属性可以指定该过渡开始之前所要等待的时间(也就是延迟时间)。如果值为0s,说明被指定了过渡效果的属性的值改变时,过渡动画马上开始。否则的话,当被指定了过渡效果的属性的值改变时,过渡动画要延迟指定的时间后才开始。

如果transition-delay属性的值为负值,过渡动画会在被指定了过渡效果的属性的值发生改变时立即开始。

transition-delay

可取值:<time> #,单位s或ms

适用于:all elements, ::before and ::after pseudo elements

默认值:0s

不可继承。

例5 transition-delay为负值的情况 点这里看在线demo

transition-delay为负值时,过渡的实现过程:

当transition-delay的绝对值大于等于transition-duration的值时,没有过渡动画效果。触发过渡时,立即从开始状态变为结束状态。

当transition-delay的绝对值小于transition-duration的值时,有过渡动画效果。触发过渡时,浏览器认为过渡动画已经经过了transition-delay的绝对值的时间,过渡实际上是花费了transition-duration – (-transition-delay)的时间从中间状态逐渐变为结束状态,具体看例5中transition-delay为-1s时的情形。

2.5.transition属性

transition属性是前面介绍的4个属性的简写属性。

可取值: <single-transition> #

<single-transition> = [ none | <single-transition-property> ] || <time> || <single-transition-timing-function> || <time>

也就是transition-property + transition-duration + transition-timing-finction + transition-delay,中间用空格分隔,省略的取分属性的默认值。如果指定多个属性的过渡效果,不同属性值的过渡效果用逗号隔开。

默认值:参见前面的4个分属性。

不可继承。

适用于:all elements, ::before and ::after pseudo elements

在使用transition属性指定可动画属性的过渡效果时,如果在值列表中存在代表transition-property属性的值为none,则整个transition声明将无效,浏览器转而使用默认值all。

3. 过渡的开始

浏览器(implementations,下同)在实现过渡时必须维持一个正在进行中的过渡(running transitions)的集合,这些过渡被应用于特定的元素及其非简写的属性。这些过渡都有各自的开始时刻(start time),结束时刻(end time),开始值(start value),结束值(end value),回动调整开始值(reversing-adjusted start value),和回动缩短系数(reversing shortening factor)。浏览器在实现过渡时按照该部分内容所描述的那样给每一个过渡添加上述一些列的参数,以及当过度完成或要取消过渡时移除这一系列的参数。对于回动调整开始值(reversing-adjusted start value),和回动缩短系数(reversing shortening factor)。

浏览器当然也必须要维持一个已完成的过渡(completed transitions)的集合。就像running transitions一样,completed transitions也被应用于特定的元素及其非简写的属性。本规范声明对于同一个元素以及同一个属性而言,永远不会同时存在一个running transition和一个completed transition。

如果一个元素不再存在于document中,浏览器必须移除应用在该元素上的所有running transitions,同时也必须从completed transitions的集合中将其移除。

为了在某些特定情况下阻止重复地过渡,一个completed transitions的集合需要被维持。本规范同时声明不相关的样式的改变不会触发过渡。

例6 过渡效果的继承

在这个例子中,一个可继承的属性被指定了过渡,completed transitions集合的维持是有必要的。父元素在一个属性上指定了较长持续时间的过渡(如:transition: 4s text-indent;)同时子元素的这个属性的值继承自父元素,而且子元素在这个属性上指定了较短持续时间的过渡(如:transition: 1s text-indent;)。

如果是后代元素的可继承属性设置了过渡,那么如果祖先元素该属性的值发生了改变同样会触发后代元素上设置的过渡。

如果后代元素过渡的完成先于祖先元素的过渡,那么后代元素将会重新开始从他的父元素继承相应的属性值(继续过渡)。这样的效果可能不是预期想要的效果,但是对于开发者的要求来说这是必要的。(我测试的结果是,Firefox中是这种效果,Chrome中不是这种效果。Chrome中是先让祖先元素到达最终状态,后代元素的过渡才开始)。

 1 p{
 2     border:1px solid black;
 3     transition:4s text-indent;
 4 }
 5 p:hover{
 6     text-indent:50px;
 7 }
 8 quote{
 9     display:block;
10     transition:1s text-indent;
11 }
12 quote:hover{
13 text-indent:inherit;    
14 }
15 <p>
16     Maintain that unrelated style changes do not trigger transitions.
17     <quote>This set of completed transitions needs to be maintained。</quote>
18 </p>

具体效果点这里

下面这段内容理解不了。

许多东西都能引起一个元素的属性的计算值的改变,包括从文档树中移除和插入元素,改变文档树引起的选择器匹配到的元素的改变,修改样式表或者样式属性以及其他一些东西。本规范没有定义什么时候更新计算值,除了说浏览器禁止使用,呈现或显示来源于无计算值更新的CSS cascading、value computation和inheritance process (这意味着浏览器不可避免地会遇到把规范声称的无计算值变更的情形作为样式变更的一部分处理)。然而,当浏览器为了反映这些改变而更新一个元素的属性的计算值时,或者计算新添加进文档中的元素的属性的计算值时,浏览器必须同一时间更新所有元素以及所有属性的计算值以反映这些变化(或者至少说虽然浏览器在不同时间更新了这些变化,但是让用户察觉不到这些更新不是同时发生的,而是让用户感觉到这些更新是同时发生的)。一组样式同步更改的过程称为样式更改事件(style change event)。浏览器通常都有一个style change event与他们所要求的屏幕刷新率(screen refresh rate)相一致。同时,计算样式或者布局信息的及时更新对于依赖于此的脚本API是必要的。

因为规范没有规定style change event具体什么时候发生,所以只要是计算值的变更都被认为是同步发生的。开发者应该清楚,在计算值改变后,不同的浏览器对过渡属性的值的更新时间存在差异,有的浏览器认为这些变更是同步的,而有些则不这么认为。

当一个style change event发生时,浏览器必须根据本次事件中计算值的变更开始实现过渡。如果在style change event发生期间或发生之前(previous style change event),有元素不在文档中,那么对于本次style change event这个元素的过渡就不会开始。另外,定义before-change style作为previous style change event的元素的所有属性的计算值(computed values),衍生自声明式动画(animations)的样式除外,比如CSS Transitions, CSS Animations ([CSS3-ANIMATIONS]), and SMIL Animations ([SMIL-ANIMATION], [SVG11]),这些样式的更新是实时更新的(updated to the current time)。同样地,定义after-change style作为style change event开始时元素的所有属性的计算值,但是源自CSS Transitions的计算样式以及继承自父元素的after-change style除外。

对于每一个有before-change style和after-change style的元素,并且对于该元素的每一个属性(非简写属性),定义matching transition-property value作为该元素after-change style中transition-property的最终值(当然是按照2.1小节的描述计算最终值)。与其相一致地,定义matching transition duration,matching transition delay,和matching transition timing function作为该元素在after-change style中transition-duration,transition-delay,和transition-timing-function的最终值。定义过渡的combined duration作为max(matching transition duration, 0s)和matching transition delay。对于每一个元素和属性,浏览器必须按以下规则执行:

具体看规范:https://drafts.csswg.org/css-transitions/#matching-transition-duration

注意:当一个可动画属性的计算值(computed value)改变时,过渡根据transition-property,transition-duration,transition-timing-function和transition-delay属性当前最新的计算值开始。也就是说当一个指定了过渡效果的属性的值改变的同时,其对应的transition-*属性也发生了改变,那么该属性的过渡效果由当前最新的transition-*属性的值控制。

这可以让开发者分别给过渡的两个不同状态(前进:forward和回动:reverse)指定不同的transition-*属性值。开发者可以在触发过渡的同时给transition-property,transition-duration,transition-timing-function和transition-delay属性指定另外的值。因为transition-*属性的当前最新值控制过渡,所以这些触发过渡后新指定transition-*的值将覆盖触发过渡前transition-*的值控制过渡。

上面两段内容说的是一个意思。

例7

li {
  transition: background-color linear 1s;
  background: blue;
}
li:hover {
  background-color: green;
  transition-duration: 2s; /* applies to the transition *to* the :hover state */
}

上面例子中,列表项进入悬停状态时背景颜色变为绿色,同时transition-duration的值变为2s,所以列表项的背景色从蓝色过渡到绿色用了2s,而不是1s;当列表项从悬停状态恢复到原来状态时,transition-duration的值变为1s,所以背景色从绿色过渡到蓝色用了1s而不是2s。

点我看demo

注意:

一旦一个属性开始过渡(包括进入延迟期间),其过渡效果仍然基于原来的timing function,duration和delay,即使在过渡完成前transition-timing-function,transition-duration或者transition-delay属性的值被修改(注意这里不是前文例子中的情形,前文例子中timing function,duration和delay是同步被修改的,这里是延迟修改)。见上面demo中的例8

但是,如果是transition-property的值被修改,那么原来指定了过渡效果的属性的过渡会立即停止,其值会立即变为最终的值。见上面demo中的例9

注意:上述规则意味着声明式动画(declarative animation,与脚本式动画(scripted animation)相反)引起的属性计算值的改变不会触发过渡。因为对于声明式动画来说,before-change style包含up-to-date style。

3.1. 快速回动过渡

对于过渡来说,在他完成之前插值改变属性的值,就会重置过渡的开始值。比如当鼠标指针悬停在元素上时,元素的过渡效果开始,然后在过渡效果完成前鼠标指针从元素中退出。如果鼠标移出和移进的过渡使用的是原来指定的durations和timing functions,那么最终的过渡的效果将会是错乱的,因为第二次过渡使用了完整的duration移动了一个缩短的距离。作为替代,规范声明让第二次过渡相应地也缩短。

上述规则中的机制用于导致reversing shortening factor和reversing-adjusted start value的回动过渡。特别是回动阶段reversing shortening factor小于1的时候。

4. 过渡事件

过渡的创建、开始、完成和取消会产生相应的DOM事件。对于指定了过渡效果的元素的属性实际上是该属性的分属性触发了过渡事件。比如通过border-width属性指定元素边框宽度从0px过渡到20px,那么border-top-width,border-right-width,border-bottom-width和border-left-width都触发了过渡事件。前进阶段可以触发过渡事件,回动阶段同样也可以触发过渡事件。

4.1. 过渡事件接口

4.1.1. 过渡事件属性

propertyName

  只读,字符串类型,返回与过渡绑定的CSS属性名。

elapsedTime

  只读,浮点数,返回过渡触发后已经运行的时间,包括延迟时间,单位s。

pseudoElement

  只读,字符串类型,返回发生过渡的CSS伪元素的名字(这种情况下,事件的目标target是与该伪元素相对应的元素)或者返回一个空字符串(也就是说发生过渡的元素不是伪元素)。

TransitionEvent(type, transitionEventInitDict)

   是一个事件构造器,用给定的参数创建一个过渡事件。具体参见MDN

4.2. 过渡事件的类型

transitionrun

   目前还没有浏览器支持该事件。

    当一个过渡被创建的时候触发(或者说当过渡被添加到running transitions集合中时触发)。

    对于transitionrun,如果transition-delay值为负,则elapsedTime属性返回值等于: min(max(-transition-delay, 0), transition-duration)。

transitionstart

    目前只有IE10+支持该事件。

    当过的延迟时间走完时触发。

    对于transitionstart,elapsedTime返回的值与transitionrun一样。

transitionend

    所有现代浏览器都支持该事件。

    当过渡完成时触发。如果在过渡完成之前该过渡被移除了,比如transition-property被移除,那么,不会触发transitionend。

    对于transitionend,elapsedTime返回的值等于transition-duration的值。

    如果过渡延迟时间为负值,且绝对值大于等于过渡持续时间时,不会产生过渡效果,也不会触发过渡事件

transitioncancel

    目前还没有浏览器支持该事件。

    当过渡被取消时触发。

    对于transitioncancel,elapsedTime返回过渡延迟结束时刻到过渡被取消时刻的秒数。

如果transition-delay的值为负,过渡开始的时刻等于过渡真正触发时刻之前transition-delay绝对值的秒数。

如果transition-delay的值为正,而且transitioncancel在过渡的延迟时间结束前触发,那么其elapsedTime返回0。

过渡事件通过DOM的 addEventListener() 方法添加。

点我查看demo

参考资料和相关文章:

1、规范原文

2、深入理解CSS过渡transition

3、CSS3 Transition的唯一事件:TransitionEnd

4、MDN

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注