thanks kirupa
Transition的故事历经考验和磨难。从被无耻浏览器厂商所忽略而必须添加厂商前缀到目前被普遍接受,该旅程肯定是崎岖不平。尽管transition
属性在过去几年有了很大进步,但与它更帅更靓的前辈animation
相比,在实现某些东西方面并不容易。
我在CSS3: Animations vs. Transitions教程中完全描述transitions和animations的差异,在这里我将重申一个transition的重要局限。这个限制就是transitions没有一个属性指定让它循环。是的!在今天这个时代,transition被设计成只执行一次。的确是个笑话。
幸运的是,这里有一个方法。在本教程里,我将通过一个小伎俩解决 transitionend event 如何使transitions支持循环。
The Example
为了证实我有点疯狂,下面的例子展示了一个永远循环的transition。鼠标移到圆形上看到transition开始运行...永远不会停止。
注意到圆形从小到大的来回振荡。学完本章,你将能创建类似的动画并理解它的运行原理。
How to Make a Transition Loop
在深入做这件事之前,让我们首先退一步理清我们将要做的是什么。为了做到这点,先引入性感的圆圈吧:
接下来做的就是添加transition改变圆形的大小。圆形有一个初始大小。我们通称为它的初始状态stateOne。要实现transition须让它变的更大些,该状态为stateTwo:
到目前为止看起来非常简单,对吗?我开始于stateOne,现在我触发一个transition改变圆的大小,为stateTwo状态定义的值:
由于我定义了transition,代替了它从stateOne突然转变到stateTwo状态,圆型会逐步变成大的尺寸。注意,如何触发transition使它的尺寸变到stateTwo状态是无关紧要的。有可能是元素的class值变化。有可能是鼠标移上指定一个不同的尺寸--就像刚刚这个例子。有可能是使用JavaScript代码直接修改它的大小。可以是各种方式写成这一点。怎样触发属性值的变化无关紧要。一旦定义了transition监听的属性发生了变化,transition将会执行。
现在,我提到所有关于transitions的东西你已经十分清楚,当作一个简单的回顾。让我们开始吧,因为这里要引入新东西。那就是让这个transition实现迭代。默认情况下,transition从stateOne过滤到stateTwo,其它什么都不会发生。有时,这取决于我的CSS设置,可能回到stateOne状态。我想要做的就是循环这个transition永不让它停止。一旦让transition运行,我想让圆型按我定义的大小stateOne和stateTwo来回振荡...永远。当它到达stateTwo状态,我想让它返回stateOne状态。当到达stateOne状态,再让它返回stateTwo状态...那么,你将得到一段影片。
下图就是我描述的影片的运行轨迹:
我们将如何做到这一点呢?当然不是最简单的!正如先前我提到的,问题在于CSS transition
属性不支持内建的属性支持循环。我们要自己把事情搞清楚。这真是有点无耻。我们要是等待trasition完成做点点什么。一旦transition完成,我们将强制改变transition监听的属性值。通过改变transition监听的属性值,你的transition又会重新运行...直到新的属性变化到指定值。
现在我们有个好的方法如何让transition实现循环,让我们开始实现它吧。判断一个transition结束通过监听transitionend
事件。在每次transitionend
事件处理器中,我们将通过一些JavaScript代码修改transition监听着的属性值。在stateOne结束的transition中,transitionend
事件将被触发。根据这一点,我们会通过JavaScript告知元素反向去stateTwo状态。当处于stateTwo状态,另一个transitionend
事件将被触发。这时,告知元素反向去stateOne状态。你可以想象,这将继续一段时间。
这里有一个图展示了上面的循环:
最初transition可以被各种方式触发。它是怎样触发并不重要。重要的是后续的transition怎样被JavaScript触发的。
在接下来的几节中,让我们看看这个技巧吧。
Getting Started
好吧,我们最后动手实践说明前面的几个段落的内容并让它们运行。如果你不想动手实践而宁愿只是阅读和被的追随。跳过这个章节继续看下一个章节。如果你想积极跟随本教程并重新创建你在上面看到的例子,创建一个HTML文档把下面的HTML/CSS代码拷进去:
<!DOCTYPE html>
<html>
<head>
<meta content="en-us" http-equiv="Content-Language">
<meta charset="utf-8">
<meta content="stuff, to, help, search, engines, not" name="keywords">
<meta content="What this page is about." name="description">
<meta content="An Interesting Title Goes Here" name="title">
<title>An Interesting Title Goes Here</title>
<style>
body {
background-color: #FFF;
margin: 30px;
margin-top: 10px;
}
#box {
width: 550px;
height: 350px;
border: 5px black solid;
overflow: hidden;
background-color: #F2F2F2;
margin: auto auto;
background-image: url('http://www.kirupa.com/images/gray_background.png');
}
#contentContainer {
position: relative;
}
#circleDiv {
background-color: #2693FF;
border-radius: 75px 75px 75px 75px;
height: 150px;
margin: 100px auto auto;
width: 150px;
transition: transform .2s ease-in-out, opacity .2s ease-in-out;
}
.stateOne {
opacity: 1;
transform: scale(1, 1);
}
.stateTwo {
opacity: .5;
transform: scale(1.9, 1.9);
}
p {
font-family: Cambria, Cochin, Georgia, Times, "Times New Roman", serif;
font-size: medium;
color: #006699;
}
</style>
</head>
<body>
<div id="box">
<div id="contentContainer">
<div id="circleDiv" class="stateOne">
</div>
</div>
</div>
<script src="http://www.kirupa.com/html5/examples/js/prefixfree.min.js"></script>
<script>
// your code goes here
</script>
</body>
</html>
在浏览器中预览该文档,你将看到类似刚前的例子。唯一不同的就是这个示例的动画不能迭代。事实上,它是静态的什么都做不了,我们很快就会解决这个问题,但首先,让我们来看看到底如何进行处理:
在代码里,圆型被定义成:
<div id="circleDiv" class="stateOne">
</div>
有趣的细节是元素的id
是circleDiv和类
名为stateOne。一会儿你就会看到它们将出现在CSS和JavaScript代码里。首先看一下CSS...#circleDiv
定义的样式:
#circleDiv {
background-color: #2693FF;
border-radius: 75px 75px 75px 75px;
height: 150px;
margin: 100px auto auto;
width: 150px;
transition:transform .2s ease-in-out, opacity .2s ease-in-out;
}
这个样式是让方形的div
元素变成圆形。除些以外,也在这里定义了transition
。注意,这里定义的transition包括transform
和opacity
两个属性。
接下来看一下stateOne
和stateTwo
样式规则,它是扮演在两个状态触发转换以及保持transition运行的角色:
.stateOne {
opacity: 1;
transform: scale(1, 1);
}
.stateTwo {
opacity: .5;
transform: scale(1.9, 1.9);
}
在上面两个规则中,opacity
和transform
两个属性均被设置成不同的值。如果从前面几行你还记得我们的transition
属性设置为应对变化transform
和opacity
。嗯…关键点正在此处!
Triggering The Transition Initially
第一件事是要我们的transition简单触发运行,这需要鼠标移上该元素。目前,正如果在HTML中看到的,元素有初始类stateOne的样式值。当我鼠标移上去后,想让它变为stateTwo样式的值。这些类名映射到选择器的样式规则,正如刚刚你看到的....stateOne
和.stateTwo
。
实现它的唯一方式就是写一些JavaScript。在script
标签内添加如下行:
var theCircle = document.querySelector("#circleDiv");
function setup() {
theCircle.addEventListener("mouseover", setInitialClass, false);
}
setup();
function setInitialClass(e) {
theCircle.className = "stateTwo";
}
这段代码应该相当简单。通过调用querySelector
变量theCircle
指向#circleDiv
元素。在这里我们设置一个mouseover
事件监听器setInitailClass
当鼠标移上去时触发。在setInitialClass
事件处理器内部,将元素的类stateOne改变为stateTwo。
再预览一下HTML文档。当你鼠标移到元素上时,你将看到transition被触发。它的触发原理就是当鼠标移上去后,元素的类值由stateOne改变为stateTwo。意思是,定义的transition
像鹰一样监视着元素的opacity
和transform
属性的变化。
当然,一旦transition完成后,什么都不会发生。至少这里你什么情况都没看到。因为transitionend
事件没有生效,所以这里表面上什么都没有发生。在下一章节,让我们看看有什么面临的麻烦事!
Causing our Transition to Loop
现在,经过.2秒动画之后,元素的class
属性值变为stateTwo并变大。我们要做的就是把元素类名变回stateOne,要做的就是监听transitionend
事件。修改一下代码,看下面高亮行部分的代码:
var theCircle = document.querySelector("#circleDiv");
function setup() {
theCircle.addEventListener("mouseover", setInitialClass, false);
theCircle.addEventListener("transitionend", loopTransition, false);
theCircle.addEventListener("webkitTransitionEnd", loopTransition, false);
theCircle.addEventListener("mozTransitionEnd", loopTransition, false);
theCircle.addEventListener("msTransitionEnd", loopTransition, false);
theCircle.addEventListener("oTransitionEnd", loopTransition, false);
}
setup();
function setInitialClass(e) {
theCircle.className = "stateTwo";
}
function loopTransition(e) {
if (e.propertyName == "opacity") {
if (theCircle.className == "stateTwo") {
theCircle.className = "stateOne";
} else {
theCircle.className = "stateTwo";
}
}
}
刚添加的代码的大部分在transitionend事件教程有详细的描述。所以,我要跳过这部分比较容易的内容。相反,我想专注于loopTransition
事件处理器发生了什么---当一个transitionend
事件触发时该事件处理器被调用:
function loopTransition(e) {
if (e.propertyName == "opacity") {
if (theCircle.className == "stateTwo") {
theCircle.className = "stateOne";
} else {
theCircle.className = "stateTwo";
}
}
}
上面高亮行的代码(if代码块)等价于下图展示意义:
loopTransition
每次被调用,代码将检测元素上的class
属性。如果元素当前的值为stateTwo,那么就将它的class
值设置为stateOne,以再次触发transition:
如果当前元素的class为stateOne时loopTransition被调用,你应猜到设置class值为stateTwo,再次触发 transition:
上述高亮行的代码目的就是保证transition永远追逐一个移动的靶子。换句话说,那5行代码的职责就是使transition循环。
Conclusion
好吧,本篇内容要比我预期的更多一些。不管怎么说,你的目标就是保证transition永远不会停下来。方法就是确定监听transitionend 事件,当事件触发时修改元素被监听的属性。具体怎么实现你已经学会了,接下来的教程,我将带来一些结合所有知识点,比较酷的你从未见的例子。
翻译水平有限,敬请各位同学批评指正。
Comments
comments powered by Disqus