原生JavaScript运动功能系列(三):多物体多值运动
- 多物体同时出发运动函数实现
- 多属性同步运动变化实现
一、多物同时触发运动函数实现
前面两个动画示例基本理解了动画的核心:位置变化和速度变化,操作的核心就是定时器分段叠加属性值。但是动画还是基于单个元素实现,如果将前面封装的动画实现方法同时触发我可以肯定的告诉你会有bug,我们先来写一个示例看看这个bug是什么?示例需求是有三个div同时居于浏览器左侧,当鼠标进入div时,当前div的宽度变宽,鼠标离开时,宽度也已动画状态恢复。
//html
<div></div>
<div></div>
<div></div> //css
div{
width: 100px;
height: 50px;
margin-bottom: 10px;
background-color: red;
border: 1px solid #000;
}
还是基于昨天的缓冲运动来做,只是将变化样式从相对浏览器位置换成元素宽度,另外因为样式变化,考虑后期也会经常需要获取不同的样式需要,封装了一个获取样式的方法getStyle();这个段代码是为了引出从单个触发动画到多个触发动画函数产生的bug,代码存在bug,也是为后面的内容做铺垫。
var divArr = document.getElementsByTagName("div");
var timer = null;
for(var i = 0; i < divArr.length; i++){
divArr[i].onmouseover = function(){
startMove(this,400,7);
}
divArr[i].onmouseout = function(){
startMove(this,100,7);
}
}
function getStyle(dom,attr){
if(dom.currentStyle){ return dom.currentStyle[attr];
}else{
return window.getComputedStyle(dom,false)[attr];
}
}
function startMove(dom,target,divisor){
clearInterval(timer);
var iSpeed,iCur;
timer = setInterval(function (){
iCur = parseInt(getStyle(dom,"width"));
iSpeed = (target-iCur)/divisor;
//console.log(iSpeed+"..."+Math.ceil(iSpeed));
iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);
if(iCur === target){
clearInterval(timer);
}else{
dom.style.width=iCur+iSpeed+'px';
}
},30);
}
这段代码出现的问题就是,当触发一个div的动画效果,动画还没有运行到终点时,又触发了另一个div的动画,这时前一个动画就会停在当前位置。就像下面这个效果(测试bug效果)。
只有最后一个元素运动到了终点,其实这个bug很明显,从单个元素运动到多个元素运动,首先要看是单点触发动画,还是多点触发动画,上面这个示例就是多点触发动画,多点触发动画就必然需要每个触发点都有自己的一个定时器来执行属于自己的动画程序。那单点触发动画会出现在什么情况呢?开篇的任务列表中有一个链式运动,就是一个触发点接着就是一个元素接着一个元素运动,这种情况就可以使用单点触发,但是为了运动函数的扩展性,一般都采用多点触发封装,在实际开发中,动画函数肯定是被多点触发的。
实质上这个bug是由闭包产生的,因为在上面的运动函数中的定时器,是由写在动画函数执行前的timer变量统一管理,当另一个动画开始执行时,会通过作用域链上的timer将之前的定时器关闭,并在这个变量上赋值自己的定时器来执行自己的动画,解决这个问题的方法很简单就是给每个执行动画拥有自己的timer来单独管理自己的定时器,就不会出现闭包冲突bug了。这个修改很简单,就是给每个触发运动的DOM对象添加一个timer属性来管理自己的定时器就可以了。修改方案如下:
//20、22、28的timer修改成dom.timer
//第二行的var timer = null;删除
二、多属性同步运动变化实现
在前面的所有示例中,我们都是在操作单一样式运动,怎么操作多个样式同步运动变化呢?其实在上一个示例中,我已经埋下了伏笔,这个伏笔就是获取对象样式的方法getStyle(dom,attr)。其实理论上很简单,我们只需要将之前传入的样式终点参数,改成多个样式的终点参数对象就可以了,然后在每次定时器执行时,循环这个对象然后对每个样式进行修改就可以了,这里不讨论js单线程,每个样式修改都是存在先后顺序的,因为计算机的执行速度是以毫秒级速度执行,人眼识别可以忽略这种先后顺序。由于这个功能从功能逻辑上来讲很简单,但是实际处理过程中还是会有些需要注意的细节,而且这些细节有时候会对你的程序造成致命的打击,在没有实现代码之前不太容易用语言表达清楚,所以先上代码,然后再来就着代码剖析具体问题:
//css
div{
position: absolute;
left: 0;
width: 100px;
height: 50px;
margin-bottom: 10px;
background-color: red;
border: 1px solid #000;
}
//html
<div class="demo"></div> //js
var demoDiv = document.getElementsByClassName("demo")[0];
var targetObj = {
width: 400,
height: 300,
opacity: 50,
left: 150,
top: 150
}
demoDiv.onclick = function(){
startMove(this,targetObj,7);
}
function getStyle(dom,attr){
if(dom.currentStyle){
return dom.currentStyle[attr];
}else{
return window.getComputedStyle(dom,false)[attr];
}
}
function startMove(dom,json,divisor){
clearInterval(dom.timer);
var iSpeed,iCur;
dom.timer = setInterval(function (){
var bStop = true;
for(var attr in json){
if(attr == "opacity"){
iCur = Math.round( parseFloat( getStyle(dom,attr) ) * 100);
}else{
iCur = parseInt( getStyle(dom,attr) );
}
iSpeed = (json[attr]-iCur)/divisor;
iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);
if(attr == "opacity"){
dom.style.opacity = (iCur + iSpeed)/100;
}else{
dom.style[attr] = iCur+iSpeed+'px';
}
if( iCur != json[attr]){
bStop = false;
}
}
if(bStop){
clearInterval(dom.timer);
}
},30);
}
一开始看到这个方法估计会有点懵,大神略过,下面我就来将这个方法的全部内容逐个剖析:
- 39~43:获取变量==>整数值变量与浮点数变量的操作差异;
- 44~45:计算单次运动距离;
- 46~50:修改元素样式值;
- 51~53:判断元素的所有样式是否都到达终点;
- 55~57:确认样式都到达终点后,结束定时器
需要重点剖析的是获取变量和变量赋值,以及什么时候结束定时器的实际业务逻辑问题。由于opacity的值是0~1,所以获取样式后转成浮点数值这个我相信都可以理解,然后放大100倍是因为考虑到需要计算移动距离,并且可以和其他值一样使用同一的逻辑计算(匹配45行代码),那为什么在乘以100的时候需要做四舍五入(Math.round)的操作呢?如果对js的数值标准IEEE_754浮点数计算标准有所了解就能明白,不明白也没关系,我来给大家解释一下,这是因为js的浮点数计算不精确造成的,比如出现(0.58*100!=58),控制台打印结果是:57.99999999999999,还有比如0.1+0.2!=0.3;太深入的原理就不在这里讲了,需要在这个地方使用Math.round四舍五入的原因就是精度丢失造成的,但是这个差值非常的小,所以可以使用四舍五入的方式获得我们想要的精确的数值,如果这个地方不使用,可能会出现死循环。(这里测试IE11可以没问题,为了准确的获取值是有必要的)。
然后什么时候结束定时器执行这个问题是因为我们不知道再调用方法时会要修改多少样式,而因为运动实际上不会同时到达终点,所以需要判断每个样式都到达终点后结束定时器。
原生JavaScript运动功能系列(三):多物体多值运动的更多相关文章
- 原生JavaScript运动功能系列(二):缓冲运动
匀速运动实现回顾 缓冲运动剖析 示例实现 方法提取 匀速运动实现回顾及缓冲运动剖析: 在这个系列的上一篇博客中原生JavaScript运动功能系列(一):运动功能剖析与匀速运动实现就运动的核心功能组成 ...
- 原生JavaScript运动功能系列(四):多物体多值链式运动
原生JavaScript运动功能系列(一):运动功能剖析与匀速运动实现 原生JavaScript运动功能系列(二):缓冲运动 原生JavaScript运动功能系列(三):多物体多值运动 多物体多值链式 ...
- 原生JavaScript运动功能系列(五):定时定点运动
原生JavaScript运动功能系列(一):运动功能剖析与匀速运动实现 原生JavaScript运动功能系列(二):缓冲运动 原生JavaScript运动功能系列(三):多物体多值运动 原生JavaS ...
- js 运动函数篇 (一) (匀速运动、缓冲运动、多物体运动、多物体不同值运动、多物体多值运动)层层深入
前言: 本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽. 本篇文章为您分析一下原生JS写 匀速运动.缓冲运动.多物体运 ...
- JavaScript 运动(缓冲运动,多物体运动 ,多物体多值运动+回调机制)
匀速运动 (当需要物体做匀速运动直接调用statMove函数) function startMove(dom,targetPosetion){ //dom : 运动对象,targetPositio ...
- 原生JavaScript运动功能系列(一):运动功能剖析与匀速运动实现
在我们日常生活中运动就是必不可少的部分,走路.跑步.打篮球等.在网页交互设计上运动也是必不可少的部分,创建的网站交互设计运动模块有轮播图,下拉菜单,还有各种炫酷的游戏效果都跟运动密切相关.所以很重要, ...
- javascript学习-原生javascript的小特效(多物体运动效果)
前些日子看了个视频所以就模仿它的技术来为大家做出几个简单的JS小特效 今天为大家做的是多个物体的运动效果, 1:HTML <body> <ul> <li> ...
- Javascript模块化编程系列三: CommonJS & AMD 模块化规范描述
CommonJS Module 规范 CommonJS 的模块化规范描述在Modules/1.1.1 中 目前实现此规格的包有: Yabble,CouchDB,Narwhal (0.2), Wakan ...
- javascript类继承系列三(对象伪装)
原理:在子类的构造器上调用超类构造器(父类构造器中的this指向子类实例),js提供了apply()和call()函数,可以实现这种调用 function baseClass() { this.col ...
随机推荐
- Catch the Theves HDU - 3870(s - t平面图最小割)
题意: 板题...建个图..跑一遍spfa就好了...嘻嘻... 注意..数组大小就好啦..400 * 400 = 1600 我也是抑郁了..沙雕的我.. #include <iostream& ...
- Codeforces Round #540 (Div. 3) A,B,C,D2,E,F1
A. Water Buying 链接:http://codeforces.com/contest/1118/problem/A 实现代码: #include<bits/stdc++.h> ...
- 【宝塔linux】 导入mysql 大文件失败的问题
导入数据库有四种方法 1.宝塔网站自带的数据库导入 2.phpmyadmin导入 3.远程到linux服务器用导入命令 使用xshell进入到控制台 1.首先建空数据库 mysql>create ...
- MT【247】恒成立画图像
若$|x^2+|x-a|+3a|\le2$对任意$x\in[-1,1]$恒成立,则$a$ 的取值范围_____ 分析:转化为$f(x)=|x-a|+3a$的图像夹在$y=-2-x^2$与$y=2-x^ ...
- 怎么让 Lua 5.3.4 支持中文变量名和中文函数名
1. 在官网下载最新版Lua源码 Lua :Download 2. 解压后进入目录,找到/src/llex.c,打开修改 找到如下内容 修改为下面代码,并保存. default: { if (lisl ...
- Treap树 笔记
预备知识:二叉查找树.堆(heap).平衡二叉树(AVL)的基本操作(左旋右旋) 定义: Treap.平衡二叉树.Tree+Heap.树堆. 每个结点两个键值(key.priority). 性质1. ...
- [2017-8-02]Android Learning Day8
自定义动画效果 新建一个customAnim类 package com.liwenchi.myapplication; import android.view.animation.Animation; ...
- 编写高质量代码:改善Java程序的151个建议 --[0~25]
警惕自增的陷阱 public class Client7 { public static void main(String[] args) { int count=0; for(int i=0; i& ...
- golang go语言通道类型的通道示例 通道的通道
几点注意:go的无缓存通道 通道make 创建后,即使里面是空的,也可以取里面内容.但是程序会被阻塞. 通道的规则是没人取,是不能往里面放的.放的线程会阻塞. 最外层的requestChan相当于一个 ...
- 【模板】ac自动机
本来是真的特别不想写这个的 但是有段时间洛谷天天智推这个可能是我太菜了 然后觉得这个也不难 乘着今早没事写下 来这保存下 方便下次食用 #include <bits/stdc++.h> u ...