diff --git a/docs/docs/group.md b/docs/docs/group.md index abeac66c49..5bfa06c23a 100644 --- a/docs/docs/group.md +++ b/docs/docs/group.md @@ -240,21 +240,22 @@ You can use it to apply effects. This is particularly useful to build effects that need to be applied to a group of elements and not one in particular. ```tsx twoslash -import {Canvas, Group, Circle, Blur, Paint, ColorMatrix, usePaintRef} from "@shopify/react-native-skia"; +import {Canvas, Group, Circle, Blur, Paint, ColorMatrix} from "@shopify/react-native-skia"; const Clip = () => { - const paint = usePaintRef(); return ( - - - - - + + + + } + > { Finally, we can assign a ref to a Paint component for later use. ```tsx twoslash -import {Canvas, Circle, Paint, usePaintRef} from "@shopify/react-native-skia"; - +import {Canvas, Circle, Paint, Skia} from "@shopify/react-native-skia"; const width = 256; const height = 256; const r = width / 2; +const paint = Skia.Paint(); +paint.setColor(Skia.Color("lightblue")); export const PaintDemo = () => { - const paint = usePaintRef(); return ( - - {/* We can assign the ref to any shape. This will be handy in advanced use-case */} - + ); }; ``` - - + \ No newline at end of file diff --git a/example/src/Examples/Gooey/Gooey.tsx b/example/src/Examples/Gooey/Gooey.tsx index 7eff614177..b24a6ccd5f 100644 --- a/example/src/Examples/Gooey/Gooey.tsx +++ b/example/src/Examples/Gooey/Gooey.tsx @@ -11,7 +11,6 @@ import { rotate, mixVector, Paint, - usePaintRef, Circle, Blur, ColorMatrix, @@ -66,7 +65,6 @@ export const Gooey = () => { [c] ); - const paint = usePaintRef(); const [toggled, setToggled] = useState(false); const onTouch = useTouchHandler({ onEnd: () => setToggled((t) => !t) }); const progress = useSpring(toggled ? 1 : 0, Spring.Config.Gentle); @@ -84,16 +82,19 @@ export const Gooey = () => { return ( - - - - - + + + + + } + > {icons.map((_, i) => ( diff --git a/example/src/Examples/Severance/CRT.tsx b/example/src/Examples/Severance/CRT.tsx index e9db828d8d..45c30935f9 100644 --- a/example/src/Examples/Severance/CRT.tsx +++ b/example/src/Examples/Severance/CRT.tsx @@ -2,7 +2,6 @@ import { Group, Skia, RuntimeShader, - usePaintRef, Paint, vec, } from "@shopify/react-native-skia"; @@ -58,17 +57,19 @@ interface CRTProps { } export const CRT = ({ children }: CRTProps) => { - const paint = usePaintRef(); const { width, height } = useWindowDimensions(); return ( - <> - - - - {children} - + + + + } + > + {children} + ); }; diff --git a/package/src/dom/nodes/JsiSkDOM.ts b/package/src/dom/nodes/JsiSkDOM.ts index ed5dc6b616..5cd12c3ef7 100644 --- a/package/src/dom/nodes/JsiSkDOM.ts +++ b/package/src/dom/nodes/JsiSkDOM.ts @@ -121,10 +121,15 @@ import { MorphologyImageFilterNode } from "./paint/ImageFilters"; import { GroupNode } from "./GroupNode"; import { PaintNode } from "./PaintNode"; import type { NodeContext } from "./Node"; +import { LayerNode } from "./LayerNode"; export class JsiSkDOM implements SkDOM { constructor(private ctx: NodeContext) {} + Layer(props?: ChildrenProps) { + return new LayerNode(this.ctx, props ?? {}); + } + Group(props?: GroupProps) { return new GroupNode(this.ctx, props ?? {}); } diff --git a/package/src/dom/nodes/LayerNode.ts b/package/src/dom/nodes/LayerNode.ts new file mode 100644 index 0000000000..537cb30beb --- /dev/null +++ b/package/src/dom/nodes/LayerNode.ts @@ -0,0 +1,35 @@ +import type { DeclarationNode, DrawingContext, Node } from "../types"; +import { NodeType } from "../types"; +import type { ChildrenProps } from "../types/Common"; +import type { SkPaint } from "../../skia"; + +import { JsiRenderNode } from "./RenderNode"; +import type { NodeContext } from "./Node"; +import { JsiDeclarationNode } from "./Node"; + +const isLayer = ( + node: Node +): node is DeclarationNode => + node instanceof JsiDeclarationNode && node.isPaint(); + +export class LayerNode extends JsiRenderNode { + constructor(ctx: NodeContext, props: ChildrenProps) { + super(ctx, NodeType.Layer, props); + } + + renderNode(ctx: DrawingContext): void { + const [layer, ...children] = this.children(); + if (isLayer(layer)) { + const paint = layer.materialize() as SkPaint; + ctx.canvas.saveLayer(paint); + } + children.map((child) => { + if (child instanceof JsiRenderNode) { + child.render(ctx); + } + }); + if (isLayer(layer)) { + ctx.canvas.restore(); + } + } +} diff --git a/package/src/dom/types/NodeType.ts b/package/src/dom/types/NodeType.ts index 9f2c597eef..0b5295aec2 100644 --- a/package/src/dom/types/NodeType.ts +++ b/package/src/dom/types/NodeType.ts @@ -1,5 +1,6 @@ export const enum NodeType { // Shaders + Layer = "skLayer", Shader = "skShader", ImageShader = "skImageShader", ColorShader = "skColorShader", diff --git a/package/src/dom/types/SkDOM.ts b/package/src/dom/types/SkDOM.ts index 319f7478ae..245ff639ca 100644 --- a/package/src/dom/types/SkDOM.ts +++ b/package/src/dom/types/SkDOM.ts @@ -76,6 +76,7 @@ type NullablePathEffectNode

= DeclarationNode; type DrawingNode

= RenderNode

; export interface SkDOM { + Layer(props?: ChildrenProps): RenderNode; Group(props?: GroupProps): RenderNode; Paint(props: PaintProps): DeclarationNode; diff --git a/package/src/renderer/HostComponents.ts b/package/src/renderer/HostComponents.ts index 1c6f0496b2..5eb30e268a 100644 --- a/package/src/renderer/HostComponents.ts +++ b/package/src/renderer/HostComponents.ts @@ -66,6 +66,7 @@ declare global { namespace JSX { interface IntrinsicElements { skGroup: SkiaProps; + skLayer: SkiaProps; skPaint: SkiaProps & { ref: ForwardedRef }; // Drawings @@ -146,6 +147,8 @@ export const createNode = ( ) => { const { Sk } = container; switch (type) { + case NodeType.Layer: + return Sk.Layer(props); case NodeType.Group: return Sk.Group(props); case NodeType.Paint: diff --git a/package/src/renderer/__tests__/documentation/Group.spec.tsx b/package/src/renderer/__tests__/documentation/Group.spec.tsx index 27468b50c9..c703a2d53c 100644 --- a/package/src/renderer/__tests__/documentation/Group.spec.tsx +++ b/package/src/renderer/__tests__/documentation/Group.spec.tsx @@ -26,21 +26,23 @@ const padding = 48; const r = 24; const TestRasterization = () => { - const { usePaintRef, vec } = importSkia(); - const paint = usePaintRef(); + const { vec } = importSkia(); const c = vec(width / 2, height / 2); const radius = c.x * 0.95; return ( <> - - - - - + + + + + } + > diff --git a/package/src/renderer/__tests__/documentation/paint/Overview.spec.tsx b/package/src/renderer/__tests__/documentation/paint/Overview.spec.tsx index 5bb152db93..599926f920 100644 --- a/package/src/renderer/__tests__/documentation/paint/Overview.spec.tsx +++ b/package/src/renderer/__tests__/documentation/paint/Overview.spec.tsx @@ -11,16 +11,11 @@ import { Circle, Group, Paint } from "../../../components"; import { docPath, processResult } from "../../../../__tests__/setup"; const TestPaintAssignment = () => { - const { usePaintRef } = importSkia(); + const { Skia } = importSkia(); + const paint = Skia.Paint(); + paint.setColor(Skia.Color("lightblue")); const r = width / 2; - const paint = usePaintRef(); - return ( - <> - - {/* We can assign the ref to any shape. This will be handy in advanced use-case */} - - - ); + return ; }; describe("Paint", () => { diff --git a/package/src/renderer/components/Group.tsx b/package/src/renderer/components/Group.tsx index 48012363ef..0461ede111 100644 --- a/package/src/renderer/components/Group.tsx +++ b/package/src/renderer/components/Group.tsx @@ -1,8 +1,21 @@ -import React from "react"; +import React, { isValidElement } from "react"; import type { SkiaProps } from "../processors"; import type { GroupProps } from "../../dom/types"; +import type { ChildrenProps } from "../../dom/types/Common"; -export const Group = (props: SkiaProps) => { - return ; +export interface PublicGroupProps extends Omit { + layer?: GroupProps["layer"] | ChildrenProps["children"]; +} + +export const Group = ({ layer, ...props }: SkiaProps) => { + if (isValidElement(layer) && typeof layer === "object") { + return ( + + {layer} + + + ); + } + return ; }; diff --git a/package/src/renderer/components/Paint.tsx b/package/src/renderer/components/Paint.tsx index a8bbb4177d..d3b8381b44 100644 --- a/package/src/renderer/components/Paint.tsx +++ b/package/src/renderer/components/Paint.tsx @@ -1,10 +1,16 @@ +/* eslint-disable max-len */ import React, { useRef, forwardRef } from "react"; import type { SkiaProps } from "../processors"; import type { DrawingNodeProps } from "../../dom/types"; import type { PaintNode } from "../../dom/nodes/PaintNode"; -export const usePaintRef = () => useRef(null); +export const usePaintRef = () => { + console.log(`usePaintRef() is now deprecated. +If you are using the layer property, simply pass the component directly: https://shopify.github.io/react-native-skia/docs/group#layer-effects. +If you are using the paint property, please the following paint properties directly: https://shopify.github.io/react-native-skia/docs/paint/overview`); + return useRef(null); +}; export const Paint = forwardRef>( (props, ref) => {