Skip to content

Commit 285fe1e

Browse files
authored
Refactor Animation Timeline (#7564)
* Add anime4 again * Add npm token * Fix registry * Fix echo * Update GHA for deploy * Refactor scrubber * Further iterations * Disable holding down keys logic * Couple of final tweaks * Fix bugs * Improve further * Fix bugs * Fix tests
1 parent 8e9c08a commit 285fe1e

File tree

15 files changed

+269
-313
lines changed

15 files changed

+269
-313
lines changed

.github/workflows/deploy.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ jobs:
5252
- name: Checkout
5353
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
5454

55+
- name: Authenticate with private NPM package
56+
run: echo -e "//npm.pkg.github.com/:_authToken=${{ secrets.NPM_TOKEN }}\n@juliangarnierorg:registry=https://npm.pkg.github.com" > ~/.npmrc
57+
5558
- name: Configure AWS credentials
5659
uses: aws-actions/configure-aws-credentials@e1e17a757e536f70e52b5a12b2e8d1d1c60e04ef
5760
with:

.github/workflows/tests.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ jobs:
1616
.dockerimages.json
1717
sparse-checkout-cone-mode: false
1818

19+
- name: Authenticate with private NPM package
20+
run: echo -e "//npm.pkg.github.com/:_authToken=${{ secrets.NPM_TOKEN }}\n@juliangarnierorg:registry=https://npm.pkg.github.com" > ~/.npmrc
21+
1922
- name: Define Docker images
2023
run: |
2124
echo "localstack_image=$(jq -r '.localstack' .dockerimages.json)" >> "$GITHUB_ENV"
@@ -36,6 +39,9 @@ jobs:
3639
steps:
3740
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
3841

42+
- name: Authenticate with private NPM package
43+
run: echo -e "//npm.pkg.github.com/:_authToken=${{ secrets.NPM_TOKEN }}\n@juliangarnierorg:registry=https://npm.pkg.github.com" > ~/.npmrc
44+
3945
- name: Set up Ruby
4046
uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b
4147
with:
@@ -73,6 +79,9 @@ jobs:
7379
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
7480
###
7581

82+
- name: Authenticate with private NPM package
83+
run: echo -e "//npm.pkg.github.com/:_authToken=${{ secrets.NPM_TOKEN }}\n@juliangarnierorg:registry=https://npm.pkg.github.com" > ~/.npmrc
84+
7685
# Caching using GitHub's caching action
7786
# https://github.com/actions/cache/blob/main/examples.md#node---yarn
7887
- name: Get yarn cache directory path
@@ -169,6 +178,9 @@ jobs:
169178
- name: Checkout code
170179
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
171180

181+
- name: Authenticate with private NPM package
182+
run: echo -e "//npm.pkg.github.com/:_authToken=${{ secrets.NPM_TOKEN }}\n@juliangarnierorg:registry=https://npm.pkg.github.com" > ~/.npmrc
183+
172184
- name: Set up Ruby
173185
uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b
174186
with:
@@ -325,6 +337,9 @@ jobs:
325337
# Checkout using GitHub's checkout action
326338
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
327339

340+
- name: Authenticate with private NPM package
341+
run: echo -e "//npm.pkg.github.com/:_authToken=${{ secrets.NPM_TOKEN }}\n@juliangarnierorg:registry=https://npm.pkg.github.com" > ~/.npmrc
342+
328343
###
329344
# Setup Ruby - this needs to match the version in the Gemfile
330345
- name: Set up Ruby
@@ -420,6 +435,9 @@ jobs:
420435
# Checkout using GitHub's checkout action
421436
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
422437

438+
- name: Authenticate with private NPM package
439+
run: echo -e "//npm.pkg.github.com/:_authToken=${{ secrets.NPM_TOKEN }}\n@juliangarnierorg:registry=https://npm.pkg.github.com" > ~/.npmrc
440+
423441
###
424442
# Setup Ruby - this needs to match the version in the Gemfile
425443
- name: Set up Ruby

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,5 @@ dump.rdb
5151

5252
# Ignore vendored local databases
5353
/vendor/*mmdb
54+
55+
.npmrc

app/javascript/components/bootcamp/SolveExercisePage/AnimationTimeline/AnimationTimeline.ts

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,51 @@
11
import { type Frame } from '@/interpreter/frames'
2-
import anime, { type AnimeInstance, type AnimeTimelineInstance } from 'animejs'
2+
import { createTimeline, stagger } from '@juliangarnierorg/anime-beta'
3+
import type {
4+
DefaultsParams,
5+
Timeline,
6+
AnimationParams,
7+
TargetSelector,
8+
TargetsParam,
9+
} from '@juliangarnierorg/anime-beta'
310
import type { AnimeCSSProperties } from './types'
411

5-
export type Animation = anime.AnimeAnimParams & {
12+
export type Animation = AnimationParams & {
13+
targets: TargetsParam
614
offset: string | number | undefined
715
transformations: AnimeCSSProperties
816
}
917

1018
export class AnimationTimeline {
11-
private animationTimeline: AnimeTimelineInstance
19+
private animationTimeline: Timeline
1220
private currentIndex: number = 0
1321
public currentFrame?: Frame
1422
public previousFrame?: Frame | null
1523
public nextFrame?: Frame | null
1624
public progress: number = 0
17-
private updateCallbacks: ((anim: AnimeInstance) => void)[] = []
18-
19-
constructor(initialOptions: anime.AnimeParams, private frames: Frame[] = []) {
20-
this.animationTimeline = anime.timeline({
21-
easing: 'linear',
25+
private updateCallbacks: ((anim: Timeline) => void)[] = []
26+
private playCallbacks: ((anim: Timeline) => void)[] = []
27+
private stopCallbacks: ((anim: Timeline) => void)[] = []
28+
29+
constructor(initialOptions: DefaultsParams, private frames: Frame[] = []) {
30+
this.animationTimeline = createTimeline({
31+
defaults: {
32+
ease: 'linear',
33+
...initialOptions,
34+
},
2235
autoplay: false,
23-
...initialOptions,
24-
update: (anim: AnimeInstance) => {
36+
onUpdate: (anim: Timeline) => {
2537
this.updateScrubber(anim)
2638
this.updateCallbacks.forEach((cb) => cb(anim))
2739
},
40+
onBegin: (anim: Timeline) => {
41+
this.playCallbacks.forEach((cb) => cb(anim))
42+
},
43+
onComplete: (anim: Timeline) => {
44+
this.stopCallbacks.forEach((cb) => cb(anim))
45+
},
46+
onPause: (anim: Timeline) => {
47+
this.stopCallbacks.forEach((cb) => cb(anim))
48+
},
2849
})
2950
}
3051

@@ -34,24 +55,32 @@ export class AnimationTimeline {
3455
this.animationTimeline = null
3556
}
3657

37-
public onUpdate(callback: (anim: AnimeInstance) => void) {
58+
public onUpdate(callback: (anim: Timeline) => void) {
3859
this.updateCallbacks.push(callback)
3960

4061
if (this.animationTimeline) {
4162
callback(this.animationTimeline)
4263
setTimeout(() => this.updateScrubber(this.animationTimeline), 1)
4364
}
4465
}
66+
public onPlay(callback: (anim: Timeline) => void) {
67+
this.playCallbacks.push(callback)
68+
}
69+
public onStop(callback: (anim: Timeline) => void) {
70+
this.stopCallbacks.push(callback)
71+
}
4572

46-
public removeUpdateCallback(callback: (anim: AnimeInstance) => void) {
73+
public removeUpdateCallback(callback: (anim: Timeline) => void) {
4774
this.updateCallbacks = this.updateCallbacks.filter((cb) => cb !== callback)
4875
}
4976

5077
public populateTimeline(animations: Animation[]) {
5178
animations.forEach((animation: Animation) => {
79+
const { targets, offset, transformations, ...rest } = animation
5280
this.animationTimeline.add(
53-
{ ...animation, ...animation.transformations },
54-
animation.offset
81+
targets,
82+
{ ...rest, ...transformations },
83+
offset
5584
)
5685
})
5786

@@ -87,7 +116,7 @@ export class AnimationTimeline {
87116
return this.animationTimeline.duration
88117
}
89118

90-
private updateScrubber(anim: AnimeInstance) {
119+
private updateScrubber(anim: Timeline) {
91120
if (!anim) return
92121
this.progress = anim.currentTime
93122

app/javascript/components/bootcamp/SolveExercisePage/AnimationTimeline/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ export type AnimeCSSProperties = {
2929
left?: string | number
3030
perspective?: string | number
3131
boxShadow?: string
32+
filter?: string
3233
}

app/javascript/components/bootcamp/SolveExercisePage/Scrubber/InformationWidgetToggleButton.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export function InformationWidgetToggleButton({
2121
const handleToggleShouldShowInformationWidget = useCallback(() => {
2222
toggleShouldShowInformationWidget()
2323

24+
// If we've not yet set the line (e.g. if we're playing the
25+
// initial animation), don't scroll to the line.
26+
if (highlightedLine == 0) return
27+
2428
// if previous toggle state is `off` - which means we are about to turn it `on`...
2529
// scroll to the highlighted line
2630
if (!shouldShowInformationWidget) {

app/javascript/components/bootcamp/SolveExercisePage/Scrubber/Scrubber.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@ function Scrubber({
2222
const { isSpotlightActive } = useContext(SolveExercisePageContext)
2323

2424
const {
25-
value,
25+
timelineValue,
2626
handleChange,
2727
handleOnMouseUp,
2828
handleOnKeyUp,
2929
handleOnKeyDown,
30-
handleMouseDown,
3130
updateInputBackground,
3231
rangeRef,
3332
handleGoToNextFrame,
@@ -78,10 +77,7 @@ function Scrubber({
7877
ref={rangeRef}
7978
max={calculateMaxInputValue(animationTimeline, frames)}
8079
onInput={updateInputBackground}
81-
value={value}
82-
onMouseDown={(event) =>
83-
handleMouseDown(event, animationTimeline, frames)
84-
}
80+
value={timelineValue}
8581
onChange={(event) => {
8682
handleChange(event, animationTimeline, frames)
8783
updateInputBackground()

0 commit comments

Comments
 (0)