学习资源来自慕课网《js动画效果》:http://www.imooc.com/video/3049/0。
在上一节《js动画效果》之多物体动画效果中的例子1 :
1. 网页上有一ul, ul中有三个li元素,要求鼠标移到(onmouseover) li 元素上产生动画效果——宽度值增加到300px,鼠标移出(onmouseout) li 元素产生动画效果——宽度值复原到200px.
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <style type="text/css">
- *{
- padding:0;
- margin:0;
- }
- <span style="color:#ff0000;">ul li{
- list-style:none;
- width:200px;
- height:100px;
- background:green;
- margin:20px;
- }</span>
- </style>
- <script type="text/javascript">
- /***
- 实现多物体运动的关键点:
- 1)获取到每个元素;
- 2)给每个元素绑定上事件;
- 3)注意使用this区分当前操作的元素;
- 4)定时器不能只设一个了,应该给每个元素分配一个定时器,避免多个元素抢同一个定时器造成bug
- 记住:多物体运动不能共用同一个东西(某一参数,比如定时器)
- ***/
- window.onload = function(){
- var liEles = document.getElementsByTagName("li");
- for(var i=0; i<liEles.length; i++){
- //给每一个li元素定义一个定时器,避免多个元素抢同一个定时器造成bug
- liEles[i].timer = null;
- liEles[i].onmouseover = function(){
- //传入this值,指定当前操作的是哪个li
- playFun(this, 300);
- };
- liEles[i].onmouseout = function(){
- playFun(this, 200);
- };
- }
- }
- //var timer = null;
- function playFun(obj,itarget){
- clearInterval(obj.timer);
- obj.timer = setInterval(function(){
- var speed = (itarget - obj.offsetWidth)/10;
- speed = speed>0?Math.ceil(speed):Math.floor(speed);
- if(obj.offsetWidth == itarget){
- clearInterval(obj.timer);
- }else{
- obj.style.width = obj.offsetWidth + speed + "px";
- }
- },50);
- }
- </script>
- </head>
- <body>
- <ul>
- <li></li>
- <li></li>
- <li></li>
- </ul>
- </body>
- </html>
li 元素的样式中没有设置 border属性,下面我们重新设置li元素的样式为:
- ul li{
- list-style:none;
- width:200px;
- height:100px;
- background:green;
- margin:20px;
- <span style="color:#ff0000;">border:2px #ccc solid;</span>
- }
代码中一直采用 offsetWidth 来计算物体的宽度, 而 offsetWidth = width + padding(left + right) + border(left + right) ,下面来分析代码,当物体停止运动,console.log(obj.offsetWidth )的输出值为 240 ,对应 width 的值为236,目前定时器仍在运行中,将target 值 200 和 obj.offsetWidth的当前值 240 带入下面公式计算得出 speed = -4
var speed = (itarget - obj.offsetWidth)/10;
speed = speed>0?Math.ceil(speed):Math.floor(speed);
再将obj.offsetWidth的值 240和speed的值 -4 带入公式
obj.style.width = obj.offsetWidth + speed + "px";
得出 width = 236;
下面进入下次定时器的运算,offsetWidth = width + border( 2+2 ) = 240, 又带入各公式计算出width的值还是 236 ,如此死循环下去。
解决方法:
法1)此法是课程中老师讲解的方法——封装一个专门获取属性值得方法,代码如下:
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <style type="text/css">
- *{
- padding:0;
- margin:0;
- }
- ul li{
- list-style:none;
- width:200px;
- height:100px;
- background:green;
- margin:20px;
- border:2px #ccc solid;
- }
- </style>
- <script type="text/javascript">
- /***
- 实现多物体运动的关键点:
- 1)获取到每个元素;
- 2)给每个元素绑定上事件;
- 3)注意使用this区分当前操作的元素;
- 4)定时器不能只设一个了,应该给每个元素分配一个定时器,避免多个元素抢同一个定时器造成bug
- 记住:多物体运动不能共用同一个东西(某一参数,比如定时器)
- ***/
- window.onload = function(){
- var liEles = document.getElementsByTagName("li");
- for(var i=0; i<liEles.length; i++){
- //给每一个li元素定义一个定时器,避免多个元素抢同一个定时器造成bug
- liEles[i].timer = null;
- liEles[i].onmouseover = function(){
- //传入this值,指定当前操作的是哪个li
- playFun(this, 300);
- };
- liEles[i].onmouseout = function(){
- playFun(this, 200);
- };
- }
- }
- //var timer = null;
- function playFun(obj,itarget){
- clearInterval(obj.timer);
- obj.timer = setInterval(function(){
- var speed = (itarget - <span style="color:#ff0000;">parseInt(getStle(obj,"width"))</span>)/10;
- speed = speed>0?Math.ceil(speed):Math.floor(speed);
- if(<span style="color:#ff0000;">parseInt(getStle(obj,"width"))</span> == itarget){
- clearInterval(obj.timer);
- }else{
- //obj.style.width = obj.offsetWidth + speed + "px";
- obj.style.width = <span style="color:#ff0000;">parseInt(getStle(obj,"width"))</span> + speed + "px";
- }
- },50);
- }
- //获取属性值
- function getStle(ele,attr){
- if(ele.currentStyle){ //兼容IE浏览器
- return ele.currentStyle[attr];
- }else{ //兼容firefox浏览器
- return getComputedStyle(ele,false)[attr];
- }
- }</span>
- </script>
- </head>
- <body>
- <ul>
- <li></li>
- <li></li>
- <li></li>
- </ul>
- </body>
- </html>
法2)offsetWidth的值包含了border, 但是 offsetClient = width + padding 没有包含border的值,单单就这个例子而言,用 offsetClient 还是可以解决问题,修改部分代码如下。但是如果css中设置了padding值,就不好使了,所以,还是采用老师的方法最好,封装一个专门获取css属性值得函数,可以获取别的属性。
- function playFun(obj,itarget){
- clearInterval(obj.timer);
- obj.timer = setInterval(function(){
- var speed = (itarget - obj.clientWidth)/10;
- speed = speed>0?Math.ceil(speed):Math.floor(speed);
- if(obj.clientWidth == itarget){
- clearInterval(obj.timer);
- }else{
- obj.style.width = obj.clientWidth + speed + "px";
- }
- },50);
- }
3. 将代码封装成可以获取任意属性值(这里主要要对属性为opacity时做单独的处理),练习代码如下:
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <style type="text/css">
- *{
- padding:0;
- margin:0;
- }
- ul li{
- list-style:none;
- width:200px;
- height:100px;
- background:green;
- margin:20px;
- border:2px #ccc solid;
- filter:alpha(opacity:30);
- opacity:0.3;
- }
- </style>
- <script type="text/javascript">
- window.onload = function(){
- var liEles = document.getElementsByTagName("li");
- for(var i=0; i<liEles.length; i++){
- //给每一个li元素定义一个定时器,避免多个元素抢同一个定时器造成bug
- liEles[i].timer = null;
- liEles[i].onmouseover = function(){
- //传入this值,指定当前操作的是哪个li
- //playFun(this, 300, "width");
- playFun(this, 100, "opacity");
- };
- liEles[i].onmouseout = function(){
- playFun(this, 30, "opacity");
- };
- }
- }
- function playFun(obj,itarget,attr){
- clearInterval(obj.timer);
- obj.timer = setInterval(function(){
- var getValue = 0;
- if(attr == "opacity"){
- //parseFloat返回小数值
- //由于计算机存储小数有误差,采用Math.round()四舍五入得整数
- getValue = Math.round(parseFloat(getStle(obj,attr))*100);
- }else{
- getValue = parseInt(getStle(obj,attr));
- }
- var speed = (itarget - getValue)/10;
- speed = speed>0?Math.ceil(speed):Math.floor(speed);
- if(getValue == itarget){
- clearInterval(obj.timer);
- }else{
- //obj.style[attr],采用中括号传参
- if(attr == "opacity"){
- getValue += speed;
- obj.style["filter"] = "alpha(opacity:"+ getValue +")";
- obj.style["opacity"] = getValue/100;
- }else{
- obj.style[attr] = getValue + speed + "px";
- }
- }
- },50);
- }
- //获取属性值
- function getStle(ele,attr){
- if(ele.currentStyle){ //兼容IE浏览器
- return ele.currentStyle[attr];
- }else{ //兼容firefox浏览器
- return getComputedStyle(ele,false)[attr];
- }
- }
- </script>
- </head>
- <body>
- <ul>
- <li></li>
- <li></li>
- <li></li>
- </ul>
- </body>
- </html>
1)opacity的取值,getStle(obj,attr)获取的opacity返回值为小数,需要采用parseFloat(getStle(obj,attr))*100 来获取并乘以100转为整数;
2)由于计算机存储小数有误差,所以还需采用Math.round()四舍五入得整数;
3)最后赋值css时,注意浏览器兼容:
obj.style["filter"] = "alpha(opacity:"+ getValue +")";obj.style["opacity"] = getValue/100; // opacity 的值以小数表示