【repost】JavaScript完美运动框架的进阶之旅
运动框架的实现思路
运动,其实就是在一段时间内改变
left
、right
、width
、height
、opactiy
的值,到达目的地之后停止。
现在按照以下步骤来进行我们的运动框架的封装:
- 匀速运动。
- 缓冲运动。
- 多物体运动。
- 任意值变化。
- 链式运动。
- 同时运动。
(一)匀速运动
速度动画
运动基础
思考:如何让div
动起来?
如下:
- 设置元素为绝对定位,只有绝对定位后,
left
,top
等值才生效。 - 定时器的使用(动态改变值),这里使用
setInterval()
每隔指定的时间执行代码。- 计时器
setInterval(函数,交互时间(毫秒))
:在执行时,从载入页面后每隔指定的时间执行代码。 - 取消计时器
clearInterval(函数)
方法可取消由setInterval()
设置的交互时间。
- 计时器
- 获取当前的位置,大小等等。
offsetLeft
(当前元素相对父元素位置)。 - 速度–物体运动的快慢
- 定时器间隔时间
- 改变值的大小
根据上面的信息我们就可以开始封装运动框架创建一个变化的div
了。
1
2
3
4
5
6
7
8
9
10
|
/**
* 运动框架-1-动起来
* @param {HTMLElement} element 进行运动的节点
*/
var timer = null;
function startMove(element) {
timer = setInterval(function () {//定时器
element.style.left = element.offsetLeft + 5 + "px";
}, 30);
}
|
你没看错,就是那么简单。但是等等, what? 怎么不会停?WTF?
那是因为我们没有运动终止条件。好再还是比较简单。直接在定时器内部,判断到达目标值,清除定时器就行拉!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/**
* 运动框架-2-运动终止
* @param {HTMLElement} element 进行运动的节点
* @param {number} iTarget 运动终止条件。
*/
var timer = null;
function startMove(element, iTarget) {
timer = setInterval(function () {
element.style.left = element.offsetLeft + 5 + "px";
if (element.offsetLeft === iTarget) {//停止条件
clearInterval(timer);
}
}, 30);
}
|
就这样是不是就完成了呢?已经ok了呢?
no。还有一些Bug需要处理。
运动中的Bug
- 速度取到某些值会无法停止
- 到达位置后再点击还会运动
- 重复点击速度加快
- 速度无法更改
解决BUG
- 速度取到某些值会无法停止(这个Bug稍后解决,在进化过程中自然解决)
- 把运动和停止隔开(if/else)
- 在开始运动时,关闭已有定时器
- 把速度用变量保存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/**
* 运动框架-3-解决Bug
*/
var timer = null;
function startMove(element, iTarget) {
clearInterval(timer);//在开始运动时,关闭已有定时器
timer = setInterval(function () {
var iSpeed = 5;//把速度用变量保存
//把运动和停止隔开(if/else)
if (element.offsetLeft === iTarget) {//结束运动
clearInterval(timer);
} else {
element.style.left = element.offsetLeft + iSpeed + "px";
}
}, 30);
}
|
这样一个简单的运动框架就完成了。但是,再等等。只能向右走?别急,我们不是定义了把速度变成为了变量吗?只需要对它进行一些处理就行啦!var iSpeed = 5;
–>
1
2
3
4
5
6
7
|
//判断距离目标位置,达到自动变化速度正负
var iSpeed = 0;
if (element.offsetLeft < iTarget) {
iSpeed = 5;
} else {
iSpeed = -5;
}
|
透明度动画
- 用变量
alpha
储存当前透明度。 - 把上面的
element.offsetLeft
改成变量alpha
。 - 运动和停止条件部分进行更改。如下:
1
2
3
4
5
6
7
8
|
//透明度浏览器兼容实现
if (alpha === iTarget) {
clearInterval(time);
} else {
alpha += speed;
element.style.filter = 'alpha(opacity:' + alpha + ')'; //兼容IE
element.style.opacity = alpha / 100;//标准
}
|
(二)缓冲动画
思考:怎么样才是缓冲动画?
应该有以下几点:
- 逐渐变慢,最后停止
- 距离越远速度越大
- 速度由距离决定
- 速度=(目标值-当前值)/缩放系数
- Bug :速度取整(使用Math方法),不然会闪
- 向上取整。
Math.ceil(iSpeed)
- 向下取整。
Math.floor(iSpeed)
- 向上取整。
还是对速度作文章:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/**
* 运动框架-4-缓冲动画
*/
function startMove(element, iTarget) {
clearInterval(timer);
timer = setInterval(function () {
//因为速度要动态改变,所以必须放在定时器中
var iSpeed = (iTarget - element.offsetLeft) / 10; //(目标值-当前值)/缩放系数=速度
iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整
if (element.offsetLeft === iTarget) {//结束运动
clearInterval(timer);
} else {
element.style.left = element.offsetLeft + iSpeed + "px";
}
}, 30);
}
|
- 做到这里,(速度取到某些值会无法停止)这个Bug就自动解决啦!
- 例子:缓冲菜单
- 跟随页面滚动的缓冲侧边栏
在线演示:codepen
- 跟随页面滚动的缓冲侧边栏
潜在问题目标值不是整数时
(三)多物体运动
思考:如何实现多物体运动?
- 单定时器,存在问题。每个div一个定时器
- 定时器作为对象的属性
- 直接使用
element.timer
把定时器变成对象上的一个属性。
- 直接使用
- 参数的传递:物体/目标值
比较简单把上面框架的进行如下更改:timer
–>element.timer
就这样就行啦!
(四)任意值变化
咳咳。我们来给div加个1px的边框。boder :1px solid #000
然后来试试下面的代码
1
2
3
|
setInterval(function () {
oDiv.style.width = oDiv.offsetWidth - 1 + "px";
}, 30)
|
嗯,神奇的事情发生了!what?我设置的不是宽度在减吗?怎么尼玛增加了! 不对啊,大兄弟。
究竟哪里出了问题呢?
一起找找资料,看看文档,原来offset
这一系列的属性都会存在,被其他属性干扰的问题。
好吧,既然不能用,那么我们就顺便把任意值变化给做了吧。
第一步:获取实际样式
使用offsetLeft..等获取样式时, 若设置了边框, padding, 等可以改变元素宽度高度的属性时会出现BUG..
- 通过查找发现
element.currentStyle(attr)
可以获取计算过之后的属性。 - 但是因为兼容性的问题,需封装getStyle函数。(万恶的IE)
- 当然配合CSS的
box-sizing
属性设为border-box
可以达到一样的效果 ? (自认为,未验证)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/**
* 获取实际样式函数
* @param {HTMLElement} element 需要寻找的样式的html节点
* @param {String]} attr 在对象中寻找的样式属性
* @returns {String} 获取到的属性
*/
function getStyle(element, attr) {
//IE写法
if (element.currentStyle) {
return element.currentStyle[attr];
//标准
} else {
return getComputedStyle(element, false)[attr];
}
}
|
第二步:改造原函数
- 添加参数,
attr
表示需要改变的属性值。 - 更改
element.offsetLeft
为getStyle(element, attr)
。- 需要注意的是:
getStyle(element, attr)
不能直接使用,因为它获取到的字符串,例:10px
。 - 变量
iCurrent
使用parseInt()
,将样式转成数字。
- 需要注意的是:
element.style.left
为element.style[attr]
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/**
* 运动框架-4-任意值变化
* @param {HTMLElement} element 运动对象
* @param {string} attr 需要改变的属性。
* @param {number} iTarget 目标值
*/
function startMove(element, attr, iTarget) {
clearInterval(element.timer);
element.timer = setInterval(function () {
//因为速度要动态改变,所以必须放在定时器中
var iCurrent=0;
iCurrent = parseInt(getStyle(element, attr));//实际样式大小
var iSpeed = (iTarget - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度
iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整
if (iCurrent === iTarget) {//结束运动
clearInterval(element.timer);
} else {
element.style[attr] = iCurrent + iSpeed + "px";
}
}, 30);
}
|
试一试,这样是不是就可以了呢?
还记得上面我们写的透明度变化吗? 再试试
果然还是不行, (废话,你见过透明度有”px”单位的么? - -白眼
)
第三步:透明度兼容处理
思考:需要对那些属性进行修改?
- 判断
attr
是不是透明度属性opacity
。 - 对于速度进行处理。
- 为透明度时,由于获取到的透明度会是小数,所以需要 * 100
- 并且由于计算机储存浮点数的问题,还需要将小数,进行四舍五入为整数。使用:
Math.round(parseFloat(getStyle(element, attr)) * 100)
。 - 否则,继续使用默认的速度。
- 对结果输出部分进行更改。
- 判断是透明度属性,使用透明度方法
- 否则,使用使用默认的输出格式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/**
* 运动框架-5-兼容透明度
* @param {HTMLElement} element 运动对象
* @param {string} attr 需要改变的属性。
* @param {number} iTarget 目标值
*/
function startMove(element, attr, iTarget) {
clearInterval(element.timer);
element.timer = setInterval(function () {
//因为速度要动态改变,所以必须放在定时器中
var iCurrent = 0;
if (attr === "opacity") { //为透明度时执行。
iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100);
} else { //默认情况
iCurrent = parseInt(getStyle(element, attr)); //实际样式大小
}
var iSpeed = (iTarget - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度
iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整
if (iCurrent === iTarget) {//结束运动
clearInterval(element.timer);
} else {
if (attr === "opacity") { //为透明度时,执行
element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE
element.style.opacity = (iCurrent + iSpeed) / 100; //标准
} else { //默认
element.style[attr] = iCurrent + iSpeed + "px";
}
}
}, 30);
}
|
到这里,这个运动框架就基本上完成了。但是,我们是追求完美的不是吗?
继续进化!
(五)链式动画
链式动画:顾名思义,就是在该次运动停止时,开始下一次运动。
如何实现呢?
- 使用回调函数:运动停止时,执行函数
- 添加
func
形参(回调函数)。 - 在当前属性到达目的地时
iCurrent === iTarget
,判断是否有回调函数存在,有则执行。
1
2
3
4
5
6
|
if (iCurrent === iTarget) {//结束运动
clearInterval(element.timer);
if (func) {
func();//回调函数
}
}
|
good,链式动画完成!距离完美还差一步!
(六)同时运动
思考:如何实现同时运动?
- 使用JSON传递多个值
- 使用
for in
循环,遍历属性,与值。 - 定时器问题!(运动提前停止)
- 在循环外设置变量,假设所有的值都到达了目的值为true
- 在循环中检测是否到达目标值,若没有值未到则为false
- 在循环结束后,检测是否全部达到目标值.是则清除定时器
实现:
- 删除
attr
与iTarget
两个形参,改为json
- 在函数开始时,设置一个标记
var flag = true; //假设所有运动到达终点.
- 在定时器内使用
for in
,遍历属性与目标,改写原来的attr
与iTarget
,为json的属性与值 - 修改运动终止条件,只有每一项的实际属性值
iCurrent
,等于目标值json[attr]
时,flag
才为true
。清除定时器,判断是否回调。 - 否则,继续执行代码,直到所有属性值等于目标值。
完美运动框架
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
/**
* 获取实际样式函数
* @param {HTMLElement} element 需要寻找的样式的html节点
* @param {String]} attr 在对象中寻找的样式属性
* @returns {String} 获取到的属性
*/
function getStyle(element, attr) {
//IE写法
if (element.currentStyle) {
return element.currentStyle[attr];
//标准
} else {
return getComputedStyle(element, false)[attr];
}
}
/**
* 完美运动框架
* @param {HTMLElement} element 运动对象
* @param {JSON} json 属性:目标值
* @property {String} attr 属性值
* @config {Number} target 目标值
* @param {function} func 可选,回调函数,链式动画。
*/
function startMove(element, json, func) {
var flag = true; //假设所有运动到达终点.
clearInterval(element.timer);
element.timer = setInterval(function () {
for (var attr in json) {
//1.取当前的属性值。
var iCurrent = 0;
if (attr === "opacity") { //为透明度时执行。
iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100);
} else { //默认情况
iCurrent = parseInt(getStyle(element, attr)); //实际样式大小
}
//2.算运动速度,动画缓冲效果
var iSpeed = (json[attr] - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度
iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整
//3.未到达目标值时,执行代码
if (iCurrent != json[attr]) {
flag = false; //终止条件
if (attr === "opacity") { //为透明度时,执行
element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE
element.style.opacity = (iCurrent + iSpeed) / 100; //标准
} else { //默认
element.style[attr] = iCurrent + iSpeed + "px";
}
} else {
flag = true;
}
//4. 运动终止,是否回调
if (flag) {
clearInterval(element.timer);
if (func) {
func();
}
}
}
}, 30);
}
|
运动框架总结
- 运动框架演变过程
框架 | 变化 |
---|---|
startMove(element) | 运动 |
startMove(element,iTarget) | 匀速–>缓冲–>多物体 |
startMove(element,attr,iTargrt) | 任意值 |
startMove(element,attr,iTargrt,func) | 链式运动 |
startMove(element,json,func) | 多值(同时)–>完美运动框架 |
【repost】JavaScript完美运动框架的进阶之旅的更多相关文章
- JavaScript “完美运动框架”
/* “完美运动框架”,所谓“完美”,就是可以实现多个参数,多个物体运动互不影响的一个运动函数move(). * 大致结构如下:运动框架 EXP: move(obj,{width:200,height ...
- Javascript 完美运动框架——逐行分析代码,让你轻松了解运动的原理
大家一听这名字就知道,有了这套框架 网上的效果基本都是可以实现的.实际上之前的运动框架还是有局限性的,就是不能让好几个值一块运动. 那这个问题怎么解决呢? 我们先来看看之前的运动框架 function ...
- 完美运动框架(js)
一.前言 学习js运动时,由于在实现多种不同运动效果过程中很多代码是重复的,故将其封装达到代码重用. 二.代码封装重用 function startMove(obj, json, fnEnd){ cl ...
- JavaScript的运动框架学习总结
一.目录 1. 入门案例——实现匀速运动 2. 入门案例——实现缓冲运动 3. 实现任意值的运动框架v.1 4. 改进任意值的运动框架v.2 5. 改进任意值的运动框架v.3 6. 实现链式运动框架 ...
- JS完美运动框架
这套框架实现了多物体,任意值,链式运动,多值运动,基本满足常见的需求. /* 功能:完美运动框架,可以实现多物体,任意值,链式运动,多值运动 版本:V1.0 兼容性:Chrome,FF,IE8+ (o ...
- JS 之完美运动框架
完美运动框架是对原来的任意值运动框架的改善和效率的提升,即利用了json对属性进行封装,从而提高效率: window.onload=function(){ var oDiv=document.getE ...
- 2015.8.2js-19(完美运动框架)
/*完美运动框架*/ //1.先清除定时期,2,获取样式,如果是opacity则单独解决,3,定义速度,4,定义当前值是否到达目的地,5,判断当前值是否到达目的地,6运动基本,如果是opacity f ...
- JS完美运动框架【利用了Json】
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- Javascript之运动框架2
运动框架2与运动框架1的不同之处在于,运动框架2是框架1的升级版,首先完善了传入值,改为move(obj,json,options),在options里面,可以选择传入或者不传入时间,运动形式,以及函 ...
随机推荐
- 初步认识Node 之Web框架
上一篇我们认识了Node是什么之后,这一篇我们主要了解的就是它的框架了.而它的框架又分为两大类,Web框架和全栈框架,接下来我们一个一个的来了解. Web框架 Web框架可以细分为Web应用程序 ...
- 快速打造跨平台开发环境 vagrant + virtualbox + box
工欲善其事必先利其器,开发环境 和 开发工具 就是 我们开发人员的剑,所以我们需要一个快并且好用的剑 刚开始做开发的时候的都是把开发环境 配置在 自己的电脑上,随着后面我们接触的东西越来越多,慢慢的电 ...
- linux内核调试技术之自构proc
1.简介 在上一篇中,在内核中使用printk可以讲调试信息保存在log_buf缓冲区中,可以使用命令 #cat /proc/kmsg 将缓冲区的数区的数数据打印出来,今天我们就来研究一下,自己写k ...
- iOS: 为画板App增加 Undo/Redo(撤销/重做)操作
这个随笔的内容以上一个随笔为基础,(在iOS中实现一个简单的画板),上一个随笔实现了一个简单的画板: 今天我们要为这个画板增加Undo/Redo操作,当画错了一笔,可以撤销它,或者撤销之后后悔了, ...
- 基于STM32Cube的脉冲输出
方法一:定时器定时I/O反转生成脉冲波形 1.建立STM32Cube选择STM32F429,我使用的STM32F429-discovery开发板,晶振是8MHz,时钟配置为180M,这样定时器内部时钟 ...
- 《高性能javascript》 领悟随笔之-------DOM编程篇(二)
<高性能javascript> 领悟随笔之-------DOM编程篇二 序:在javaSctipt中,ECMASCRIPT规定了它的语法,BOM实现了页面与浏览器的交互,而DOM则承载着整 ...
- C# 条件编译
本文导读: C#的预处理器指令从来不会转化为可执行代码的命令,但是会影响编译过程的各个方面,常用的预处理器指令有#define.#undef.#if,#elif,#else和#endif等等,下面介绍 ...
- EC笔记:第3部分:15、对原始资源的访问
使用对象来管理资源,可以避免因个人疏忽带来的一些低级错误,但是不是每件事都是称心如意的. 一些函数依然使用原始的资源对象,那么我们就需要为这些函数提供一个接口,让他们可以获取到原始对象. 继续拿13节 ...
- input文本框录入字母自动大写
向文本框输入文字时,如何让小写字母自动变为大写呢?有一个简单有效的做法是用CSS. <input name="t1" type="text" style= ...
- SharePoint 2010/2013/2016内容数据库与网站集的关系
总得来说,内容数据库和网站集的关系是: 一个内容数据库里可以有多个网站集,但是一个网站集只能存在于一个内容数据库. 那么问题来了 问题1:我能否在创建网站集时指定内容数据库呢?或者说我能在指定的内容数 ...