就是把一个元素运动的过程封装起来
需求: 页面上有一个 div
=> 当你触发事件(可以是任何事件)的时候
=> 可以改变 div 的位置
=> 需要运动的改变
=> 问题1: 这个运动函数需要多少个参数 ?
-> 要运动的元素 ele
-> 要运动的样式名 type
-> 要运动的目标位置 target
问题: 如果我的元素起始位置不是 0 ?
=> 因为我在封装的时候, distance 始终是 0
=> 不管你元素的开始位置是什么
=> 运动的开始位置一定是 0
解决:
=> 再加一个参数
-> 不好
-> 尽量不添加参数
=> 直接去拿到元素的开始位置
-> 当元素需要运动的是 left 属性
-> 只要在运动开始之前, 把元素本身的 left 属性拿到
-> 把我拿到的元素初始值, 当做 distance 的初始值
问题: 如何获取元素的初始位置 ?
=> 获取元素的 非行内 样式 left
=> 标准浏览器: window.getComputedStyle(元素).样式名
=> 注意: 你拿到的值是带有 px 的, 不要忘记使用 parseInt()
问题: 元素运动的初始值和目标值不一定是 整数 ?
=> 不容易刚好到达目标位置, 可能会差一点
解决: 把每次运动的距离改成 1
=> 把定时器的事件调整的快一些
问题: 如果调整为每次运动距离为 1, 那么当多个属性一起运动的时候
=> 有的先完成有的后完成
解决: 把匀速运动改成非匀速运动
=> 通过速度的不一致, 得到最终运动时间一致
实现:
=> 只要不让你每次都运动 1px
=> 不管你运动总距离是多少, 我都固定每次运动剩余距离的 10分之1
=> 每次的定时器拿到
-> 当前的 left 位置是多少
-> 目标位置 - 当前位置, 求出来的是 剩余距离
-> 剩余距离 / 10, 求出, 本次应该运动的距离
-> 判断:1
+ 判断当前位置是否到达目标位置
+ 如果到达目标位置, 停止定时器
+ 如果没有到达目标位置, 赋值: 当前位置 + 本次运动距离
问题: 没有办法到达目标位置 ?
=> 随着运动, 一定会到达一个临界点, 还差不到 10px 到目标位置了
=> 当你剩余距离还剩 9px 的时候
=> 计算出的本次运动距离 0.9px
=> 因为浏览器能描述的最小像素是 1px
=> 0.9 px 就不会运动了, 就停在原位
=> 导致下次还是剩余 9px, 计算的运动距离还是 0.9, 还是不会运动
解决: 向上取整
问题: 向负方向运动 ?
=> 因为你操作向上取整
=> 当你向负方向运动的时候
=> 一定能在过程中计算出 -0.9 的数字
=> -0.9 向上取整, 得到 0
=> 导致本次运动距离是 0, 下次拿到的运动距离依旧是 -0.9
=> 改成向下取整
问题: 正负方向不能兼容 ?
=> 判断, 当他 大于0 的时候, 向上取整
=> 当他 小于0 的时候, 向下取整
问题: 我想运动一下 opacity ?
=> 赋值的时候, 带有 px 单位
=> 因为 opacity 是不需要带有单位的
=> 赋值的时候, 需要判断一下, 如果是 opacity 不需要加单位
=> 如果不是 opacity 才加 px 单位
问题: 没有运动的过程了 ?
=> 因为你拿到的值是 1, 目标位置是 0
=> 本次的运动距离一定是 (0 - 1) / 10, 得到的是 -0.1, 取整以后, 得到的是 -1
=> 赋值的时候, opacity = 1 + -1, 直接等于 0
解决: 获取值的时候, 放大 100倍
=> 你如果当前是 1, 那么拿到的是 100
=> 你赋值的时候, 缩小 100 倍
=> 你赋值的时候如果是 90, 真实赋值是 0.9
问题: 没办法向正方向运动 ?
=> 注意: 当你需要运动 opacity 的时候
=> 最好你給值的时候, 按照 0 ~ 100 的区间給值
问题: 一个函数只能运动一个属性 ?
=> 因为我们设计的函数就是 单属性 运动函数
解决: 我想把函数设计成多属性运动函数
=> 方案1: 添加参数个数
-> 不好
=> 方案2: 以对象的形式传递需要运动的内容
{
left: 500,
top: 300,
width: 400
}
问题: 我如何捕获到运动完全结束了 ?
=> 我需要在运动结束的时候, 在控制台打印一下结束了
=> 因为你开启多少个定时器, 就会关闭多少个定时器
=> 每关闭一个定时器就会执行一次运动结束的打印
=> 运动多少个属性, 就会打印多少次
=> 中间有四次不是真的运动完全结束
问题2: 什么时候才是运动真的结束 ?
=> 最后一个定时器关闭以后
解决:
=> 准备一个 计数器, 每开启一个定时器, 计数器 ++
=> 每关闭一个定时器, 计数器 --
=> 等到计数器回归到 0 的时候, 最后一个定时器关闭了
需求: div 运动完全结束以后, 在控制台打印结束
p 运动完全结束以后, 用 alert 的形式来告知结束
问题: 我想在运动结束以后, 做点事情 ?
=> 因为 move 函数是封装的, 一次封装多次调用
=> 我可以把我想在运动结束做的事情, 放在一个 "锦囊" 里面
=> 把这个 "锦囊" 交给 move 运动函数
=> 封装 move 的时候, 会在运动完全结束的时候, 把 "锦囊" 内的代码完全执行
=> 可以承载一段代码的 "盒子" 是 函数
let move = (ele, target, fn) => {
let count = 0
for (let k in target) {
count++
const timer = setInterval(() => {
let current
if (k === 'opacity') {
current = window.getComputedStyle(ele)[k] * 100
} else {
current = parseInt(window.getComputedStyle(ele)[k])
}
let distance = (target[k] - current) / 10
distance = distance > 0 ? Math.ceil(distance) : Math.floor(distance)
if (current === target[k]) {
clearInterval(timer)
count--
if (count === 0) fn()
} else {
if (k === 'opacity') {
ele.style[k] = (current + distance) / 100
} else {
ele.style[k] = current + distance + 'px'
}
}
}, 20)
}
}
调用
// 将来我需要使用的时候
const box = document.querySelector('div')
box.onclick = function () {
move(box, {
left: 600,
top: 300,
width: 400,
height: 200,
opacity: 35
}, () => { console.log('div 运动结束了') })
}