[JS]封装嵌套动画效果
封装
主 动画
import SubAnimation from './SubAnimation'
export default class AnimationManager {
constructor(duration) {
this.duration = duration
this.subAnimations = []
this.startTime = null
this.pausedTime = null
this.isPlaying = false
this.animationFrame = null
this.currentProgress = 0
}
addSubAnimation(startRatio, endRatio, animationFunction, completionFunction = null) {
this.subAnimations.push(new SubAnimation(startRatio, endRatio, animationFunction, completionFunction))
}
start() {
if (!this.isPlaying) {
this.isPlaying = true
this.startTime = performance.now() - this.currentProgress * this.duration * 1000
this.animate()
}
}
pause() {
if (this.isPlaying) {
this.isPlaying = false
cancelAnimationFrame(this.animationFrame)
this.currentProgress = this.calculateProgress()
}
}
reset() {
this.pause()
this.startTime = null
this.pausedTime = null
}
setProgress(newProgress) {
if (newProgress < 0 || newProgress > 1) throw new Error('[确保进度在 0 到 1 之间]')
this.currentProgress = newProgress
this.resetAnimationState()
if (this.isPlaying) {
cancelAnimationFrame(this.animationFrame)
this.startTime = performance.now() - this.currentProgress * this.duration * 1000
this.animate()
} else {
this.executeAnimationsAtProgress(newProgress)
}
}
resetAnimationState() {
this.subAnimations.forEach(subAnim => {
subAnim.hasCompleted = false
})
}
calculateProgress() {
return Math.min((performance.now() - this.startTime) / (this.duration * 1000), 1)
}
executeAnimationsAtProgress(progress) {
this.subAnimations.forEach(subAnim => {
if (progress >= subAnim.startRatio && progress <= subAnim.endRatio) {
const subProgress = (progress - subAnim.startRatio) / (subAnim.endRatio - subAnim.startRatio)
subAnim.animationFunction(subProgress)
} else if (progress > subAnim.endRatio && !subAnim.hasCompleted) {
subAnim.completionFunction && subAnim.completionFunction()
subAnim.hasCompleted = true
} else if (progress < subAnim.startRatio) {
subAnim.hasCompleted = false
}
})
}
animate() {
const progress = this.calculateProgress()
this.executeAnimationsAtProgress(progress)
if (progress < 1) {
this.animationFrame = requestAnimationFrame(() => this.animate())
} else {
this.isPlaying = false
}
}
}
子 动画
export default class SubAnimation {
constructor(startRatio, endRatio, animationFunction, completionFunction = null) {
this.startRatio = startRatio
this.endRatio = endRatio
this.animationFunction = animationFunction
this.completionFunction = completionFunction
this.hasCompleted = false
}
execute(progress) {
if (progress >= this.startRatio && progress <= this.endRatio) {
const subProgress = (progress - this.startRatio) / (this.endRatio - this.startRatio)
this.animationFunction(subProgress)
this.hasCompleted = false
} else if (progress > this.endRatio && !this.hasCompleted) {
if (this.completionFunction) {
this.completionFunction()
}
this.hasCompleted = true
}
}
}
方法
const animManager = new AnimationManager(10);
const isPlaying = animManager.isPlaying
animManager.pause()
animManager.start()
animManager.reset()
animManager.setProgress(0.1)
使用
const animManager = new AnimationManager(10);
animManager.addSubAnimation(0, 0.3, (progress) => {
console.log("0-0.1", progress);
});
let movingPoint;
animManager.addSubAnimation(
0.3,
0.7,
(progress) => {
console.log("0.3-0.7", progress);
},
() => {
console.log("0.3-0.7 结束");
}
);
animManager.addSubAnimation(
0.5,
1,
(progress) => {
console.log("0.5-1", progress);
},
() => {
console.log("完全结束");
}
);
animManager.start();