1.防抖和节流
1.1 防抖
防抖的概念 :用在高频触发的事件,等待n秒后执行一次函数,如果在n秒内再次被触发,那么会重新计算等待时间
如何实现:函数每执行一次就会启动一个定时器,来延迟执行fn的执行时间,如果在规定的时间内又一次执行了函数,那就把上一次的定时器清除,再重新启动一个新的定时器
function debounce(func, wait = 1000){
// 当前的作用域是不销毁的
let timer = null;
return function(...params){
// 当前作用域的this是btn元素
//支持给函数传参 用params接收参数
clearTimeout(timer);
timer = setTimeout(()=>{
func.call(this,...params);
timer = null;
},wait);
}
}
-------------------上面为防抖的实现方法,下面为使用方法-------------------------
function fn(){
console.log(2,this);
}
let lazyFunc = debounce(fn,2000);
btn.onclick = lazyFunc;
----------------------------或者可以传参使用--------------------------------
function fn(arg){
console.log(2,this,arg);
}
let lazyFunc = debounce(fn,2000);
btn.onclick = function (){
lazyFunc(100);
};
注意:这里面也涉及到了很多关于this的问题
/*
1.函数执行,前面没有点,那么函数里面的this是window,
就比如 debounce执行,前面没有点,所以里面的this是window,
这也就可以解释为什么在防抖的源码中,func函数执行的时候需要用call改变this,
如果不用call,那么里面的this就是window了
2.给元素绑定点击事件,函数触发执行时,里面的this就是当前元素
3.箭头函数没有自己的this,会在声明时继承上级作用域的this,这里提到了上级作用域的概念,简单说一下,
上级作用域是在函数声明时就已经确定了的,箭头函数是在代码执行到setTimeout这一句代码的时候被声明的,
并且箭头函数在这里的时候把this的指向也已经确定了,就是上级作用域即元素绑定的函数执行所形成的私有作用域,
而又因为绑定的函数里面的this是当前元素,所以箭头函数里面的this是当前元素,
这就可以解释为什么func函数执行的时候需要用call改变this了,改成了箭头函数中的this
箭头函数中的this是当前元素,改变完以后,func函数执行时,里面的this就可以正常的指向当前元素了。
*/
1.2 节流
节流的概念 :也是使用在高频触发的事件,但在n秒内会执行一次,会稀释函数的执行次数,注意和防抖的区别在于节流不会等待,而是稀释了执行的次数,在n秒内执行了,只是执行次数变少了。
如何实现 :lastTime记录的是上一次执行的时间,然后每一次都要把当前执行的时间-上一次执行的时间和wait进行比较;如果当前时间减去上一次的时间(上一次执行func的时间)大于你设置的wait的时间了,说明这一系执行和上一次执行的时间间隔已经满足规定的时间,那就让func执行,并且重新记录上一次时间
function throttle(func,wait) {
let lastTime = 0; //用来记录上一次的时间
return function (...params) {
var nowTime = Date.now(); //获取当前距离1970年1月1号凌晨的毫秒级的时间差
if(nowTime - lastTime >=wait){
func.call(this,...params);
lastTime = nowTime;
}
}
}
------------------上面为节流的实现方法,下面为使用方法-------------------------
function fn() {
console.log(1);
}
let lazyFunc = throttle(fn, 500);
box.onmousemove = lazyFunc;
2.Promise
Promise是ES6新增的一个类,他就是一个状态机
pending ==> fulfilled pending==>rejected
他的状态一旦发生改变,就会凝固,不可能再次变化
new Promise是一个同步的过程,当new Promise的时候传进去的回调函数会立马执行,所以它也是同步的
let p = new Promise(function (resolve, reject) {
// setTimeout(() => {
resolve(900); // pending ==> fulfilled
},10)
reject(); // pending==> rejected
console.log(100);
});
console.log(200);
// then他是同步的函数
// then里边的回调函数的执行取决于当前的promise实例的状态,如果是成功态,那then的第一个回调函数就会执行,如果是失败态,那then的第二个回调函数就会执行
p.then((num) => {
console.log(1, num);
return new Promise((res,rej)=>{
// rej();
console.log(a); // 如果当前代码运行会出错,那他还会默认把promise的状态改为失败态
});
}, () => {
console.log(2);
}).then(()=>{
console.log(3);
},()=>{
console.log(4);
})
console.log(300);
// 当前的then的回调函数的执行要受上一个then里的函数执行的返回的promise的状态的控制
// 如果上一个then执行时没有返回promise的实例,那当前的then会默认执行成功的回调
//---------------------------------------------------------------
// 他可以把异步的代码变得看起来同步化
let p = new Promise((res,rej)=>{
// res()
rej();
});
p.then(()=>{
// return new Promise((res,rej)=>{
// rej()
// })
}).then(()=>{
}).catch((e)=>{
console.log(3);
}).finally(()=>{
console.log(6000);
})
//
setTimeout(()=>{
},100);
setTimeout(()=>{
},100);
setTimeout(()=>{
},100);
3.几类动画的实现
3.1 CSS3动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box {
width: 200px;
height: 200px;
border-radius: 50%;
background:pink;
text-align: center;
line-height: 200px;
/* transform: skew(30deg); */
transition: all 1s ease-in-out 3s;
}
/* #box:hover {
transform: rotate(180deg);
} */
@keyframes aaa {
0% {
transform: rotate(0deg);
}
25% {
transform: rotate(90deg);
}
50% {
transform: rotate(180deg);
}
75% {
transform: rotate(270deg);
}
100% {
transform: rotate(360deg);
}
}
#box {
animation: aaa 4s linear infinite;
}
</style>
</head>
<body>
<div id="box">12345</div>
<script>
/*
css3中的变形属性: transform,改变当前元素的样式
// css的性能要比js好
+ scale(n):缩放
+ translate(x,y):位移 translate3d(x,y,z) translateX/translateY/translateZ
+ rotate(n deg): 如果是正数是顺时针, 否则就是逆时针
+ skew(n deg): 倾斜
*/
/*
transition:过度动画
给元素的变形设置过度效果,当某个元素的某个样式发生变化,不并不是立即发生,而是慢慢的去改变,从而实现动画效果
transition-property: 设置过度的样式属性(哪些样式的改变会有过度效果),默认是all
transition-duration:过度需要的时间 ms s
transition-timing-function:过度的方式: linear这也是默认值(匀速) ease
transition-delay:设置延迟的时间,默认是0ms
// 他只能实现从A到B的动画,延时做一些复杂的动画就要用到帧动画
*/
/*
css3中的帧动画 animation;他是分为两部分
1.制作动画的轨迹(每一帧元素的样式) @keyframs
@keyframs 动画 {
0% {}
25% {}
50% {}
75% {}
50% {}
}
2.播放动画
animation: 名字 4s linear 2s infinite
*/
</script>
</body>
</html>
3.2 通过js的定时器实现动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
#box {
width: 100px;
height: 100px;
background: pink;
border-radius: 50%;
position: absolute;
left: 0;
}
button {
width: 80px;
height: 30px;
background: orangered;
line-height: 30px;
text-align: center;
position: relative;
top: 200px;
}
#right {
margin-left: 20px
}
</style>
</head>
<body>
<div id="box" style="left:0"></div>
<button id="left">往左跑</button>
<button id="right">往右跑</button>
<script>
// 固定步长的动画
let box = document.getElementById('box');
let left = document.getElementById('left');
let right = document.getElementById('right');
let target = document.documentElement.clientWidth - box.offsetWidth; // 当前球需要运动的总距离 100
let timer = null;
let step = 8; // 当前定时器每运行一次球需要运动的距离
right.onclick = function () {
clearInterval(timer);
timer = setInterval(() => {
let curL = parseFloat(box.style.left); // 是一个字符串的值而且有单位 96
curL += step; // 104
if (curL >= target) {
// 当前的left值加上一个步长如果大于等于target的值了,说明球运动到位置了
clearInterval(timer);
box.style.left = target + 'px'; // 为了让当前的球运动到指定的位置
return; // 当清除定时器时,当前次的函数的走完,为了不让下边的那一句代码走,所以加了return
}
box.style.left = curL + 'px';
}, 17)
};
left.onclick = function () {
clearInterval(timer);
timer = setInterval(() => {
let curL = parseFloat(box.style.left); // 是一个字符串的值而且有单位 96
curL -= step; // 104
if (curL <= 0) {
// 当前的left值加上一个步长如果大于等于target的值了,说明球运动到位置了
clearInterval(timer);
box.style.left = 0 + 'px'; // 为了让当前的球运动到指定的位置
return; // 当清除定时器时,当前次的函数的走完,为了不让下边的那一句代码走,所以加了return
}
box.style.left = curL + 'px';
}, 17)
}
// 当点击左右的时候,小球会出现不知道往那边跑的现象,因为你开辟了多个定时器,每一个定时器里都去操作当前的小球,所以这时候你在点击按钮的时候,把之前的定时器清空就好了
</script>
</body>
</html>
3.3 固定时间的动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
#box {
width: 100px;
height: 100px;
background: pink;
border-radius: 50%;
position: absolute;
}
</style>
</head>
<body>
<div id="box" style="left:100px"></div>
<script>
// 固定时长的动画
let box = document.getElementById('box');
// let target = document.documentElement.clientWidth - box.offsetWidth; // 需要运动的总长度
// let duration = 2000;
// let timer = null;
/*
t:timey 已经运动过的时间
b:begin 起始位置
d:duration 总时间
c:change 总共需要运动的距离
求当前的位置:
时间比 === 位置比
当前运动的时间/总时间 === 当前的距离/总距离
t/d === 当前的距离/c
c/d = 当前的位置/t
当前的位置 = t/d*c+b
小明 target duration
100m 10s
小王 target
100m
*/
function linear(t, b, d, c) {
// t:time 已经运动过的时间
// b:begin 起始位置
// d:duration 总时间
// c:change 总共需要运动的距离
return t/d*c+b; // 代表当前球距离左边的位置
}
let target = document.documentElement.clientWidth - box.offsetWidth; // 目标位置
let begin = parseFloat(box.style.left); // 当前起始位置
let change = target - begin; // 需要移动的总距离
let duration = 1000;
let timer= null;
function move() {
let flag = 0;
timer = setInterval(() => {
flag += 20;
if (flag >= duration) {
clearInterval(timer);
timer = null;
box.style.left = target + 'px';
return;
// 到此为止定时器的回调函数执行的次数已经确定好了
}
box.style.left = linear(flag,begin,duration,change) + 'px'; // 设置的是当前left的最新值
console.log(1);
}, 20);
}
move()
</script>
</body>
</html>
3.4 多方向运动动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
#box {
width: 100px;
height: 100px;
background: pink;
position: relative;
border-radius: 50%;
}
</style>
</head>
<body>
<div id="box" style="left:0;top:0"></div>
<script src="./jquery-1.11.3.js"></script>
<script>
// 多方向运动动画
// 一会咱们实现当前元素left、top、width、height同时变化
// 500 500 500 500
// let box = document.getElementById('box');
// function linear(t, b, d, c) {
// // t:time 已经运动过的时间
// // b:begin 起始位置
// // d:duration 总时间
// // c:change 总共需要运动的距离
// // 距离body左边的位置
// return t / d * c + b; // 代表当前球距离左边的位置
// }
// let begin = {
// left:parseFloat(getComputedStyle(box).left),
// top:parseFloat(getComputedStyle(box).top),
// width:parseFloat(getComputedStyle(box).width),
// height:parseFloat(getComputedStyle(box).height)
// }
// let target = {
// left: 200,
// top: 200,
// width: 300,
// height: 300
// }
// let change = {
// }
// for(var key in begin){
// // 计算每一个样式需要运动的总距离增加到change里
// change[key] = target[key] - begin[key]
// }
// // console.log(change);
// let duration = 3000;
// let timer = null;
// let flag = 0;
// timer = setInterval(()=>{
// flag+=17;
// for(let key in change){
// let cur = linear(flag,begin[key],duration,change[key]);
// box.style[key] = cur + 'px';
// // box.style.left = xxx
// }
// if(flag>=duration){
// clearInterval(timer);
// }
// },17);
//-------------------------------------------------------------------
// jq中的多方向动画
// $('#box').stop().animate({
// left:300,
// top:300,
// width:300,
// height:300
// },3000,'linear',function(){
// console.log(200);
// })
/*
curEle:当前需要添加样式的元素
target:移动的目标的位置
duration:运动的总时间
callBack:动画执行完成的回调函数
*/
function move(curEle,target,duration,callBack){
}
</script>
</body>
</html>