UNPKG

2.88 kBTypeScriptView Raw
1import { Platform } from '@unimodules/core';
2import * as React from 'react';
3import { findDOMNode } from 'react-dom';
4import { LayoutChangeEvent, PixelRatio, StyleSheet, View, ViewProps } from 'react-native';
5import createElement from 'react-native-web/dist/exports/createElement';
6
7function getElement(component) {
8 try {
9 return findDOMNode(component);
10 } catch (e) {
11 return component;
12 }
13}
14
15function setRef<T>(refProp: React.Ref<T>, ref: T | null) {
16 if (!refProp) return;
17
18 if (typeof refProp === 'function') {
19 refProp(ref);
20 } else if ('current' in refProp) {
21 // @ts-ignore
22 refProp.current = ref;
23 }
24}
25
26const Canvas = React.forwardRef(
27 (props: React.ComponentProps<typeof View>, ref: React.Ref<HTMLCanvasElement>) =>
28 createElement('canvas', { ...props, ref })
29);
30
31const CanvasWrapper: React.FunctionComponent<ViewProps & {
32 canvasRef: React.Ref<HTMLCanvasElement>;
33}> = ({ pointerEvents, children, ...props }) => {
34 const [size, setSize] = React.useState<{ width: number; height: number } | null>(null);
35
36 const ref = React.useRef<View>(null);
37 const _canvasRef = React.useRef<HTMLCanvasElement>(null);
38
39 function updateCanvasSize(): void {
40 const canvas = _canvasRef.current;
41 // eslint-disable-next-line no-undef
42 if (typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement) {
43 const size = getSize();
44 const scale = PixelRatio.get();
45
46 canvas.style.width = `${size.width}px`;
47 canvas.style.height = `${size.height}px`;
48
49 canvas.width = size.width * scale;
50 canvas.height = size.height * scale;
51 }
52 }
53
54 function getSize(): { width: number; height: number } {
55 if (size) {
56 return size;
57 } else if (!ref.current || !Platform.isDOMAvailable) {
58 return { width: 0, height: 0 };
59 }
60 const element = getElement(ref.current);
61 const { offsetWidth: width = 0, offsetHeight: height = 0 } = element;
62 return { width, height };
63 }
64
65 function onLayout(event: LayoutChangeEvent): void {
66 const {
67 nativeEvent: {
68 layout: { width, height },
69 },
70 } = event;
71
72 if (width !== size?.width || height !== size.height) {
73 setSize({ width, height });
74
75 if (props.onLayout) {
76 props.onLayout(event);
77 }
78 }
79 }
80
81 React.useEffect(() => {
82 if (ref.current != null) {
83 setSize(getSize());
84 }
85 }, [ref]);
86
87 React.useEffect(() => {
88 updateCanvasSize();
89 }, [size]);
90
91 React.useEffect(() => {
92 const canvas = _canvasRef.current;
93 if (canvas) {
94 updateCanvasSize();
95 }
96 setRef(props.canvasRef, canvas);
97 }, [_canvasRef]);
98
99 return (
100 <View {...props} pointerEvents="box-none" ref={ref} onLayout={onLayout}>
101 <Canvas ref={_canvasRef} pointerEvents={pointerEvents} style={StyleSheet.absoluteFill} />
102 {children}
103 </View>
104 );
105};
106
107export default CanvasWrapper;