@tamagui/animations-react-native 1.0.1-beta.192 → 1.0.1-beta.194

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,3 @@
1
- import { PresenceContext, usePresence } from '@tamagui/animate-presence'
2
1
  import {
3
2
  AnimatedNumberStrategy,
4
3
  AnimationDriver,
@@ -7,7 +6,9 @@ import {
7
6
  isWeb,
8
7
  useEvent,
9
8
  useIsomorphicLayoutEffect,
9
+ useSafeRef,
10
10
  } from '@tamagui/core'
11
+ import { PresenceContext, usePresence } from '@tamagui/use-presence'
11
12
  import { useContext, useEffect, useMemo, useRef } from 'react'
12
13
  import { Animated } from 'react-native'
13
14
 
@@ -40,7 +41,7 @@ export const AnimatedView = Animated.View
40
41
  export const AnimatedText = Animated.Text
41
42
 
42
43
  export function useAnimatedNumber(initial: number): UniversalAnimatedNumber<Animated.Value> {
43
- const state = useRef(
44
+ const state = useSafeRef(
44
45
  null as any as {
45
46
  val: Animated.Value
46
47
  composite: Animated.CompositeAnimation | null
@@ -121,7 +122,7 @@ export function createAnimations<A extends AnimationsConfig>(animations: A): Ani
121
122
  AnimatedText['displayName'] = 'AnimatedText'
122
123
 
123
124
  return {
124
- avoidClasses: true,
125
+ isReactNativeWeb: true,
125
126
  animations,
126
127
  View: AnimatedView,
127
128
  Text: AnimatedText,
@@ -147,113 +148,124 @@ export function createAnimations<A extends AnimationsConfig>(animations: A): Ani
147
148
  enterVariant: presence?.enterVariant,
148
149
  })
149
150
 
150
- const animateStyles = useRef<Record<string, Animated.Value>>({})
151
- const animatedTranforms = useRef<{ [key: string]: Animated.Value }[]>([])
152
- const interpolations = useRef(new WeakMap<Animated.Value, Animated.AnimatedInterpolation>())
153
-
154
- // TODO loop and create values, run them if they change
151
+ const animateStyles = useSafeRef<Record<string, Animated.Value>>({})
152
+ const animatedTranforms = useSafeRef<{ [key: string]: Animated.Value }[]>([])
153
+ const interpolations = useSafeRef(
154
+ new WeakMap<
155
+ Animated.Value,
156
+ { interopolation: Animated.AnimatedInterpolation; current?: number }
157
+ >()
158
+ )
155
159
 
156
160
  const runners: Function[] = []
157
161
  const completions: Promise<void>[] = []
158
162
 
159
- function update(key: string, animated: Animated.Value | undefined, valIn: string | number) {
160
- const [val, type] = getValue(valIn)
161
- const value = animated || new Animated.Value(val)
162
- if (type) {
163
- interpolations.current.set(value, getInterpolated(value, type, val))
164
- }
165
- if (animated) {
166
- const animationConfig = getAnimationConfig(key, animations, props.animation)
163
+ const args = [
164
+ JSON.stringify(all),
165
+ state.mounted,
166
+ state.hover,
167
+ state.press,
168
+ state.pressIn,
169
+ state.focus,
170
+ delay,
171
+ isPresent,
172
+ onDidAnimate,
173
+ presence?.exitVariant,
174
+ presence?.enterVariant,
175
+ ]
167
176
 
168
- let resolve
169
- const promise = new Promise<void>((res) => {
170
- resolve = res
171
- })
172
- completions.push(promise)
177
+ const res = useMemo(() => {
178
+ function update(key: string, animated: Animated.Value | undefined, valIn: string | number) {
179
+ const [val, type] = getValue(valIn)
180
+ const value = animated || new Animated.Value(val)
181
+ let interpolateArgs: any
182
+ if (type) {
183
+ const curInterpolation = interpolations.current.get(value)
184
+ interpolateArgs = getInterpolated(
185
+ curInterpolation?.current ?? value['_value'],
186
+ val,
187
+ type
188
+ )
189
+ interpolations.current!.set(value, {
190
+ interopolation: value.interpolate(interpolateArgs),
191
+ current: val,
192
+ })
193
+ }
194
+ if (animated) {
195
+ const animationConfig = getAnimationConfig(key, animations, props.animation)
173
196
 
174
- runners.push(() => {
175
- if (animated['_value'] === val) {
176
- resolve()
177
- // avoid update when its the same
178
- return
179
- }
180
- animated.stopAnimation()
181
- Animated.spring(animated, {
182
- toValue: val,
183
- useNativeDriver: !isWeb,
184
- ...animationConfig,
185
- }).start(({ finished }) => {
186
- if (finished) {
187
- resolve()
188
- }
197
+ let resolve
198
+ const promise = new Promise<void>((res) => {
199
+ resolve = res
189
200
  })
190
- })
191
- }
192
- return value
193
- }
201
+ completions.push(promise)
194
202
 
195
- function getValue(input: number | string) {
196
- if (typeof input !== 'string') {
197
- return [input] as const
203
+ runners.push(() => {
204
+ animated.stopAnimation()
205
+ Animated.spring(animated, {
206
+ toValue: val,
207
+ useNativeDriver: !isWeb,
208
+ ...animationConfig,
209
+ }).start(({ finished }) => {
210
+ if (finished) {
211
+ resolve()
212
+ }
213
+ })
214
+ })
215
+ }
216
+ if (process.env.NODE_ENV === 'development') {
217
+ if (props['debug']) {
218
+ // prettier-ignore
219
+ // eslint-disable-next-line no-console
220
+ console.log('AnimatedValue', key, 'mapped value', valIn, 'of type', type, 'to', val, 'interpolated', interpolateArgs, '- current Animated.Value', value['_value'])
221
+ }
222
+ }
223
+ return value
198
224
  }
199
- const neg = input[0] === '-'
200
- if (neg) input = input.slice(1)
201
- const [_, number, after] = input.match(/([-0-9]+)(deg|%)/) ?? []
202
- return [+number * (neg ? -1 : 1), after] as const
203
- }
204
225
 
205
- const nonAnimatedStyle = {}
206
- for (const key in all) {
207
- const val = all[key]
208
- if (animatedStyleKey[key]) {
209
- if (key === 'transform') {
210
- // for now just support one transform key
211
- if (val) {
212
- for (const [index, transform] of val.entries()) {
213
- if (!transform) continue
214
- const tkey = Object.keys(transform)[0]
215
- animatedTranforms.current[index] = {
216
- [tkey]: update(tkey, animatedTranforms.current[index]?.[tkey], transform[tkey]),
226
+ const nonAnimatedStyle = {}
227
+ for (const key in all) {
228
+ const val = all[key]
229
+ if (animatedStyleKey[key]) {
230
+ if (key === 'transform') {
231
+ // for now just support one transform key
232
+ if (val) {
233
+ for (const [index, transform] of val.entries()) {
234
+ if (!transform) continue
235
+ const tkey = Object.keys(transform)[0]
236
+ animatedTranforms.current[index] = {
237
+ [tkey]: update(tkey, animatedTranforms.current[index]?.[tkey], transform[tkey]),
238
+ }
217
239
  }
218
240
  }
241
+ } else {
242
+ animateStyles.current[key] = update(key, animateStyles.current[key], val)
219
243
  }
220
244
  } else {
221
- animateStyles.current[key] = update(key, animateStyles.current[key], val)
245
+ nonAnimatedStyle[key] = val
222
246
  }
223
- } else {
224
- nonAnimatedStyle[key] = val
225
247
  }
226
- }
227
248
 
228
- const animatedStyle = {
229
- ...Object.fromEntries(
230
- Object.entries({
231
- ...animateStyles.current,
232
- }).map(([k, v]) => [k, interpolations.current.get(v) || v])
233
- ),
234
- transform: animatedTranforms.current.map((r) => {
235
- const key = Object.keys(r)[0]
236
- const val = interpolations.current.get(r[key]) || r[key]
237
- return { [key]: val }
238
- }),
239
- }
249
+ const animatedStyle = {
250
+ ...Object.fromEntries(
251
+ Object.entries({
252
+ ...animateStyles.current,
253
+ }).map(([k, v]) => [k, interpolations.current!.get(v)?.interopolation || v])
254
+ ),
255
+ transform: animatedTranforms.current.map((r) => {
256
+ const key = Object.keys(r)[0]
257
+ const val = interpolations.current!.get(r[key])?.interopolation || r[key]
258
+ return { [key]: val }
259
+ }),
260
+ }
240
261
 
241
- const args = [
242
- JSON.stringify(all),
243
- state.mounted,
244
- state.hover,
245
- state.press,
246
- state.pressIn,
247
- state.focus,
248
- delay,
249
- isPresent,
250
- onDidAnimate,
251
- presence?.exitVariant,
252
- presence?.enterVariant,
253
- ]
262
+ return {
263
+ style: [nonAnimatedStyle, animatedStyle],
264
+ }
265
+ // eslint-disable-next-line react-hooks/exhaustive-deps
266
+ }, args)
254
267
 
255
268
  useIsomorphicLayoutEffect(() => {
256
- //
257
269
  for (const runner of runners) {
258
270
  runner()
259
271
  }
@@ -265,28 +277,25 @@ export function createAnimations<A extends AnimationsConfig>(animations: A): Ani
265
277
  })
266
278
  }, args)
267
279
 
268
- return useMemo(() => {
269
- return {
270
- style: [nonAnimatedStyle, animatedStyle],
271
- }
272
- // eslint-disable-next-line react-hooks/exhaustive-deps
273
- }, args)
280
+ return res
274
281
  },
275
282
  }
276
283
  }
277
284
 
278
- function getInterpolated(val: Animated.Value, postfix: string, next: number) {
279
- const cur = val['_value'] as number
280
- const inputRange = [cur, next]
281
- const outputRange = [`${cur}deg`, `${next}deg`]
282
- if (next < cur) {
285
+ function getInterpolated(current: number, next: number, postfix = 'deg') {
286
+ if (next === current) {
287
+ current = next - 0.000000001
288
+ }
289
+ const inputRange = [current, next]
290
+ const outputRange = [`${current}${postfix}`, `${next}${postfix}`]
291
+ if (next < current) {
283
292
  inputRange.reverse()
284
293
  outputRange.reverse()
285
294
  }
286
- return val.interpolate({
295
+ return {
287
296
  inputRange,
288
297
  outputRange,
289
- })
298
+ }
290
299
  }
291
300
 
292
301
  function getAnimationConfig(key: string, animations: AnimationsConfig, animation?: AnimationProp) {
@@ -320,3 +329,13 @@ function getAnimationConfig(key: string, animations: AnimationsConfig, animation
320
329
  ...extraConf,
321
330
  }
322
331
  }
332
+
333
+ function getValue(input: number | string) {
334
+ if (typeof input !== 'string') {
335
+ return [input] as const
336
+ }
337
+ const neg = input[0] === '-'
338
+ if (neg) input = input.slice(1)
339
+ const [_, number, after] = input.match(/([-0-9]+)(deg|%)/) ?? []
340
+ return [+number * (neg ? -1 : 1), after] as const
341
+ }