@shopify/react-native-skia 0.1.143 → 0.1.145

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. package/cpp/api/JsiSkPaint.h +6 -0
  2. package/lib/commonjs/renderer/Canvas.js +17 -7
  3. package/lib/commonjs/renderer/Canvas.js.map +1 -1
  4. package/lib/commonjs/renderer/DependencyManager.js +144 -36
  5. package/lib/commonjs/renderer/DependencyManager.js.map +1 -1
  6. package/lib/commonjs/renderer/HostConfig.js +1 -1
  7. package/lib/commonjs/renderer/HostConfig.js.map +1 -1
  8. package/lib/commonjs/renderer/components/backdrop/BackdropFilter.js.map +1 -1
  9. package/lib/commonjs/renderer/nodes/Declaration.js +2 -3
  10. package/lib/commonjs/renderer/nodes/Declaration.js.map +1 -1
  11. package/lib/commonjs/renderer/nodes/Drawing.js +3 -7
  12. package/lib/commonjs/renderer/nodes/Drawing.js.map +1 -1
  13. package/lib/commonjs/renderer/nodes/Node.js +9 -7
  14. package/lib/commonjs/renderer/nodes/Node.js.map +1 -1
  15. package/lib/commonjs/renderer/processors/Animations/Animations.js +3 -22
  16. package/lib/commonjs/renderer/processors/Animations/Animations.js.map +1 -1
  17. package/lib/commonjs/skia/types/Paint/Paint.js.map +1 -1
  18. package/lib/commonjs/skia/web/Host.js.map +1 -1
  19. package/lib/commonjs/skia/web/JsiSkPaint.js +4 -0
  20. package/lib/commonjs/skia/web/JsiSkPaint.js.map +1 -1
  21. package/lib/module/renderer/Canvas.js +17 -6
  22. package/lib/module/renderer/Canvas.js.map +1 -1
  23. package/lib/module/renderer/DependencyManager.js +140 -33
  24. package/lib/module/renderer/DependencyManager.js.map +1 -1
  25. package/lib/module/renderer/HostConfig.js +1 -1
  26. package/lib/module/renderer/HostConfig.js.map +1 -1
  27. package/lib/module/renderer/components/backdrop/BackdropFilter.js.map +1 -1
  28. package/lib/module/renderer/nodes/Declaration.js +3 -4
  29. package/lib/module/renderer/nodes/Declaration.js.map +1 -1
  30. package/lib/module/renderer/nodes/Drawing.js +3 -6
  31. package/lib/module/renderer/nodes/Drawing.js.map +1 -1
  32. package/lib/module/renderer/nodes/Node.js +9 -7
  33. package/lib/module/renderer/nodes/Node.js.map +1 -1
  34. package/lib/module/renderer/processors/Animations/Animations.js +1 -16
  35. package/lib/module/renderer/processors/Animations/Animations.js.map +1 -1
  36. package/lib/module/skia/types/Paint/Paint.js.map +1 -1
  37. package/lib/module/skia/web/Host.js.map +1 -1
  38. package/lib/module/skia/web/JsiSkPaint.js +4 -0
  39. package/lib/module/skia/web/JsiSkPaint.js.map +1 -1
  40. package/lib/typescript/src/renderer/Canvas.d.ts +1 -1
  41. package/lib/typescript/src/renderer/DependencyManager.d.ts +40 -14
  42. package/lib/typescript/src/renderer/nodes/Declaration.d.ts +2 -2
  43. package/lib/typescript/src/renderer/nodes/Drawing.d.ts +2 -2
  44. package/lib/typescript/src/renderer/nodes/Node.d.ts +3 -2
  45. package/lib/typescript/src/renderer/processors/Animations/Animations.d.ts +0 -1
  46. package/lib/typescript/src/skia/types/Paint/Paint.d.ts +5 -0
  47. package/lib/typescript/src/skia/web/Host.d.ts +1 -1
  48. package/lib/typescript/src/skia/web/JsiSkPaint.d.ts +1 -0
  49. package/libs/ios/libskia.xcframework/Info.plist +5 -5
  50. package/libs/ios/libskia.xcframework/ios-arm64_arm64e/libskia.a +0 -0
  51. package/libs/ios/libskia.xcframework/ios-arm64_arm64e_x86_64-simulator/libskia.a +0 -0
  52. package/libs/ios/libskshaper.xcframework/ios-arm64_arm64e/libskshaper.a +0 -0
  53. package/libs/ios/libskshaper.xcframework/ios-arm64_arm64e_x86_64-simulator/libskshaper.a +0 -0
  54. package/libs/ios/libsvg.xcframework/ios-arm64_arm64e/libsvg.a +0 -0
  55. package/libs/ios/libsvg.xcframework/ios-arm64_arm64e_x86_64-simulator/libsvg.a +0 -0
  56. package/package.json +1 -1
  57. package/src/renderer/Canvas.tsx +19 -7
  58. package/src/renderer/DependencyManager.tsx +170 -39
  59. package/src/renderer/HostConfig.ts +1 -1
  60. package/src/renderer/components/backdrop/BackdropFilter.tsx +1 -1
  61. package/src/renderer/nodes/Declaration.tsx +6 -8
  62. package/src/renderer/nodes/Drawing.tsx +5 -7
  63. package/src/renderer/nodes/Node.ts +11 -9
  64. package/src/renderer/processors/Animations/Animations.ts +2 -15
  65. package/src/skia/types/Paint/Paint.ts +6 -0
  66. package/src/skia/web/Host.ts +1 -1
  67. package/src/skia/web/JsiSkPaint.ts +4 -0
@@ -20,6 +20,7 @@ import { SkiaView, useDrawCallback } from "../views";
20
20
  import type { TouchHandler } from "../views";
21
21
  import { useValue } from "../values/hooks/useValue";
22
22
  import { Skia } from "../skia/Skia";
23
+ import type { SkiaValue } from "../values";
23
24
 
24
25
  import { debug as hostDebug, skHostConfig } from "./HostConfig";
25
26
  // import { debugTree } from "./nodes";
@@ -38,8 +39,7 @@ skiaReconciler.injectIntoDevTools({
38
39
  const render = (element: ReactNode, root: OpaqueRoot, container: Container) => {
39
40
  skiaReconciler.updateContainer(element, root, null, () => {
40
41
  hostDebug("updateContainer");
41
-
42
- container.depMgr.subscribe();
42
+ container.depMgr.update();
43
43
  });
44
44
  };
45
45
 
@@ -60,9 +60,19 @@ export const Canvas = forwardRef<SkiaView, CanvasProps>(
60
60
  const [tick, setTick] = useState(0);
61
61
  const redraw = useCallback(() => setTick((t) => t + 1), []);
62
62
 
63
+ const registerValues = useCallback(
64
+ (values: Array<SkiaValue<unknown>>) => {
65
+ if (ref.current === null) {
66
+ throw new Error("Canvas ref is not set");
67
+ }
68
+ return ref.current.registerValues(values);
69
+ },
70
+ [ref]
71
+ );
72
+
63
73
  const container = useMemo(
64
- () => new Container(new DependencyManager(ref), redraw),
65
- [redraw, ref]
74
+ () => new Container(new DependencyManager(registerValues), redraw),
75
+ [redraw, registerValues]
66
76
  );
67
77
 
68
78
  const root = useMemo(
@@ -78,6 +88,8 @@ export const Canvas = forwardRef<SkiaView, CanvasProps>(
78
88
  );
79
89
  }, [children, root, redraw, container, canvasCtx]);
80
90
 
91
+ const paint = useMemo(() => Skia.Paint(), []);
92
+
81
93
  // Draw callback
82
94
  const onDraw = useDrawCallback(
83
95
  (canvas, info) => {
@@ -92,7 +104,7 @@ export const Canvas = forwardRef<SkiaView, CanvasProps>(
92
104
  ) {
93
105
  canvasCtx.size.current = { width, height };
94
106
  }
95
- const paint = Skia.Paint();
107
+ paint.reset();
96
108
  const ctx = {
97
109
  width,
98
110
  height,
@@ -101,7 +113,7 @@ export const Canvas = forwardRef<SkiaView, CanvasProps>(
101
113
  paint,
102
114
  opacity: 1,
103
115
  ref,
104
- center: Skia.Point(width / 2, height / 2),
116
+ center: { x: width / 2, y: height / 2 },
105
117
  Skia,
106
118
  };
107
119
  container.draw(ctx);
@@ -112,7 +124,7 @@ export const Canvas = forwardRef<SkiaView, CanvasProps>(
112
124
  useEffect(() => {
113
125
  return () => {
114
126
  skiaReconciler.updateContainer(null, root, null, () => {
115
- container.depMgr.unsubscribe();
127
+ container.depMgr.remove();
116
128
  });
117
129
  };
118
130
  }, [container, root]);
@@ -1,62 +1,193 @@
1
- import type { RefObject } from "react";
2
-
3
- import type { SkiaView } from "../views";
4
1
  import type { SkiaValue } from "../values";
5
2
 
6
- import { isSelector, isValue } from "./processors";
7
3
  import type { Node } from "./nodes";
4
+ import type { AnimatedProps } from "./processors";
5
+ import { isSelector, isValue } from "./processors";
6
+ import { mapKeys } from "./typeddash";
8
7
 
9
8
  type Unsubscribe = () => void;
10
- type Props = { [key: string]: unknown };
9
+ type Mutator = (value: unknown) => void;
10
+
11
+ type SubscriptionState = {
12
+ nodes: Map<Node, Mutator[]>;
13
+ unsubscribe: null | Unsubscribe;
14
+ };
11
15
 
12
16
  export class DependencyManager {
13
- ref: RefObject<SkiaView>;
14
- subscriptions: Map<
15
- Node,
16
- { values: SkiaValue<unknown>[]; unsubscribe: null | Unsubscribe }
17
- > = new Map();
18
-
19
- constructor(ref: RefObject<SkiaView>) {
20
- this.ref = ref;
21
- }
17
+ registerValues: (values: Array<SkiaValue<unknown>>) => () => void;
18
+ subscriptions: Map<SkiaValue<unknown>, SubscriptionState> = new Map();
19
+ unregisterDependantValues: null | Unsubscribe = null;
22
20
 
23
- unSubscribeNode(node: Node) {
24
- const subscription = this.subscriptions.get(node);
25
- if (subscription && subscription.unsubscribe) {
26
- subscription.unsubscribe();
27
- }
28
- this.subscriptions.delete(node);
21
+ constructor(
22
+ registerValues: (values: Array<SkiaValue<unknown>>) => () => void
23
+ ) {
24
+ this.registerValues = registerValues;
29
25
  }
30
26
 
31
- subscribeNode(node: Node, props: Props) {
32
- const values = Object.values(props)
33
- .filter((v) => isValue(v) || isSelector(v))
34
- .map((v) => (isSelector(v) ? v.value : (v as SkiaValue<unknown>)));
27
+ /**
28
+ * Call to unsubscribe all value listeners from the given node based
29
+ * on the current list of subscriptions for the node. This function
30
+ * is typically called when the node is unmounted or when one or more
31
+ * properties have changed.
32
+ * @param node Node to unsubscribe value listeners from
33
+ */
34
+ unsubscribeNode(node: Node) {
35
+ const subscriptions = Array.from(this.subscriptions.values()).filter((p) =>
36
+ p.nodes.has(node)
37
+ );
35
38
 
36
- if (values.length > 0) {
37
- this.subscriptions.set(node, { values, unsubscribe: null });
39
+ if (subscriptions) {
40
+ subscriptions.forEach((si) => {
41
+ // Delete node from subscription
42
+ si.nodes.delete(node);
43
+
44
+ // Remove subscription if there are no listeneres left on the value
45
+ if (si.nodes.size === 0) {
46
+ // There are no more nodes subscribing to this value, we can call
47
+ // unsubscribe on it.
48
+ if (!si.unsubscribe) {
49
+ throw new Error("Failed to unsubscribe to value subscription");
50
+ }
51
+ si.unsubscribe && si.unsubscribe();
52
+
53
+ // Remove from subscription states as well
54
+ const element = Array.from(this.subscriptions.entries()).find(
55
+ ([_, sub]) => sub === si
56
+ );
57
+ if (!element) {
58
+ throw new Error("Failed to find value subscription");
59
+ }
60
+ if (!this.subscriptions.delete(element[0])) {
61
+ throw new Error("Failed to delete value subscription");
62
+ }
63
+ }
64
+ });
38
65
  }
39
66
  }
40
67
 
41
- subscribe() {
42
- if (this.ref.current === null) {
43
- throw new Error("Canvas ref is not set");
68
+ /**
69
+ * Adds listeners to the provided values so that the node is notified
70
+ * when a value changes. This is done in an optimized way so that this
71
+ * class only needs to listen to the value once and then forwards the
72
+ * change to the node and its listener. This method is typically called
73
+ * when the node is mounted and when one or more props on the node changes.
74
+ * @param node Node to subscribe to value changes for
75
+ * @param props Node's properties
76
+ * @param onResolveProp Callback when a property value changes
77
+ */
78
+ subscribeNode<P extends Record<string, unknown>>(
79
+ node: Node,
80
+ props: AnimatedProps<P>
81
+ ) {
82
+ // Get mutators from node's properties
83
+ const propSubscriptions = initializePropertySubscriptions(node, props);
84
+ if (propSubscriptions.length === 0) {
85
+ return;
44
86
  }
45
- this.subscriptions.forEach((subscription) => {
46
- if (subscription.unsubscribe === null) {
47
- subscription.unsubscribe = this.ref.current!.registerValues(
48
- subscription.values
49
- );
87
+
88
+ // Install all mutators for the node
89
+ propSubscriptions.forEach((ps) => {
90
+ // Do we already have a state for this SkiaValue
91
+ let subscriptionState = this.subscriptions.get(ps.value);
92
+ if (!subscriptionState) {
93
+ // Let's create a new subscription state for the skia value
94
+ subscriptionState = {
95
+ nodes: new Map(),
96
+ unsubscribe: null,
97
+ };
98
+ // Add single subscription to the new value
99
+ subscriptionState.unsubscribe = ps.value.addListener((v) => {
100
+ subscriptionState!.nodes.forEach((mutators) =>
101
+ mutators.forEach((m) => m(v))
102
+ );
103
+ });
104
+ this.subscriptions.set(ps.value, subscriptionState);
50
105
  }
106
+ // subscription mutators
107
+ subscriptionState.nodes.set(
108
+ node,
109
+ propSubscriptions
110
+ .filter((m) => m.value === ps.value)
111
+ .map((m) => m.mutator)
112
+ );
51
113
  });
52
114
  }
53
115
 
54
- unsubscribe() {
55
- this.subscriptions.forEach(({ unsubscribe }) => {
56
- if (unsubscribe) {
57
- unsubscribe();
58
- }
116
+ /**
117
+ * Called when the hosting container is mounted or updated. This ensures that we have
118
+ * a ref to the underlying SkiaView so that we can registers redraw listeners
119
+ * on values used in the current View automatically.
120
+ */
121
+ update() {
122
+ // Remove any previous registrations
123
+ if (this.unregisterDependantValues) {
124
+ this.unregisterDependantValues();
125
+ }
126
+
127
+ // Register redraw requests on the SkiaView for each unique value
128
+ this.unregisterDependantValues = this.registerValues(
129
+ Array.from(this.subscriptions.keys())
130
+ );
131
+ }
132
+
133
+ /**
134
+ * Called when the hosting container is unmounted or recreated. This ensures that we remove
135
+ * all subscriptions to Skia values so that we don't have any listeners left after
136
+ * the component is removed.
137
+ */
138
+ remove() {
139
+ // 1) Unregister redraw requests
140
+ if (this.unregisterDependantValues) {
141
+ this.unregisterDependantValues();
142
+ this.unregisterDependantValues = null;
143
+ }
144
+
145
+ // 2) Unregister nodes
146
+ Array.from(this.subscriptions.values()).forEach((si) => {
147
+ Array.from(si.nodes.keys()).forEach((node) => this.unsubscribeNode(node));
59
148
  });
149
+
150
+ // 3) Clear the rest of the subscriptions
60
151
  this.subscriptions.clear();
61
152
  }
62
153
  }
154
+
155
+ const initializePropertySubscriptions = <P,>(
156
+ node: Node<P>,
157
+ props: AnimatedProps<P>
158
+ ) => {
159
+ const nodePropSubscriptions: Array<{
160
+ value: SkiaValue<unknown>;
161
+ mutator: Mutator;
162
+ }> = [];
163
+
164
+ mapKeys(props).forEach((key) => {
165
+ const propvalue = props[key];
166
+
167
+ if (isValue(propvalue)) {
168
+ // Subscribe to changes
169
+ nodePropSubscriptions.push({
170
+ value: propvalue,
171
+ mutator: (v) => (node.resolvedProps[key] = v as P[typeof key]),
172
+ });
173
+ // Set initial value
174
+ node.resolvedProps[key] = (propvalue as SkiaValue<P[typeof key]>).current;
175
+ } else if (isSelector(propvalue)) {
176
+ // Subscribe to changes
177
+ nodePropSubscriptions.push({
178
+ value: propvalue.value,
179
+ mutator: (v) =>
180
+ (node.resolvedProps[key] = propvalue.selector(v) as P[typeof key]),
181
+ });
182
+ // Set initial value
183
+ node.resolvedProps[key] = propvalue.selector(
184
+ propvalue.value.current
185
+ ) as P[typeof key];
186
+ } else {
187
+ // Set initial value
188
+ node.resolvedProps[key] = propvalue as P[typeof key];
189
+ }
190
+ });
191
+
192
+ return nodePropSubscriptions;
193
+ };
@@ -101,7 +101,7 @@ const removeNode = (parent: Node, child: Node) => {
101
101
  bustBranchMemoization(parent);
102
102
  const index = parent.children.indexOf(child);
103
103
  parent.children.splice(index, 1);
104
- child.depMgr.unSubscribeNode(child);
104
+ child.depMgr.unsubscribeNode(child);
105
105
  // unsubscribe to all children as well
106
106
  for (const c of child.children) {
107
107
  removeNode(child, c);
@@ -8,7 +8,7 @@ import { getInput } from "../imageFilters/getInput";
8
8
  import type { GroupProps } from "../Group";
9
9
  import { Group } from "../Group";
10
10
 
11
- const disableFilterMemoization = (children: Node<unknown>[]) => {
11
+ const disableFilterMemoization = (children: Node[]) => {
12
12
  children.forEach((child) => {
13
13
  child.memoizable = false;
14
14
  disableFilterMemoization(child.children);
@@ -3,7 +3,7 @@ import { useCallback } from "react";
3
3
 
4
4
  import type { DrawingContext } from "../DrawingContext";
5
5
  import type { AnimatedProps } from "../processors";
6
- import { isAnimated, materialize } from "../processors";
6
+ import { isAnimated } from "../processors";
7
7
  import type { DependencyManager } from "../DependencyManager";
8
8
  import type { SkJSIInstance } from "../../skia/types";
9
9
 
@@ -28,9 +28,8 @@ export const useDeclaration = <P,>(
28
28
  // eslint-disable-next-line react-hooks/exhaustive-deps
29
29
  useCallback(cb, deps ?? []);
30
30
 
31
- export const isDeclarationNode = (
32
- node: Node
33
- ): node is DeclarationNode<unknown> => node instanceof DeclarationNode;
31
+ export const isDeclarationNode = <P,>(node: Node): node is DeclarationNode<P> =>
32
+ node instanceof DeclarationNode;
34
33
 
35
34
  export interface DeclarationProps<P> {
36
35
  onDeclare: DeclarationCallback<P>;
@@ -54,14 +53,13 @@ export class DeclarationNode<P> extends Node<P> {
54
53
  super.props = props;
55
54
  }
56
55
 
57
- get props() {
58
- return this._props;
56
+ get props(): P {
57
+ return this.resolvedProps;
59
58
  }
60
59
 
61
60
  draw(ctx: DrawingContext) {
62
61
  const children = this.visit(ctx);
63
- const props = materialize(this.props);
64
- const obj = this.onDeclare(props, children, ctx);
62
+ const obj = this.onDeclare(this.props, children, ctx);
65
63
  return obj;
66
64
  }
67
65
  }
@@ -3,7 +3,6 @@ import { useCallback } from "react";
3
3
 
4
4
  import type { DrawingContext } from "../DrawingContext";
5
5
  import type { AnimatedProps } from "../processors/Animations/Animations";
6
- import { materialize } from "../processors/Animations/Animations";
7
6
  import { isPaint } from "../../skia/types";
8
7
  import type { DependencyManager } from "../DependencyManager";
9
8
  import { processPaint } from "../processors";
@@ -25,8 +24,8 @@ export const useDrawing = <P,>(cb: OnDrawCallback<P>, deps?: DependencyList) =>
25
24
  // eslint-disable-next-line react-hooks/exhaustive-deps
26
25
  useCallback(cb, deps ?? []);
27
26
 
28
- export type DrawingProps<T> = {
29
- onDraw: DrawingCallback<T>;
27
+ export type DrawingProps<P> = {
28
+ onDraw: DrawingCallback<P>;
30
29
  skipProcessing?: boolean;
31
30
  children?: ReactNode | ReactNode[];
32
31
  };
@@ -47,20 +46,19 @@ export class DrawingNode<P> extends Node<P> {
47
46
  }
48
47
 
49
48
  draw(ctx: DrawingContext) {
50
- const drawingProps = materialize(this.props);
51
49
  if (this.skipProcessing) {
52
- this.onDraw(ctx, drawingProps, this);
50
+ this.onDraw(ctx, this.props, this);
53
51
  } else {
54
52
  const declarations = this.visit(ctx);
55
53
  const paint = processPaint(
56
54
  ctx.Skia,
57
55
  ctx.paint.copy(),
58
56
  ctx.opacity,
59
- drawingProps,
57
+ this.props,
60
58
  declarations
61
59
  );
62
60
  [paint, ...declarations.filter(isPaint)].forEach((currentPaint) => {
63
- this.onDraw({ ...ctx, paint: currentPaint }, drawingProps, this);
61
+ this.onDraw({ ...ctx, paint: currentPaint }, this.props, this);
64
62
  });
65
63
  }
66
64
  }
@@ -12,29 +12,31 @@ type DeclarationResult = SkJSIInstance<string> | null;
12
12
 
13
13
  export abstract class Node<P = unknown> {
14
14
  readonly children: Node[] = [];
15
- _props: AnimatedProps<P>;
15
+ // This cast is ok because we understand that the dependency manager will setup the initial props
16
+ resolvedProps: P = {} as P;
16
17
  memoizable = false;
17
18
  memoized: DeclarationResult | null = null;
18
19
  parent?: Node;
19
20
  depMgr: DependencyManager;
20
21
 
21
22
  constructor(depMgr: DependencyManager, props: AnimatedProps<P>) {
22
- this._props = props;
23
23
  this.depMgr = depMgr;
24
- this.depMgr.unSubscribeNode(this);
25
- this.depMgr.subscribeNode(this, props);
24
+ this.updatePropSubscriptions(props);
26
25
  }
27
26
 
28
27
  abstract draw(ctx: DrawingContext): void | DeclarationResult;
29
28
 
30
- set props(props: AnimatedProps<P>) {
31
- this.depMgr.unSubscribeNode(this);
29
+ updatePropSubscriptions(props: AnimatedProps<P>) {
32
30
  this.depMgr.subscribeNode(this, props);
33
- this._props = props;
34
31
  }
35
32
 
36
- get props() {
37
- return this._props;
33
+ set props(props: AnimatedProps<P>) {
34
+ this.depMgr.unsubscribeNode(this);
35
+ this.updatePropSubscriptions(props);
36
+ }
37
+
38
+ get props(): P {
39
+ return this.resolvedProps;
38
40
  }
39
41
 
40
42
  visit(ctx: DrawingContext, children?: Node[]) {
@@ -1,5 +1,4 @@
1
1
  import type { SkiaSelector, SkiaValue } from "../../../values";
2
- import { mapKeys } from "../../typeddash";
3
2
 
4
3
  export const isValue = (value: unknown): value is SkiaValue<unknown> => {
5
4
  if (value === undefined || value === null) {
@@ -37,28 +36,16 @@ export const isSelector = <T, R>(
37
36
 
38
37
  export const isAnimated = <T>(props: AnimatedProps<T>) => {
39
38
  for (const value of Object.values(props)) {
40
- if (isValue(value)) {
39
+ if (isValue(value) || isSelector(value)) {
41
40
  return true;
42
41
  }
43
42
  }
44
43
  return false;
45
44
  };
46
45
 
47
- export const materialize = <T>(props: AnimatedProps<T>) => {
48
- const result = { ...props };
49
- mapKeys(props).forEach((key) => {
50
- const prop = props[key];
51
- if (isValue(prop)) {
52
- result[key] = (prop as SkiaValue<T[typeof key]>).current;
53
- } else if (isSelector(prop)) {
54
- result[key] = prop.selector(prop.value.current) as T[typeof key];
55
- }
56
- });
57
- return result as T;
58
- };
59
-
60
46
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
47
  export type AnimatedProp<T, P = any> = T | SkiaValue<T> | SkiaSelector<T, P>;
48
+
62
49
  export type AnimatedProps<T> = {
63
50
  [K in keyof T]: AnimatedProp<T[K]>;
64
51
  };
@@ -34,6 +34,12 @@ export interface SkPaint extends SkJSIInstance<"Paint"> {
34
34
  */
35
35
  copy(): SkPaint;
36
36
 
37
+ /**
38
+ * Sets all SkPaint contents to their initial values. This is equivalent to replacing
39
+ SkPaint with the result of SkPaint().
40
+ */
41
+ reset(): void;
42
+
37
43
  /**
38
44
  * Retrieves the alpha and RGB unpremultiplied. RGB are extended sRGB values
39
45
  * (sRGB gamut, and encoded with the sRGB transfer function).
@@ -21,7 +21,7 @@ export abstract class BaseHostObject<T, N extends string>
21
21
  implements SkJSIInstance<N>
22
22
  {
23
23
  readonly __typename__: N;
24
- readonly ref: T;
24
+ ref: T;
25
25
 
26
26
  constructor(CanvasKit: CanvasKit, ref: T, typename: N) {
27
27
  super(CanvasKit);
@@ -30,6 +30,10 @@ export class JsiSkPaint extends HostObject<Paint, "Paint"> implements SkPaint {
30
30
  return new JsiSkPaint(this.CanvasKit, this.ref.copy());
31
31
  }
32
32
 
33
+ reset() {
34
+ this.ref = new this.CanvasKit.Paint();
35
+ }
36
+
33
37
  getColor() {
34
38
  return this.ref.getColor();
35
39
  }