Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web Animations Broken #2943

Open
1 task
nathan-ahn opened this issue Feb 5, 2025 · 19 comments
Open
1 task

Web Animations Broken #2943

nathan-ahn opened this issue Feb 5, 2025 · 19 comments
Labels
bug Something isn't working

Comments

@nathan-ahn
Copy link
Contributor

Description

Looks like web animations are broken on the newest version. I don't know the exact version this bug was introduced in, but I can confirm that our web animations were working in v1.7.6. As far as I can tell, this affects all Skia animations using Reanimated.

Expected: Blue square in reproduction slides back and forth.
Actual: Blue square in reproduction does not move.

React Native Skia Version

1.11.4

React Native Version

React Native Web: 0.19.12

Using New Architecture

  • Enabled

Steps to Reproduce

https://github.com/nathan-ahn/skia-web-animation-bug

Reproducing

  1. Install yarn install
  2. Run web yarn web
  3. See the blue square (Skia) not move
    (Pink square included to demonstrate Reanimated is working)

Notes

I usually prefer to use Expo for minimum reproducible examples but can't until #2914 is resolved. I'm using the same Solito setup to that we use in production to get Skia web to work. I don't believe that any of the Next.js workarounds are causing this issue, but happy to reproduce using Expo Router once #2914 is fixed.

Snack, Code Example, Screenshot, or Link to Repository

https://github.com/nathan-ahn/skia-web-animation-bug

@nathan-ahn nathan-ahn added the bug Something isn't working label Feb 5, 2025
@nathan-ahn
Copy link
Contributor Author

If it helps with debugging, our Storybook usage of Skia animations is reporting the following error: "x" is read-only (where x is a shared value). I haven't been able to reproduce the error in Next.js.

@wcandillon
Copy link
Contributor

wcandillon commented Feb 6, 2025 via email

@nathan-ahn
Copy link
Contributor Author

For sure! I'll see if I can get a simpler minimum reproducible example, since Solito is a bit heavy for just an example. Ideally, I think it'd make most sense to have an Expo Router example (assuming this issue exists in Expo Router as well). However, Skia is currently broken in Expo Router due to #2914 (which I guess is another great reason for us to have it in apps).

Once I get the chance this afternoon, I'll create an Expo Router minimum reproducible app in apps to guard against both this issue and #2914 in the future. If this issue isn't reproducible in Expo Router, then I'll resort to likely a Solito or pure React Native Web reproduction of this animation bug.

@wcandillon
Copy link
Contributor

wcandillon commented Feb 6, 2025 via email

@nathan-ahn
Copy link
Contributor Author

Created Expo Router demo in #2949! Holding off on Solito unless absolutely necessary for reproducibility, since Solito is quite complex and can be difficult to maintain as an example. (+ Expo Router is actually mentioned in the docs for web support).

This was referenced Feb 8, 2025
@nathan-ahn
Copy link
Contributor Author

This issue has been partially fixed in v1.11.6! We're now seeing some of our animations working. However, there's still a few that aren't with the following errors:

  • Uncaught TypeError: "opacity" is read-only
  • Warning: Maximum update depth exceeded.
    I'll do some investigation on my own end since these are likely going to be difficult to reproduce considering they're only occurring for certain animations.

@nathan-ahn
Copy link
Contributor Author

I don't have a full reproducible example yet (waiting on #2958 or #2949), but I've pinpointed the issue in our codebase to Reanimated Shared Values with <Text/>:

const TextComponent = () => {
	const x = useDerivedValue(() => 0, []);
	return <Text font={font} text={text} x={x} color={color} {...props} />;
}

@JohnWilliamForcier-Spiria
Copy link

JohnWilliamForcier-Spiria commented Feb 18, 2025

Same here. Using a library like this one with the latest version of Skia 1.11.8 on the web : https://github.com/doublelam/react-native-free-canvas

The animation does not work.

Also when a rerender occur with a different stroke property on my SkiaPath, I have the error Cannot read properties of undefined (reading 'Kd'). The stack :

Uncaught Error
Cannot read properties of undefined (reading 'Kd')
Call Stack
pc
node_modules/canvaskit-wasm/bin/full/canvaskit.js
<anonymous>
node_modules/canvaskit-wasm/bin/full/canvaskit.js
a.Canvas.prototype.drawPath
node_modules/canvaskit-wasm/bin/full/canvaskit.js
drawPath
node_modules/@shopify/react-native-skia/lib/module/skia/web/JsiSkCanvas.js
drawPath
node_modules/@shopify/react-native-skia/lib/module/sksg/Recorder/commands/Drawing.js
paints.forEach$argument_0
node_modules/@shopify/react-native-skia/lib/module/sksg/Recorder/Player.js

To reproduce in summary :

const pathSharedVal = useSharedValue(Skia.Path.Make());
const derivedPathSharedVal = useDerivedValue(
    () => pathSharedVal.value.toSVGString(),
    [],
  );

const gesture = useMemo(
    () =>
      Gesture.Pan()
        .averageTouches(true)
        .minDistance(0)
        .maxPointers(1)
        .onBegin((e) => {
          "worklet";
          const touch = e;
          pathSharedVal.modify((v) => {
            v.reset();
            v.moveTo(touch.x || 0, touch.y || 0);
            v.lineTo(touch.x || 0, touch.y || 0);
            return v;
          });
        })
        .onUpdate((e) => {
          "worklet";
          if (pathSharedVal.value.isEmpty()) {
            return;
          }
          pathSharedVal.modify((v) => {
            v.lineTo(e.x || 0, e.y || 0);
            return v;
          });
        })
        .onFinalize(() => {
          "worklet";
          if (pathSharedVal.value.isEmpty()) {
            return;
          }
          runOnJS(addDrawn)({
            key: generateUniqueKey("path"),
            path: getSharedValue(derivedPathSharedVal),
            ...stroke,
          } as DrawingPath);
        }),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [stroke],
  );

return (
    <GestureDetector gesture={gesture}>
      <View style={styles.canvas} ref={viewRef}>
          <Canvas style={styles.canvas}>
            {drawnPaths.map((path) => (
              <Path style="stroke" {...path} key={path.key} />
            ))}
            <Path path={derivedPathSharedVal} style="stroke" {...stroke} />
          </Canvas>
      </View>
    </GestureDetector>
  );

@JohnWilliamForcier-Spiria
Copy link

JohnWilliamForcier-Spiria commented Feb 21, 2025

@nathan-ahn @wcandillon

I was able to kindof fix it by adding the key property to my <Path path={derivedPathSharedVal} style="stroke" {...stroke} /> tag. Each time I start drawing OR I change the stroke color of that path, I need to set a new key. Otherwise if I change the stroke color dynamically I get the error Cannot read properties of undefined (reading 'Kd') or the drawing just doesn't work.

This "fix" the drawing animation and the error when my stroke property changes. Because I change the key prop, there is a small rerender glitch that occurs when I finish drawing... You might try that for your animations...

...

const [pathId, setPathId] = useState(0);

const stroke = useMemo(
    (): Partial<DrawingPath> => ({
      color: drawingMode === "eraser" ? "white" : "black",
    }),
    [drawingMode],
  );


const onStartDrawing = () => {
 setPathId((prev) => prev + 1);
}

...

return (
    <GestureDetector gesture={gesture}>
      <View style={styles.canvas} ref={viewRef}>
          <Canvas style={styles.canvas}>
            {drawnPaths.map((path) => (
              <Path style="stroke" {...path} key={path.key} />
            ))}
             <Path
              key={pathId.toString() + stroke.color}
              path={derivedPathSharedVal}
              style="stroke"
              {...stroke}
            />
          </Canvas>
      </View>
    </GestureDetector>
  );

@codeion
Copy link

codeion commented Feb 22, 2025

Related issue...

const radialGradientColors = useDerivedValue(() => [color.value, DEFAULT_COLOR], [color]);

<RadialGradient c={vec(width / 2, height / 2)} r={radius} colors={radialGradientColors}/>

Uncaught TypeError: Cannot assign to read only property 'colors' of object '#'
at shopify_CoreJs1 (@shopify_react-native-skia.js:23426:26)
at play2 (@shopify_react-native-skia.js:25244:5)
at @shopify_react-native-skia.js:25354:7
at Array.forEach ()
at shopify_PlayerJs2 (@shopify_react-native-skia.js:25353:14)
at shopify_ContainerJs1 (@shopify_react-native-skia.js:25663:5)
at shopify_ContainerJs4 (@shopify_react-native-skia.js:25759:9)
at chunk-AEG2KI5L.js?v=30e4affe:1202:21
at Array.forEach ()
at reactNativeReanimated_threadsJs5 (chunk-AEG2KI5L.js?v=30e4affe:1201:25)

@wcandillon
Copy link
Contributor

Can you provide a small standalone/reproducible example? You can also contribute the example to https://github.com/Shopify/react-native-skia/tree/main/external-apps/expo-router-app, we can test it there

@codeion
Copy link

codeion commented Mar 4, 2025

Can you provide a small standalone/reproducible example? You can also contribute the example to https://github.com/Shopify/react-native-skia/tree/main/external-apps/expo-router-app, we can test it there

Sure, I can do that.

@agustinferrari
Copy link

agustinferrari commented Mar 4, 2025

Hey @wcandillon we are also having the same issues, added a minimal example in #2996, hope it helps testing!
Can also confirm the animations were also working correctly on v1.7.6 for us

@agustinferrari
Copy link

agustinferrari commented Mar 5, 2025

Most of the issues seem to be fixed in 1.11.11! Thanks @wcandillon, added a new example in #3000 as there seems to be a loose end

@JohnWilliamForcier-Spiria

@wcandillon Added an example for the drawing problems : #3001

@wcandillon
Copy link
Contributor

wcandillon commented Mar 5, 2025 via email

@JohnWilliamForcier-Spiria

Is there any change you could update the example to be smaller and
help me understand quicker the root cause issue?

Not sure I can really simplify it. The problem is really with the Path tag not showing the reanimated path value until the view rerender.

@agustinferrari
Copy link

agustinferrari commented Mar 5, 2025

@JohnWilliamForcier-Spiria are you still getting that issue with v1.11.11?, my issues with path where solved by that one, but I still have the issue in #3000

@JohnWilliamForcier-Spiria
Copy link

JohnWilliamForcier-Spiria commented Mar 5, 2025

@agustinferrari Yes I still have the issue. We can see it in my PR #3001

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants