JavaScript完美运动框架的进阶之旅

运动框架的实现思路

 

运动,其实就是在一段时间内改变left、right、width、height、opactiy的值,到达目的地之后停止。

 
现在按照以下步骤来进行我们的运动框架的封装:
 
匀速运动。
 
缓冲运动。
 
多物体运动。
 
任意值变化。
 
链式运动。
 
同时运动。
 
(一)匀速运动
 
速度动画
 
运动基础
 
思考:如何让div动起来?
如下:
 
设置元素为绝对定位,只有绝对定位后,left,top等值才生效。
 
定时器的使用(动态改变值),这里使用setInterval()每隔指定的时间执行代码。
 
计时器setInterval(函数,交互时间(毫秒)):在执行时,从载入页面后每隔指定的时间执行代码。
 
取消计时器clearInterval(函数) 方法可取消由 setInterval() 设置的交互时间。
 
获取当前的位置,大小等等。offsetLeft(当前元素相对父元素位置)。
 
速度--物体运动的快慢
 
定时器间隔时间
 
改变值的大小
 
根据上面的信息我们就可以开始封装运动框架创建一个变化的div了。
 

  1. /** 
  2.  * 运动框架-1-动起来 
  3.  * @param {HTMLElement} element 进行运动的节点 
  4.  */ 
  5. var timer = null; 
  6. function startMove(element) { 
  7.     timer = setInterval(function () {//定时器 
  8.         element.style.left = element.offsetLeft + 5 + "px"; 
  9.     }, 30); 

你没看错,就是那么简单。但是等等, what? 怎么不会停?WTF?
 
那是因为我们没有运动终止条件。好再还是比较简单。直接在定时器内部,判断到达目标值,清除定时器就行拉!
 

  1. /** 
  2.  * 运动框架-2-运动终止 
  3.  * @param {HTMLElement} element 进行运动的节点 
  4.  * @param {number}      iTarget 运动终止条件。 
  5.  */ 
  6.  var timer = null; 
  7. function startMove(element, iTarget) { 
  8.     timer = setInterval(function () { 
  9.         element.style.left = element.offsetLeft + 5 + "px"; 
  10.         if (element.offsetLeft === iTarget) {//停止条件 
  11.             clearInterval(timer); 
  12.         } 
  13.     }, 30); 

就这样是不是就完成了呢?已经ok了呢? 
no。还有一些Bug需要处理。
 
运动中的Bug
 
速度取到某些值会无法停止
 
到达位置后再点击还会运动
 
重复点击速度加快
 
速度无法更改
 
解决BUG
 
速度取到某些值会无法停止(这个Bug稍后解决,在进化过程中自然解决)
 
把运动和停止隔开(if/else)
 
在开始运动时,关闭已有定时器
 
把速度用变量保存
 

  1. /** 
  2.  * 运动框架-3-解决Bug 
  3.  */ 
  4. var timer = null; 
  5. function startMove(element, iTarget) { 
  6.     clearInterval(timer);//在开始运动时,关闭已有定时器 
  7.     timer = setInterval(function () { 
  8.         var iSpeed = 5;//把速度用变量保存 
  9.         //把运动和停止隔开(if/else) 
  10.         if (element.offsetLeft === iTarget) {//结束运动 
  11.             clearInterval(timer); 
  12.         } else { 
  13.             element.style.left = element.offsetLeft + iSpeed + "px"; 
  14.         } 
  15.     }, 30); 

这样一个简单的运动框架就完成了。但是,再等等。只能向右走?别急,我们不是定义了把速度变成为了变量吗?只需要对它进行一些处理就行啦!
var iSpeed = 5;
 

  1. //判断距离目标位置,达到自动变化速度正负 
  2. var iSpeed = 0; 
  3. if (element.offsetLeft < iTarget) { 
  4.     iSpeed = 5; 
  5. } else { 
  6.     iSpeed = -5; 

透明度动画
 
用变量alpha储存当前透明度。
 
把上面的element.offsetLeft改成变量alpha。
 
运动和停止条件部分进行更改。如下:
 

  1. //透明度浏览器兼容实现 
  2. if (alpha === iTarget) { 
  3.     clearInterval(time); 
  4. } else { 
  5.     alpha += speed; 
  6.     element.style.filter = 'alpha(opacity:' + alpha + ')'; //兼容IE 
  7.     element.style.opacity = alpha / 100;//标准 

(二)缓冲动画

思考:怎么样才是缓冲动画?
 
应该有以下几点:
 
逐渐变慢,最后停止
 
距离越远速度越大
 
速度由距离决定
 
速度=(目标值-当前值)/缩放系数
 
Bug :速度取整(使用Math方法),不然会闪
 
向上取整。Math.ceil(iSpeed)
 
向下取整。Math.floor(iSpeed)
 
还是对速度作文章:
 

  1. /** 
  2.  * 运动框架-4-缓冲动画 
  3.  */ 
  4. function startMove(element, iTarget) { 
  5.     clearInterval(timer); 
  6.     timer = setInterval(function () { 
  7.     //因为速度要动态改变,所以必须放在定时器中 
  8.         var iSpeed = (iTarget - element.offsetLeft) / 10; //(目标值-当前值)/缩放系数=速度 
  9.         iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 
  10.         if (element.offsetLeft === iTarget) {//结束运动 
  11.             clearInterval(timer); 
  12.         } else { 
  13.             element.style.left = element.offsetLeft + iSpeed + "px"; 
  14.         } 
  15.     }, 30); 

做到这里,(速度取到某些值会无法停止)这个Bug就自动解决啦!
 
例子:缓冲菜单
 
跟随页面滚动的缓冲侧边栏
在线演示:codepen
 
潜在问题目标值不是整数时

(三)多物体运动

思考:如何实现多物体运动?
 
单定时器,存在问题。每个div一个定时器
 
定时器作为对象的属性
 
直接使用element.timer把定时器变成对象上的一个属性。
 
参数的传递:物体/目标值
比较简单把上面框架的进行如下更改:timer-->element.timer
 
就这样就行啦!

(四)任意值变化

咳咳。我们来给div加个1px的边框。boder :1px solid #000
 
然后来试试下面的代码
 

  1. setInterval(function () { 
  2.     oDiv.style.width = oDiv.offsetWidth - 1 + "px"; 
  3. }, 30) 

嗯,神奇的事情发生了!what?我设置的不是宽度在减吗?怎么尼玛增加了! 不对啊,大兄弟。
 
究竟哪里出了问题呢?
 
一起找找资料,看看文档,原来offset这一系列的属性都会存在,被其他属性干扰的问题。
 
好吧,既然不能用,那么我们就顺便把任意值变化给做了吧。
 
第一步:获取实际样式
 
使用offsetLeft..等获取样式时, 若设置了边框, padding, 等可以改变元素宽度高度的属性时会出现BUG..
 
通过查找发现element.currentStyle(attr)可以获取计算过之后的属性。
 
但是因为兼容性的问题,需封装getStyle函数。(万恶的IE)
 
当然配合CSS的box-sizing属性设为border-box可以达到一样的效果 ? (自认为,未验证)。
 

  1. /** 
  2.  * 获取实际样式函数 
  3.  * @param   {HTMLElement}   element  需要寻找的样式的html节点 
  4.  * @param   {String]} attr 在对象中寻找的样式属性 
  5.  * @returns {String} 获取到的属性 
  6.  */ 
  7. function getStyle(element, attr) { 
  8.     //IE写法 
  9.     if (element.currentStyle) { 
  10.         return element.currentStyle[attr]; 
  11.     //标准 
  12.     } else { 
  13.         return getComputedStyle(element, false)[attr]; 
  14.     } 

第二步:改造原函数
 
添加参数,attr表示需要改变的属性值。
 
更改element.offsetLeft为getStyle(element, attr)。
 
需要注意的是:getStyle(element, attr)不能直接使用,因为它获取到的字符串,例:10px。
 
变量iCurrent使用parseInt(),将样式转成数字。
 
element.style.left为element.style[attr]。
 

  1. /** 
  2.  * 运动框架-4-任意值变化 
  3.  * @param {HTMLElement} element 运动对象 
  4.  * @param {string}      attr    需要改变的属性。 
  5.  * @param {number}      iTarget 目标值 
  6.  */ 
  7. function startMove(element, attr, iTarget) { 
  8.     clearInterval(element.timer); 
  9.     element.timer = setInterval(function () { 
  10.         //因为速度要动态改变,所以必须放在定时器中 
  11.     var iCurrent=0; 
  12.     iCurrent = parseInt(getStyle(element, attr));//实际样式大小 
  13.         var iSpeed = (iTarget - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度 
  14.         iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 
  15.         if (iCurrent === iTarget) {//结束运动 
  16.             clearInterval(element.timer); 
  17.         } else { 
  18.             element.style[attr] = iCurrent + iSpeed + "px"; 
  19.         } 
  20.     }, 30); 

试一试,这样是不是就可以了呢?
 
还记得上面我们写的透明度变化吗? 再试试
 
果然还是不行, (废话,你见过透明度有"px"单位的么? - -白眼 )
 
第三步:透明度兼容处理
 
思考:需要对那些属性进行修改?
 
判断attr是不是透明度属性opacity 。
 
对于速度进行处理。
 
为透明度时,由于获取到的透明度会是小数,所以需要 * 100
 
并且由于计算机储存浮点数的问题,还需要将小数,进行四舍五入为整数。使用:Math.round(parseFloat(getStyle(element, attr)) * 100)。
 
否则,继续使用默认的速度。
 
对结果输出部分进行更改。
 
判断是透明度属性,使用透明度方法
 
否则,使用使用默认的输出格式。
 

  1. /** 
  2.  * 运动框架-5-兼容透明度 
  3.  * @param {HTMLElement} element 运动对象 
  4.  * @param {string}      attr    需要改变的属性。 
  5.  * @param {number}      iTarget 目标值 
  6.  */ 
  7. function startMove(element, attr, iTarget) { 
  8.     clearInterval(element.timer); 
  9.     element.timer = setInterval(function () { 
  10.         //因为速度要动态改变,所以必须放在定时器中 
  11.         var iCurrent = 0; 
  12.         if (attr === "opacity") { //为透明度时执行。 
  13.             iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100); 
  14.         } else { //默认情况 
  15.             iCurrent = parseInt(getStyle(element, attr)); //实际样式大小 
  16.         } 
  17.         var iSpeed = (iTarget - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度 
  18.         iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 
  19.         if (iCurrent === iTarget) {//结束运动 
  20.             clearInterval(element.timer); 
  21.         } else { 
  22.             if (attr === "opacity") { //为透明度时,执行 
  23.                 element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE 
  24.                 element.style.opacity = (iCurrent + iSpeed) / 100; //标准 
  25.             } else { //默认 
  26.                 element.style[attr] = iCurrent + iSpeed + "px"; 
  27.             } 
  28.         } 
  29.     }, 30); 

到这里,这个运动框架就基本上完成了。但是,我们是追求完美的不是吗?

继续进化!

(五)链式动画

链式动画:顾名思义,就是在该次运动停止时,开始下一次运动。
 
如何实现呢?
 
使用回调函数:运动停止时,执行函数
 
添加func形参(回调函数)。
 
在当前属性到达目的地时iCurrent === iTarget,判断是否有回调函数存在,有则执行。
 
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.  * @param   {HTMLElement}   element  需要寻找的样式的html节点 
  4.  * @param   {String]} attr 在对象中寻找的样式属性 
  5.  * @returns {String} 获取到的属性 
  6.  */ 
  7. function getStyle(element, attr) { 
  8.     //IE写法 
  9.     if (element.currentStyle) { 
  10.         return element.currentStyle[attr]; 
  11.         //标准 
  12.     } else { 
  13.         return getComputedStyle(element, false)[attr]; 
  14.     } 
  15. /** 
  16.  * 完美运动框架 
  17.  * @param {HTMLElement} element 运动对象 
  18.  * @param {JSON}        json    属性:目标值       
  19.  *   @property {String} attr    属性值 
  20.  *   @config   {Number} target  目标值 
  21.  * @param {function}    func    可选,回调函数,链式动画。 
  22.  */ 
  23. function startMove(element, json, func) { 
  24.     var flag = true; //假设所有运动到达终点. 
  25.     clearInterval(element.timer); 
  26.     element.timer = setInterval(function () { 
  27.         for (var attr in json) { 
  28.             //1.取当前的属性值。 
  29.             var iCurrent = 0; 
  30.             if (attr === "opacity") { //为透明度时执行。 
  31.                 iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100); 
  32.             } else { //默认情况 
  33.                 iCurrent = parseInt(getStyle(element, attr)); //实际样式大小 
  34.             } 
  35.             //2.算运动速度,动画缓冲效果 
  36.             var iSpeed = (json[attr] - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度 
  37.             iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 
  38.  
  39.             //3.未到达目标值时,执行代码  
  40.             if (iCurrent != json[attr]) { 
  41.                 flag = false; //终止条件 
  42.                 if (attr === "opacity") { //为透明度时,执行 
  43.                     element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE 
  44.                     element.style.opacity = (iCurrent + iSpeed) / 100; //标准 
  45.                 } else { //默认 
  46.                     element.style[attr] = iCurrent + iSpeed + "px"; 
  47.                 } 
  48.             } else { 
  49.                 flag = true; 
  50.             } 
  51.             //4. 运动终止,是否回调 
  52.             if (flag) { 
  53.                 clearInterval(element.timer); 
  54.                 if (func) { 
  55.                     func(); 
  56.                 } 
  57.             } 
  58.         } 
  59.     }, 30); 

运动框架总结
 
运动框架演变过程
 
框架                                      变化
startMove(element)                        运动
startMove(element,iTarget)                匀速-->缓冲-->多物体
startMove(element,attr,iTargrt)           任意值
startMove(element,attr,iTargrt,func)      链式运动
startMove(element,json,func)              多值(同时)-->完美运动框架
 

时间: 2024-09-06 19:31:06

JavaScript完美运动框架的进阶之旅的相关文章

Javascript 完美运动框架(逐行分析代码,让你轻松了运动的原理)_javascript技巧

大家一听这名字就知道,有了这套框架 网上的效果基本都是可以实现的.实际上之前的运动框架还是有局限性的,就是不能让好几个值一块运动. 那这个问题怎么解决呢? 我们先来看看之前的运动框架 function getStyle(obj, name) { if (obj.currentStyle) { return obj.currentStyle[name]; } else { return getComputedStyle(obj, null)[name]; } } function startMov

JavaScript 创建运动框架的实现代码_基础知识

封装好的运动框架Move(obj,attr,iTarget),可直接调用: 可用于设置width.border.fontSize.marginLeft.opacity等许多常见属性值的变速变化,实现各种有趣效果. 兼容IE和FF. 复制代码 代码如下: /****************    *    *   IE-BUG:    *   在IE中,设置opacity属性时,元素样式中需要设置opacity属性,才能读取到opacity值.    *    *   obj:元素对象.   at

javascript运动框架用法实例分析(实现放大与缩小效果)_javascript技巧

本文实例讲述了javascript运动框架用法.分享给大家供大家参考,具体如下: 该运动框架可以实现多物体任意值运动 运行效果截图如下: 例子: <!doctype html> <html> <head> <meta charset="utf-8"> <title>运动框架</title> <style> #div1{ width:100px; height:100px; background:red;

完美解决jQuery符号$与其他javascript 库、框架冲突的问题_jquery

目前有大量的 javascript 开发框架,其中有一部分使用 $ 作为调用符号,这可能导致相互之间的冲突,而 jQuery 为解决这个问题,可以在 jQuery 导入时放弃 $ 使用权,届时 $ 则由其它框架使用,这样可以避免相同名字的函数调用不再冲突. jQuery 使用 noConflict 方法来放弃 $ 调用时的命名,之后由 jQuery 代替 $ 进行编写. 例如:alert($('#message').val()); 必须修改为 alert(jQuery('#message').v

javascript关于运动的各种问题经典总结

  javascript关于运动的各种问题经典总结          本文实例总结了javascript关于运动的各种问题.分享给大家供大家参考.具体如下: 一.JS运动的各种问题 问题一: 错误代码: ? 1 2 3 4 5 6 7 8 9 10 11 function startMove(){ var timer=null; var div1=document.getElementById("div1"); if (div1.offsetLeft==300){ clearInter

JS运动框架之分享侧边栏动画实例

 这篇文章主要介绍了JS运动框架之分享侧边栏动画,实例分析了javascript操作div及css的技巧,需要的朋友可以参考下     本文实例讲述了JS运动框架之分享侧边栏动画实现方法.分享给大家供大家参考.具体实现方法如下:   代码如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style type=&q

我的Android进阶之旅】GitHub 上排名前 100 的 Android 开源库进行简单的介绍

GitHub Android Libraries Top 100 简介 本文转载于:https://github.com/Freelander/Android_Data/blob/master/Android-Librarys-Top-100.md 本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果, 然后过滤了跟 Android 不相关的项目, 所以排名并

JS运动框架之分享侧边栏动画实例_javascript技巧

本文实例讲述了JS运动框架之分享侧边栏动画实现方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: <!DOCTYPE html>  <html>      <head>          <meta charset="utf-8">          <title></title>          <style type="text/css">         

原生JS实现风箱式demo,并封装了一个运动框架(实例代码)_javascript技巧

声明,该DEMO依托于某个培训机构中,非常感谢这个培训结构.话不多说,现在开始改demo的制作. 首先,在前端的学习过程中,轮播图是我们一定要学习的,所以为了更加高效的实现各种轮播图,封装了一个运动的框架. function getStyle(obj,attr) { if(obj.currentStyle){ return obj.currentStyle[attr];//为了获取IE下的属性值 }else{ return window.getComputedStyle(obj,null)[at