JavaScript圆满活动框架的进阶之旅

转自个人博客三省吾身丶丶
本来是JS动画效果,然则我会过甚再看的时刻,发明太大略了,因而从新写了一篇。
喜好别只珍藏啊,点个引荐,大兄弟^ ^!

活动框架的完成思绪

活动,实在就是在一段时候内转变leftrightwidthheightopactiy的值,抵达目标地以后住手。

如今依据以下步骤来举行我们的活动框架的封装:

  1. 匀速活动。

  2. 缓冲活动。

  3. 多物体活动。

  4. 恣意值变化。

  5. 链式活动。

  6. 同时活动。

(一)匀速活动

速率动画

活动基本

思索:怎样让div动起来?
以下:

  1. 设置元素为相对定位,只要相对定位后,left,top等值才见效。

  2. 定时器的运用(动态转变值),这里运用setInterval()每隔指定的时候实行代码。

    • 计时器setInterval(函数,交互时候(毫秒)):在实行时,从载入页面后每隔指定的时候实行代码。

    • 作废计时器clearInterval(函数) 要领可作废由 setInterval() 设置的交互时候。

  3. 猎取当前的位置,大小等等。offsetLeft(当前元素相对父元素位置)。

  4. 速率–物体活动的快慢

    • 定时器间隔时候

    • 转变值的大小

依据上面的信息我们就能够最先封装活动框架建立一个变化的div了。

/**
 * 活动框架-1-动起来
 * @param {HTMLElement} element 举行活动的节点
 */
var timer = null;
function startMove(element) {
    timer = setInterval(function () {//定时器
        element.style.left = element.offsetLeft + 5 + "px";
    }, 30);
}

你没看错,就是那末简朴。然则等等, what? 怎样不会停?WTF?

那是因为我们没有活动停止前提。好再照样比较简朴。直接在定时器内部,推断抵达目标值,消灭定时器就行拉!

/**
 * 活动框架-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

  1. 速率取到某些值会没法住手

  2. 抵达位置后再点击还会活动

  3. 反复点击速率加速

  4. 速率没法变动

处理BUG

  1. 速率取到某些值会没法住手(这个Bug稍后处理,在进化历程当中天然处理)

  2. 把活动和住手离隔(if/else)

  3. 在最先活动时,封闭已有定时器

  4. 把速率用变量保留

/**
 * 活动框架-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;–>

//推断间隔目标位置,到达自动变化速率正负
var iSpeed = 0;
if (element.offsetLeft < iTarget) {
    iSpeed = 5;
} else {
    iSpeed = -5;
}

透明度动画

  1. 用变量alpha贮存当前透明度。

  2. 把上面的element.offsetLeft改成变量alpha

  3. 活动和住手前提部份举行变动。以下:

//透明度浏览器兼容完成
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)

照样对速率作文章:

/**
 * 活动框架-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

然后来尝尝下面的代码

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能够到达一样的效果 ? (自认为,未考证)。

/**
 * 猎取现实款式函数
 * @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];
    }
}

第二步:革新原函数

  1. 增加参数,attr示意须要转变的属性值。

  2. 变动element.offsetLeftgetStyle(element, attr)

    • 须要注重的是:getStyle(element, attr)不能直接运用,因为它猎取到的字符串,例:10px

    • 变量iCurrent运用parseInt(),将款式转成数字。

  3. element.style.leftelement.style[attr]

/**
 * 活动框架-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”单元的么? – –白眼

第三步:透明度兼容处置惩罚

思索:须要对那些属性举行修正?

  1. 推断attr是不是是透明度属性opacity

  2. 关于速率举行处置惩罚。

    • 为透明度时,因为猎取到的透明度会是小数,所以须要 * 100

    • 而且因为计算机贮存浮点数的题目,还须要将小数,举行四舍五入为整数。运用: Math.round(parseFloat(getStyle(element, attr)) * 100)

    • 不然,继承运用默许的速率。

  3. 对效果输出部份举行变动。

    • 推断是透明度属性,运用透明度要领

    • 不然,运用运用默许的输出花样。

/**
 * 活动框架-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,推断是不是有回调函数存在,有则实行。

if (iCurrent === iTarget) {//完毕活动
    clearInterval(element.timer);
    if (func) {
        func();//回调函数
    }
}

good,链式动画完成!间隔圆满还差一步!

(六)同时活动

思索:怎样完成同时活动?

  1. 运用JSON通报多个值

  2. 运用for in轮回,遍历属性,与值。

  3. 定时器题目!(活动提早住手)

    • 在轮回外设置变量,假定一切的值都抵达了目标值为true

    • 在轮回中检测是不是抵达目标值,若没有值未到则为false

    • 在轮回完毕后,检测是不是悉数到达目标值.是则消灭定时器

完成:

  1. 删除attriTarget两个形参,改成json

  2. 在函数最先时,设置一个标记var flag = true; //假定一切活动抵达尽头.

  3. 在定时器内运用for in,遍历属性与目标,改写本来的attriTarget,为json的属性与值

  4. 修正活动停止前提,只要每一项的现实属性值iCurrent,即是目标值json[attr]时,flag才为true。消灭定时器,推断是不是回调。

  5. 不然,继承实行代码,直到一切属性值即是目标值。

圆满活动框架

/**
 * 猎取现实款式函数
 * @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)多值(同时)–>圆满活动框架
    原文作者:三省吾身丶丶
    原文地址: https://segmentfault.com/a/1190000003964573
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞