JS动画原理与实现
1. 动画的基本原理
- 定时器改变对象的属性
- 根据新的属性重新渲染动画
function update(context) {
// 更新属性
}
const ticker = new Ticker();
ticker.tick(update, context);
2. 动画的种类
- JavaScript 动画
- 操作DOM
- Canvas - CSS 动画
- transition
- animation - SVG 动画
- SMIL
JavaScript 动画优缺点:
- 优点:灵活度、可控性、性能
- 缺点:易用性
2.1 简单动画第一个版本:
let rotation = 0;
requestAnimationFrame(function update() {
block.style.transform = `rotate(${
rotation++}deg)`; //增量,很难精确知道旋转周期
requestAnimationFrame(update);
});
2.2 第二个版本:
let rotation = 0;
let startTime = null;
const T = 2000; //2s周期
requestAnimationFrame(function update() {
if(!startTime) startTime = Date.now();
const p = (Date.now() - startTime)/T;
block.style.transform = `rotate(${
360 * p}deg)`;
requestAnimationFrame(update);
});
2.3 通用化
function update({
target}, count) {
target.style.transform = `rotate(${
count++}deg)`;
}
class Ticker {
tick(update, context) {
let count = 0;
requestAnimationFrame(function next() {
if(update(context, ++count) !== false) {
requestAnimationFrame(next);
}
});
}
}
const ticker = new Ticker();
ticker.tick(update, {
target: block});
2.3 通用化2
function update({
target}, {
time}) {
target.style.transform = `rotate(${
360 * time / 2000}deg)`;
}
class Ticker {
tick(update, context) {
let count = 0;
let startTime = Date.now();
requestAnimationFrame(function next() {
count++;
const time = Date.now() - startTime;
if(update(context, {
count, time}) !== false) {
requestAnimationFrame(next);
}
});
}
}
const ticker = new Ticker();
ticker.tick(update, {
target: block});
2.3 通用化3
function update({
context}, {
time}) {
context.clearRect(0, 0, 512, 512);
context.save();
context.translate(100, 100);
context.rotate(time * 0.005);
context.fillStyle = '#00f';
context.fillRect(-50, -50, 100, 100);
context.restore();
}
class Ticker {
tick(update, context) {
let count = 0;
let startTime = Date.now();
requestAnimationFrame(function next() {
count++;
const time = Date.now() - startTime;
if(update(context, {
count, time}) !== false) {
requestAnimationFrame(next);
}
});
}
}
2.4 封装Timing
class Timing {
constructor({
duration, easing} = {
}) {
this.startTime = Date.now();
this.duration = duration;
this.easing = easing || function(p){
return p};
}
get time() {
return Date.now() - this.startTime;
}
get p() {
return this.easing(Math.min(this.time / this.duration, 1.0));
}
}
class Ticker {
tick(update, context, timing) {
let count = 0;
timing = new Timing(timing);
requestAnimationFrame(function next() {
count++;
if(update(context, {
count, timing}) !== false) {
requestAnimationFrame(next);
}
});
}
}