匀速动画
匀速运动(横向)
1.初始化速度变量
2.开启定时器
(1)获取当前值
(2)改变当前值:当前值+速度
(3)将改变后的值赋值给元素的样式
(4)当改变后的值大于等于目标值,清除定时器,同时将改变后的值改成目标值。这一步应该在赋值给样式之前
//下面若无重写,均采用这个css和html
#katsuki{position: absolute;left: 0;top: 0;}
<img src="../img/katsuki.jpg" id="katsuki">
<script>
document.addEventListener('DOMContentLoaded',function(){
var katsuki = document.querySelector('#katsuki');
//初始化速度变量
var speed = 10;
//开启定时器
var timer = setInterval(function(){
//获取元素当前值,一般用window.getComputedStyle
var x = katsuki.offsetLeft;
//改变当前值:当前值+速度
x += speed;
//当改变后的值大于等于目标值,清除定时器,
//同时将改变后的值改成目标值。这一步应该在赋值给样式之前
if(x > window.innerWidth - katsuki.offsetWidth){
x = window.innerWidth - katsuki.offsetWidth;
clearInterval(timer);
}
//将改变后的值赋值给元素的样式
katsuki.style.left = x + 'px';
},50)
})
</script>
加速运动
document.addEventListener('DOMContentLoaded',function(){
var katsuki = document.querySelector('#katsuki');
var speed = 10;
var timer = setInterval(function(){
var x = katsuki.offsetLeft;
x += speed;
//在匀速的基础上,这里设置速度增加
speed += 2;
if(x > window.innerWidth - katsuki.offsetWidth){
x = window.innerWidth - katsuki.offsetWidth;
clearInterval(timer);
}
katsuki.style.left = x + 'px';
},50)
})
减速运动
document.addEventListener('DOMContentLoaded',function(){
var katsuki = document.querySelector('#katsuki');
var speed = 60;
var timer = setInterval(function(){
var x = katsuki.offsetLeft;
x += speed;
//在匀速的基础上,这里设置速度减少
speed -= 2;
//速度减为负数不清定时器,图片就会倒退
if(speed < 0){
clearInterval(timer);
}
katsuki.style.left = x + 'px';
},50)
})
抛物线
1.初始化速度变量(水平、垂直)
2.开启定时器
(1)获取当前值(水平、垂直)
(2)改变当前值:当前值+速度
(3)速度不断减小(垂直方向的速度)
(4)将改变后的值赋值给元素的样式
(5)当改变后的值大于等于目标值,清除定时器,同时将改变后的值改成目标值。这一步应该在赋值给样式之前
#katsuki{position: absolute;left: 0;bottom: 0;}
document.addEventListener('DOMContentLoaded',function(){
var katsuki = document.querySelector('#katsuki');
//水平速度
var xspeed = 7;
//垂直速度
var yspeed = 13;
var timer = setInterval(function(){
var x = katsuki.offsetLeft;
var y = katsuki.offsetTop;
x += xspeed;
y -= yspeed;
//垂直方向速度减少
yspeed -= 0.2;
if(y > window.innerHeight - katsuki.offsetHeight){
//将改变后的值改成目标值,防止图片超出浏览器可视区域
y = window.innerHeight - katsuki.offsetHeight;
clearInterval(timer);
}
katsuki.style.left = x + 'px';
katsuki.style.top = y + 'px';
},50)
})
缓冲动画
关键:动态计算速度(目标值-当前值有关)
1.开启定时器
(1)获取当前值
(2)获取当前速度(目标值-当前值).
当速度大于0时,Math.ceil()
当速度小于0时,Math.floor()
避免出现减到小数一直减却减不到目标值,进行取整
(4)改变当前值:当前值+速度
(5)将改变后的值赋值给元素的样式
(6)当改变后的值等于目标值,清除定时器
若事件里面开启定时器,记得开启定时器先清除定时器
//返回顶部缓冲
body{height: 3000px;}
#katsuki{
width: 100px;
height: 100px;
position:fixed;
right : 20px;
bottom: 20px;
}
document.addEventListener('DOMContentLoaded',function(){
var katsuki = document.querySelector('#katsuki');
katsuki.onclick = function(){
//避免多次点击启动多个定时器,进入就执行清除
/*
以节点进行绑定:katsuki.timer,是为了保证清除的是同一个timer,
点击多次有多个执行函数,若每次定义var timer,timer不是同一个,无法清除
*/
clearInterval(katsuki.timer);
katsuki.timer = setInterval(function(){
//获取当前值
var y = window.scrollY;
//动态计算速度(目标值-当前值)除10是避免速度过快
var speed = Math.floor((0-y)/10);
y += speed
//设置window滚动条位置
window.scrollTo(0,y);
//取整,必定会减为0,直接判断等于0时
if(y == 0){
clearInterval(katsuki.timer);
}
},50)
}
})
动画的封装
缓冲动画
1.开启定时器
(1)获取当前值
(2)获取当前速度(目标值-当前值).
当速度大于0时,Math.ceil()
当速度小于0时,Math.floor()
(3)改变当前值:当前值+速度
(4)将改变后的值赋值给元素的样式
(5)当改变后的值等于目标值,清除定时器
备注: 事件开启定时器之前,一定要记得先清除已存在的定时器。
/**
* [缓冲动画]
* @param {Element} ele [获取动画的元素]
* @param {String} attr [ele的属性名]
* @param {Number} target [动画的目标值]
* @param {Number} time [定时器时间(毫秒)]
*/
function animate(ele,attr,target,time){
clearInterval(ele.timer);
ele.timer = setInterval(function(){
var current = window.getComputedStyle(ele)[attr];//200px,180deg,0.5
//提取单位
var unit = current.match(/[a-z]+$/);
//判断有无单位并赋值
unit = unit? unit[0] : '';
//取到数值,不用parseInt是考虑到可能小数
current = parseFloat(current);
//计算缓冲速度
var speed = (target-current)/10
if(attr === 'opacity'){
//针对图片淡入淡出使用opacity时
speed = speed>0? 0.01 : -0.01;
}else{
//speed取整,避免速度过小或为0
speed = speed>0? Math.ceil(speed) : Math.floor(speed);
}
current += speed;
if(current == target){
clearInterval(ele.timer);
//避免超出目标值
current = target;
}
ele.style[attr] = current + unit;
},time)
}
缓冲动画完善
缓冲动画(改进)
1.定时器名字根据css属性进行命名,从而保证多个定时器赋值给的变量名不同,不会发生覆盖。
2.在一个动画函数里面,可以定义多个css属性同时改变
参数变成对象{attr:target}
for…in遍历对象,拿到每个attr及对应target值
利用let,将attr、target的值保留在当前的块级作用域
利用函数的形参,将attr、target的值存在局部作用域。
3.需求:所有动画执行完毕后,进行一堆操作。
(1)在清除定时器后再执行这堆操作,会出现执行多次的问题
统计出attr的个数,每次清除定时器就对个数进行–,直到为0,代表所有动画执行完毕。
(2) 封装动画函数结束后,别人要做什么,我不知道。所以只能帮你执行。你需要把你要做的东西封装成函数,传参给我
别人不一定会传递回调函数,要判断。
/**
* [缓冲动画]
* @param {Element} ele [获取动画的元素]
* @param {Object} obj [ele的属性对象]
* attr {Sting} 属性名
* target {Number} 动画的目标值
* @param {Number} time [定时器时间(毫秒)]
* @param {function} fn [函数]
*/
function animate(ele,obj,time,fn){
//3为实现所有动画执行完毕后,进行fn操作,首先存储执行次数
var count = 0;
//对象遍历
for(var key in obj){
count ++;
//2.1利用let,将attr、target的值保留在当前的块级作用域
let attr = key;
let target = obj[key];
/*2.2利用函数的形参,将attr、target的值存在局部作用域
var attr = key;
var target = attr[key];
然后将下面代码封装成函数,传参,假若封装成show
show(attr,target);*/
clearInterval(ele[attr + "Timer"]);
//1定时器赋值给的变量名不同,不会发生覆盖,实现多个动画进行
ele[attr + "Timer"] = setInterval(function(){
var current = window.getComputedStyle(ele)[attr];
console.log(current);
var unit = current.match(/[a-z]+$/);
unit = unit? unit[0] : '';
current = parseFloat(current);
console.log(current);
var speed = (target-current)/10;
if(attr === 'opacity'){
speed = speed>0? 0.01 : -0.01;
}else{
speed = speed>0? Math.ceil(speed) : Math.floor(speed);
}
current += speed;
if(current == target){
clearInterval(ele[attr + "Timer"]);
current = target;
//3当count减为0时,即为最后一个动画执行完毕
count --;
if(count == 0 && fn && typeof(fn) == "function"){
fn();
}
}
ele.style[attr] = current + unit;
},time)
}
}
匀速动画
1.初始化速度变量
2.开启定时器
(1)获取当前值
(2)改变当前值:当前值+速度
(3)将改变后的值赋值给元素的样式
(4)当改变后的值大于等于目标值,清除定时器,同时将改变后的值改成目标值。这一步应该在赋值给样式之前
/**
* [匀速动画]
* @param {String} speed [速度值]
* @param {Element} ele [获取动画的元素]
* @param {String} attr [ele的属性名]
* @param {Number} target [动画的目标值]
* @param {Number} time [定时器时间(毫秒)]
*/
function linearAnimate(speed,ele,attr,target,time){
clearInterval(ele.timer);
ele.timer = setInterval(function(){
//获取当前值
var current = window.getComputedStyle(ele)[attr];
//提取单位
var unit = current.match(/[a-z]+$/);
//判断有无单位并赋值
unit = unit? unit[0] : '';
//取到数值,不用parseInt是考虑到可能小数
current = parseFloat(current);
//改变当前值
current += speed;
ele.style[attr] = current + unit;
if(speed>0 && current >= target || speed<0 && current <= target){
current = target;
clearInterval(ele.timer);
}
})
}
瀑布流
1)计算当前容器能容纳多少列
列数 = parseInt(容器宽度/图片宽度)
2)计算水平间隔
间隔 = 容器宽度%图片宽度/(列数+1)
3)创建一个数组pos
用来保存第一行每列图片左上角坐标(left,top)
4)遍历所有图片,往容器里添加图片
遍历数组pos,查找最小top值,然后更新当前top值
top = top + vgap + 图片高度
document.addEventListener('DOMContentLoaded',function(){
// 获取元素
let wrap = document.querySelector('#wrap');
let items = wrap.children;
// 获取单个图片宽度
let itemWidth = items[0].offsetWidth;
waterfall();
// 窗口大小改变,自适应
window.onresize = waterfall;
function waterfall(){
// 获取容器宽度(考虑滚动条宽度)
let containerWidth = wrap.clientWidth-17;
// 1)计算当前容器能容纳多少列
let len = Math.floor(containerWidth/itemWidth);
// 2)计算间隔
let gap = containerWidth%itemWidth/(len+1);
// 3)创建一个数组pos
// 用于存放左上角坐标(小红点)
let pos = [];
for(let i=0;i<len;i++){
pos[i] = {
top: gap,
left:itemWidth*i + gap*(i+1)
}
}
console.log(pos);
// 开始定位图片
// 4)遍历所有图片,设置top,left
for(let i=0;i<items.length;i++){
// 找出当前图片
let img = items[i].querySelector('img');
// 图片如果被浏览器缓存,图片的属性complete为true
if(img.complete){
showImg();
}else{
// 等待图片加载完成
img.onload = showImg;
}
function showImg(){
// 找出最小top值
// 假设第一个最小
let minIdx = 0;
let min = pos[minIdx].top;
for(let j=1;j<pos.length;j++){
// 判断是否存在更小的top值
if(pos[j].top < min){
min = pos[j].top;
// 更新索引值
minIdx = j;
}
}
// items[i].style.left = pos[minIdx].left + 'px';
// items[i].style.top = pos[minIdx].top + 'px';
animate(items[i],{left:Math.round(pos[minIdx].left),top:Math.round(pos[minIdx].top)});
// 定位后,更新top值
pos[minIdx].top += gap + items[i].offsetHeight;
}
}
}
});