Shopify / react-native-skia

High-performance React Native Graphics using Skia

Home Page:https://shopify.github.io/react-native-skia

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Android rendering artefact not working correctly

MorneBitcube opened this issue · comments

Description

I'm using a custom component to render an SVG path with rounded caps. It works fine on iOS. But on Android, it causes a weird render artefact. See image below

1715247013733

1715247090433

This only occurs when 4 or more SVGs in separate canvases are stacked on top of each other

Version

1.2.0

Steps to reproduce

  • Add the given code to react-native code base and build the app on Android.

Snack, code example, screenshot, or link to a repository

  • The ring component
import * as React from "react"
import { useEffect } from "react"
import { observer } from "mobx-react-lite"
import { Easing, useSharedValue, withDelay, withTiming } from "react-native-reanimated"
import { Canvas, Color, Path, Skia } from "@shopify/react-native-skia"
import { View } from "react-native"

export type RingSizePreset = keyof typeof $sizePresets

export interface RingProgressProps {
  progress: number
  size: RingSizePreset
  color: Color
  canvasSize: number
}

function degToRad(degrees: number): number {
  return degrees * (Math.PI / 180)
}

export const RingProgress = observer(function RingProgress(props: RingProgressProps) {
  const { progress, size, color, canvasSize } = props

  const { width, startAngleDeg, endAngleDeg } = $sizePresets[size]

  const progressValue = useSharedValue(0)
  useEffect(() => {
    progressValue.value = withDelay(
      100,
      withTiming(progress, { duration: 700, easing: Easing.elastic(1) }),
    )
  }, [progress])

  const strokeWidth = 25
  const center = canvasSize / 2
  const r = (width + canvasSize - strokeWidth * 2) / 2
  const startAngle = degToRad(startAngleDeg)
  const endAngle = degToRad(endAngleDeg)

  const x1 = center - r * Math.cos(endAngle)
  const y1 = -r * Math.sin(endAngle) + center
  const x2 = center - r * Math.cos(startAngle)
  const y2 = -r * Math.sin(startAngle) + center

  const backgroundPath = `M ${x1} ${y1} A ${r} ${r} 0 1 0 ${x2} ${y2}`
  const foregroundPath = `M ${x2} ${y2} A ${r} ${r} 0 1 1 ${x1} ${y1}`

  const skiaBackgroundPath = Skia.Path.MakeFromSVGString(backgroundPath)
  const skiaForegroundPath = Skia.Path.MakeFromSVGString(foregroundPath)

  if (!skiaBackgroundPath || !skiaForegroundPath) {
    return <View />
  }
  return (
    <Canvas
      style={{
        width: canvasSize,
        height: canvasSize,
        position: "absolute",
      }}
    >
      <Path
        path={skiaBackgroundPath}
        strokeCap={"round"}
        strokeWidth={strokeWidth}
        style={"stroke"}
        color={color}
        opacity={0.1}
      />

      <Path
        path={skiaForegroundPath}
        strokeCap={"round"}
        strokeWidth={strokeWidth}
        start={0}
        end={progressValue}
        style={"stroke"}
        color={color}
      />
    </Canvas>
  )
})

const $sizePresets = {
  extraSmall: {
    width: -180,
    startAngleDeg: -70,
    endAngleDeg: 250,
  },
  small: {
    width: -120,
    startAngleDeg: -72,
    endAngleDeg: 252,
  },
medium: { width: -60, startAngleDeg: -74, endAngleDeg: 254 },
  large: { width: 0, startAngleDeg: -76, endAngleDeg: 256 },
}

  • Implement the component for use
 export const ProgressRings = () => {

const dataList = [
  { 
    id: 1,
    progress: 0,
    name: "FirstRing",
   size: 'extraSmall',
   color: 'blue'
  },
  {
    id: 2,
    progress: 0.5,
    name: "SecondRing",
    size: 'small',
    color: 'red'
  },
  {
    id: 3,
    progress: 0.5,
    name: "Third",
    size: 'medium',
    color: 'red'
  },
  {
    id: 4,
    progress: 0.5,
    name: "Fourth",
    size: 'large',
    color: 'red'
  },
]

  const $canvasContainer: ViewStyle = {
    width: 400,
    height: 400,
    alignItems: "center",
    justifyContent: "center",
  }

  return (
    <View style={$canvasContainer}>
      {dataList.map((item) => (
        <RingProgress
          color={item.color}
          size={item.size}
          progress={item.progress}
          canvasSize={400}
          key={item.id}
        />
      ))}
    </View>
  )
})

@wcandillon As requested here is a single file
rings.tsx.zip

@wcandillon have you managed to reproduce this issue yet? I have tried to fix this on my side but was not successful.