canvas实现动画的基本步骤
说简单点就是重复的清除绘制每一帧
- 清空canvas(clearRect)
- 保存canvas状态(save)
- 根据动画当前状态要求 平移(translate)、旋转(rotate)、缩放(scale)、错切(skew)绘制目标元素
- 恢复canvas状态(resotre)
执行动画
通过requestAnimationFrame平缓有效率的来执行动画的重复绘制。
封装一个动画执行的类
export default class CanvasAnimation {
private _ctx: CanvasRenderingContext2D;
private _duration: number; // 动画执行时间
private _startTime: number; // 动画执行开始时间
private _animationTyep: string; // 动画类型
private _draw: () => void; // 传入进来的元素绘制方法
private _clearRect: () => void; // 画布情况方法
private _progressing: boolean; // 动画是否执行中
private _width: number; // 执行动画元素的宽度
private _height: number; // 执行动画元素的高度
public onEnd: () => void = () => {}; // 动画执行结束回调
constructor(
ctx: CanvasRenderingContext2D,
draw: () => void,
clearRect: () => void
) {
this._ctx = ctx;
this._duration = 0;
this._startTime = 0;
this._animationTyep = "";
this._draw = draw;
this._clearRect = clearRect;
this._progressing = false;
this._width = 100;
this._height = 100;
}
// 设置要执行动画的基础配置
setOptions(options: IAnimationOptions) {
this._animationTyep = options.type;
this._duration = options.duration;
this._width = options.width || 100;
this._height = options.height || 100;
}
// 开始执行动画
public start() {
this._progressing = true;
this._startTime = Date.now();
this._action();
}
// 3d 绕轴旋转!!!!!!!!!!!
// 待研究
private _action() {
if (!this._progressing) return;
const duration = Date.now() - this._startTime;
if (duration > this._duration) {
// 动画执行结束
this._progressing = false;
this.onEnd();
return;
}
// 获取当前动画类型对应的canvas状态
const animationStatus = getAnimationStatus(
this._animationTyep,
(duration / this._duration) * 100,
this._width,
this._height
);
// 清空画布
this._clearRect();
// 保存canvas状态
this._ctx.save();
// 一些特殊动画状态的特别处理
if (
this._animationTyep === "rotateInDownLeft" ||
this._animationTyep === "rotateInUpLeft" ||
this._animationTyep === "rotateOutDownLeft" ||
this._animationTyep === "rotateOutUpLeft" ||
this._animationTyep === "hinge"
) {
this._ctx.translate(-this._width / 2, this._height / 2);
} else if (
this._animationTyep === "rotateInDownRight" ||
this._animationTyep === "rotateInUpRight" ||
this._animationTyep === "rotateOutDownRight" ||
this._animationTyep === "rotateOutUpRight"
) {
this._ctx.translate(this._width / 2, this._height / 2);
}
// 缩放状态设置
this._ctx.scale(...animationStatus.scale);
// 平移状态设置
this._ctx.translate(...animationStatus.translate);
// 透明度状态设置
this._ctx.globalAlpha = animationStatus.opacity;
// 旋转状态设置
this._ctx.rotate((animationStatus.rotate * Math.PI) / 180);
// 错切状态这设置
this._ctx.transform(
1,
(animationStatus.skew[0] * Math.PI) / 180,
(animationStatus.skew[1] * Math.PI) / 180,
1,
0,
0
);
// 动画元素的绘制
this._draw();
// 恢复canvas状态
this._ctx.restore();
// 执行下一次动画绘制
window.requestAnimationFrame(this._action.bind(this));
}
// 结束动画
public stop() {
this._progressing = false;
}
}
关于动画类的使用
const animation = new CanvasAnimation(
context, () => {
drawAnimationText(context, text, x, y);
},
clearRect
);
// ...
// 执行动画
animation.setOptions({
type,
duration,
width,
height
});
animation.start();
// ...
// 结束动画
animation.stop();