@shopify/react-native-skia
Version:
High-performance React Native Graphics using Skia
111 lines (99 loc) • 2.75 kB
text/typescript
import React, { useEffect, useRef } from "react";
import { StyleSheet } from "react-native";
import type { Int32 } from "react-native/Libraries/Types/CodegenTypes";
import type { ViewProps } from "react-native";
import { contextIdToId } from "./utils";
export interface NativeProps extends ViewProps {
contextId: Int32;
transparent: boolean;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function debounce<T extends (...args: any[]) => void>(
func: T,
wait: number,
immediate = false
) {
let timeout: ReturnType<typeof setTimeout> | undefined;
return function debounced(
this: ThisParameterType<T>,
...args: Parameters<T>
) {
const context = this;
const callNow = immediate && !timeout;
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
timeout = undefined;
if (!immediate) {
func.apply(context, args);
}
}, wait);
if (callNow) {
func.apply(context, args);
}
};
}
function resizeCanvas(canvas: HTMLCanvasElement | null) {
if (!canvas) {
return;
}
const dpr = window.devicePixelRatio || 1;
const { height, width } = canvas.getBoundingClientRect();
canvas.setAttribute("height", (height * dpr).toString());
canvas.setAttribute("width", (width * dpr).toString());
}
// eslint-disable-next-line import/no-default-export
export default function WebGPUViewNativeComponent(
props: NativeProps
): React.JSX.Element {
const { contextId, style, transparent, ...rest } = props;
const canvasElm = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const onResize = debounce(() => resizeCanvas(canvasElm.current), 100);
window.addEventListener("resize", onResize);
return () => {
window.removeEventListener("resize", onResize);
};
}, []);
return React.createElement("canvas", {
...rest,
id: contextIdToId(contextId),
style: {
...styles.view,
...styles.flex1,
...(transparent === false ? { backgroundColor: "white" } : {}),
...(typeof style === "object" ? style : {}),
},
ref: (ref: HTMLCanvasElement) => {
canvasElm.current = ref;
if (ref) {
resizeCanvas(ref);
}
},
});
}
const styles = StyleSheet.create({
flex1: {
flex: 1,
},
view: {
alignItems: "stretch",
backgroundColor: "transparent",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
border: "0 solid black",
boxSizing: "border-box",
display: "flex",
flexBasis: "auto",
flexDirection: "column",
flexShrink: 0,
listStyle: "none",
margin: 0,
minHeight: 0,
minWidth: 0,
padding: 0,
position: "relative",
zIndex: 0,
},
});