如何让用户滚动非常大的 Skia 画布?

问题描述 投票:0回答:1

我有一个 React Native 应用程序,其中包含一个用户可以滚动浏览的图表。在本示例中,我仅使用水平行的圆圈。

为了绘制图表,我使用 Skia (https://shopify.github.io/react-native-skia/)

通常,我会创建一个足够大以包含整个图形的 Canvas 对象,并将其放入 ScrollView 中。

<ScrollView horizontal={true}>
    <Canvas style={{ width:5000, height:200 }}>
            <GetCircles />
    </Canvas>
</ScrollView>

问题是这个画布需要非常大,因为用户将能够滚动几个月的数据。因此,

width
属性可能需要太大,以至于创建的底层 OpenGL 纹理的大小对于硬件或 OpenGL 实现来说太大(或者我根据在其他环境(例如 Xamarin.Forms)中使用 Skia 的经验假设是这样).

在我的测试中,将宽度设置为 6300 左右会导致应用程序立即崩溃。

为了解决这个问题,我的想法是在 Skia Canvas 上覆盖一个 ScrollView。 ScrollView 可以包含一个大的 View 对象来匹配我打算模拟的大小,并且每当 ScrollView 滚动(其

onScroll
事件被触发)时,我都会设置一个状态,最终导致 Skia Canvas 被绘制为翻译,如以下片段所示:

const [scrollOffset, setScrollOffset] = useState(0);

function handleScroll(event: NativeSyntheticEvent<NativeScrollEvent>): void {
    setScrollOffset(event.nativeEvent.contentOffset.x);
}
...
<Canvas style={{ width:800, height:200 }}>
    <Group transform={[{translateX:-scrollOffset}]}>
        <GetCircles />
    </Group>
</Canvas>

<ScrollView horizontal={true} style={{height:100}} onScroll={handleScroll}>
    {/* These are added just for the sake of the example: */}
    <View style={{width:150, backgroundColor:"red"}} ></View>
    <View style={{width:150, backgroundColor:"blue"}} ></View>
    <View style={{width:150, backgroundColor:"red"}} ></View>
    <View style={{width:150, backgroundColor:"blue"}} ></View>
    <View style={{width:150, backgroundColor:"red"}} ></View>
    <View style={{width:150, backgroundColor:"blue"}} ></View>
    <View style={{width:150, backgroundColor:"red"}} ></View>
    <View style={{width:150, backgroundColor:"blue"}} ></View>
</ScrollView>

此方法的工作原理是让 Skia Canvas 根据滚动视图更新其变换,但正如您在下面的 GIF 中所看到的,存在一些相当严重的滞后。我已经在实际硬件上对此进行了测试,所以我知道问题不是来自我的屏幕镜像/录制。

如何解决这个问题?是否有一些替代方法可以在 React Native 中的 Skia 中渲染非常大的可滚动图形,并且不会导致崩溃或滞后?

react-native skia react-native-scrollview react-native-skia
1个回答
0
投票

您可以使用 React Native Reanimated 和 React Native Gesture Handler,而不是滚动视图。

onScroll
处理程序在JS线程上运行,这使得动画变得滞后。

如果您用

GestureDetector
包裹画布,并使用手势更新共享的重新激活值,然后将其传递给组,则应该修复滞后。使用共享值时,它们不是从处理程序通过 UI 线程返回到 UI 组件,而是直接传递给重新激活的组件。

要使用动画值对组变换进行动画处理,应使用

createAnimatedComponent

对其进行包装
const AnimatedGroup = Animated.createAnimatedComponent(Group)
const changeX = useSharedValue( 0 )
const pan = Gesture.Pan().onChange( ( e ) => {
    changeX.value = e.changeX
} ) 

...
<GestureDetector gesture={ pan }>
    <Canvas style={{ width:800, height:200 }}>
        <AnimatedGroup transform={[{translateX: changeX }]}>
            <GetCircles />
        </AnimatedGroup>
    </Canvas>
</GestureDetector>

希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.