1 | import { Platform } from '@unimodules/core';
|
2 | import * as React from 'react';
|
3 | import { findDOMNode } from 'react-dom';
|
4 | import { LayoutChangeEvent, PixelRatio, StyleSheet, View, ViewProps } from 'react-native';
|
5 | import createElement from 'react-native-web/dist/exports/createElement';
|
6 |
|
7 | function getElement(component) {
|
8 | try {
|
9 | return findDOMNode(component);
|
10 | } catch (e) {
|
11 | return component;
|
12 | }
|
13 | }
|
14 |
|
15 | function 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 |
|
22 | refProp.current = ref;
|
23 | }
|
24 | }
|
25 |
|
26 | const Canvas = React.forwardRef(
|
27 | (props: React.ComponentProps<typeof View>, ref: React.Ref<HTMLCanvasElement>) =>
|
28 | createElement('canvas', { ...props, ref })
|
29 | );
|
30 |
|
31 | const 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 |
|
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 |
|
107 | export default CanvasWrapper;
|