Skip to content

Commit 2d9811d

Browse files
authored
fix(🐛): fix regression with onSize on RN Web (#3574)
1 parent 5dabc23 commit 2d9811d

File tree

3 files changed

+120
-24
lines changed

3 files changed

+120
-24
lines changed

apps/example/src/Examples/API/UseCanvas.tsx

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,18 @@ import {
99
polar2Canvas,
1010
vec,
1111
} from "@shopify/react-native-skia";
12-
import React, { useEffect, useRef } from "react";
13-
import { View, Animated } from "react-native";
12+
import React, { useEffect } from "react";
13+
import { View } from "react-native";
1414
import { useNavigation } from "@react-navigation/native";
1515
import { useContextBridge } from "its-fine";
1616
import type { SharedValue } from "react-native-reanimated";
17-
import { useDerivedValue, useSharedValue } from "react-native-reanimated";
17+
import Animated, {
18+
useDerivedValue,
19+
useSharedValue,
20+
useAnimatedStyle,
21+
withRepeat,
22+
withTiming,
23+
} from "react-native-reanimated";
1824

1925
import { useLoop } from "../../components/Animations";
2026

@@ -58,9 +64,10 @@ const Ring = ({ index, progress, size }: RingProps) => {
5864
};
5965

6066
const BreatheDemo = ({ size }: SizeProps) => {
61-
const center = useDerivedValue(() =>
62-
vec(size.value.width / 2, size.value.height / 2 - 64)
63-
);
67+
const center = useDerivedValue(() => {
68+
console.log(size.value);
69+
return vec(size.value.width / 2, size.value.height / 2 - 64);
70+
});
6471

6572
const progress = useLoop({ duration: 3000 });
6673

@@ -98,25 +105,26 @@ const MyComp = ({ size }: SizeProps) => {
98105
export const UseCanvas = () => {
99106
const Bridge = useContextBridge();
100107
const size = useSharedValue({ width: 0, height: 0 });
101-
const height = useRef(new Animated.Value(0));
108+
const height = useSharedValue(0);
109+
102110
useEffect(() => {
103-
Animated.loop(
104-
Animated.timing(height.current, {
105-
toValue: 500,
106-
duration: 4000,
107-
useNativeDriver: false,
108-
})
109-
).start();
110-
}, []);
111+
height.value = withRepeat(withTiming(500, { duration: 4000 }), -1, true);
112+
}, [height]);
113+
114+
const animatedStyle = useAnimatedStyle(() => ({
115+
height: height.value,
116+
}));
111117

112118
return (
113119
<View style={{ flex: 1 }}>
114-
<Canvas style={{ flex: 1 }} onSize={size}>
115-
<Bridge>
116-
<MyComp size={size} />
117-
</Bridge>
118-
</Canvas>
119-
<Animated.View style={{ height: height.current }} />
120+
<View style={{ flex: 1 }}>
121+
<Canvas style={{ flex: 1 }} onSize={size}>
122+
<Bridge>
123+
<MyComp size={size} />
124+
</Bridge>
125+
</Canvas>
126+
</View>
127+
<Animated.View style={animatedStyle} />
120128
</View>
121129
);
122130
};

packages/skia/src/renderer/Canvas.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,17 @@ export const Canvas = ({
103103
useReanimatedFrame(() => {
104104
"worklet";
105105
if (onSize && measure) {
106-
const result = measure(viewRef as AnimatedRef<View>);
106+
const result =
107+
// eslint-disable-next-line no-nested-ternary
108+
Platform.OS === "web"
109+
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
110+
// @ts-expect-error
111+
viewRef.current?.canvasRef
112+
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
113+
// @ts-expect-error
114+
measure(viewRef.current.canvasRef)
115+
: { width: 0, height: 0 }
116+
: measure(viewRef as AnimatedRef<View>);
107117
if (result) {
108118
const { width, height } = result;
109119
if (onSize.value.width !== width || onSize.value.height !== height) {

packages/skia/src/views/SkiaPictureView.web.tsx

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,19 @@ export interface SkiaPictureViewHandle {
224224
getSize(): { width: number; height: number };
225225
redraw(): void;
226226
makeImageSnapshot(rect?: SkRect): SkImage | null;
227+
measure(
228+
callback: (
229+
x: number,
230+
y: number,
231+
width: number,
232+
height: number,
233+
pageX: number,
234+
pageY: number
235+
) => void
236+
): void;
237+
measureInWindow(
238+
callback: (x: number, y: number, width: number, height: number) => void
239+
): void;
227240
}
228241

229242
export const SkiaPictureView = forwardRef<
@@ -264,6 +277,56 @@ export const SkiaPictureView = forwardRef<
264277
return null;
265278
}, []);
266279

280+
const measure = useCallback(
281+
(
282+
callback: (
283+
x: number,
284+
y: number,
285+
width: number,
286+
height: number,
287+
pageX: number,
288+
pageY: number
289+
) => void
290+
) => {
291+
if (canvasRef.current) {
292+
const rect = canvasRef.current.getBoundingClientRect();
293+
const parentElement = canvasRef.current.offsetParent as HTMLElement;
294+
const parentRect = parentElement?.getBoundingClientRect() || {
295+
left: 0,
296+
top: 0,
297+
};
298+
299+
// x, y are relative to the parent
300+
const x = rect.left - parentRect.left;
301+
const y = rect.top - parentRect.top;
302+
303+
// pageX, pageY are absolute screen coordinates
304+
const pageX = rect.left + window.scrollX;
305+
const pageY = rect.top + window.scrollY;
306+
307+
callback(x, y, rect.width, rect.height, pageX, pageY);
308+
}
309+
},
310+
[]
311+
);
312+
313+
const measureInWindow = useCallback(
314+
(
315+
callback: (x: number, y: number, width: number, height: number) => void
316+
) => {
317+
if (canvasRef.current) {
318+
const rect = canvasRef.current.getBoundingClientRect();
319+
320+
// x, y are the absolute coordinates in the window
321+
const x = rect.left;
322+
const y = rect.top;
323+
324+
callback(x, y, rect.width, rect.height);
325+
}
326+
},
327+
[]
328+
);
329+
267330
const tick = useCallback(() => {
268331
if (redrawRequestsRef.current > 0) {
269332
redrawRequestsRef.current = 0;
@@ -300,8 +363,13 @@ export const SkiaPictureView = forwardRef<
300363
getSize,
301364
redraw,
302365
makeImageSnapshot,
366+
measure,
367+
measureInWindow,
368+
get canvasRef() {
369+
return () => canvasRef.current;
370+
},
303371
}),
304-
[setPicture, getSize, redraw, makeImageSnapshot]
372+
[setPicture, getSize, redraw, makeImageSnapshot, measure, measureInWindow]
305373
);
306374

307375
useEffect(() => {
@@ -311,8 +379,18 @@ export const SkiaPictureView = forwardRef<
311379
getSize,
312380
redraw,
313381
makeImageSnapshot,
382+
measure,
383+
measureInWindow,
314384
} as SkiaPictureViewHandle);
315-
}, [setPicture, getSize, redraw, makeImageSnapshot, props.nativeID]);
385+
}, [
386+
setPicture,
387+
getSize,
388+
redraw,
389+
makeImageSnapshot,
390+
measure,
391+
measureInWindow,
392+
props.nativeID,
393+
]);
316394

317395
useEffect(() => {
318396
if (props.picture) {

0 commit comments

Comments
 (0)