zoukankan      html  css  js  c++  java
  • Javascript之封装运动函数

    本文采取逐步深入的方式讲解原生JS封装运动函数的过程,
    封装结果适用于元素大部分属性的运动,
    运动方式将根据需求持续更新,目前主要支持常用的两种:匀速运动和缓冲运动。

    阶段一、仅适用单位带px属性的匀速运动

    效果图:
    在这里插入图片描述
    封装思路:

    1. 传入需要运动的属性attr、运动的目标值target_value、运动速度speed
    2. 对速度进行简单处理speed = speed || 5,即不传入速度参数值时,默认速度为5;
    3. 使用getComputedStyle()获取元素该属性的当前值now_value,通过target_value > now_value ? Math.abs(speed) : -Math.abs(speed);获取运动的方向;
    4. 通过Math.abs(target_value - now_value) <= Math.abs(speed)获取终止条件,需要终止时关闭定时器并将运动元素送至终点box_ele.style[attr] = target_value + "px";

    完整代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            *{
                margin: 0;
                padding: 0;
            }
            .box{
                 200px;
                height: 200px;
                background: skyblue;
                position: absolute;
            }
        </style>
    </head>
    <body>
        <div class="box"></div>
        <script>
            var timer = null;
            function animate(target_value, attr, speed){
                var now_value = parseInt(getComputedStyle(box_ele)[attr]);
                speed = speed || 5;
                speed = target_value > now_value ? Math.abs(speed) : -Math.abs(speed);
                clearInterval(timer);
                timer = setInterval(function(){
                    if(Math.abs(target_value - now_value) <= Math.abs(speed)){
                        box_ele.style[attr] = target_value + "px";
                        clearInterval(timer);
                    }else{
                        now_value += speed;
                        box_ele.style[attr] = now_value + "px";
                    }
                }, 30)
            }
            
            box_ele = document.querySelector(".box");
            box_ele.addEventListener("mouseenter", function(){
                animate(400, "left");
            })
        </script>
    </body>
    </html>
    

    阶段二、可适用单位不带px属性(如opacity)的匀速运动

    效果图:
    在这里插入图片描述
    封装思路:

    1. 在阶段一的基础上添加对元素运动属性的判定,若运动属性为opacity,需要对相关值进行处理;
    2. 由于默认速度为5,而opacity的范围为0~1,故需要对目标值和当前值进行处理,即now_value = parseInt(getComputedStyle(box_ele)[attr] * 100); target_value *= 100;
    3. 渲染元素运动效果时由opacity属性不带单位px,故也需要进行处理,即运动时 box_ele.style[attr] = now_value / 100;,终止时box_ele.style[attr] = target_value / 100;

    完整代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            *{
                margin: 0;
                padding: 0;
            }
            .box{
                 200px;
                height: 200px;
                background: skyblue;
                position: absolute;
            }
        </style>
    </head>
    <body>
        <div class="box"></div>
        <script>
            var timer = null;
            function animate(target_value, attr, speed){
                if(attr === "opacity"){
                    var now_value = parseInt(getComputedStyle(box_ele)[attr] * 100);
                    target_value *= 100;
                }else{
                    var now_value = parseInt(getComputedStyle(box_ele)[attr]);
                }
                speed = speed || 5;
                speed = target_value > now_value ? Math.abs(speed) : -Math.abs(speed);
                clearInterval(timer);
                timer = setInterval(function(){
                    if(Math.abs(target_value - now_value) <= Math.abs(speed)){
                        if(attr === "opacity"){
                            box_ele.style[attr] = target_value / 100;
                        }else{
                            box_ele.style[attr] = target_value + "px";
                        }
                        clearInterval(timer);
                    }else{
                        now_value += speed;
                        if(attr === "opacity"){
                            box_ele.style[attr] = now_value / 100;
                        }else{
                            box_ele.style[attr] = now_value + "px";
                        }
                    }
                }, 30)
            }
            
            box_ele = document.querySelector(".box");
            box_ele.addEventListener("mouseenter", function(){
                animate(0, "opacity");
            })
        </script>
    </body>
    </html>
    

    阶段三、适用于多元素单一属性的匀速运动

    效果图:
    在这里插入图片描述
    封装思路:

    1. 在阶段二的基础上添加参数ele,从而可以控制不同元素的运动;
    2. 将定时器放入运动元素对象中,即ele.timer = setInterval(function(){},避免相互之间造成干扰

    完整代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            *{
                margin: 0;
                padding: 0;
            }
            .box{
                 200px;
                height: 200px;
                background: skyblue;
                margin-bottom: 10px;
            }
        </style>
    </head>
    <body>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <script>
    
            function animate(ele, target_value, attr, speed){
                if(attr === "opacity"){
                    var now_value = parseInt(getComputedStyle(ele)[attr] * 100);
                    target_value *= 100;
                }else{
                    var now_value = parseInt(getComputedStyle(ele)[attr]);
                }
                speed = speed || 5;
                speed = target_value > now_value ? Math.abs(speed) : -Math.abs(speed);
                // 定时器放入ele对象中,保证每个元素使用自己的定时器互不干扰
                clearInterval(ele.timer);
                ele.timer = setInterval(function(){
                    if(Math.abs(target_value - now_value) <= Math.abs(speed)){
                        clearInterval(ele.timer);
                        if(attr === "opacity"){
                            ele.style[attr] = target_value / 100;
                        }else{
                            ele.style[attr] = target_value + "px";
                        }
                    }else{
                        now_value += speed;
                        if(attr === "opacity"){
                            ele.style[attr] = now_value / 100;
                        }else{
                            ele.style[attr] = now_value + "px";
                        }
                    }
                }, 30)
            }
    
            var box_eles = document.querySelectorAll(".box");
            document.body.onclick = function(){
                animate(box_eles[0], 500, "width");
                animate(box_eles[1], 200, "margin-left");
                animate(box_eles[2], 0.2, "opacity");
            }
        </script>
    </body>
    </html>
    

    阶段四、适用于多元素单一属性的匀速或缓冲运动

    效果图:
    在这里插入图片描述
    封装思路:

    1. 在阶段三的基础上添加参数animate_mode,并设置animate_mode = "uniform_motion",即默认为匀速运动;
    2. 根据传入的运动方式计算速度,若传入的参数为"butter_motion",速度计算方法为speed = (target_value - now_value) / 10,即根据距离目标值的距离不断调整速度,越靠近目标点速度越慢;
    3. 注意速度计算需要放入定时器中,因为只有定时器中的now_value在不断变化。

    完整代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            *{
                margin: 0;
                padding: 0;
            }
            .box{
                 200px;
                height: 200px;
                background: skyblue;
                margin-bottom: 10px;
            }
        </style>
    </head>
    <body>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <script>
    
            function animate(ele, target_value, attr, animate_mode = "uniform_motion", speed){
                if(attr === "opacity"){
                    var now_value = parseInt(getComputedStyle(ele)[attr] * 100);
                    target_value *= 100;
                }else{
                    var now_value = parseInt(getComputedStyle(ele)[attr]);
                }
                // 匀速运动模式下的速度
                if(animate_mode === "uniform_motion"){
                    speed = speed || 5;
                    speed = target_value > now_value ? Math.abs(speed) : -Math.abs(speed);
                }
                clearInterval(ele.timer);
                ele.timer = setInterval(function(){
                    // 缓冲运动模式下的速度
                    if(animate_mode === "butter_motion"){
                        // 根据距离目标值的距离不断调整速度,越靠近目标点速度越慢
                        speed = (target_value - now_value) / 10;
                        speed = target_value > now_value ? Math.abs(speed) : -Math.abs(speed);
                    }
                    if(Math.abs(target_value - now_value) <= Math.abs(speed)){
                        clearInterval(ele.timer);
                        if(attr === "opacity"){
                            ele.style[attr] = target_value / 100;
                        }else{
                            ele.style[attr] = target_value + "px";
                        }
                    }else{
                        now_value += speed;
                        if(attr === "opacity"){
                            ele.style[attr] = now_value / 100;
                        }else{
                            ele.style[attr] = now_value + "px";
                        }
                    }
                }, 30)
            }
    
            var box_eles = document.querySelectorAll(".box");
            document.body.onclick = function(){
                animate(box_eles[0], 500, "width", "butter_motion");
                animate(box_eles[1], 200, "margin-left", "butter_motion");
                animate(box_eles[2], 0.2, "opacity");
            }
        </script>
    </body>
    </html>
    

    阶段五、适用于多元素多属性的匀速或缓冲运动

    效果图:
    在这里插入图片描述
    封装思路:

    1. 在阶段四的基础上将属性参数attr、目标值参数target_value删除,替换为attr_obj
    2. 遍历传入的属性对象,对每个属性值进行处理,分别设置属性的目标值target_value和当前值now_value
    3. 在定时器中遍历传入的属性对象,逐个属性进行运动;
    4. 由于运动目标的不一致会让运动执行次数不同,有可能提前关闭定时器,故某条属性运动完成时删除该条属性数据,直到对象里没有属性,关闭定时器。

    完整代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            *{
                margin: 0;
                padding: 0;
            }
            .box{
                 200px;
                height: 200px;
                background: skyblue;
                margin-bottom: 10px;
            }
        </style>
    </head>
    <body>
        <div class="box"></div>
        <script>
    
            function animate(ele, attr_obj, animate_mode = "butter_motion", speed){ // 默认运动方式为缓冲运动
                // 遍历传入的属性对象,对每个属性值进行处理,分别设置属性的目标值和当前值
                for(var attr in attr_obj){
                    attr_obj[attr] = {
                        // 考虑属性为“opacity”的特殊情况
                        target_value : attr === "opacity" ? attr_obj[attr] * 100 : attr_obj[attr],
                        now_value : attr === "opacity" ? parseInt(getComputedStyle(ele)[attr]) * 100 : parseInt(getComputedStyle(ele)[attr])
                    }
                }
                // 定时器都放入ele的对象中,保证每个元素使用自己的定时器互不干扰
                clearInterval(ele.timer);
                ele.timer = setInterval(function(){
                    // 遍历传入的属性对象,逐个属性进行运动
                    for(var attr in attr_obj){
                        // 匀速运动下的速度设置
                        if(animate_mode === "uniform_motion"){
                            // 匀速运动模式可以传入速度参数,不传入时默认为5
                            speed = speed || 5;
                            // 判断运动方向,即speed的正负
                            speed = attr_obj[attr].target_value > attr_obj[attr].now_value ? Math.abs(speed) : -Math.abs(speed);
                        }
                        // 缓冲运动下的速度设置
                        if(animate_mode === "butter_motion"){
                            // 根据距离目标值的距离不断调整速度,越靠近目标点速度越慢,且能判断运动方向
                            speed = (attr_obj[attr].target_value - attr_obj[attr].now_value) / 10;
                            // 速度的精确处理
                            speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed)
                        }
                        // 终止条件
                        if(Math.abs(attr_obj[attr].target_value - attr_obj[attr].now_value) <= Math.abs(speed)){
                            ele.style[attr] = attr === "opacity" ? attr_obj[attr].target_value / 100 : attr_obj[attr].target_value + "px";
                            // 目标的不一致会让运动执行次数不同,有可能提前关闭定时器,故某条属性运动完成则删除对象里的属性数据
                            delete attr_obj[attr];
                            // 若对象里还存在属性,则继续运动(不关闭定时器)
                            for(var num in attr_obj){
                                return false;
                            }
                            // 直到对象里没有属性,关闭定时器
                            clearInterval(ele.timer);
                        // 运动条件
                        }else{
                            attr_obj[attr].now_value += speed;
                            ele.style[attr] = attr === "opacity" ? attr_obj[attr].target_value / 100 : attr_obj[attr].now_value + "px";
                        }
                    }
                }, 30)
            }
      
    
            var box_ele = document.querySelector(".box");
            document.body.onclick = function(){
                animate(box_ele, {
                    "width" : 103,
                    "height" : 402,
                    "opacity" : 0.3,
                    "margin-left" : 200
                });
            }
        </script>
    </body>
    </html>
    

    总结

    至此运动函数已封装完成,该功能适用于大多数情况下元素的运动。
    可以实现轮播图、萤火虫、放烟花、商品放大镜等多种效果。

  • 相关阅读:
    BZOJ 2821: 作诗(Poetize)( 分块 )
    BZOJ 2440: [中山市选2011]完全平方数( 二分答案 + 容斥原理 + 莫比乌斯函数 )
    BZOJ 1058: [ZJOI2007]报表统计( 链表 + set )
    BZOJ 1034: [ZJOI2008]泡泡堂BNB( 贪心 )
    BZOJ 1016: [JSOI2008]最小生成树计数( kruskal + dfs )
    BZOJ 2329: [HNOI2011]括号修复( splay )
    BZOJ 3143: [Hnoi2013]游走( 高斯消元 )
    BZOJAC400题留念
    BZOJ 2982: combination( lucas )
    poj 3233
  • 原文地址:https://www.cnblogs.com/qs-cnblogs/p/12599154.html
Copyright © 2011-2022 走看看