JavaScript——运动函数

123 阅读1分钟

运动函数

1.

<style>
    * {
        padding: 0;
        margin: 0;
    }

    .box {
        width: 200px;
        height: 200px;
        background-color: aquamarine;
        position: absolute;
        top: 0;
        left: 0;
    }
</style>
<div class="box"></div>
<script>
/**
 *  需求: 点击 .box 后 让他的 left 更改为 500
 * 
 *  新需求: 让移动有一个动画效果
 * 
 *  新需求2: 点击 .box 后 让他的 top 更改为 500
 * 
 *  新需求3: 点击 .box 后 让他的 top 更改为 300
*/

// 0. 获取元素
var box = document.querySelector('.box')

// 1. 给元素绑定点击事件
box.onclick = function () {
    // 调用运动函数
    move(this, 'top', 300)
}
// 2. 封装运动函数, 方便多次调用, 减少代码量
function move(ele, type, target) {
    /**
     *  参数解析(形参名无所谓):
     *      ele: 移动的目标元素
     *      type: 修改目标的那个属性
     *      target: 移动到那个位置
    */
    // 1. 创建初始值
    let init = 0
    // 2. 开启定时器, 每 20 ms 执行一次
    const timer = setInterval(() => {
        // 2.1 修改初始值
        init += 5
        // 2.2 将修改后的初始值赋值给元素的对应属性
        ele.style[type] = init + 'px'
        // 2.3 判断移动到指定位置后, 结束定时器
        if (init >= target) clearInterval(timer)
    }, 20)
}


</script>

2.

<style>
    * {
        padding: 0;
        margin: 0;
    }

    .box {
        width: 200px;
        height: 200px;
        background-color: aquamarine;
        position: absolute;
        top: 100px;
        left: 0;
    }
</style>
<div class="box"></div>
<script>
    /**
     *  需求: 原本坐标 top: 100, 移动至 top: 300
     * 
     *  问题: 不管元素的原本坐标是什么, 每次运动都是从 0 开始
     * 
     *  解决: 更改 init 的初始值为 元素本身的属性值
    */

    var box = document.querySelector('.box')

    box.onclick = function () {
        move(this, 'top', 300)
    }

    function move(ele, type, target) {
        // 改变: 将 init 的初始值设置为 元素本身的属性值, 这样可以让元素每次移动的时候从自身的位置开始移动
        let init = parseInt(window.getComputedStyle(ele)[type])
        const timer = setInterval(() => {
            init += 5
            ele.style[type] = init + 'px'
            if (init >= target) clearInterval(timer)
        }, 50)
    }

</script>

3.

<style>
    * {
        padding: 0;
        margin: 0;
    }

    .box {
        width: 200px;
        height: 200px;
        background-color: aquamarine;
        position: absolute;
        top: 0px;
        left: 0px;
    }
</style>
<div class="box"></div>
<script>
/**
 *  需求: 原本坐标 left: 200; 移动到 501
 * 
 *  问题: 移动的距离不是 5 的倍数, 不能准确的移动到目标值上
 * 
 *  解决:
 *      1. 每次移动的距离为 1
 *          速度固定, 如果距离不同, 那么需要的时间也不相同
 * 
 *      2. 每次移动总路程的 1/10
 *          假设当前 位置 200, 然后要移动到 501, 移动的总距离为 301
 *          那么每次移动 30.1
 * 
 *      3. 每次移动剩余路程的 1/10
 *              假设当前位置 200, 要移动到 501, 移动的总距离为 301
 *              第一次移动的距离 30.1
 *              第二次移动的时候, 当前位置 230.1, 要移动到 501, 
 *                  移动的总距离 501 - 230.1
*/

    var box = document.querySelector('.box')

    box.onclick = function () {
        move(this, 'left', 100)

    }
    function move(ele, type, target) {
        // 1. 开启定时器
        const timer = setInterval(() => {
            // 2. 获取当前位置
            let init = parseInt(window.getComputedStyle(ele)[type])
            // 3. 计算本次移动距离  (目标 - 当前值) / 10
            let duration = (target - init) / 10

            // 4. 判断走还是不走
            if (target <= init) {
                clearInterval(timer)
            } else {
                // 元素原本的位置 + 要移动的距离, 然后赋值给元素
                ele.style[type] = init + duration + 'px'
            }
        }, 20)
    }

</script>

4.

<style>
    * {
        padding: 0;
        margin: 0;
    }

    .box {
        width: 200px;
        height: 200px;
        background-color: aquamarine;
        position: absolute;
        top: 0px;
        left: 0px;
    }
</style>
<div class="box"></div>
<script>
/**
 *  问题: 从 0px 移动, 移动到 100px, 但实际移动到 91.9px 就停止了
 * 
 *  分析:
 *      在某一次移动到 91px 当前位置 91px, 要移动的点为 100px
 *                          我们的移动方式为移动剩余路程的 1/10
 * 
 *      => 也就是 (目标 - 当前值) / 10 ->  (100 - 91) / 10 === 0.9
 *      => 所以本次移动距离为 0.9px, 也就是说从 91px 移动到了 91.9px
 *      => 因为浏览器的最小像素表述单位为 1px, 所以你设置了 0.9px 浏览器实际无法移动到 0.9px
 *      => 所以我们下一次获取这个元素位置的时候, 还是在 91px
 * 
 * 
 *  解决: 让每次移动的最小值向上取整为 1px
 * 
 * 
 *  新问题: 从 100px 移动到 0px  实际上移动到 9px 的时候就停止了
 * 
 *      每次移动的距离  (目标 - 当前值) / 10        (0 - 9) / 10 === -0.9
 * 
 *      计算完成这个移动距离后, 会有一个 分支语句 判断 是否小于1, 如果小于1, 那么向上取整
*/

    var box = document.querySelector('.box')

    box.onclick = function () {
        move(this, 'left', 100)
    }

    function move(ele, type, target) {
        // 1. 开启定时器
        const timer = setInterval(() => {
            // 2. 获取当前位置
            let init = parseInt(window.getComputedStyle(ele)[type])
            // 3. 计算本次移动距离  (目标 - 当前值) / 10
            let duration = (target - init) / 10
            // 4. 判断移动距离是否大于0, 从而决定取整方式
            duration = duration > 0 ? Math.ceil(duration) : Math.floor(duration)
            // 5. 判断走还是不走
            if (target === init) {
                clearInterval(timer)
            } else {
                // 元素原本的位置 + 要移动的距离, 然后赋值给元素
                ele.style[type] = init + duration + 'px'
            }
        }, 20)
    }

</script>

5.

<style>
    * {
        padding: 0;
        margin: 0;
    }

    .box {
        width: 200px;
        height: 200px;
        background-color: aquamarine;
        position: absolute;
        top: 0px;
        left: 0px;
    }
</style>
<div class="box"></div>
<script>
/**
 *  问题: 改变多个属性时, 需要调用多次 move 函数
 * 
 *  解决: 
 *      1. 多写参数 能解决, 但是并不友好
 *      2. 将参数修改为 对象格式的, 你要修改哪些属性, 就直接在对象内部书写
*/
var box = document.querySelector('.box')

box.onclick = function () {
    // move(this, 'left', 100)
    // move(this, 'top', 100)
    // move(this, 'width', 400)

    move2(this, {
        left: 100,
        top: 100,
        width: 400,
        height: 600
    })
}

function move2(ele, options) {
    for (let key in options) {
        let type = key
        let target = options[key]

        // 1. 开启定时器
        const timer = setInterval(() => {
            // 2. 获取当前位置
            let init = parseInt(window.getComputedStyle(ele)[type])
            // 3. 计算本次移动距离  (目标 - 当前值) / 10
            let duration = (target - init) / 10
            // 4. 判断移动距离是否大于0, 从而决定取整方式
            duration = duration > 0 ? Math.ceil(duration) : Math.floor(duration)
            // 5. 判断走还是不走
            if (target === init) {
                clearInterval(timer)
            } else {
                // 元素原本的位置 + 要移动的距离, 然后赋值给元素
                ele.style[type] = init + duration + 'px'
            }
        }, 20)
    }
}
</script>

9.

<style>
* {
    padding: 0;
    margin: 0;
}

.box {
    width: 200px;
    height: 200px;
    background-color: aquamarine;
    position: absolute;
    top: 0px;
    left: 0px;
}
</style>
<div class="box"></div>
<script>
/**
 *  问题: 没有办法真正捕获到运动函数的结束
 * 
 *  解决: 
 *      利用计数器思想
 *          1. 再函数开始的时候创建一个变量作为计数器, 变量名与变量的值不重要
 *          2. for...in 循环每执行一次, 就代表开启了一个运动函数, 那么将 count 的值自增1
 *          3. clearInterval 每执行一次说明有一个运动函数结束了, 所以此时将 count 的值自减1
 *          4. 当 count 的值 再一次等于 0 的时候(因为初始值给的是0), 说明所有的运动函数执行完毕, 此时为 move 函数的真正结束
*/
    var box = document.querySelector('.box')

    function move(ele, options) {
        // 创建一个变量作为计数器
        let count = 0

        for (let key in options) {
            let type = key
            let target = options[key]

            // 循环每执行一次, 让计数器自增一次, 因为循环执行一次说明要开启一个 运动
            count++

            // 1. 开启定时器
            const timer = setInterval(() => {
                // 2. 获取当前位置
                let init = parseInt(window.getComputedStyle(ele)[type])
                // 3. 计算本次移动距离  (目标 - 当前值) / 10
                let duration = (target - init) / 10
                // 4. 判断移动距离是否大于0, 从而决定取整方式
                duration = duration > 0 ? Math.ceil(duration) : Math.floor(duration)

                // 5. 判断走还是不走
                if (target === init) {
                    clearInterval(timer)

                    // 每清除一个定时器, 说明有一个运动函数结束了, 所以我们将 count--
                    count--

                    if (count === 0) {
                        // 当前分支执行, 说明所有的运动函数全都执行完毕了
                        console.log('说明所有的运动函数全都执行完毕了')
                    }

                } else {
                    // 元素原本的位置 + 要移动的距离, 然后赋值给元素
                    ele.style[type] = init + duration + 'px'
                }
            }, 20)
        }
    }

    box.onclick = function () {
        move(this, {
            left: 100,
            top: 100,
            width: 400,
            height: 600
        })
    }


</script>

9.

<style>
    * {
        padding: 0;
        margin: 0;
    }

    .box {
        width: 200px;
        height: 200px;
        background-color: aquamarine;
        position: absolute;
        top: 0px;
        left: 0px;
    }
</style>
<div class="box"></div>
<script>

/**
 *  回调函数
 * 
 *      本质上就是一个函数
 *          1. 把 函数 A, 以实参的形式传递到 函数 B 内部
 *          2. 在 函数 B 内部, 以形参的方式 调用 函数 A
 *          3. 此时我们可以把 函数 A 叫做 函数 B 的 回调函数
*/
var box = document.querySelector('.box')

function move(ele, options, fn) {

    console.log(fn)

    // 创建一个变量作为计数器
    let count = 0

    for (let key in options) {
        let type = key
        let target = options[key]

        // 循环每执行一次, 让计数器自增一次, 因为循环执行一次说明要开启一个 运动
        count++

        // 1. 开启定时器
        const timer = setInterval(() => {
            // 2. 获取当前位置
            let init = parseInt(window.getComputedStyle(ele)[type])
            // 3. 计算本次移动距离  (目标 - 当前值) / 10
            let duration = (target - init) / 10
            // 4. 判断移动距离是否大于0, 从而决定取整方式
            duration = duration > 0 ? Math.ceil(duration) : Math.floor(duration)

            // 5. 判断走还是不走
            if (target === init) {
                clearInterval(timer)

                // 每清除一个定时器, 说明有一个运动函数结束了, 所以我们将 count--
                count--

                if (count === 0) {
                    // 当前分支执行, 说明所有的运动函数全都执行完毕了
                    // console.log('说明所有的运动函数全都执行完毕了')
                    fn()
                }

            } else {
                // 元素原本的位置 + 要移动的距离, 然后赋值给元素
                ele.style[type] = init + duration + 'px'
            }
        }, 20)
    }
}

box.onclick = function () {
    // 运动函数的开始
    move(this, {
        left: 100,
        top: 100,
        width: 400,
        height: 600
    }, () => {
        // console.log('我是一个回调函数')
        box.style.backgroundColor = 'red'
    })
    // 运动函数结束了
}


</script>