原生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 ...
随机推荐
- JeeSite 部署到linux服务器
1.打包 改数据库连接 打包war包https://www.cnblogs.com/wdw31210/p/9878422.html 2.上传到linux 服务器的tomcat/webapps/ 下 安 ...
- 15 Zabbix Item类型之Zabbix trapper类型
点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 15 Zabbix Item类型之Zabbix trapper类型 zabbix获取数据时有时会出 ...
- 【BZOJ1831】[AHOI2008]逆序对(动态规划)
[BZOJ1831][AHOI2008]逆序对(动态规划) 题面 BZOJ 洛谷 题解 显然填入的数拎出来是不降的. 那么就可以直接大力\(dp\). 设\(f[i][j]\)表示当前填到了\(i\) ...
- 【UOJ#177】欧拉回路
[UOJ#177]欧拉回路 题面 UOJ 题解 首先图不连通就没啥好搞的了. 对于无向图而言,每个点度数为偶数. 对于有向图而言,每个点入度等于出度. 然后就是一本通上有的做法,直接\(dfs\)一遍 ...
- portainer图形化监控
步骤1,在Swarm集群中创建一个新的覆盖网络:$ docker network create --driver overlay --attachable portainer_agent_networ ...
- Jmeter工具之上传图片,上传音频文件接口
https://www.jianshu.com/p/f23f7fe20bf3 互联网时代的来临,不同手机上安装的APP,还是PC端的应用软件或多或多都会涉及到图片的上传,那么在Jmeter工具如何模拟 ...
- 【UNR #1】火车管理
题目描述 uoj 旗下有一个火车站,用来管理属于 uoj 的小火车. 火车站一共有 nn 条编号为 1,…,n1,…,n 的,只有一端的用来存放小火车的铁路,由于小火车特殊的构造,每条铁路可以停放无数 ...
- 洛谷P2831 愤怒的小鸟 + 篮球比赛1 2
这三道题一起做,有一点心得吧. 愤怒的小鸟,一眼看上去是爆搜,但是实现起来有困难(我打了0分出来). 还有一种解法是状压DP. 抛物线一共只有那么多条,我们枚举抛物线(枚举两个点),这样就能够预处理出 ...
- 没有上司的舞会 codevs 1380
上树DP,记忆化搜索. 本题老师讲的方法是直接树形DP,但是由于我对树并不够了解,什么dfs也不想尝试(虽然感觉自己可以搞),于是搞了个结构体存点以及该点的信息,用f[i][j]作为记忆化数组.以后最 ...
- A1137. Final Grading
For a student taking the online course "Data Structures" on China University MOOC (http:// ...