Skip to content

Commit

Permalink
switch: from animejs to gsap
Browse files Browse the repository at this point in the history
  • Loading branch information
zenkyuv committed Oct 16, 2024
1 parent a595e91 commit 3c48d36
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 91 deletions.
13 changes: 7 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@
"@benev/slate": "^0.1.0-x.14",
"@ffmpeg/ffmpeg": "^0.12.9",
"@ffmpeg/util": "^0.12.1",
"animejs": "^3.2.2",
"coi-serviceworker": "^0.1.7",
"es-module-shims": "^1.8.2",
"fabric": "^6.0.0-beta20",
"ffprobe-wasm": "^0.3.1",
"gsap": "^3.12.5",
"lit": "^2.6.1",
"mediainfo.js": "^0.3.2",
"mp4box": "^0.5.2",
Expand Down
3 changes: 0 additions & 3 deletions s/context/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ export class OmniContext extends Context {
this.#save_to_storage(state)
this.#updateAnimationTimeline(state)
})
watch.track(() => this.#core.state.effects, (effects) => {
this.controllers.compositor.managers.animationManager.refreshAnimations(effects, this.state)
})
}

#save_to_storage(state: HistoricalState) {
Expand Down
153 changes: 72 additions & 81 deletions s/context/controllers/compositor/parts/animation-manager.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import gsap from "gsap"
import {pub} from "@benev/slate"
import {FabricObject} from "fabric"
import anime from "animejs/lib/anime.es.js"

import {Compositor} from "../controller.js"
import {AnyEffect, ImageEffect, State, VideoEffect} from "../../../types.js"
import { calculateProjectDuration } from "../../../../utils/calculate-project-duration.js"
import {calculateProjectDuration} from "../../../../utils/calculate-project-duration.js"

interface AnimationBase<T = AnimationIn | AnimationOut> {
targetEffect: VideoEffect | ImageEffect
Expand All @@ -19,9 +19,9 @@ export type AnimationNone = AnimationBase<(typeof animationNone)[number]>
export type Animation = AnimationIn | AnimationOut

export class AnimationManager {
timeline = anime.timeline({
duration: 10000,
autoplay: false,
timeline = gsap.timeline({
duration: 10, // GSAP uses seconds instead of milliseconds
paused: true,
})

#animations: Animation[] = []
Expand All @@ -30,123 +30,116 @@ export class AnimationManager {
constructor(private compositor: Compositor) {}

selectedAnimationForEffect(effect: AnyEffect | null, animation: Animation) {
if(!effect) return
const haveAnimation = this.#animations.find(anim => anim.targetEffect.id === effect.id && animation.type === anim.type)
if(haveAnimation) {
return true
} else return false
if (!effect) return
const haveAnimation = this.#animations.find(
(anim) => anim.targetEffect.id === effect.id && animation.type === anim.type
)
return !!haveAnimation
}

isAnyAnimationInSelected(effect: AnyEffect | null) {
if(!effect) return
const haveAnimation = this.#animations.find(animation => animation.targetEffect.id === effect.id && animation.type.includes("In"))
if(haveAnimation) {
return true
} else false
if (!effect) return false
const haveAnimation = this.#animations.find(
(animation) => animation.targetEffect.id === effect.id && animation.type.includes("In")
)
return !!haveAnimation
}

isAnyAnimationOutSelected(effect: AnyEffect | null) {
if(!effect) return
const haveAnimation = this.#animations.find(animation => animation.targetEffect.id === effect.id && animation.type.includes("Out"))
if(haveAnimation) {
return true
} else false
if (!effect) return false
const haveAnimation = this.#animations.find(
(animation) => animation.targetEffect.id === effect.id && animation.type.includes("Out")
)
return !!haveAnimation
}

updateTimelineDuration(duration: number) {
this.timeline.duration = duration
this.timeline.duration(duration / 1000) // GSAP duration in seconds
}

#getObject(effect: VideoEffect | ImageEffect) {
const videoObject = this.compositor.managers.videoManager.get(effect.id)
const imageObject = this.compositor.managers.imageManager.get(effect.id)
if(videoObject) {
if (videoObject) {
return videoObject
} else if(imageObject) {
} else if (imageObject) {
return imageObject
}
}

refreshAnimations(
effects: AnyEffect[],
state: State
) {
anime.remove(this.timeline)
this.timeline = anime.timeline({
duration: this.timeline.duration,
autoplay: false,
})
const updated = this.#animations.map(animation => {
const effect = effects.find(effect => effect.id === animation.targetEffect.id) as ImageEffect | VideoEffect | undefined
return {
...animation,
targetEffect: effect ?? animation.targetEffect
}})
this.#animations = updated
this.#animations.forEach(animation => {
this.removeAnimationFromEffect(animation.targetEffect, animation.type.includes("In") ? "In" : "Out", state)
this.addAnimationToEffect(animation.targetEffect, animation)
})
}

addAnimationToEffect(
effect: ImageEffect | VideoEffect,
animation: AnimationIn | AnimationOut,
state?: State
) {
if(state) {this.timeline.duration = calculateProjectDuration(state.effects)}
const object = this.#getObject(effect)
if (state) {
this.timeline.duration(calculateProjectDuration(state.effects) / 1000)
}

const object = this.#getObject(effect)!
this.#animations.push(animation)

switch (animation.type) {
case "slideIn": {
const targetPosition = {left: effect.rect.position_on_canvas.x}
const startPosition = {left: -effect.rect.width}
this.timeline.add({
targets: object,
duration: 1000,
left: [startPosition.left, targetPosition.left],
easing: "linear",
update: () => this.compositor.canvas.renderAll()
},
effect.start_at_position,
const targetPosition = { left: effect.rect.position_on_canvas.x }
const startPosition = { left: -effect.rect.width }

// Set the start position before animation begins
gsap.set(object, { left: startPosition.left })

// Animate the object to the target position
this.timeline.add(
gsap.to(object, {
duration: 1,
left: targetPosition.left,
ease: "linear",
onUpdate: () => this.compositor.canvas.renderAll()
}),
effect.start_at_position / 1000
)

break
}

case "slideOut": {
const targetPosition = {left: effect.rect.width}
const startPosition = {left: effect.rect.position_on_canvas.x}
this.timeline.add({
targets: object,
duration: 1000,
left: [startPosition.left, targetPosition.left],
easing: "linear",
update: () => this.compositor.canvas.renderAll(),
},
effect.start_at_position + (effect.end - effect.start) - 1000,
const targetPosition = { left: effect.rect.width }
const startPosition = { left: effect.rect.position_on_canvas.x }

// Set the start position before animation begins
gsap.set(object, { left: startPosition.left })

// Animate the object to the target position
this.timeline.add(
gsap.to(object, {
duration: 1,
left: targetPosition.left,
ease: "linear",
onUpdate: () => this.compositor.canvas.renderAll()
}),
(effect.start_at_position + (effect.end - effect.start) - 1000) / 1000
)

break
}
}

this.onChange.publish(true)
}

play(time: number) {
// console.log(this.timeline.currentTime, this.timeline.progress, "PLAYI", time)
// this.timeline.seek(time)
// this.timeline.play()
// console.log("anim plays", this.timeline)
this.timeline.play(time / 1000)
}

pause() {
this.timeline.pause()
}

seek(time: number) {
this.timeline.seek(time)
this.timeline.seek(time / 1000)
}

updateAnimationTimelineDuration(duration: number) {
this.timeline.duration = duration
this.timeline.duration(duration / 1000)
}

#resetObjectProperties(fabric: FabricObject, effect: ImageEffect | VideoEffect) {
Expand All @@ -162,20 +155,18 @@ export class AnimationManager {
removeAnimationFromEffect(effect: ImageEffect | VideoEffect, type: "In" | "Out", state: State) {
const object = this.#getObject(effect)
this.#resetObjectProperties(object!, effect)
anime.remove(object!)
const filtered = this.#animations.filter(animation => !(animation.targetEffect.id === effect.id && animation.type.includes(type)))
this.#animations = filtered
this.refreshAnimations(state.effects, state)
gsap.killTweensOf(object!)
this.#animations = this.#animations.filter(
(animation) => !(animation.targetEffect.id === effect.id && animation.type.includes(type))
)
this.onChange.publish(true)
}

removeAllAnimationsFromEffect(effect: ImageEffect | VideoEffect, state: State) {
const object = this.#getObject(effect)
this.#resetObjectProperties(object!, effect)
anime.remove(object!)
const filtered = this.#animations.filter(animation => !(animation.targetEffect.id === effect.id))
this.#animations = filtered
this.refreshAnimations(state.effects, state)
gsap.killTweensOf(object!)
this.#animations = this.#animations.filter((animation) => animation.targetEffect.id !== effect.id)
this.onChange.publish(true)
}
}

0 comments on commit 3c48d36

Please sign in to comment.