运动函数
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>