原生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 ...
随机推荐
- 【XSY2754】求和 莫比乌斯反演 杜教筛
题目描述 给你\(n,p\),求 \[ \sum_{i=1}^n\sum_{j=1}^i\sum_{k=1}^i\gcd(i,j,k)\mod p \] \(n\leq {10}^9\) 题解 \[ ...
- Shell基础 - Bash基础功能
历史命令 history选项: -c 清空历史命令 -w 立即保存历史命令Linux 下输入过的历史命令,都会保存在根目录下的:~/root/.bash_history 文件中默认保存 1000 条, ...
- Mysql 从入门到遗忘
高级数据过滤: WHERE AND OR NOT 总是与其他操作符一起使用,用在要过滤的前面. 通配符过滤: LIKE: %相当于正则中的.*?,_相当于正则中的.. $ select id from ...
- PHP基础学习----字符串操作
1.单引号和双引号的区别 在php中,字符串的定义可以使用英文单引号'',也可以使用英文双引号“”: <?php $str = 'hello'; echo "str is $str&q ...
- 牛客小白月赛12C (线性筛积性函数)
链接:https://ac.nowcoder.com/acm/contest/392/C来源:牛客网 题目描述 华华刚刚帮月月完成了作业.为了展示自己的学习水平之高超,华华还给月月出了一道类似的题: ...
- AtCoder arc061C Snuke's Subway Trip
大意: 给你一张无向图,边有种类. 当你第一次/重新进入某种边时费用 + 1 在同一种边之间行走无费用. 求 1 到 n 的最小费用. 嗯...乍一看有一个很直观的想法:记录每个点的最短路的上一条边的 ...
- A1040. Longest Symmetric String
Given a string, you are supposed to output the length of the longest symmetric sub-string. For examp ...
- A1042. Shuffling Machine
Shuffling is a procedure used to randomize a deck of playing cards. Because standard shuffling techn ...
- django 配置media 存放调用 图片、图标等文件
一.需求分析: 一般在网站开发中,有很多类似于用户头像.用户上传的文件,这些经常要改变的媒体文件,需要有一个地方存放,于是就需要media目录,起到跟static类似的功能. 二.在settings. ...
- IntelliJ IDEA Cannot resolve symbol ''
study from : https://www.cnblogs.com/linmengfei/p/7909196.html File->Invalidate Caches 点击File | I ...