版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。更多學習資料請通路我愛科技論壇:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/79659313
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#div {
width: 100px;
height: 100px;
background-color: #00a8c6;
position: absolute;
}
#block {
width: 80px;
height: 80px;
background-color: #0a001f;
position: absolute;
margin-left: 120px;
}
#blockTwo {
width: 80px;
height: 80px;
background-color: #00FA9A;
position: absolute;
margin-left: 190px;
}
#ball {
width: 100px;
height: 100px;
border-radius: 50px;
border: solid rgb(100, 100, 100) 1px;
position: absolute;
margin-top: 100px;
background-color: #1b1b1b;
}
#car {
width: 80px;
height: 80px;
background-color: #4B0082;
position: absolute;
margin-left: 220px;
}
#stone {
width: 50px;
height: 50px;
border-radius: 25px;
border: solid rgb(100, 100, 100) 1px;
position: absolute;
margin-top: 150px;
margin-left: 150px;
background-color: #8B0000;
}
#SinMove {
width: 50px;
height: 50px;
border-radius: 25px;
border: solid rgb(100, 100, 100) 1px;
position: absolute;
margin-top: 800px;
margin-left: 220px;
background-color: #00FA9A;
}
#CircleMove {
width: 50px;
height: 50px;
background-color: #4B0082;
margin-top: 500px;
margin-left: 500px;
}
#moveAny {
width: 100px;
height: 100px;
border-radius: 50px;
border: solid rgb(100, 100, 100) 1px;
position: absolute;
margin-top: 800px;
margin-left: 220px;
background-color: #7FFF00;
text-align: center;
padding-top: 20px;
}
#controlPan {
margin-top: 400px;
border: 2px solid blue;
height: 400px;
width: auto;
}
</style>
<!--1.實作一個簡單的div塊元素-->
<script>
var id;
var direct = -1;
var times = 0;
function step() {
times++;
// 擷取這個塊元素
var div = document.getElementById("div");
console.log(div.offsetLeft);
if (div.offsetLeft == 800) {
direct = 0;
} else if (div.offsetLeft == 8) {
direct = 1;
}
if (direct == 1) {
// 設定這個div元素向左的偏移量
var temp = div.offsetLeft + 2;
// 設定坐标的距離
div.style.left = temp + "px";
} else if (direct == 0) {
// 設定這個div元素向左的偏移量
var temp = div.offsetLeft - 2;
// 設定坐标的距離
div.style.left = temp + "px";
}
//和setTimeout一樣,要手動調用才能實作連續動畫。
id = window.requestAnimationFrame(step); //傳回值是一個id,可以通過這個id來取消
// 複雜的計算
for (var i = 0; i < 50; i++) {
console.log("再牛逼的定時器也得等到我執行完才能執行");
}
//取消回調函數
if (times == 0) {
window.cancelAnimationFrame(id);
}
}
// 第一次調用
//id = window.requestAnimationFrame(step);
</script>
<!--2.setTimeout和setInterval深入了解-->
<script>
/*console.log("1");
setTimeout(function () {
console.log("3");
}, 0);
console.log("2"); // 輸出: 1 2 3*/
// 2. 使用動畫的正确姿勢,動畫其實是 “位移”關于“時間”的函數:s=f(t)
// 解決動畫變慢的問題:把動畫與時間關聯起來
function startAnimation() {
var startTime = Date.now();
requestAnimationFrame(function change(time) {
var current = Date.now() - startTime;
console.log("動畫已經執行的時間為:" + current);
requestAnimationFrame(startAnimation);
});
}
//startAnimation();
// 動畫通常情況下有終止時間,如果是循環動畫,我們也可以看做特殊的——當動畫達到終止時間之後,重新開始動畫。是以,我們可以将動畫時間歸一(Normalize)表示:
//duration 是動畫執行時間 isLoop是否為循環執行。
function startAnimation(duration, isLoop) {
var startTime = Date.now();
requestAnimationFrame(function change() {
// 動畫已經用去的時間占總時間的比值
var p = (Date.now() - startTime) / duration;
//console.log(p);
if (p >= 1.0) {
if (isLoop) { // 如果是循環執行,則開啟下一個循環周期。并且把開始時間改成上個周期的結束時間
startTime += duration;
p -= 1.0; //動畫進度初始化
} else {
p = 1.0; //如果不是循環,則把時間進度至為 1.0 表示動畫執行結束
}
}
console.log("動畫已執行進度", p);
if (p < 1.0) { //如果小于1.0表示動畫還诶有值完畢,繼續執行動畫。
requestAnimationFrame(change);
}
});
}
//startAnimation(100, true);
// 示例1:用時間控制動畫周期精确到2s中
function blockClick(obj) {
var block = document.getElementById("block");
var self = obj,
startTime = Date.now(),
duration = 2000;
// 添加一個動畫
setInterval(function () {
var p = (Date.now() - startTime) / duration;
// 時間已經完成了2000的比例,則360度也是進行了這麼個比例。
self.style.transform = "rotate(" + (360 * p) + "deg)";
}, 100);
}
// 示例2:讓滑塊在2秒内向右勻速移動600px
function blockTwoClick(obj) {
var self = obj;
var startTime = Date.now(),
distance = 600,
duration = 2000;
requestAnimationFrame(function step(time) {
var p = Math.min(1.0, (Date.now() - startTime) / duration);
//沿着X方向運動
self.style.transform = "translateX(" + (distance * p) + "px)";
// 如果動畫沒有執行完畢, 就繼續執行
if (p < 1.0) {
requestAnimationFrame(step);
}
})
};
// 執行個體3: 實作小球的自由落體運動
function ballClick(obj) {
var self = obj,
startTime = Date.now(),
distance = 1000,
duration = 1500;
requestAnimationFrame(function step() {
var p = Math.min(1.0, (Date.now() - startTime) / duration);
self.style.transform = "translateY(" + (distance * p * p) + "px)";
if (p < 1.0) requestAnimationFrame(step);
});
}
// 執行個體4 : 實作汽車的勻減速運動
function carClick(obj) {
var self = obj, startTime = Date.now(),
distance = 1000, duration = 2000;
requestAnimationFrame(function step() {
var p = Math.min(1.0, (Date.now() - startTime) / duration);
self.style.transform = "translateX("
+ (distance * p * (2 - p)) + "px)";
if (p < 1.0) requestAnimationFrame(step);
});
}
// 執行個體5 : 水準抛物運動
function stoneClick(obj) {
var self = obj, startTime = Date.now(),
disX = 1000, disY = 1000,
duration = Math.sqrt(2 * disY / 10 / 9.8) * 1000; // 落到地面需要的時間 機關ms
//假設10px是1米,disY = 100米
requestAnimationFrame(function step() {
var p = Math.min(1.0, (Date.now() - startTime) / duration);
var tx = disX * p; //水準方向是勻速運動
var ty = disY * p * p; //垂直方向是勻加速運動
self.style.transform = "translate("
+ tx + "px" + "," + ty + "px)";
if (p < 1.0) requestAnimationFrame(step);
});
}
// 執行個體6 : 正弦曲線運動
function SinMoveClick(obj) {
var self = obj, startTime = Date.now(),
distance = 800,
duration = 5000;
requestAnimationFrame(function step() {
var p = Math.min(1.0, (Date.now() - startTime) / duration);
var ty = distance * Math.sin(2 * Math.PI * p);
var tx = 2 * distance * p;
self.style.transform = "translate("
+ tx + "px," + ty + "px)";
if (p < 1.0) requestAnimationFrame(step);
});
}
// 執行個體7 : 圓周運動
function CircleMoveClick(obj) {
var self = obj,
startTime = Date.now(),
r = 100,
duration = 2000;
requestAnimationFrame(function step() {
var p = Math.min(1.0, (Date.now() - startTime) / duration);
var tx = r * Math.sin(2 * Math.PI * p),
ty = -r * Math.cos(2 * Math.PI * p);
self.style.transform = "translate(" +
tx + "px," + ty + "px)";
requestAnimationFrame(step);
});
}
// 實作動畫算子的封裝
// pow() 方法可傳回 x 的 y 次幂的值
var pow = Math.pow,
BACK_CONST = 1.70158;
// t指的的是動畫進度 前面的p
Easing = {
// 勻速運動
linear: function (t) {
return t;
},
// 加速運動
easeIn: function (t) {
return t * t;
},
// 減速運動
easeOut: function (t) {
return (2 - t) * t;
},
//先加速後減速
easeBoth: function (t) {
return (t *= 2) < 1 ? .5 * t * t : .5 * (1 - (--t) * (t - 2));
},
// 4次方加速
easeInStrong: function (t) {
return t * t * t * t;
},
// 4次方法的減速
easeOutStrong: function (t) {
return 1 - (--t) * t * t * t;
},
// 先加速後減速,加速和減速的都比較劇烈
easeBothStrong: function (t) {
return (t *= 2) < 1 ? .5 * t * t * t * t : .5 * (2 - (t -= 2) * t * t * t);
},
//
easeOutQuart: function (t) {
return -(Math.pow((t - 1), 4) - 1)
},
// 指數變化 加減速
easeInOutExpo: function (t) {
if (t === 0) return 0;
if (t === 1) return 1;
if ((t /= 0.5) < 1) return 0.5 * Math.pow(2, 10 * (t - 1));
return 0.5 * (-Math.pow(2, -10 * --t) + 2);
},
//指數式減速
easeOutExpo: function (t) {
return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
},
// 先回彈,再加速
swingFrom: function (t) {
return t * t * ((BACK_CONST + 1) * t - BACK_CONST);
},
// 多走一段,再慢慢的回彈
swingTo: function (t) {
return (t -= 1) * t * ((BACK_CONST + 1) * t + BACK_CONST) + 1;
},
//彈跳
bounce: function (t) {
var s = 7.5625,
r;
if (t < (1 / 2.75)) {
r = s * t * t;
} else if (t < (2 / 2.75)) {
r = s * (t -= (1.5 / 2.75)) * t + .75;
} else if (t < (2.5 / 2.75)) {
r = s * (t -= (2.25 / 2.75)) * t + .9375;
} else {
r = s * (t -= (2.625 / 2.75)) * t + .984375;
}
return r;
}
};
/*
參數1:動畫的執行時間
參數2:動畫執行的時候的回調函數(動畫執行的要幹的事情)
參數3:動畫算子. 如果沒有傳入動畫算子,則預設使用勻速算子
*/
function Animator(duration, progress, easing) {
this.duration = duration;
this.progress = progress;
this.easing = easing || function (p) {
return p
};
}
Animator.prototype = {
/*開始動畫的方法,
參數:一個布爾值
true表示動畫不循環執行。
*/
start: function (finished) {
/*動畫開始時間*/
var startTime = Date.now();
/*動畫執行時間*/
var duration = this.duration,
self = this;
/*定義動畫執行函數*/
requestAnimationFrame(function step() {
/*得到動畫執行進度*/
var p = (Date.now() - startTime) / duration;
/*是否執行下一幀動畫*/
var next = true;
/*判斷動畫進度是否完成*/
if (p < 1.0) {
self.progress(self.easing(p), p); //執行動畫回調函數,并傳入動畫算子的結果和動畫進度。
} else {
if (finished) { //判斷是否停止動畫。如果是true代表停止動畫。
next = false;
} else {
startTime = Date.now();
}
}
// 如果next是true執行下一幀動畫
if (next) requestAnimationFrame(step);
});
}
};
function ChangeState() {
var self = document.getElementById("moveAny");
// 建立一個動畫
new Animator(2000, function (p) {
self.style.top = 500 * p + "px";
}, Easing.easeBoth).start(false);
}
</script>
</head>
<body>
<div style="border: 2px solid red">
<!--動畫實作方式:通過操作JavaScript間接操作CSS樣式,每隔一段時間更新一次;-->
<div id="div"></div>
<div id="block" onclick="blockClick(this)"></div>
<div id="blockTwo" onclick="blockTwoClick(this)"></div>
<div id="ball" onclick="ballClick(this)"></div>
<div id="car" onclick="carClick(this)"></div>
<div id="stone" onclick="stoneClick(this)"></div>
<div id="SinMove" onclick="SinMoveClick(this)"></div>
<div id="CircleMove" onclick="CircleMoveClick(this)"></div>
</div>
<div id="moveAny">我是一個可以做任意運動的物體</div>
<div id="controlPan">控制台
<button onclick="ChangeState(this)">切換運動狀态</button>
</div>
</body>
</html>