react-native-skia icon indicating copy to clipboard operation
react-native-skia copied to clipboard

[Android] Canvas resizing is janky on Android

Open thespacemanatee opened this issue 2 years ago • 8 comments

Hello, thanks again for making such a fantastic library!

I'm here to report an issue with resizing Canvas with onLayout (or any other methods) on Android. I have console logged the LayoutChangeEvent and ensured that the component is only re-rendered once when the layout changes.

Android Demo

https://user-images.githubusercontent.com/6837599/167069421-7630350b-3350-4eae-9b3e-3d7d07bb3a3a.mp4

iOS Demo

https://user-images.githubusercontent.com/6837599/167069443-bbe1e2e3-5948-4212-a107-83d6365f855e.mov

Minimal Repro

import React, { useState } from 'react';
import { Button, Dimensions, View } from 'react-native';
import { Canvas, Group, RoundedRect, Shadow } from '@shopify/react-native-skia';

const { width } = Dimensions.get('window');
const PADDING = 32;
const WIDTH = width - PADDING;

const HomeContent = () => {
  const [change, setChange] = useState(false);
  const [height, setHeight] = useState(0);

  return (
    <View>
      <View style={{ height: height + PADDING }}>
        <Canvas style={{ flex: 1 }}>
          <Group>
            <Shadow dx={0} dy={10} blur={5} color="rgba(179, 197, 234, 0.6)" />
            <Shadow
              dx={2}
              dy={2}
              blur={4}
              color="rgba(238, 243, 255, 0.75)"
              inner
            />
            <Shadow
              dx={-4}
              dy={-4}
              blur={8}
              color="rgba(223, 230, 245, 1)"
              inner
            />
            <RoundedRect
              x={PADDING / 2}
              y={0}
              width={WIDTH}
              height={height}
              r={20}
              color="white"
            />
          </Group>
        </Canvas>
        {change ? (
          <View
            style={{ position: 'absolute', height: 100 }}
            onLayout={event => {
              setHeight(event.nativeEvent.layout.height);
            }}
          />
        ) : (
          <View
            style={{ position: 'absolute', height: 200 }}
            onLayout={event => {
              setHeight(event.nativeEvent.layout.height);
            }}
          />
        )}
      </View>
      <Button
        title="Change Layout"
        onPress={() => {
          setChange(!change);
        }}
      />
    </View>
  );
};

export default HomeContent;

thespacemanatee avatar May 06 '22 05:05 thespacemanatee

Can you use box-shadow instead: https://shopify.github.io/react-native-skia/docs/shapes/box? These inner shadows a notoriously slow especially on Android. They should only be used for complex shapes like paths or text.

wcandillon avatar May 07 '22 12:05 wcandillon

@wcandillon Hey William, thank you for the suggestion! I saw your Neumorphism video but forgot to use it here 😅. However, I tried it out and while it is slightly faster, I am still getting the same weird resizing effect on Android. Furthermore, there's an additional random graphical glitch now, and it is appearing outside of the Canvas that I am resizing as well:

photo_2022-05-07_22-38-17

thespacemanatee avatar May 07 '22 14:05 thespacemanatee

Hi @wcandillon, after updating the small top right button to use BoxShadow as well, the problem has been magnified on Android. It still works great on iOS though. Will there be any investigation done into this issue? Thank you very much!

https://user-images.githubusercontent.com/6837599/168550464-d6c92da9-1197-4aae-9107-2c61c42c46dd.mp4

thespacemanatee avatar May 16 '22 08:05 thespacemanatee

Should use the size as a Skia value (https://shopify.github.io/react-native-skia/docs/animations/values#canvas). I played with it a little bit and it seems to work nicely. And also to offer the performance level you need?

wcandillon avatar May 17 '22 04:05 wcandillon

@wcandillon Just tried this and it does sort of improve the situation on Android, as I no longer see inconsistent Canvas sizes and graphical glitches. However, it is still stuttery and less than ideal, but we can live with it for now.

However, we would like to report that using BoxShadow is still causing intense graphical glitches as seen above. Switching back to the regular Shadow is actually performing much better on Android for us.

thespacemanatee avatar May 17 '22 14:05 thespacemanatee

I would like to investigate this further. Is the code snippet under "Minimal Repro" still the source of truth there, or you have an updated minimal example I should be looking at? Let's get to the bottom of this.

wcandillon avatar May 18 '22 04:05 wcandillon

@wcandillon sorry for the late reply, yes the Minimal Repro is still valid here to reproduce jank, but if you would like to reproduce the intense graphical glitches that I experienced on Android as well, you just need to convert it to use BoxShadow. It might also make the issue more obvious if you have multiple Canvas rendering BoxShadow, as I am doing in my application. Thank you!

thespacemanatee avatar May 20 '22 00:05 thespacemanatee

@wcandillon on production, the jank is almost gone with Shadow. The only 'issue' left here is the visual glitch caused by animating BoxShadow maybe? But thank you for the fix shown above!

thespacemanatee avatar May 21 '22 03:05 thespacemanatee