react-native-shine 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +21 -6
  2. package/lib/module/components/Content.js +232 -0
  3. package/lib/module/components/Content.js.map +1 -0
  4. package/lib/module/components/Shine.js +18 -254
  5. package/lib/module/components/Shine.js.map +1 -1
  6. package/lib/module/components/ShineGroup.js +11 -23
  7. package/lib/module/components/ShineGroup.js.map +1 -1
  8. package/lib/module/hooks/useAnimationFrame.js +17 -0
  9. package/lib/module/hooks/useAnimationFrame.js.map +1 -0
  10. package/lib/module/hooks/useOrientation.js +2 -8
  11. package/lib/module/hooks/useOrientation.js.map +1 -1
  12. package/lib/module/index.js +3 -2
  13. package/lib/module/index.js.map +1 -1
  14. package/lib/module/shaders/bindGroupLayouts.js +8 -8
  15. package/lib/module/shaders/bindGroupLayouts.js.map +1 -1
  16. package/lib/module/shaders/bindGroupUtils.js +18 -40
  17. package/lib/module/shaders/bindGroupUtils.js.map +1 -1
  18. package/lib/module/shaders/fragmentShaders/glareFragment.js +8 -7
  19. package/lib/module/shaders/fragmentShaders/glareFragment.js.map +1 -1
  20. package/lib/module/shaders/fragmentShaders/holoFragment.js +5 -10
  21. package/lib/module/shaders/fragmentShaders/holoFragment.js.map +1 -1
  22. package/lib/module/shaders/fragmentShaders/reverseHoloFragment.js +3 -3
  23. package/lib/module/shaders/fragmentShaders/reverseHoloFragment.js.map +1 -1
  24. package/lib/module/shaders/pipelineSetups.js +29 -43
  25. package/lib/module/shaders/pipelineSetups.js.map +1 -1
  26. package/lib/module/shaders/resourceManagement/textures.js +7 -0
  27. package/lib/module/shaders/resourceManagement/textures.js.map +1 -1
  28. package/lib/module/shaders/utils.js +8 -22
  29. package/lib/module/shaders/utils.js.map +1 -1
  30. package/lib/module/shaders/vertexShaders/mainRotationEffectVertex.js +2 -2
  31. package/lib/module/shaders/vertexShaders/mainRotationEffectVertex.js.map +1 -1
  32. package/lib/module/types/size.js +2 -0
  33. package/lib/module/types/size.js.map +1 -0
  34. package/lib/module/types/typeUtils.js +0 -14
  35. package/lib/module/types/typeUtils.js.map +1 -1
  36. package/lib/module/types/vector.js +2 -0
  37. package/lib/module/types/vector.js.map +1 -0
  38. package/lib/module/utils/size.js +25 -0
  39. package/lib/module/utils/size.js.map +1 -0
  40. package/lib/module/utils/vector.js +168 -0
  41. package/lib/module/utils/vector.js.map +1 -0
  42. package/lib/typescript/src/components/Content.d.ts +23 -0
  43. package/lib/typescript/src/components/Content.d.ts.map +1 -0
  44. package/lib/typescript/src/components/Shine.d.ts +3 -13
  45. package/lib/typescript/src/components/Shine.d.ts.map +1 -1
  46. package/lib/typescript/src/components/ShineGroup.d.ts +3 -5
  47. package/lib/typescript/src/components/ShineGroup.d.ts.map +1 -1
  48. package/lib/typescript/src/hooks/useAnimationFrame.d.ts +2 -0
  49. package/lib/typescript/src/hooks/useAnimationFrame.d.ts.map +1 -0
  50. package/lib/typescript/src/hooks/useOrientation.d.ts +3 -1
  51. package/lib/typescript/src/hooks/useOrientation.d.ts.map +1 -1
  52. package/lib/typescript/src/index.d.ts +4 -2
  53. package/lib/typescript/src/index.d.ts.map +1 -1
  54. package/lib/typescript/src/shaders/bindGroupLayouts.d.ts +9 -9
  55. package/lib/typescript/src/shaders/bindGroupLayouts.d.ts.map +1 -1
  56. package/lib/typescript/src/shaders/bindGroupUtils.d.ts +3 -3
  57. package/lib/typescript/src/shaders/bindGroupUtils.d.ts.map +1 -1
  58. package/lib/typescript/src/shaders/fragmentShaders/glareFragment.d.ts.map +1 -1
  59. package/lib/typescript/src/shaders/fragmentShaders/holoFragment.d.ts.map +1 -1
  60. package/lib/typescript/src/shaders/pipelineSetups.d.ts +7 -7
  61. package/lib/typescript/src/shaders/pipelineSetups.d.ts.map +1 -1
  62. package/lib/typescript/src/shaders/resourceManagement/textures.d.ts +2 -1
  63. package/lib/typescript/src/shaders/resourceManagement/textures.d.ts.map +1 -1
  64. package/lib/typescript/src/shaders/utils.d.ts +3 -4
  65. package/lib/typescript/src/shaders/utils.d.ts.map +1 -1
  66. package/lib/typescript/src/types/size.d.ts +5 -0
  67. package/lib/typescript/src/types/size.d.ts.map +1 -0
  68. package/lib/typescript/src/types/typeUtils.d.ts +1 -4
  69. package/lib/typescript/src/types/typeUtils.d.ts.map +1 -1
  70. package/lib/typescript/src/types/types.d.ts +3 -1
  71. package/lib/typescript/src/types/types.d.ts.map +1 -1
  72. package/lib/typescript/src/types/vector.d.ts +11 -0
  73. package/lib/typescript/src/types/vector.d.ts.map +1 -0
  74. package/lib/typescript/src/utils/size.d.ts +5 -0
  75. package/lib/typescript/src/utils/size.d.ts.map +1 -0
  76. package/lib/typescript/src/utils/vector.d.ts +33 -0
  77. package/lib/typescript/src/utils/vector.d.ts.map +1 -0
  78. package/package.json +8 -7
  79. package/src/components/Content.tsx +403 -0
  80. package/src/components/Shine.tsx +24 -466
  81. package/src/components/ShineGroup.tsx +17 -24
  82. package/src/hooks/useAnimationFrame.ts +21 -0
  83. package/src/hooks/useOrientation.ts +11 -13
  84. package/src/index.tsx +9 -1
  85. package/src/shaders/bindGroupLayouts.ts +11 -11
  86. package/src/shaders/bindGroupUtils.ts +31 -61
  87. package/src/shaders/fragmentShaders/glareFragment.ts +8 -7
  88. package/src/shaders/fragmentShaders/holoFragment.ts +5 -13
  89. package/src/shaders/fragmentShaders/reverseHoloFragment.ts +4 -4
  90. package/src/shaders/pipelineSetups.ts +54 -69
  91. package/src/shaders/resourceManagement/textures.ts +13 -1
  92. package/src/shaders/utils.ts +13 -27
  93. package/src/shaders/vertexShaders/mainRotationEffectVertex.ts +2 -2
  94. package/src/types/size.ts +4 -0
  95. package/src/types/typeUtils.ts +0 -28
  96. package/src/types/types.ts +11 -1
  97. package/src/types/vector.ts +13 -0
  98. package/src/utils/size.ts +12 -0
  99. package/src/utils/vector.ts +132 -0
@@ -1,480 +1,38 @@
1
- import { useEffect, useMemo, useRef, useState } from 'react';
2
- import { Canvas, useDevice, useGPUContext } from 'react-native-wgpu';
1
+ import { useEffect, useState } from 'react';
2
+ import { useDevice } from 'react-native-wgpu';
3
+ import type { TgpuTexture } from 'typegpu';
3
4
  import { getOrInitRoot } from '../roots';
4
- import mainVertex from '../shaders/vertexShaders/mainVertex';
5
- import getBitmapFromURI from '../shaders/resourceManagement/bitmaps';
6
- import {
7
- clamp,
8
- rotate2D,
9
- subscribeToOrientationChange,
10
- getAngleFromDimensions,
11
- } from '../shaders/utils';
12
- import type { TgpuRenderPipeline, TgpuTexture } from 'typegpu';
13
- import {
14
- glareOptionsBindGroupLayout,
15
- colorMaskBindGroupLayout,
16
- rotationValuesBindGroupLayout,
17
- textureBindGroupLayout,
18
- type BufferDataMap,
19
- bufferData,
20
- } from '../shaders/bindGroupLayouts';
21
- import Animated, {
22
- SensorType,
23
- useAnimatedSensor,
24
- useAnimatedStyle,
25
- useDerivedValue,
26
- useSharedValue,
27
- type SharedValue,
28
- } from 'react-native-reanimated';
29
- import * as d from 'typegpu/data';
30
- import { PixelRatio, Platform, View } from 'react-native';
31
- import {
32
- createGlareOptionsBindGroup,
33
- createColorMaskBindGroup,
34
- createRotationValuesBindGroup,
35
- } from '../shaders/bindGroupUtils';
36
- import {
37
- createBindGroupPairs,
38
- createGlareOptions,
39
- createColorMask,
40
- colorMaskToTyped,
41
- } from '../types/typeUtils';
42
- import type {
43
- BindGroupPair,
44
- GlareOptions,
45
- ColorMask,
46
- DeepPartiallyOptional,
47
- } from '../types/types';
48
- import {
49
- attachBindGroups,
50
- blend,
51
- createReverseHoloPipeline,
52
- createMaskPipeline,
53
- getDefaultTarget,
54
- pipelineRenderFunction,
55
- createRainbowHoloPipeline as createHoloPipeline,
56
- } from '../shaders/pipelineSetups';
57
- import colorMaskFragment from '../shaders/fragmentShaders/colorMaskFragment';
58
- import {
59
- createTexture,
60
- loadTexture,
61
- } from '../shaders/resourceManagement/textures';
62
- import { newGlareFragment } from '../shaders/fragmentShaders/glareFragment';
63
- import { TypedBufferMap } from '../shaders/resourceManagement/bufferManager';
5
+ import { loadBitmap } from '../shaders/resourceManagement/textures';
6
+ import Content, { type SharedProps } from './Content';
64
7
 
65
- export interface ShineProps {
66
- width: number;
67
- height: number;
8
+ export interface ShineProps extends SharedProps {
68
9
  imageURI: string;
69
- glareOptions?: Partial<GlareOptions>;
70
- colorMaskOptions?: DeepPartiallyOptional<ColorMask, 'baseColor'>;
71
10
  maskURI?: string;
72
- useTouchControl?: boolean;
73
- touchPosition?: SharedValue<[number, number]>;
74
- addTextureMask?: boolean;
75
- addReverseHolo?: boolean;
76
- addHolo?: boolean;
77
11
  }
78
12
 
79
- export function Shine({
80
- width,
81
- height,
82
- imageURI,
83
- glareOptions: glareOptions,
84
- colorMaskOptions,
85
- maskURI,
86
- touchPosition,
87
- useTouchControl = false,
88
- addTextureMask = false,
89
- addHolo = false,
90
- addReverseHolo = false,
91
- }: ShineProps) {
92
- const { device = null } = useDevice();
93
- const root = device ? getOrInitRoot(device) : null;
94
- const { ref, context } = useGPUContext();
95
- const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
96
- const frameRef = useRef<number | null>(null);
13
+ export function Shine({ imageURI, maskURI, ...props }: ShineProps) {
14
+ const { device } = useDevice();
15
+ const root = device && getOrInitRoot(device);
16
+ const [imageTexture, setImageTexture] = useState<TgpuTexture>();
17
+ const [maskTexture, setMaskTexture] = useState<TgpuTexture>();
97
18
 
98
- //changing canvas size to prevent blur
99
- const dpr = PixelRatio.get();
100
- const logicalWidth = width;
101
- const logicalHeight = height;
102
- const pixelWidth = Math.max(1, Math.round(logicalWidth * dpr));
103
- const pixelHeight = Math.max(1, Math.round(logicalHeight * dpr));
104
-
105
- const [imageTexture, setImageTexture] = useState<TgpuTexture | null>(null);
106
- const [maskTexture, setMaskTexture] = useState<TgpuTexture | null>(null);
107
-
108
- const orientationAngle = useSharedValue<number>(0); // degrees
109
- const rotationShared = useSharedValue<[number, number, number]>([0, 0, 0]); // final GPU offsets
110
-
111
- // Calibration shared values (UI thread)
112
- const initialGravity = useSharedValue<[number, number, number]>([0, 0, 0]);
113
- const calibSum = useSharedValue<[number, number, number]>([0, 0, 0]);
114
- const calibCount = useSharedValue<number>(0);
115
- const calibrated = useSharedValue<boolean>(false);
116
- const gravitySensor = useAnimatedSensor(SensorType.GRAVITY, { interval: 20 });
117
-
118
- const bufferManager = useMemo(
119
- () => new TypedBufferMap(bufferData as BufferDataMap),
120
- []
121
- );
122
-
123
- //TODO: add once again, when the wgpu issues are fixed :3
124
-
125
- const animatedStyle = useAnimatedStyle(() => {
126
- // const rotX = rotationShared.value[0] * 10;
127
- // const rotY = rotationShared.value[1] * 10;
128
-
129
- return {
130
- transform: [
131
- { perspective: 300 },
132
- // { rotateX: `${-rotX}deg` },
133
- // { rotateY: `${rotY}deg` },
134
- // { rotateZ: `${rotX * 5}deg` },
135
- ],
136
- };
137
- });
138
- // Subscribe to orientation changes and reset calibration on change
139
19
  useEffect(() => {
140
- orientationAngle.value = getAngleFromDimensions();
141
- const unsubscribe = subscribeToOrientationChange((angleDeg) => {
142
- orientationAngle.value = angleDeg;
143
- });
144
-
145
- return () => unsubscribe();
146
- }, [orientationAngle]);
147
-
148
- // Calibration & mapping logic
149
- useDerivedValue(() => {
150
- 'worklet';
151
-
152
- if (useTouchControl) {
153
- rotationShared.value = touchPosition
154
- ? [...touchPosition.value, 0]
155
- : [0, 0, 0];
156
-
157
- return;
158
- }
159
-
160
- // console.log(orientationAngle.value);
161
- const v: { x: number; y: number; z: number } = gravitySensor.sensor
162
- ?.value ??
163
- gravitySensor.sensor.value ?? { x: 0, y: 0, z: 0 };
164
-
165
- const gx = v.x ?? 0;
166
- const gy = v.y ?? 0;
167
- const gz = v.z ?? 0;
168
-
169
- const CALIBRATION_SAMPLES = 40;
170
- const alpha = 0.15; // smoothing
171
- const scale = 0.6;
172
-
173
- if (!calibrated.value) {
174
- // accumulate baseline in device coordinates
175
- const s = calibSum.value;
176
- const c = calibCount.value + 1;
177
- calibSum.value = [s[0] + gx, s[1] + gy, s[2] + gz];
178
- calibCount.value = c;
179
-
180
- if (c >= CALIBRATION_SAMPLES) {
181
- const avg = calibSum.value;
182
- initialGravity.value = [avg[0] / c, avg[1] / c, avg[2] / c];
183
- calibrated.value = true;
184
- }
185
-
186
- rotationShared.value = [0, 0, 0];
187
- return;
188
- }
189
-
190
- const init = initialGravity.value;
191
- const dx = gx - init[0];
192
- const dy = gy - init[1];
193
- const dz = gz - init[2];
194
-
195
- // Rotate into screen coordinates so offsets auto-swap with orientation
196
- const [mx, my] = rotate2D([dx, dy], -orientationAngle.value);
197
- const screenX = mx;
198
- const screenY = -my;
199
-
200
- const prev = rotationShared.value;
201
- const smoothX = prev[0] * (1 - alpha) + screenX * alpha;
202
- const smoothY = prev[1] * (1 - alpha) + screenY * alpha;
203
- const smoothZ = prev[2] * (1 - alpha) + dz * alpha;
204
-
205
- if (orientationAngle.value === 90) {
206
- rotationShared.value = [
207
- clamp(smoothY * scale, -1, 1),
208
- clamp(-smoothX * scale, -1, 1),
209
- clamp(smoothZ * scale, -1, 1),
210
- ];
211
- } else {
212
- rotationShared.value = [
213
- clamp(smoothX * scale, -1, 1),
214
- clamp(smoothY * scale, -1, 1),
215
- clamp(smoothZ * scale, -1, 1),
216
- ];
217
- }
218
- });
219
-
220
- // Resource setup
221
- useEffect(() => {
222
- if (!root || !device || !context) return;
223
-
224
- (async () => {
225
- const bitmap = await getBitmapFromURI(imageURI);
226
- const texture = await createTexture(root, bitmap);
227
- setImageTexture(texture);
228
- await loadTexture(root, bitmap, texture);
20
+ if (root) loadBitmap(root, imageURI, setImageTexture);
21
+ }, [root, imageURI]);
229
22
 
230
- if (!maskURI) return;
231
- const maskBitmap = await getBitmapFromURI(maskURI);
232
- const maskTex = await createTexture(root, maskBitmap);
233
- setMaskTexture(maskTex);
234
- await loadTexture(root, maskBitmap, maskTex);
235
- })();
236
- }, [root, device, context, imageURI, maskURI]);
237
-
238
- // Render loop
239
23
  useEffect(() => {
240
- if (!root || !device || !context || !imageTexture) return;
241
-
242
- //this sets the underlying resolution of the canvas to prevent blurriness
243
- const canvasElement = context.canvas;
244
- if (
245
- canvasElement &&
246
- canvasElement.width !== pixelWidth &&
247
- canvasElement.height !== pixelHeight
248
- ) {
249
- canvasElement.width = pixelWidth;
250
- canvasElement.height = pixelHeight;
251
- }
252
-
253
- context.configure({
254
- device,
255
- format: presentationFormat,
256
- alphaMode: 'premultiplied',
257
- });
258
-
259
- const sampler = device.createSampler({
260
- magFilter: 'linear',
261
- minFilter: 'linear',
262
- });
263
-
264
- const imageTextureBindGroup = root.createBindGroup(textureBindGroupLayout, {
265
- texture: root.unwrap(imageTexture).createView(),
266
- sampler: sampler,
267
- });
268
-
269
- const rotationBuffer = bufferManager.addBuffer(
270
- root,
271
- 'rotationBuffer',
272
- d.vec3f(0.0)
273
- );
274
-
275
- const rotationBindGroup = createRotationValuesBindGroup(
276
- root,
277
- rotationBuffer
278
- );
279
-
280
- const glareOptionsBuffer = bufferManager.addBuffer(
281
- root,
282
- 'glareBuffer',
283
- createGlareOptions(glareOptions ?? {})
284
- );
285
- const glareOptionsBindGroup = createGlareOptionsBindGroup(
286
- root,
287
- glareOptionsBuffer
288
- );
289
-
290
- const colorMaskBuffer = bufferManager.addBuffer(
291
- root,
292
- 'colorMaskBuffer',
293
- colorMaskToTyped(
294
- createColorMask(colorMaskOptions ?? { baseColor: [-20, -20, -20] })
295
- )
296
- );
297
- const colorMaskBindGroup = createColorMaskBindGroup(root, colorMaskBuffer);
298
-
299
- const glareBGP: BindGroupPair[] = createBindGroupPairs(
300
- [
301
- textureBindGroupLayout,
302
- rotationValuesBindGroupLayout,
303
- glareOptionsBindGroupLayout,
304
- colorMaskBindGroupLayout,
305
- ],
306
- [
307
- imageTextureBindGroup,
308
- rotationBindGroup,
309
- glareOptionsBindGroup,
310
- colorMaskBindGroup,
311
- ]
312
- );
313
-
314
- const colorMaskBGP: BindGroupPair[] = createBindGroupPairs(
315
- [
316
- textureBindGroupLayout,
317
- colorMaskBindGroupLayout,
318
- rotationValuesBindGroupLayout,
319
- ],
320
- [imageTextureBindGroup, colorMaskBindGroup, rotationBindGroup]
321
- );
322
-
323
- let glarePipeline = root['~unstable']
324
- .withVertex(mainVertex, {})
325
- .withFragment(newGlareFragment, getDefaultTarget(presentationFormat))
326
- .createPipeline();
327
- glarePipeline = attachBindGroups(glarePipeline, glareBGP);
328
-
329
- let colorMaskPipeline = root['~unstable']
330
- .withVertex(mainVertex, {})
331
- .withFragment(
332
- colorMaskFragment,
333
- getDefaultTarget(presentationFormat, blend)
334
- )
335
- .createPipeline();
336
- colorMaskPipeline = attachBindGroups(colorMaskPipeline, colorMaskBGP);
337
-
338
- //optional pipeline - mask
339
- const maskPipeline = createMaskPipeline(
340
- root,
341
- maskTexture,
342
- createBindGroupPairs(
343
- [textureBindGroupLayout, rotationValuesBindGroupLayout],
344
- [imageTextureBindGroup, rotationBindGroup]
345
- ),
346
- sampler,
347
- presentationFormat
348
- );
349
-
350
- const reverseHoloBGP: BindGroupPair[] = createBindGroupPairs(
351
- [
352
- textureBindGroupLayout,
353
- rotationValuesBindGroupLayout,
354
- glareOptionsBindGroupLayout,
355
- ],
356
- [imageTextureBindGroup, rotationBindGroup, glareOptionsBindGroup]
357
- );
358
-
359
- const reverseHoloPipeline = createReverseHoloPipeline(
360
- root,
361
- maskTexture,
362
- reverseHoloBGP,
363
- sampler,
364
- presentationFormat
365
- );
366
-
367
- const holoBGP: BindGroupPair[] = createBindGroupPairs(
368
- [rotationValuesBindGroupLayout],
369
- [rotationBindGroup]
370
- );
371
-
372
- const holoPipeline = createHoloPipeline(
373
- root,
374
- imageTexture,
375
- holoBGP,
376
- sampler,
377
- presentationFormat
378
- );
379
-
380
- const pipelines: TgpuRenderPipeline[] = [glarePipeline];
381
- if (addTextureMask && maskPipeline) pipelines.push(maskPipeline);
382
- if (addReverseHolo && reverseHoloPipeline)
383
- pipelines.push(reverseHoloPipeline);
384
- if (addHolo && holoPipeline) pipelines.push(holoPipeline);
385
- if (colorMaskOptions) pipelines.push(colorMaskPipeline);
386
-
387
- const rot = d.vec3f(0.0);
388
- let view: GPUTextureView;
389
- let initialAttachment;
390
- let loadingAttachment;
391
- const isInSinglePass = false;
392
- const render = () => {
393
- rot[0] = rotationShared.value[0];
394
- rot[1] = rotationShared.value[1];
395
- rot[2] = rotationShared.value[2];
396
- rotationBuffer.write(rot);
397
-
398
- view = context.getCurrentTexture().createView();
399
- initialAttachment = {
400
- view: view,
401
- clearValue: [0, 0, 0, 0],
402
- loadOp: 'clear' as GPULoadOp,
403
- storeOp: 'store' as GPUStoreOp,
404
- };
405
- loadingAttachment = {
406
- view: view,
407
- clearValue: [0, 0, 0, 0],
408
- loadOp: 'load' as GPULoadOp,
409
- storeOp: 'store' as GPUStoreOp,
410
- };
411
-
412
- pipelineRenderFunction(
413
- root,
414
- pipelines,
415
- [
416
- initialAttachment,
417
- loadingAttachment,
418
- loadingAttachment,
419
- loadingAttachment,
420
- loadingAttachment,
421
- ],
422
- view,
423
- isInSinglePass
424
- );
425
-
426
- context.present();
427
- frameRef.current = requestAnimationFrame(render);
428
- };
429
- frameRef.current = requestAnimationFrame(render);
430
-
431
- return () => {
432
- if (frameRef.current) cancelAnimationFrame(frameRef.current);
433
- };
434
- }, [
435
- device,
436
- context,
437
- root,
438
- presentationFormat,
439
- imageTexture,
440
- maskTexture,
441
- rotationShared,
442
- bufferManager,
443
- glareOptions,
444
- colorMaskOptions,
445
- maskURI,
446
- addHolo,
447
- addReverseHolo,
448
- addTextureMask,
449
- pixelWidth,
450
- pixelHeight,
451
- ]);
24
+ if (root && maskURI) loadBitmap(root, maskURI, setMaskTexture);
25
+ }, [root, imageURI, maskURI]);
452
26
 
453
27
  return (
454
- <Animated.View style={[animatedStyle]}>
455
- <View
456
- style={
457
- [
458
- // styles.container,
459
- // { width: logicalWidth, height: logicalHeight },
460
- ]
461
- }
462
- >
463
- <Canvas
464
- ref={ref}
465
- style={[
466
- { width: logicalWidth, height: logicalHeight },
467
- // aspectRatio: pixelWidth / pixelHeight,
468
- // { transform: [{ scaleX: 1 / dpr }, { scaleY: 1 / dpr }] },
469
- ]}
470
- transparent={Platform.OS === 'ios'}
471
- // transparent={true}
472
- />
473
- </View>
474
- </Animated.View>
28
+ root &&
29
+ imageTexture && (
30
+ <Content
31
+ {...props}
32
+ root={root}
33
+ imageTexture={imageTexture}
34
+ maskTexture={maskTexture}
35
+ />
36
+ )
475
37
  );
476
38
  }
477
-
478
- // const styles = StyleSheet.create({
479
- // container: { overflow: 'hidden' },
480
- // });
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import { type PropsWithChildren, useEffect, useRef, useState } from 'react';
2
2
  import {
3
3
  View,
4
4
  StyleSheet,
@@ -7,11 +7,12 @@ import {
7
7
  PixelRatio,
8
8
  } from 'react-native';
9
9
  import ViewShot, { captureRef } from 'react-native-view-shot';
10
+ import type { V2d } from '../types/vector';
11
+ import { sizeFromV2d, sizeToV2d } from '../utils/size';
12
+ import { areV2dEqual, multiplyV2d, round2D } from '../utils/vector';
10
13
  import { Shine, type ShineProps } from './Shine';
11
14
 
12
- interface ShineGroupProps {
13
- children: React.ReactNode;
14
- }
15
+ type ShineGroupProps = PropsWithChildren<Partial<ShineProps>>;
15
16
 
16
17
  export function ShineGroup({
17
18
  children,
@@ -23,21 +24,17 @@ export function ShineGroup({
23
24
  addTextureMask = false,
24
25
  addHolo = false,
25
26
  addReverseHolo = false,
26
- }: ShineGroupProps & Partial<ShineProps>) {
27
- const viewShotRef = useRef<any>(null);
27
+ }: ShineGroupProps) {
28
+ const viewShotRef = useRef<ViewShot>(null);
28
29
  const [capturedURI, setCapturedURI] = useState<string | null>(null);
29
- const [size, setSize] = useState<{ width: number; height: number } | null>(
30
- null
31
- );
30
+ const [size, setSize] = useState<V2d | null>(null);
32
31
 
33
32
  const onInnerLayout = (e: LayoutChangeEvent) => {
34
- const { width, height } = e.nativeEvent.layout;
35
- if (width > 0 && height > 0) {
36
- if (!size || size.width !== width || size.height !== height) {
37
- setSize({ width, height });
38
- }
33
+ const layoutV2d = sizeToV2d(e.nativeEvent.layout);
34
+
35
+ if (!size || !areV2dEqual(size, layoutV2d)) {
36
+ setSize(layoutV2d);
39
37
  }
40
- console.log('onInnerLayout', width, height);
41
38
  };
42
39
 
43
40
  // When we have a valid measured size, take a snapshot (after a short tick)
@@ -48,15 +45,12 @@ export function ShineGroup({
48
45
  let mounted = true;
49
46
  const t = setTimeout(async () => {
50
47
  try {
51
- const dpr = PixelRatio.get();
52
- const pixelW = Math.round(size.width * dpr);
53
- const pixelH = Math.round(size.height * dpr);
48
+ const pixel = round2D(multiplyV2d(size, PixelRatio.get()));
54
49
 
55
- const uri = await captureRef(viewShotRef.current, {
50
+ const uri = await captureRef(viewShotRef, {
56
51
  format: 'png',
57
52
  quality: 1,
58
- width: pixelW,
59
- height: pixelH,
53
+ ...sizeFromV2d(pixel),
60
54
  });
61
55
  if (mounted) setCapturedURI(uri);
62
56
  } catch (err) {
@@ -79,13 +73,12 @@ export function ShineGroup({
79
73
  </ViewShot>
80
74
 
81
75
  {capturedURI && size && (
82
- <Image src={capturedURI} width={size.width} height={size.height} />
76
+ <Image src={capturedURI} {...sizeFromV2d(size)} />
83
77
  )}
84
78
 
85
79
  {capturedURI && size && (
86
80
  <Shine
87
- width={size.width}
88
- height={size.height}
81
+ {...sizeFromV2d(size)}
89
82
  imageURI={capturedURI}
90
83
  glareOptions={glareOptions}
91
84
  colorMaskOptions={colorMaskOptions}
@@ -0,0 +1,21 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+
3
+ export default function useAnimationFrame(cb: () => void) {
4
+ const requestId = useRef<number>(null);
5
+
6
+ const onFrame = useCallback(
7
+ function () {
8
+ cb();
9
+ requestId.current = requestAnimationFrame(onFrame);
10
+ },
11
+ [cb]
12
+ );
13
+
14
+ useEffect(() => {
15
+ requestId.current = requestAnimationFrame(onFrame);
16
+
17
+ return () => {
18
+ requestId.current === null || cancelAnimationFrame(requestId.current);
19
+ };
20
+ }, [onFrame]);
21
+ }
@@ -1,20 +1,18 @@
1
1
  import { useEffect, useState } from 'react';
2
- import {
3
- getAngleFromDimensions,
4
- subscribeToOrientationChange,
5
- } from '../shaders/utils';
2
+ import { subscribeToOrientationChange } from '../shaders/utils';
6
3
 
7
- export const useOrientation = () => {
8
- const [orientation, setOrientation] = useState<string>();
4
+ type Orientation = 'LANDSCAPE' | 'PORTRAIT';
9
5
 
10
- useEffect(() => {
11
- setOrientation(getAngleFromDimensions() === 0 ? 'PORTRAIT' : 'LANDSCAPE');
12
- const unsubscribe = subscribeToOrientationChange((angleDeg) => {
13
- setOrientation(angleDeg === 0 ? 'PORTRAIT' : 'LANDSCAPE');
14
- });
6
+ export const useOrientation = () => {
7
+ const [orientation, setOrientation] = useState<Orientation>();
15
8
 
16
- return () => unsubscribe();
17
- }, []);
9
+ useEffect(
10
+ () =>
11
+ subscribeToOrientationChange((isLandscape) =>
12
+ setOrientation(isLandscape ? 'LANDSCAPE' : 'PORTRAIT')
13
+ ),
14
+ []
15
+ );
18
16
 
19
17
  return orientation;
20
18
  };
package/src/index.tsx CHANGED
@@ -1,10 +1,18 @@
1
1
  import { useOrientation } from './hooks/useOrientation';
2
2
  import {
3
3
  getAngleFromDimensions,
4
+ isLandscapeMode,
4
5
  subscribeToOrientationChange,
5
6
  } from './shaders/utils';
6
7
 
7
- export { subscribeToOrientationChange, getAngleFromDimensions, useOrientation };
8
+ export {
9
+ subscribeToOrientationChange,
10
+ getAngleFromDimensions,
11
+ isLandscapeMode,
12
+ useOrientation,
13
+ };
8
14
  export { Shine } from './components/Shine';
9
15
  export { ShineGroup } from './components/ShineGroup';
10
16
  export type { ShineProps } from './components/Shine';
17
+ export * from './utils/vector';
18
+ export type { V2d, V3d } from './types/vector';