@qyu/anim-core
v1.2.0
Published
Animation definition and implementation
Maintainers
Readme
@qyu/anim-core
Animation definition and implementation
Concept
- Animation is one-way transition from initial
PointtoTargetthroughPath Animinterface representsPathandTargetof the definitionPointis externalised and represents absolute state of the animationAnimhas method.emitto emit from point and.stepthat returns newPointafter some time passed
import * as ac from "@qyu/anim-core"
const line = ac.anim_new_line({
target: 50,
velocity: 0.1,
// will run when animation changes state
effect(state) {
console.log("linear animation", { state })
}
})
let now_line_point = {
state: 0
} as const
line.emit(now_line_point)
{
const next_line_point = line.step(now_line_point, 100)
line.emitdiff(now_line_point, next_line_point)
now_line_point = next_line_point
}Animation Flow
- Create
Animand initialPointcompatible withAnim - Call
.emitat the beginning - Every tick do
.step(now_point, timepassed)and emit new point with.emitdiff(last_point, now_point) - Check
.finished(now_point)and ifAnimis not finished yet - repeat - If config changes - update
Animand continue using samePoint. That process is calledadapting the animation emitter_new_intervaldoes most of the work for you
import * as ac from "@qyu/anim-core"
let point = { state: 0 }
const start = function (target: number, velocity: number): VoidFunction {
const line = ac.anim_new_line({
target,
velocity,
effect(state) {
console.log("linear animation", { state })
}
})
// will run animation automatically
const emitter = ac.emitter_new_interval({
point,
anim: line,
})
// cleanup
return () => {
emitter.softstop()
point = emitter.point()
}
}
// start the animation
const cleanup = start(150, 0.1)
// will restart it after 1 second
setTimeout(() => {
cleanup()
// change speed and target of the animation
start(-50, 0.5)
}, 1000)Implemented Animations
- Library implements multiple kinds of animations to create complex compositions
anim_new_linesimple linear animation (A -> A1)anim_new_springspring based animation (A -> A1)anim_new_mergemerge the animations (A | B -> A1 | B1)anim_new_mergemapmerge the animations, but different format of thePoint(a: A | b: B -> a: A1 | b: B1)anim_new_loopreapeating animation ((A -> A1) * n)anim_new_sequenceemit animations in sequence, onadapt- merge already finished steps (A -> A1 & B -> B1)anim_new_sequence_strictemit animations in sequence, emits only one-at-a-time (no merge onadapt) (A -> A1 & B -> B1)anim_new_chainemit sequence of animations with sharedPoint(later step starts where earlier ends) (A -> A1 -> A2)anim_new_chainmapsame as normal chain, but allows having multiple threads with optional gaps (a: A | b: B -> a: A1 -> a: A2 | b: B1)anim_new_playbackspeed up or slow down the animationanim_new_pipeconvertPointof animation to be compatible with others (eg. for runningchainof incompatible animations)
import * as ac from "@qyu/anim-core"
let point = {
ptr: 0,
child: {
state: 0,
velocity: 0,
}
} satisfies ac.AnimChain_Point<ac.AnimSpring_Point>
const start = function(base: number): VoidFunction {
let i = 0
// state will reach the base in the first animation
// then it will go to -base in the second animation
const anim = ac.anim_new_chain([
ac.anim_new_spring({
dampratio: 0.9,
natfreq: 5e-3,
target: base,
precision: {
velocity: 0.1,
displacement: 1,
},
effect: (state, velocity) => {
// only print every 10th update
if (++i % 10 === 0) {
console.log("first animation", state, velocity)
}
}
}),
ac.anim_new_spring({
target: base,
natfreq: 1e-2,
dampratio: 0.1,
effect: (state, velocity) => {
// only print every 10th update
if (++i % 10 === 0) {
console.log("second animation", state, velocity)
}
}
})
])
console.log("Init Animation")
const emitter = ac.emitter_new_interval({
point,
anim,
})
return () => {
emitter.softstop()
point = emitter.point()
}
}
const cleanup = start(1000)
setTimeout(() => {
cleanup()
// update to new targets
// spring will maintain it's speed at this point
start(2000)
}, 5000)Emitters
Emitteris something that runs your animationemitter_new_manualwill make you press.stepmanually, does not provide much functionalityemitter_new_intervalwill run you animation every frame according toFrameSchedulerFrameSchedulercould be provided at.scheduler, by default it is based onrequestAnimationFramein browser fallbacks tosetTimeoutbatchfunction allows to add a batching function that will be used for every tickhooksallow to add hooks for emitter
