.box {
width: 200px;
height: 200px;
background-color: aqua;
position: absolute;
top: 0;
left: 0;
}
* {
margin: 0;
padding: 0;
}
<div class="box"></div>
需求1 :
- 点击 box 后让他的 left 值 更改为 500
var box = document.querySelector('.box')
box.onclick = function(){
this.style.left = 500 +'px'
}
需求2 :
- 点击 box 后 让他的 top 值 更改为 500
var box = document.querySelector('.box')
box.onclick = function () {
move(this, 'top', 300)
}
function move(ele, type, target) {
let init = 0
const timer = setInterval(() => {
init += 5
ele.style[type] = init + 'px'
if (init >= target) clearInterval(timer)
}, 20)
}
需求3 :
- 原本坐标 top 为 200 移动到 400
-
- 问题 : 不管元素的原来坐标值是什么, 每次运动都是从0 开始
-
- 解决 : 修改 init 的初始值为 元素本身的属性值
var box = document.querySelector('.box')
box.onclick = function () {
move(this, 'top', 300)
}
function move(ele, type, target) {
let init = parseInt(window.getComputedStyle(ele)[type])
const timer = setInterval(() => {
init += 5
ele.style[type] = init + 'px'
if (init >= target) clearInterval(timer)
}, 50)
}
需求4 :
- 原来坐标 left 为 200 移动到 501
- 原来坐标 top 为 200 移动到 602
-
问题 : 移动的距离不是 5的倍数, 不能准确的移动到目标值上
-
解决 :
1. 每次移动的 距离为 5, 不能准确的移动到 目标值
2. 每次移动的 总路程为 1/10
-
- 假设 当前位置 为 200, 要 移动到 501, 移动的 总距离为 301, 那么每次移动 30.1
3. 每次移动 剩余路程的 1/10
-
- 假设当前位置 为 200,要 移动到 501, 移动的 总距离为 301
-
-
- 第二次 移动的时候, 当前位置为 230.1 ( 200 + 30.1), 要移动到501, 移动的总距离为 501 - 230.1
var box = document.querySelector('.box')
box.onclick = function () {
move(this, 'left', 100)
}
function move(ele, type, target) {
const timer = setInterval(() => {
let init = parseInt(window.getComputedStyle(ele)[type])
let duration = (target - init) / 10
if (target <= init) {
clearInterval(timer)
} else {
ele.style[type] = init + duration + 'px'
}
}, 20)
}
-
问题 : 从 0px 移动, 移动到 100px, 但实际移动到 91.9px 就停止了
-
分析 :
-
- 在某一次移动到 91px ,当前位置为 91px, 但要移动到 100px
-
-
- 也就是 ( 目标值 - 当前值 ) / 10 -> ( 100 - 91 ) / 10 === 0.9
-
- 所以本次移动的距离为 0.9px, 也就是说从 91px 移动到了 91.9px
-
- 因为浏览器的最小像素表述单位为 1px, 所以虽然设置了 0.9, 但浏览器实际无法移动到 0.9px
-
- 所以我们下一次获取到这个元素位置的时候, 还是在 91px
-
解决 :
-
- 让每次移动的最小值 向上取整为 1px ( Math.ceil() )
-
新问题 : 从 100px 移动到 0px, 但实际上移动到 9px 就停止了
-
- 每次移动的距离 ( 目标值 - 当前值 ) / 10 -> ( 0 - 9 ) / 10 === -0.9
-
- 计算完成这个移动距离后, 判断是否小于1,如果小于1,那么向下取整为 -1 ( Math.floor() )
-
- 如果 目标值 大于 当前值 === ( 目标值 向右/向下 ) , 向上取整
-
-
- 如果 目标值 小于 当前值 === ( 目标值 向左/向上 ) , 向下取整
-
var box = document.querySelector('.box')
box.onclick = function () {
stop(this, 'left', 501)
console.log('点击了');
}
function stop(ele, type, target) {
const timer = setInterval(() => {
let init = parseInt(window.getComputedStyle(ele)[type])
let duration = (target - init) / 10
duration = duration > 0 ? Math.ceil(duration) : Math.floor(duration)
if (target === init) {
clearInterval(timer)
} else {
ele.style[type] = init + duration + 'px'
}
}, 20)
}
运动函数完结篇
-
问题 : 改变多个属性时, 需要调用多次 move 函数
-
解决 :
1. 多写参数 , 但是并不友好
box.onclick = function () {
move(this, 'left', 100)
move(this, 'top', 100)
move(this, 'width', 400)
}
2. 将参数修改为 对象格式, 修改哪些属性值, 就在对象内部修改
box.onclick = function () {
move(this, {
left: 100,
top: 100,
width: 300
})
}
function move(ele,options){
for(let key in options){
let type = key
let target = options[key]
let timer = setInterval( () => {
let init = parseInt(window.getComputedStyle(ele)[type])
let duration = (target - init) / 10
duration = duration > 0 ? Math.ceil(duration) : Math.floor(duration)
if(init === target){
clearInterval(timer)
}else{
ele.style[type] = init + duration + 'px'
}
},20)
}
}
运动函数大结局
-
问题 : 没有办法真正捕获到运动那个函数的结束
-
解决 :
-
-
- 在函数开始时创建一个变量 作为计数器(变量名与变量的值不重要)
-
- for...in 循环每执行一次, 就代表开启了一个运动函数, 那么将 count 的值自增 1
-
- clearInterval 每执行一次, 说明有一个运动函数结束了, 所以此时将 count 值 自减1
-
- 当 count 的值再一次 等于 0 时( 因为初始值给的0 ), 说明所有的运动函数执行完毕,此时为 move 函数真正的结束
var box = document.querySelector('.box')
box.onclick = function () {
console.log('点击了');
move(this, {
left: 100,
top: 100,
width: 300
})
}
function move(ele, options) {
let count = 0;
for (let key in options) {
let type = key
let target = options[key]
count++
const timer = setInterval(() => {
let init = parseInt(window.getComputedStyle(ele)[type])
let duration = (target - init) / 10
duration = duration > 0 ? Math.ceil(duration) : Math.floor(duration)
if (init === target) {
clearInterval(timer)
count --
if(count === 0){
console.log('执行完毕');
}
} else {
ele.style[type] = init + duration + 'px'
}
}, 20)
}
}
运动函数的彩蛋
回调函数
- 本质上就是一个函数
-
- 把 函数A, 以实参的形式传递到 函数B 内部
-
- 在 函数B 内部, 以 形参的方式 调用 函数A
-
- 此时 我们可以把 函数A 叫做 函数B 的回调函数
box.onclick = function () {
console.log('点击了');
move(this, {
width: 200,
height: 300,
top: 200,
left: 300
}, () => {
box.style.backgroundColor = 'red'
})
}
function move(ele, options, fn) {
let count = 0
for (let key in options) {
let type = key
let target = options[key]
count++
const timer = setInterval(() => {
let init = parseInt(window.getComputedStyle(ele)[type])
let duration = (target - init) / 10
duration = duration > 0 ? Math.ceil(duration) : Math.floor(duration)
if (init === target) {
clearInterval(timer)
count--
if (count === 0) {
fn()
}
} else {
ele.style[type] = init + duration + 'px'
}
}, 20)
}
}