运动
概述
运动其实就是指定JavaScript制作的动画,也就是使用定时器控制对应的dom的样式不断的变化,呈现一个运动的状态。(JavaScript制作的动画 其实就是不断的重绘回流,JavaScript制作动画它是使用cpu进行渲染的,渲染效率低(css3的动画使用GPU进行渲染的,渲染效率更高,效果更优))
运动三要素
- 当前值
- 目标值
- 步长 (迭代量)
主要过程
- 获取当前的值,定义步长
- 使用定时器不断更改当前值(使用当前值加上步长),更改当前的样式,使用其到达目标值
- 到达目标值,清除对应的定时器
运动的分类
- 匀速运动 (步长不变)
- 缓冲运动 (步长不断变化)
- 链式运动 (执行完当前动画可以调用下一次动画(链式调用的行为))
匀速运动
主要是步长不变,控制其始终保持一个步长变化
示例(盒子移动)
<div></div>
<script>
//获取div
var box = document.querySelector('div')
//获取当前的定位
// var current = parseFloat(getStyle(box).left)
var current = box.offsetLeft
//目标值
var target = 100
//定义步长 步长为正还是负 (如果目标值大于当前值 步长为正 否则为负)
var step = target > current ? 10 : -10
//使用定时器不断更改对应的当前值
var timer = setInterval(()=>{
//控制当前值的变化
current += step
//更改当前样式
box.style.left = current + 'px'
//判断当前值是否到达目标值
if(current == target){
//清除定时器
clearInterval(timer)
}
},100)
//封装对应的获取样式的方法
function getStyle(element){
return getComputedStyle ? getComputedStyle(element) : element.currentStyle
}
</script>
简单的匀速移动封装
//封装一个匀速移动的方法 传入移动的元素 传入对应的目标对象 {left:200,top:200}
export const uniform = function(element, target){
if (arguments.length < 2) {
throw new Error('最少需要传入俩个参数')
}
//判断当前传入的是否为元素
if (!(element instanceof HTMLElement)) {
throw new TypeError(`${element} is not element`)
}
//进来的时候先将定时器清除 防止上次定时器影响这次
clearInterval(element.timer)
//给元素对象的timer属性赋值
element.timer = setInterval(() => {
let flag = true
//遍历对象 拿出里面的key
for (let key in target) {
//获取当前值
var current = parseFloat(getStyle(element)[key])
//获取步长
var step = target[key] > current ? 1 : -1
//变化对应的样式
element.style[key] = current + step +'px'
//判断是否走完
if(current + step != target[key]){
flag = false
}
}
//判断是否走完
if(flag){
clearInterval(element.timer)
}
},20)
}
//封装对应的获取样式的方法
export function getStyle(element) {
return getComputedStyle ? getComputedStyle(element) : element.currentStyle
}
缓冲运动
步长不断变小,逐渐到达目标位置的一个运动动画称为缓冲运动。
示例
<div></div>
<script>
//获取元素
var div = document.querySelector('div')
//声明对应的当前值 及 目标值
var current = parseFloat(getStyle(div).left)
var target = 100
//使用定时器不断的设置div的位置
var timer = setInterval(()=>{
//获取对应的步长 ( target - current ) / 10
//防止小数除不尽 取整操作 大于0向上取整 小于0的向下取整
var step = target > current ? Math.ceil(( target - current ) / 10) : Math.floor(( target - current ) / 10)
//变化对应的当前值
current += step
//设置样式
div.style.left = current + 'px'
//到达目标位置清除定时器
if(current == target){
clearInterval(timer)
}
},100)
//获取样式的方法
function getStyle(element) {
return getComputedStyle ? getComputedStyle(element) : element.currentStyle
}
</script>
缓冲运动封装
//传入对应的需要运动的元素 传入目标对象
export function animation(element, target) {
if (arguments.length < 2) {
throw new Error('最少需要传入俩个参数')
}
//判断当前传入的是否为元素
if (!(element instanceof HTMLElement)) {
throw new TypeError(`${element} is not element`)
}
//进来的时候先将定时器清除 防止上次定时器影响这次
clearInterval(element.timer)
//给元素对象的timer属性赋值
element.timer = setInterval(() => {
let flag = true
//遍历对象 拿出里面的key
for (let key in target) {
//取出当前的样式值 进行转换
var current = parseFloat(getStyle(element)[key])
//定义步长
var step = target[key] > current ? Math.ceil((target[key] - current) / 10) : Math.floor((target[key] - current) / 10)
//针对传入的目标对象的key进行判断
//如果是透明度设置
if(key == "opacity"){
step = target[key] > current ? Math.ceil((target[key] - current) * 1000 / 10) / 1000 : Math.floor((target[key] - current) * 1000 / 10) / 1000
element.style[key] = current + step
}else if(key == "zIndex" || key == "backgroundColor"){
//直接设置值
element.style[key] = target[key]
}else{
// left top width...
element.style[key] = current + step + 'px'
}
//判断到达目标位置 排除对应的key里面color的
if (parseFloat(getStyle(element)[key]) != target[key] && !key.includes('Color')) {
flag = false
}
}
//判断是否走完
if (flag) {
clearInterval(element.timer)
}
}, 20)
}
链式调用
在一次动画执行完里面可以开始执行下次一个动画。
解决方案及问题
动画是使用定时器来完成的,它里面的代码是异步的。主要要解决的问题是异步代码同步执行。利用回调函数来解决对应的异步代码同步执行的问题。
示例
animation(div,{
left:300,
opacity:0.8
},()=>{
animation(div,{
paddingLeft:100,
opacity:0.5
})
})