Skip to content

Commit 5df1cc1

Browse files
committed
PickEmotion CSS Transitions -> Animation Loop
The end result is different; not better, not worse, just different. The code is a bit more complicated. The motivating reason for this change is simple: there's no CSS transitions to use in native, so in order to keep parity, I need to implement this without them! This also incorporates another compatibility change: on native, theres no convenient way to get the rendered size of components until a frame *after* they successfully render. This doesn't go as far as delaying checking the size until a frame later, but it does ensure the interface is compatible with that and moves the size check to be after the words are already mounted. Hence the passing WriteableValueWithCallbacks to the Word component to get the size, rather then precomputing. Same deal with the container size, since that depends on the word sizes
1 parent b132e11 commit 5df1cc1

File tree

3 files changed

+497
-155
lines changed

3 files changed

+497
-155
lines changed

src/shared/anim/AnimationLoop.ts

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import { MutableRefObject, useCallback, useEffect, useMemo, useRef } from 'react';
22
import { Bezier } from '../lib/Bezier';
3-
import {
4-
BezierAnimation,
5-
animIsComplete,
6-
calculateAnimValue,
7-
updateAnim,
8-
} from '../lib/BezierAnimation';
3+
import { BezierAnimation, animIsComplete, calculateAnimValue } from '../lib/BezierAnimation';
94
import { Callbacks } from '../lib/Callbacks';
105

116
export interface Animator<P extends object> {
@@ -73,6 +68,26 @@ export class TrivialAnimator<K extends string, T, P extends { [key in K]: T }>
7368
reset(): void {}
7469
}
7570

71+
type StdAnimatorOpts = {
72+
/**
73+
* What to do if the target changes while animating. We will need to
74+
* recreate the animation easing, but the question comes with what to
75+
* do with the duration: should we have the animation complete at the
76+
* original time, or should it complete later?
77+
*
78+
* Generally this is not a subjective choice; if the target changes
79+
* often and smoothly, replace is appropriate. Otherwise, extend is
80+
* appropriate.
81+
*
82+
* @default true
83+
*/
84+
onTargetChange?: 'extend' | 'replace';
85+
};
86+
87+
const defaultAnimatorOpts: Required<StdAnimatorOpts> = {
88+
onTargetChange: 'extend',
89+
};
90+
7691
/**
7792
* Uses a Bezier animation curve for a single number field in the props.
7893
* This animator will animate the field from the rendered value to the
@@ -87,19 +102,22 @@ export class BezierAnimator<P extends object> implements Animator<P> {
87102
private readonly duration: number;
88103
private readonly getter: (p: P) => number;
89104
private readonly setter: (p: P, v: number) => void;
105+
private readonly opts: Required<StdAnimatorOpts>;
90106

91107
private animation: BezierAnimation | null;
92108

93109
constructor(
94110
ease: Bezier,
95111
duration: number,
96112
getter: (p: P) => number,
97-
setter: (p: P, v: number) => void
113+
setter: (p: P, v: number) => void,
114+
opts?: StdAnimatorOpts
98115
) {
99116
this.ease = ease;
100117
this.duration = duration;
101118
this.getter = getter;
102119
this.setter = setter;
120+
this.opts = Object.assign({}, defaultAnimatorOpts, opts);
103121

104122
this.animation = null;
105123
}
@@ -113,16 +131,31 @@ export class BezierAnimator<P extends object> implements Animator<P> {
113131
return 'done';
114132
}
115133

116-
this.animation = updateAnim({
117-
now,
118-
current: this.getter(toRender),
119-
target: this.getter(target),
120-
oldAnim: this.animation,
121-
duration: this.duration,
122-
ease: this.ease,
123-
});
134+
if (this.animation !== null && this.animation.to !== this.getter(target)) {
135+
this.animation = {
136+
startedAt: this.opts.onTargetChange === 'extend' ? now : this.animation.startedAt,
137+
from: this.opts.onTargetChange === 'extend' ? this.getter(toRender) : this.animation.from,
138+
to: this.getter(target),
139+
ease: this.ease,
140+
duration: this.duration,
141+
};
142+
143+
if (animIsComplete(this.animation, now)) {
144+
this.animation = null;
145+
}
146+
}
124147

125148
if (this.animation === null) {
149+
this.animation = {
150+
startedAt: now,
151+
from: this.getter(toRender),
152+
to: this.getter(target),
153+
ease: this.ease,
154+
duration: this.duration,
155+
};
156+
}
157+
158+
if (animIsComplete(this.animation, now)) {
126159
this.setter(toRender, this.getter(target));
127160
this.animation = null;
128161
return 'done';

src/user/core/features/pickEmotionJourney/PickEmotion.module.css

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -57,49 +57,22 @@
5757
}
5858

5959
.word {
60+
position: absolute;
6061
cursor: pointer;
6162
outline: none;
6263
appearance: none;
6364
-webkit-tap-highlight-color: transparent;
6465
box-shadow: none;
6566
border: none;
66-
position: absolute;
67-
font-family: 'Open Sans', sans-serif;
6867
text-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25);
6968
color: white;
70-
transition: font-size 0.7s ease, padding 0.7s ease, left 0.7s ease, top 0.7s ease,
71-
opacity 0.7s ease, background-color 0.3s ease;
72-
background: rgba(255, 255, 255, 0.2);
73-
backdrop-filter: blur(2px);
74-
opacity: 100%;
75-
}
76-
77-
.horizontalWords.wordsWithPressed .word:not(.pressedWord) {
78-
opacity: 50%;
79-
}
80-
81-
.wordsWithPressed .pressedWord {
82-
opacity: 100%;
83-
background: linear-gradient(95.08deg, #57b8a2 2.49%, #009999 97.19%);
84-
}
85-
86-
.word:hover:not(.pressedWord) {
87-
background: rgba(255, 255, 255, 0.3);
88-
}
89-
90-
.horizontalWord {
91-
font-size: 16px;
92-
border-radius: 24px;
93-
padding: 12px 14px;
94-
line-height: 16px;
69+
backdrop-filter: 'blur(2px)';
70+
font-family: 'Open Sans', sans-serif;
71+
line-height: 100%;
9572
}
9673

97-
.verticalWord {
98-
font-size: 14px;
99-
line-height: 14px;
100-
letter-spacing: 0.25px;
101-
padding: 10px 12px;
102-
border-radius: 24px;
74+
.word:not(.pressed):hover {
75+
background: rgba(255, 255, 255, 0.3) !important;
10376
}
10477

10578
.votes {
@@ -110,7 +83,6 @@
11083
font-size: 12px;
11184
line-height: 12px;
11285
color: white;
113-
transition: left 0.7s ease, top 0.7s ease, opacity 0.7s ease;
11486
}
11587

11688
.profilePicturesContainer {

0 commit comments

Comments
 (0)