Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable new reconciler by default on iOS and Android #2865

Merged
merged 30 commits into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a72ecfb
:wrench:
wcandillon Jan 6, 2025
e09c15b
:wrench:
wcandillon Jan 7, 2025
9d85afb
:wrench:
wcandillon Jan 7, 2025
c46b7c6
:wrench:
wcandillon Jan 7, 2025
33e9ded
:wrench:
wcandillon Jan 7, 2025
dfb4d2d
:wrench:
wcandillon Jan 7, 2025
ff49105
:wrench:
wcandillon Jan 7, 2025
fab65f8
:wrench:
wcandillon Jan 7, 2025
4351a88
:wrench:
wcandillon Jan 7, 2025
a70d771
:wrench:
wcandillon Jan 7, 2025
73fa371
Merge branch 'main' into new-sksg
wcandillon Jan 7, 2025
f7441fe
remove bogus change
wcandillon Jan 7, 2025
14aa9e0
Merge branch 'new-sksg' of https://github.com/Shopify/react-native-sk…
wcandillon Jan 8, 2025
02fa18b
:green_heart:
wcandillon Jan 8, 2025
f29f5b1
:green_heart:
wcandillon Jan 8, 2025
1622cbc
:green_heart:
wcandillon Jan 8, 2025
1669ec1
Add deprecation warning
wcandillon Jan 8, 2025
0e917d2
:green_heart:
wcandillon Jan 8, 2025
3451c7c
Merge branch 'main' into new-sksg
wcandillon Jan 9, 2025
28bcb63
Merge branch 'main' into new-sksg
wcandillon Jan 10, 2025
d7784ef
Merge branch 'main' into new-sksg
wcandillon Jan 10, 2025
81f2daa
Merge branch 'main' into new-sksg
wcandillon Jan 11, 2025
829203b
:wrench: (#2884)
wcandillon Jan 11, 2025
fde723c
:green_heart:
wcandillon Jan 11, 2025
d2e7994
:lipstick:
wcandillon Jan 11, 2025
e713595
Merge branch 'main' into new-sksg
wcandillon Jan 12, 2025
55ac933
remove bogus change
wcandillon Jan 12, 2025
b011896
remove bogus change
wcandillon Jan 12, 2025
19a2f1e
:wrench:
wcandillon Jan 12, 2025
d34533d
:wrench:
wcandillon Jan 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/paper/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"react-native-reanimated": "^3.16.5",
"react-native-safe-area-context": "^4.10.9",
"react-native-screens": "^4.3.0",
"react-native-svg": "^15.9.0",
"react-native-svg": "^15.10.1",
"react-native-wgpu": "0.1.19",
"typescript": "^5.2.2"
},
Expand Down
106 changes: 106 additions & 0 deletions apps/paper/src/Examples/Breathe/TestReconciller.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React, { useCallback, useMemo, useState } from "react";
import { Button, StyleSheet, useWindowDimensions, View } from "react-native";
import {
BlurMask,
vec,
Canvas,
Circle,
Fill,
Group,
polar2Canvas,
mix,
} from "@shopify/react-native-skia";
import type { SharedValue } from "react-native-reanimated";
import { useDerivedValue } from "react-native-reanimated";

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

const c1 = "#61bea2";
const c2 = "#529ca0";

interface RingProps {
index: number;
progress: SharedValue<number>;
total: number;
}

const Ring = ({ index, progress, total }: RingProps) => {
const { width, height } = useWindowDimensions();
const R = width / 4;
const center = useMemo(
() => vec(width / 2, height / 2 - 64),
[height, width]
);

const theta = (index * (2 * Math.PI)) / total;
const transform = useDerivedValue(() => {
const { x, y } = polar2Canvas(
{ theta, radius: progress.value * R },
{ x: 0, y: 0 }
);
const scale = mix(progress.value, 0.3, 1);
return [{ translateX: x }, { translateY: y }, { scale }];
});

return (
<Circle
c={center}
r={R}
color={index % 2 ? c1 : c2}
origin={center}
transform={transform}
/>
);
};

export const Breathe = () => {
const [rings, setRings] = useState(12);
const { width, height } = useWindowDimensions();
const center = useMemo(
() => vec(width / 2, height / 2 - 64),
[height, width]
);

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

const transform = useDerivedValue(() => [
{ rotate: mix(progress.value, -Math.PI, 0) },
]);

const add = useCallback(() => {
setRings((r) => r + 1);
}, []);
const remove = useCallback(() => {
setRings((r) => Math.max(1, r - 1));
}, []);
return (
<View style={{ flex: 1 }}>
<View>
<Button onPress={add} title="add" />
<Button onPress={remove} title="remove" />
</View>
<Canvas style={styles.container} opaque>
<Fill color="rgb(36,43,56)" />
<Group origin={center} transform={transform} blendMode="screen">
<BlurMask style="solid" blur={40} />
{new Array(rings).fill(0).map((_, index) => {
return (
<Ring
key={index}
index={index}
progress={progress}
total={rings}
/>
);
})}
</Group>
</Canvas>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
},
});
2 changes: 1 addition & 1 deletion packages/skia/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import "./skia/NativeSetup";
export { JsiSkImage } from "./skia/web/JsiSkImage";
export * from "./renderer";
export * from "./renderer/CanvasOld";
export * from "./renderer/Canvas";
export * from "./renderer/Canvas2";
export * from "./renderer/Offscreen";
export * from "./views";
export * from "./skia";
Expand Down
158 changes: 80 additions & 78 deletions packages/skia/src/renderer/Canvas.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
import React, {
useEffect,
import {
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
forwardRef,
useRef,
} from "react";
import type {
RefObject,
ReactNode,
MutableRefObject,
ForwardedRef,
FunctionComponent,
} from "react";
import type { LayoutChangeEvent } from "react-native";
import type { LayoutChangeEvent, ViewProps } from "react-native";
import type { SharedValue } from "react-native-reanimated";

import { SkiaDomView } from "../views";
import { SkiaViewNativeId } from "../views/SkiaViewNativeId";
import SkiaPictureViewNativeComponent from "../specs/SkiaPictureViewNativeComponent";
import type { SkRect, SkSize } from "../skia/types";
import { SkiaSGRoot } from "../sksg/Reconciler";
import { Skia } from "../skia";
import type { SkiaBaseViewProps } from "../views";

import { SkiaRoot } from "./Reconciler";

export const useCanvasRef = () => useRef<SkiaDomView>(null);

export interface CanvasProps extends SkiaBaseViewProps {
ref?: RefObject<SkiaDomView>;
children: ReactNode;
mode?: "default" | "continuous";
}
const NativeSkiaPictureView = SkiaPictureViewNativeComponent;

// TODO: no need to go through the JS thread for this
const useOnSizeEvent = (
resultValue: SkiaBaseViewProps["onSize"],
onLayout?: (event: LayoutChangeEvent) => void
Expand All @@ -46,81 +38,91 @@ const useOnSizeEvent = (
);
};

export const Canvas = forwardRef<SkiaDomView, CanvasProps>(
export interface CanvasProps extends ViewProps {
debug?: boolean;
opaque?: boolean;
onSize?: SharedValue<SkSize>;
mode?: "continuous" | "default";
}

export const Canvas = forwardRef(
(
{
children,
style,
mode,
debug,
mode = "default",
onSize: _onSize,
opaque,
children,
onSize,
onLayout: _onLayout,
...props
},
forwardedRef
...viewProps
}: CanvasProps,
ref
) => {
const onLayout = useOnSizeEvent(_onSize, _onLayout);
const innerRef = useCanvasRef();
const ref = useCombinedRefs(forwardedRef, innerRef);
const redraw = useCallback(() => {
innerRef.current?.redraw();
}, [innerRef]);
const getNativeId = useCallback(() => {
const id = innerRef.current?.nativeId ?? -1;
return id;
}, [innerRef]);
const rafId = useRef<number | null>(null);
const onLayout = useOnSizeEvent(onSize, _onLayout);
// Native ID
const nativeId = useMemo(() => {
return SkiaViewNativeId.current++;
}, []);

const root = useMemo(
() => new SkiaRoot(redraw, getNativeId),
[redraw, getNativeId]
);
// Root
const root = useMemo(() => new SkiaSGRoot(Skia, nativeId), [nativeId]);

// Render effect
// Render effects
useEffect(() => {
root.render(children);
}, [children, root, redraw]);
}, [children, root]);

useEffect(() => {
return () => {
root.unmount();
};
}, [root]);

const requestRedraw = useCallback(() => {
rafId.current = requestAnimationFrame(() => {
root.render(children);
if (mode === "continuous") {
requestRedraw();
}
});
}, [children, mode, root]);

useEffect(() => {
if (mode === "continuous") {
console.warn("The `mode` property in `Canvas` is deprecated.");
requestRedraw();
}
return () => {
if (rafId.current !== null) {
cancelAnimationFrame(rafId.current);
}
};
}, [mode, requestRedraw]);
// Component methods
useImperativeHandle(ref, () => ({
makeImageSnapshot: (rect?: SkRect) => {
return SkiaViewApi.makeImageSnapshot(nativeId, rect);
},
makeImageSnapshotAsync: (rect?: SkRect) => {
return SkiaViewApi.makeImageSnapshotAsync(nativeId, rect);
},
redraw: () => {
SkiaViewApi.requestRedraw(nativeId);
},
getNativeId: () => {
return nativeId;
},
}));
return (
<SkiaDomView
ref={ref}
style={style}
root={root.dom}
onLayout={onLayout}
<NativeSkiaPictureView
collapsable={false}
nativeID={`${nativeId}`}
debug={debug}
mode={mode}
{...props}
opaque={opaque}
onLayout={onLayout}
{...viewProps}
/>
);
}
) as FunctionComponent<CanvasProps & React.RefAttributes<SkiaDomView>>;

/**
* Combines a list of refs into a single ref. This can be used to provide
* both a forwarded ref and an internal ref keeping the same functionality
* on both of the refs.
* @param refs Array of refs to combine
* @returns A single ref that can be used in a ref prop.
*/
const useCombinedRefs = <T,>(
...refs: Array<MutableRefObject<T> | ForwardedRef<T>>
) => {
const targetRef = React.useRef<T>(null);
React.useEffect(() => {
refs.forEach((ref) => {
if (ref) {
if (typeof ref === "function") {
ref(targetRef.current);
} else {
ref.current = targetRef.current;
}
}
});
}, [refs]);
return targetRef;
};
);
Loading
Loading