跟着步骤一起实现运动函数 ---- 一起来试试吧

101 阅读8分钟

运动函数

// css 样式
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        div{
            width: 100px;
            height: 100px;
            background-color: pink;
            position: absolute;
            top: 0;
            left: 0;
        }

    </style>
// html
    <div class="box"></div>

js 实现

  1. 运动函数实现 1
  • 需求: 点击这个 div 让向右移动 100px
// 获取元素
        const oBox = document.querySelector('.box');   // 获取box

        // 给box添加点击事件
        oBox.onclick = function(){
            // console.log(this); // 查看oBox的this指向   =>  <div class="box"></div>
            
            this.style.left = 100 + 'px';  // 点击oBox 使其运动100px

            let count = 0; // 定义计数器
            // 定义一个变量  把oBox 的this指向赋值给变量
            const that = this;
            // 定义计时器
            const time = setInterval(function(){
                // console.log(this);  // 查看 this 的指向  => window
                //设置步长  =>  一次运动的距离
                count += 5;
                // 当 计数器等于 目标距离时/ 原始距离等于目标距离时 清楚计时器
                // 运动距离等于最后的开始距离
                that.style.left = count + 'px'; // 左边距离
                // 当开始距离等于 目标距离时 清除定时器 不再运动  => 即移动
                if(count == 100){
                    clearInterval(time); // 清除计时器
                }

            },50)
            console.log(that.style.left);  // 查看运动后的距离  =>  打印结果  100px

        }   
  1. 运动函数实现 2
  • 问题: 每次运动都从 0 的位置开始移动
  • 原因: 运动函数的 初始值, 是从 0 开始的
  • 解决: 获取到元素本身值, 然后再次基础上移动
        // 获取元素
        const oBox = document.querySelector('.box');   // 获取box

        // 运动函数封装 
           /* ele: 移动那个元素
            type: 修改那个属性
            target: 移动的目标距离*/
        function move(ele,type,target){
            // ele.style.left = 100 + 'px';  // 点击oBox 使其运动100px
            // console.log(window.getComputedStyle(ele)[type]); // 获取元素本身的值  作为运动起点

            let count = parseInt(window.getComputedStyle(ele)[type]); // 运动起点
            // 定义一个变量  把oBox 的this指向赋值给变量
            // const that = this;
            // 定义计时器
            const time = setInterval(function(){

                count += 5;
                // 当 计数器等于 目标距离时/ 原始距离等于目标距离时 清楚计时器
                // 运动距离等于最后的开始距离
                ele.style[type] = count + 'px'; // 左边距离
                // 当开始距离等于目标距离时  清除计时器 停止运动  =>  即移动
                if(count == target){
                    clearInterval(time); // 清除计时器
                }

            },50)

        }

        // 给box添加点击事件
        oBox.onclick = function(){
            // 调用运动函数
            move(this,'left',100);  // this(元素本身)  '属性'  '目标距离'  
            
        }
  1. 运动函数实现 3
  • 问题: 从 200 移动到 501 时, 不会停止
    • 原因: 移动距离, 不是 每次移动的整倍数
    • 现在从 200开始移动, 移动到501 移动距离 301
    • 301 不是每次运动的整倍数
    • 解决: 1. 每次移动距离修改为 1
    • 弊端, 运动速度固定了, 如果运动的路程不同, 就会导致时间不同
    1. 每次移动总路程的 1/10 (匀速)
    • 现在从 200开始移动, 移动到501 移动距离 301
    • 每次都移动 30.1
    • 现在从 300开始移动, 移动501 移动距离 201
    • 每次都移动 20.1
    1. 每次移动剩余路程的 1/10 (降速)
    • 3.1 现在 从 0 开始移动, 移动到 100 移动距离 100
      • 现在移动 100 / 10 === 10
    • 3.2 现在 从 10 开始移动, 移动到 100 移动距离 90
      • 现在移动 90 / 10 === 9
        // 获取元素
        const oBox = document.querySelector('.box');   // 获取box

        // 运动函数封装 
           /* ele: 移动那个元素
            type: 修改那个属性
            target: 移动的目标距离*/
        function move(ele,type,target){
            // ele.style.left = 100 + 'px';  // 点击oBox 使其运动100px
            
            // 定义一个变量  把oBox 的this指向赋值给变量
            // const that = this;
            // 定义计时器
            const time = setInterval(function(){
            // console.log(window.getComputedStyle(ele)[type]); // 获取元素本身的值  作为运动起点

                let count = parseInt(window.getComputedStyle(ele)[type]); // 运动起点
                // count += 1;    
                // count += 1;  =>   count += 5  运动距离200-501 => 到501时 不会停止 步长为5  => 修改:count += 1  
                // 运动距离 = 目标距离 - 起始距离
                let def = (target - count) / 10;
                // 当 计数器等于 目标距离时/ 原始距离等于目标距离时 清楚计时器
                
                // 当开始距离等于目标距离时  清除计时器 停止运动  =>  即移动
                if(count === target){
                    clearInterval(time); // 清除计时器
                }else{
                    // 运动距离 = 起始距离 + 运动距离
                    ele.style[type] = count + def + 'px'; // 左边距离
                }

            },20)

        }
        /*  html
        
        
        // 给box添加点击事件
        oBox.onclick = function(){
            // 调用运动函数
            move(this,'left',501);  // this(元素本身)  '属性'  '目标距离'  
            move(this,'top',200)
            
        }
        */
    
  1. 运动函数实现 4
  • 问题: 没有移动到指定的距离
    • 原因: 当前在的位置 91px 需要移动到 100px, 下一次移动距离 (100 - 91) / 10 === 0.9
    • 当前移动的距离 应该是 91 + 0.9 + 'px' === 91.9px
    • 但是浏览器可识别的最小移动距离 为 1px, 所以赋值移动到 91.9px 时, 实际位置 还是91px
    • 解决: 将移动的距离 向上取整
  • 新问题:
    • 当前在的位置 9px 需要移动 0px, 下一次移动距离 (0 - 9) / 10 === -0.9
    • 然后我们将移动值, 向上取整 -0.9 => 0
    • 解决: 当移动的值 大于0的时候, 向上取整, 否则 向下取整
        function move(ele, type, target) {
            const timer = setInterval(function () {
                // 1. 获取当前的位置
                let count = parseInt(window.getComputedStyle(ele)[type])

                // 2. 计算要移动的距离 (目标 - 当前的值) / 10
                let des = (target - count) 

                // 3. 判断是否需要继续移动
                if (target === count) {
                    // 3.1 此时说明移动到了, 可以结束定时器
                    clearInterval(timer)
                } else {
                    // 3.2 此时说明还没有移动到, 需要继续移动
                    ele.style[type] = count + des + 'px'
                }
            }, 20)
  
        }  
 
  1. 运动函数实现 5
  • 问题: 透明没有调整
    • 原因: 透明度没有单位
    • 解决: 在赋值的时候, 判断如果时 透明度, 那么就别带单位
  • 新问题: 没有运动过程
    • 原因: 因为做了取整之后, 得到的值, 只会是 -1或者1
    • 解决: 想个办法, 将值放大
        // 0. 封装一个运动函数
        function move(ele, type, target) {
            if (type == 'opacity') {
                target *= 100
            }
            const timer = setInterval(function () {
                // 1. 获取当前的位置
                let count
                if (type == 'opacity') {
                    count = window.getComputedStyle(ele)[type] * 100
                } else {
                    count = parseInt(window.getComputedStyle(ele)[type])
                }

                // 2. 计算要移动的距离 (目标 - 当前的值) / 10
                let des = (target - count) / 10
                // 大于0 向上取整, 小于0 向下取整
                des = des > 0 ? Math.ceil(des) : Math.floor(des)

                // 3. 判断是否需要继续移动
                if (target === count) {
                    // 3.1 此时说明移动到了, 可以结束定时器
                    clearInterval(timer)
                } else {
                    // 3.2 此时说明还没有移动到, 需要继续移动
                    if (type == 'opacity') {
                        ele.style[type] = (count + des) / 100
                    } else {
                        ele.style[type] = count + des + 'px'
                    }
                }
            }, 50)
        }
  1. 运动函数实现 6
  • 问题: 我们现在想在运动函数结束之后在某些事, 但是目前没法做到
    • 原因: 运动函数是 异步的, 所以会先执行同步代码
    • 解决: 在运动函数开始的时候, 给他一个 "锦囊" 然后告诉运动函数
    • 在你运动完了的时候, 才能打开 "锦囊"
    • 回调函数 (本质上就是一个函数)
    • 将函数A以实参的形式传递给函数B, 然后函数B在函数内部以形参的方式调用函数A
    • 此时函数A可以称为函数B的回调函数
    • 回调函数一般用异步代码的封装
// 获取元素  
        const oBox = document.querySelector('.box');   // 获取box  
        // 准备变量  用于计数作用  
        let num = 0;  
  
        function move(ele,obj,fun = () => {}){  // ele => 运动元素   obj =>  传入参数对象  
            // console.log(obj); // =>  打印结果  对象  
            // 通过循环拿到对象每一个键和值  
            for(let key in obj){  
                // console.log(obj[key]);  // 拿到值  =>   目标距离  
                // console.log(key);  // 拿到 相应的type值   
                let type = key;  
                let target = obj[key];  
              
                if(type == 'opacity'){  
                    target *= 100// 初始值  放大100   =>   目标距离放大100  => 保证放大后  计算没有改变  
                }  
                num++;    // 计时器开始前让计数器自增   用于记录计时器是否已经结束  
                // 定义计时器  
                const time = setInterval(function(){  
                // console.log(window.getComputedStyle(ele)[type]); // 获取元素本身的值  作为运动起点  
                    let count;  
                    if(type == 'opacity'){  // 判断是否为透明度  为透明度将值放大100倍  
                        count = window.getComputedStyle(ele)[type] * 100// 不会带单位  所以去掉parseInt  
                    }else{  
                        count = parseInt(window.getComputedStyle(ele)[type]); // 运动起点  
                    }  
                      
                    // count += 1;  =>   count += 5  运动距离200-501 => 到501时 不会停止 步长为5  => 修改:count += 1    
                    // 运动距离 = 目标距离 - 起始距离  
                    let def = (target - count) / 10;  
                    // 当 计数器等于 目标距离时/ 原始距离等于目标距离时 清楚计时器  
                      
                    // 解决91-100  一直停留在91.9  
                    // def = Math.ceil(def);  
                      
                    // 解决0-9 无法移动到目标位置  
                    def = def > 0 ? Math.ceil(def) : Math.floor(def);  
                      
                    // 当开始距离等于目标距离时  清除计时器 停止运动  =>  即移动  
                    if(count === target){  
                        num--;  
                        clearInterval(time); // 清除计时器  
                        if(num == 0){  
                            console.log('计时器结束');  
                            fun(); // 计时器结束调用函数  执行结束后的操作  
                        }  
                    }else{  
                        if(type == 'opacity'){  
                            // 初始值放大100 倍   变化值缩小100倍   
                            ele.style[type] = (count + def) / 100// 判断传入参数里有无透明度  如果有 就不带px单位  
                        }else{  // 不是透明度 就带px单位  
                            ele.style[type] = count + def + 'px'// 左边距离  
                        }  
                        // 运动距离 = 起始距离 + 运动距离  
                          
                    }  
                    // console.log(ele.style[type]);  // 一直停留在91.9  无法到达 目标位置  
  
                },20)  
            }  
  
        }