Sequence not working since reanimated 2.3.0
msantang78 opened this issue Β· 28 comments
Since reanimated 2.3.0 I was no longer able to use Moti sequence animations
I'm getting the error:
Exception in HostFunction: Javascript worklet error
For any sequence animation
<MotiView from={{scale: 1}} animate={{scale: [1, 1.5, 1, 1.5, 1]}}>
<Text>Test</Test>
</MotiView>
I tested updating reanimated to different versions (even the last one) without luck
While creating a minimal app to reproduce the issue I found that the problem seems to be with the Hermes Engine. If Hermes is turned off the sequences seem to work properly
@nandorojo I created a repo using react-native init to reproduce the issue on iOS (adding only reanimated, moti, and enabling Hermes)
https://github.com/msantang78/MotiSequenceReproduction
I also added an animation with reanimated sequences and they seem to be working fine.
I hope it helps
For reference:
I originally reported this on #131, there were other reports #140 #183
can you reproduce on a snack with SDK 44?
But the problem seems to be with the Hermes engine, I'm not sure if there is a way of enabling Hermes on a snack
Thanks for reporting this issue. I'm also seeing this when trying to upgrade.
The issue seems to happen when it tries to call this line, since stepAnimation
is undefined.
It references this destructured animation
outside of the getSequenceArray
const, which does have a value when it's destructured, but is then undefined when it's used in getSequenceArray.
example of logs:
LOG === animation value right after destructured from animationConfig() {"animation": [Function hostFunction]}
LOG === getSequenceArray called for {"sequenceArray": [0.25, 1.25, 1.25, 1.25, 0.25, 0.25, 0.25, 1.25, 0.25, 0.25], "sequenceKey": "opacity"}
LOG === value of stepAnimation (animation) just before stepAnimation called {"stepAnimation": undefined}
interesting find. can this be reproduced on snack?
if the issue is hermes, then i assume itβs a reanimated issue? or is it the fact that iβm using a function with moti to build the sequence?
If i disable hermes, it gets further (the result of the function at least finishes), but it's freezing for me still, so it might not be an hermes issue per se (it might just break in a different way). I'll see if a snack can use hermes.
LOG === animation value right after destructured from animationConfig() {"animation": [Function hostFunction]}
LOG === getSequenceArray called for {"sequenceArray": [1.25, 1.25, 0.25, 0.25, 0.25, 1.25, 1.25, 1.25, 1.25, 0.25], "sequenceKey": "scale"}
LOG === value of stepAnimation (animation) just before stepAnimation called {"stepAnimation": [Function hostFunction]}
LOG === result of getSequenceArray: {"sequence": [{"callback": [Function hostFunction], "current": 1.25, "easing": [Function hostFunction], "onFrame": [Function hostFunction], "onStart": [Function hostFunction], "progress": 0, "startTime": 0, "startValue": 0, "toValue": 1.25, "type": "timing"}, {"callback": [Function hostFunction], "current": 1.25, "easing": [Function hostFunction], "onFrame": [Function hostFunction], "onStart": [Function hostFunction], "progress": 0, "startTime": 0, "startValue": 0, "toValue": 1.25, "type": "timing"}, {"callback": [Function hostFunction], "current": 0.25, "easing": [Function hostFunction], "onFrame": [Function hostFunction], "onStart": [Function hostFunction], "progress": 0, "startTime": 0, "startValue": 0, "toValue": 0.25, "type": "timing"}, {"callback": [Function hostFunction], "current": 0.25, "easing": [Function hostFunction], "onFrame": [Function hostFunction], "onStart": [Function hostFunction], "progress": 0, "startTime": 0, "startValue": 0, "toValue": 0.25, "type": "timing"}, {"callback": [Function hostFunction], "current": 0.25, "easing": [Function hostFunction], "onFrame": [Function hostFunction], "onStart": [Function hostFunction], "progress": 0, "startTime": 0, "startValue": 0, "toValue": 0.25, "type": "timing"}, {"callback": [Function hostFunction], "current": 1.25, "easing": [Function hostFunction], "onFrame": [Function hostFunction], "onStart": [Function hostFunction], "progress": 0, "startTime": 0, "startValue": 0, "toValue": 1.25, "type": "timing"}, {"callback": [Function hostFunction], "current": 1.25, "easing": [Function hostFunction], "onFrame": [Function hostFunction], "onStart": [Function hostFunction], "progress": 0, "startTime": 0, "startValue": 0, "toValue": 1.25, "type": "timing"}, {"callback": [Function hostFunction], "current": 1.25, "easing": [Function hostFunction], "onFrame": [Function hostFunction], "onStart": [Function hostFunction], "progress": 0, "startTime": 0, "startValue": 0, "toValue": 1.25, "type": "timing"}, {"callback": [Function hostFunction], "current": 1.25, "easing": [Function hostFunction], "onFrame": [Function hostFunction], "onStart": [Function hostFunction], "progress": 0, "startTime": 0, "startValue": 0, "toValue": 1.25, "type": "timing"}, {"callback": [Function hostFunction], "current": 0.25, "easing": [Function hostFunction], "onFrame": [Function hostFunction], "onStart": [Function hostFunction], "progress": 0, "startTime": 0, "startValue": 0, "toValue": 0.25, "type": "timing"}]}
So even without hermes, the sequence with Moti doesn't work despite it working with Reanimated, so I'm thinking there's an issue with the function.
I took @msantang78's repo and set moti to 0.18.0 and reanimted to 2.3.1 (the highest expo 0.44 allows currently).
When you run it, you can see that only the Reanimated sequence works on iOS and Android (web works):
https://snack.expo.dev/lTiRIGySt
However, I also tried to get it to work on reanimated 2.2.0 (what expo 0.43 allows), and it doesn't work either for moti (i even made sure to use the transform array since it's less than reanimated 2.3):
https://snack.expo.dev/NZSL2U0FE
So I don't think snacks work, unfortunately, due to the limitations of expo with reanimated.
or is it the fact that iβm using a function with moti to build the sequence?
So I just removed the function and just do what the function does inline and the issue doesn't happen anymore. π So it looks like it's because of the inline getSequenceArray const like you suspected.
this patch-package diff resolves the issue (extracted function and marked it as a worklet).
diff --git a/node_modules/@motify/core/src/use-map-animate-to-style.ts b/node_modules/@motify/core/src/use-map-animate-to-style.ts
index dce4aa1..9cda484 100644
--- a/node_modules/@motify/core/src/use-map-animate-to-style.ts
+++ b/node_modules/@motify/core/src/use-map-animate-to-style.ts
@@ -220,6 +220,64 @@ function animationConfig<Animate>(
}
}
+// this used to be an inline function, but it caused the error outlined here:
+// https://github.com/nandorojo/moti/issues/195
+const getSequenceArray = (
+ sequenceKey: string,
+ sequenceArray: SequenceItem<any>[],
+ delayMs: number,
+ config: {},
+ animation: (...props: any) => any,
+ callback: (completed: boolean, value?: any) => void
+) => {
+ 'worklet'
+
+ const sequence: any[] = []
+
+ for (const step of sequenceArray) {
+ const shouldPush =
+ typeof step === 'object'
+ ? step && step?.value != null && step?.value !== false
+ : step != null && step !== false
+ if (shouldPush) {
+ let stepDelay = delayMs
+ let stepValue = step
+ let stepConfig = Object.assign({}, config)
+ let stepAnimation = animation
+
+ if (typeof step === 'object') {
+ // not allowed in Reanimated: { delay, value, ...transition } = step
+ const stepTransition = Object.assign({}, step)
+
+ delete stepTransition.delay
+ delete stepTransition.value
+
+ const { config: inlineStepConfig, animation } = animationConfig(
+ sequenceKey,
+ stepTransition
+ )
+
+ stepConfig = Object.assign({}, stepConfig, inlineStepConfig)
+ stepAnimation = animation
+
+ if (step.delay != null) {
+ stepDelay = step.delay
+ }
+ stepValue = step.value
+ }
+
+ const sequenceValue = stepAnimation(stepValue, stepConfig, callback)
+ if (stepDelay != null) {
+ sequence.push(withDelay(stepDelay, sequenceValue))
+ } else {
+ sequence.push(sequenceValue)
+ }
+ }
+ }
+
+ return sequence
+}
+
export function useMotify<Animate>({
animate: animateProp,
from: fromProp = false,
@@ -385,55 +443,6 @@ export function useMotify<Animate>({
continue
}
- const getSequenceArray = (
- sequenceKey: string,
- sequenceArray: SequenceItem<any>[]
- ) => {
- const sequence: any[] = []
-
- for (const step of sequenceArray) {
- const shouldPush =
- typeof step === 'object'
- ? step && step?.value != null && step?.value !== false
- : step != null && step !== false
- if (shouldPush) {
- let stepDelay = delayMs
- let stepValue = step
- let stepConfig = Object.assign({}, config)
- let stepAnimation = animation
- if (typeof step === 'object') {
- // not allowed in Reanimated: { delay, value, ...transition } = step
- const stepTransition = Object.assign({}, step)
-
- delete stepTransition.delay
- delete stepTransition.value
-
- const { config: inlineStepConfig, animation } = animationConfig(
- sequenceKey,
- stepTransition
- )
-
- stepConfig = Object.assign({}, stepConfig, inlineStepConfig)
- stepAnimation = animation
-
- if (step.delay != null) {
- stepDelay = step.delay
- }
- stepValue = step.value
- }
-
- const sequenceValue = stepAnimation(stepValue, stepConfig, callback)
- if (stepDelay != null) {
- sequence.push(withDelay(stepDelay, sequenceValue))
- } else {
- sequence.push(sequenceValue)
- }
- }
- }
-
- return sequence
- }
-
if (key === 'transform') {
if (!Array.isArray(value)) {
console.error(
@@ -448,7 +457,7 @@ export function useMotify<Animate>({
if (Array.isArray(transformValue)) {
// we have a sequence in this transform...
- const sequence = getSequenceArray(transformKey, transformValue)
+ const sequence = getSequenceArray(transformKey, transformValue, delayMs, config, animation, callback)
if (sequence.length) {
let finalValue = withSequence(sequence[0], ...sequence.slice(1))
@@ -484,8 +493,7 @@ export function useMotify<Animate>({
}
} else if (Array.isArray(value)) {
// we have a sequence
- const sequence = getSequenceArray(key, value)
+ const sequence = getSequenceArray(key, value, delayMs, config, animation, callback)
let finalValue = withSequence(sequence[0], ...sequence.slice(1))
if (shouldRepeat) {
finalValue = withRepeat(finalValue, repeatCount, repeatReverse)
if that patch fixed it, why did you close your PR?
if that patch fixed it, why did you close your PR?
π I didn't close it...but it says I did. π²
I'll reopen it.
if that patch fixed it, why did you close your PR?
π I didn't close it...but it says I did. π²
I'll reopen it.
It looks like when i referenced the PR on our private repo and merged changes there, it closed the issue. Just reopened it! It definitely resolves the issue still. π―
just fyi: this is still the case with moti 0.18, also the patch above still works π
just merged @jstheoriginal's PR, thanks for your patience here. will release it in the next version
Sequences are now fixed in 0.19.0-alpha.2
:
yarn add moti@canary
Please see #215 for the upgrade guide.
Thanks guys!
@nandorojo I loved moti,
I tried 0.19.0-alpha.2 & getting this error
error: Error: Unable to resolve module framer-motion from /node_modules/moti/src/core/index.ts: framer-motion could not be found within the project or in these directories:
node_modules
../../../node_modules
1 | export { default as motify } from './motify'
2 | export { AnimatePresence } from 'framer-motion'
| ^
3 |
4 | export * from './types'
5 | export { default as useAnimationState } from './use-animator'
@nandorojo I loved moti,
I tried 0.19.0-alpha.2 & getting this error
error: Error: Unable to resolve module framer-motion from /node_modules/moti/src/core/index.ts: framer-motion could not be found within the project or in these directories:
node_modules
../../../node_modules
1 | export { default as motify } from './motify'
2 | export { AnimatePresence } from 'framer-motion'
| ^
3 |
4 | export * from './types'
5 | export { default as useAnimationState } from './use-animator'
Import AnimatePresence
from moti
instead of framer-motion
.
no need to quote the big messages haha
could be a caching issue?
okay, thanks @nandorojo
but I cleared everything not resolved.
try yarn why framer-motion
π§ what about yarn why moti
will try to fix it today, thanks!
Okay, thank you.
Can you try updating again? yarn add moti@canary
. 0.19.0-alpha.6
should work now.
Thank you @nandorojo It is resolved. ππ