课程名称:** 破解JavaScript高级玩法
课程章节: 动起来:计时器和JS动画
主讲老师: Cloud
课程内容:
今天学习的内容包括:
CSS的动画实现
课程收获:
13.1 心得:
setInterval
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<style>
.warp {
position: relative;
height: 100px;
background: red;
}
</style>
<body>
setIntervale
<div id="progress" class="warp" style="width: 0">0%</div>
<button id="start">开始</button>
<script>
//同步耗时操作
function syncSleep(duration) {
const now = Date.now();
while (now + duration > Date.now()) { }
}
var interval = null;
var timeout = null;
var curCount = 0;
var timeLine = Date.now();
var lastIntervalTime;
function printTime(text, isInterval) {
if (isInterval) {
var intervalTime = lastIntervalTime ? Date.now() - lastIntervalTime : '第一次执行';
console.log(text, "==时间线:", Date.now() - timeLine, "上一次间隔:", intervalTime);
lastIntervalTime = Date.now();
} else {
console.log(text, "==时间线:", Date.now() - timeLine);
}
}
function intervalFun(totalCount) {
interval = setInterval(() => {
printTime("执行interval", true);
syncSleep(4000);
printTime("执行interval完毕");
curCount += 1;
if (curCount > totalCount) {
window.clearInterval(interval);
}
}, 5000);
}
intervalFun(6);
timeout = setTimeout(() => {
printTime("模拟click");
start.click();
}, 3000),
timeout = setTimeout(() => {
printTime("执行timeout");
syncSleep(9000);
printTime("执行timeout完毕");
}, 4000),
start.onclick = function () {
printTime("执行onClick");
syncSleep(6000);
printTime("执行onClick完毕");
};
window.onbeforeunload = function (event) {
console.log("卸载")
if (timeout) {
clearTimeout(timeout);
}
if (interval) {
clearInterval(interval);
}
};
//onclick(当前) 3s + 6
//onclick(当前),timeout 4s
//onclick(当前),timeout,interval-1 5s
//timeout(当前),interval-1 9s + 9
//timeout(当前),interval-1 10s interval-2
//timeout(当前),interval-1 15s interval-3
//interval-1(当前) 18s + 4
//interval-1(当前) 18s + 4
//interval-1(当前),interval-4 20s
//interval-4(当前) 22s + 4
//interval-4(当前),interval-5 25s
//interval-5(当前), 26s + 4
//interval-6 30s
//interval-7 35s
</script>
</body>
</html>
requestAnimationFrame
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.animate-ele {
position: relative;
width: 100px;
height: 100px;
background: red;
}
* {
font-size: 28px;
}
</style>
<body>
<div id="animateEle" class="animate-ele"></div>
<button id="start">开始</button>
<script>
const element = document.getElementById('animateEle');
let count=0;
function step() {
count++;
if(count<500){
element.style.transform = 'translateX(' + count + 'px)';
window.requestAnimationFrame(step);
}
}
start.onclick=function(){
window.requestAnimationFrame(step);
}
</script>
</body>
</html>
requestIdleCallback
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.animate-ele {
position: relative;
width: 100px;
height: 100px;
background: red;
}
* {
font-size: 28px;
}
</style>
<body>
<div id="animateEle" class="animate-ele"></div>
<button id="start">开始</button>
<script>
//同步耗时操作
function syncSleep(duration) {
const now = Date.now();
while (now + duration > Date.now()) { }
}
const element = document.getElementById('animateEle');
let count = 0;
function step(timestamp) {
console.log("渲染帧");
count++;
if (count < 500) {
element.style.transform = 'translateX(' + count + 'px)';
window.requestAnimationFrame(step);
}
}
start.onclick = function () {
console.log("启动帧");
window.requestAnimationFrame(step);
requestIdleCallback((idleDeadline) => {
// didTimeout表示是否超时正在执行
const didTimeout = idleDeadline.didTimeout ? '超时正在执行' : '未超时执行'
// timeRemaining()表示当前帧还剩余多少时间(以毫秒计算)
const timeRemaining = idleDeadline.timeRemaining();
console.log("didTimeout==", didTimeout, "==", timeRemaining)
// { timeout: 50 }
}, {timeout: 50});//{timeout:50}
console.log("执行onClick")
setTimeout(() => {
console.log("执行timeout")
syncSleep(1000);
console.log("执行timeout完成");
Promise.resolve().then(function () {
console.log("promise 微任务");
});
}, 30)
syncSleep(1000);
console.log("执行onClick完毕")
};
</script>
</body>
</html>
基于传统定时器的动画实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
* {
font-size: 28px;
;
}
.warp {
position: absolute;
width: 100px;
height: 300px;
border: 1px solid #F60093;
overflow: hidden;
}
.progress-in {
position: relative;
width: 100%;
height: 100%;
background: red;
animation: 3s linear 0s progress forwards;
transform: translateY(100%);
animation-play-state: paused;
}
@keyframes progress {
from {
transform: translateY(100%);
}
to {
transform: translateY(0%);
}
}
#start {
position: absolute;
width: 100px;
left: 200px;
}
</style>
<body>
<div class="warp">
<div class="progress-in">
</div>
</div>
<button id="start">开始</button>
<script>
const progressIn = document.querySelector(".progress-in");
start.onclick = function () {
// progressIn.style.animationPlayState = `running`;
const state = progressIn.style.animationPlayState;
progressIn.style.animationPlayState = state == `running` ? "paused" : "running";
}
progressIn.addEventListener("webkitAnimationEnd", (ele) => {
window.alert(`动画结束`);
}, true);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.warp {
position: absolute;
width: 100px;
height: 300px;
border: 1px solid #F60093;
overflow: hidden;
}
.progress-in {
position: relative;
width: 100%;
height: 100%;
background: red;
opacity: 1;
transform: translateY(100%);
transition: transform 3s, opacity 3s;
}
#start {
position: absolute;
width: 100px;
height: 30px;
left: 200px;
}
</style>
<body>
<div class="warp">
<div class="progress-in">
</div>
</div>
<button id="start">开始</button>
<script>
const progressIn = document.querySelector(".progress-in");
start.onclick = function () {
progressIn.style.transform = `translateY(0%)`;
progressIn.style.opacity = 0.4;
}
progressIn.addEventListener("transitionend", (ele) => {
console.log(`过渡动画完成:过渡属性${ele.propertyName}=过渡时间:${ele.elapsedTime}s`);
}, true);
</script>
</body>
</html>
购物车抛物线
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>购物车抛物线</title>
<style>
html,
body {
margin: 0
}
body {
position: relative;
}
.container {
/* width: 30vw; */
margin: auto;
height: 94vh;
background-color: #fff4e8;
position: relative;
padding-top: 50px;
}
.container section {
display: flex;
margin: 8px;
max-width: 500px;
margin: auto;
}
.container section div {
align-self: center;
}
.container .add-car {
background: #e54346;
height: 30px;
text-align: center;
align-self: center;
}
.fixed-bottom {
position: fixed;
bottom: 0px;
z-index: 999;
width: 100vw;
height: 6vh;
}
.bottom-wrapper {
width: 30vw;
margin: auto;
border: silver solid 1px;
position: relative;
overflow: hidden;
}
.car {
height: 5vh;
margin-left: 10px;
}
.prod-img {
border: 1px solid #eee;
}
.moving-point {
height: 20px;
position: absolute;
display: none;
transform: translate(-50%, -50%);
z-index: 999;
}
@media screen and (max-width: 500px) {
.container section{
max-width: 100vw;
}
}
</style>
</head>
<body>
<div class="container">
<section>
<img class="prod-img"
src="//img10.360buyimg.com/cms/s80x80_jfs/t18700/63/435443749/91471/d622467/5a780f67Nc9f4b35b.jpg">
<div>花王碧柔(Biore)轻透倍护防晒乳SPF50+ PA+++ 40ml 轻透</div>
<img src="./add.jpg" class="add-car" />
</section>
<section>
<img class="prod-img"
src="//img10.360buyimg.com/cms/s80x80_jfs/t18700/63/435443749/91471/d622467/5a780f67Nc9f4b35b.jpg">
<div>花王碧柔(Biore)轻透倍护防晒乳SPF50+ PA+++ 40ml 轻透</div>
<img src="./add.jpg" class="add-car" />
</section>
<section>
<img class="prod-img"
src="//img10.360buyimg.com/cms/s80x80_jfs/t18700/63/435443749/91471/d622467/5a780f67Nc9f4b35b.jpg">
<div>花王碧柔(Biore)轻透倍护防晒乳SPF50+ PA+++ 40ml 轻透</div>
<img src="./add.jpg" class="add-car" />
</section>
<section>
<img class="prod-img"
src="//img10.360buyimg.com/cms/s80x80_jfs/t18700/63/435443749/91471/d622467/5a780f67Nc9f4b35b.jpg">
<div>花王碧柔(Biore)轻透倍护防晒乳SPF50+ PA+++ 40ml 轻透</div>
<img src="./add.jpg" class="add-car" />
</section>
<section>
<img class="prod-img"
src="//img10.360buyimg.com/cms/s80x80_jfs/t18700/63/435443749/91471/d622467/5a780f67Nc9f4b35b.jpg">
<div>花王碧柔(Biore)轻透倍护防晒乳SPF50+ PA+++ 40ml 轻透</div>
<img src="./add.jpg" class="add-car" />
</section>
<section>
<img class="prod-img"
src="//img10.360buyimg.com/cms/s80x80_jfs/t18700/63/435443749/91471/d622467/5a780f67Nc9f4b35b.jpg">
<div>花王碧柔(Biore)轻透倍护防晒乳SPF50+ PA+++ 40ml 轻透</div>
<img src="./add.jpg" class="add-car" />
</section>
</div>
<div class="fixed-bottom">
<div class="bottom-wrapper">
<img src="./car.jpg" alt="" class="car">
</div>
</div>
<img src="./add.jpg" alt="" class="moving-point">
<script>
var animating = false;
var carEl = document.querySelector(".car");
var pointEl = document.querySelector(".moving-point");
document.querySelector(".container").addEventListener("click", function (ev) {
const el = ev.target;
if (el.classList.contains("add-car")) {
if (animating) {
return;
}
animating = true;
reset();
var sourcePos = {
x: ev.x,
y: ev.y
};
pointEl.style.top = numberToPx(sourcePos.y);
pointEl.style.left = numberToPx(sourcePos.x);
pointEl.style.display = "block";
window.getComputedStyle(document.body);
requestAnimationFrame(transition)
}
})
function reset() {
pointEl.style.transition = "";
}
function transition() {
var targetPos = getTargetPos();
pointEl.style.transition = `top 500ms ease-in,left 500ms linear`;
pointEl.style.top = numberToPx(targetPos.y);
pointEl.style.left = numberToPx(targetPos.x);
}
function numberToPx(n) {
return n + "px";
}
function getTargetPos() {
var pos = carEl.getBoundingClientRect();
return {
x: pos.x + pos.width / 2,
y: pos.y + pos.height / 2
}
}
pointEl.addEventListener("transitionend", function () {
animating = false;
pointEl.style.transition = "";
pointEl.style.display = "none";
})
</script>
</body>
</html>
内置贝塞尔的运动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>内置贝塞尔的运动</title>
<style>
* {
font-size: 28px;
color: #FFF;
font-weight: bold
}
.bg-red {
background-color: red;
}
.bg-green {
background-color: green;
}
.bg-blue {
background-color: blue;
}
.bg-sliver {
background-color: silver;
}
.bg-aqua {
background-color: aqua
}
.transition div {
height: 100px;
width: 100px;
position: relative;
left: 0;
}
.transition-s {
transition-property: left;
transition-duration: 3000ms;
}
.transition-fn-ease {
transition-timing-function: ease;
}
.transition-fn-ease-in {
transition-timing-function: ease-in;
}
.transition-fn-linear {
transition-timing-function: linear;
}
.transition-fn-ease-out {
transition-timing-function: ease-out;
}
.transition-fn-ease-in-out {
transition-timing-function: ease-in-out;
/* transition-timing-function: cubic-bezier(.95,.05,.59,.75) */
}
.transition.ani div{
left: calc(100% - 100px);
}
</style>
</head>
<body>
<div class="transition">
<div class="bg-red transition-s transition-fn-ease" data-t="ease">ease</div>
<div class="bg-green transition-s transition-fn-linear" data-t="linear">linear</div>
<div class="bg-blue transition-s transition-fn-ease-in" data-t="ease-in">ease-in</div>
<div class="bg-sliver transition-s transition-fn-ease-out" data-t="ease-out">ease-out</div>
<div class="bg-aqua transition-s transition-fn-ease-in-out" data-t="ease-in-out">ease-in-out</div>
</div>
<div>
<button id="btnStart" style="color:red">开始</button>
</div>
<script>
var el = document.querySelector(".transition");
document.getElementById("btnStart").addEventListener("click", function(){
el.classList.add("ani");
})
document.querySelector(".transition-s").addEventListener("transitionend", function(){
el.classList.remove("ani");
})
</script>
</body>
</html>
进度条
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>加载进度条</title>
<style>
html,
body {
height: 100%;
cursor: pointer;
}
body {
padding-top: 100px;
background-color: rgba(0, 0, 0)
}
.outer {
margin: auto;
width: 90%;
height: 10px;
border-radius: 8px;
background-color : #000;
border-left: 2px solid rgba(0, 198, 255, .3);
border-top: 2px solid rgba(0, 198, 255, .3);
border-right: 2px solid rgba(0, 198, 255, .3);
border-bottom: 2px solid rgba(0, 198, 255, .3);
}
.inner {
height: 10px;
box-shadow: 0px 0px 10px rgba(0, 198, 255, 1) inset;
width: 0;
border-radius: 8px;
}
.ts {
transition: width 5000ms ease 20ms;
}
.title {
text-align: center;
margin: 10px;
color: #fff;
}
.progress {
width: 100%;
}
</style>
</head>
<body>
<div class="title">
来来来, 点击任何地方开始加载
</div>
<div class="outer">
<div class="inner"></div>
</div>
<script>
var innerEl = document.querySelector(".inner");
function reset() {
innerEl.classList.remove("progress");
innerEl.classList.remove("ts");
}
document.body.addEventListener("click", function () {
reset();
window.getComputedStyle(document.body);
requestAnimationFrame(function () {
innerEl.classList.add("ts");
if (!innerEl.classList.contains("progress")) {
innerEl.classList.add("progress");
}
})
})
</script>
</body>
</html>
雪花飘飘
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Snow</title>
<style>
body {
background-color: black;
overflow: hidden;
height: 100vh;
cursor: pointer;
}
.ts {
transition-property: all;
transition-timing-function: ease;
}
img {
height: 15px;
position: absolute;
top: 0;
left: 50vw;
transform: translate(-50%);
}
</style>
</head>
<body>
</body>
<script>
var animating = false;
var created = false;
function createSnows() {
if (created) {
return
}
for (var i = 0; i < 800; i++) {
var img = document.createElement("img");
img.src = "./snow.png";
img.className = "ts";
var w = (10 + Math.round(Math.random() * 30)) + "px";
img.style.width = w;
img.style.height = w;
document.body.appendChild(img)
}
created = true;
}
function updateSnows() {
[...document.images].forEach(function (el, i) {
el.classList.add("ts");
el.style.transitionDuration = 3000 + Math.floor(Math.random() * i * 1000/800 ) + "ms";
el.style.left = 50 + (Math.random() > 0.49 ? 1 : -1) * Math.random() * 200 + "vw";
el.style.top = 100 + 1 * Math.round(Math.random() * 50) + "vh";
el.style.transitionTimingFunction = `cubic-bezier(${Math.random().toFixed(2)},${Math.random().toFixed(2)},${Math.random().toFixed(2)},${Math.random().toFixed(2)})`
})
}
function resetSnows() {
[...document.images].forEach(function (el, i) {
el.style.transition = "";
el.style.top = "0";
el.style.left = "50vw";
el.classList.remove("ts");
})
}
createSnows();
document.body.addEventListener("click", function () {
resetSnows();
window.getComputedStyle(document.body);
requestAnimationFrame(updateSnows)
})
</script>
</html>