@3driseai/3drise-viewer 0.1.29 → 0.1.30

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 (164) hide show
  1. package/dist/animationsV2/ClipsRuntime.d.ts +4 -0
  2. package/dist/animationsV2/ClipsRuntime.d.ts.map +1 -1
  3. package/dist/animationsV2/ClipsRuntime.js +66 -2
  4. package/dist/animationsV2/clipUtils.js +1 -1
  5. package/dist/animationsV2/sequenceUtils.d.ts +5 -0
  6. package/dist/animationsV2/sequenceUtils.d.ts.map +1 -1
  7. package/dist/animationsV2/sequenceUtils.js +10 -0
  8. package/dist/animationsV2/types.d.ts +6 -0
  9. package/dist/animationsV2/types.d.ts.map +1 -1
  10. package/dist/components/EffectsGenerator.d.ts.map +1 -1
  11. package/dist/components/EffectsGenerator.js +43 -38
  12. package/dist/components/GeometryShape.d.ts.map +1 -1
  13. package/dist/components/GeometryShape.js +22 -2
  14. package/dist/components/ObjectNode.d.ts.map +1 -1
  15. package/dist/components/ObjectNode.js +6 -17
  16. package/dist/components/ParticlesGenerator.d.ts.map +1 -1
  17. package/dist/components/ParticlesGenerator.js +44 -24
  18. package/dist/components/PickVolume.d.ts +17 -0
  19. package/dist/components/PickVolume.d.ts.map +1 -0
  20. package/dist/components/PickVolume.js +20 -0
  21. package/dist/components/SkyStars.d.ts.map +1 -1
  22. package/dist/components/SkyStars.js +83 -2
  23. package/dist/components/TextGenerator.d.ts.map +1 -1
  24. package/dist/components/TextGenerator.js +3 -2
  25. package/dist/components/index.d.ts +1 -8
  26. package/dist/components/index.d.ts.map +1 -1
  27. package/dist/components/index.js +1 -4
  28. package/dist/components/particles/BioluminescentWakeParticles.d.ts +5 -0
  29. package/dist/components/particles/BioluminescentWakeParticles.d.ts.map +1 -1
  30. package/dist/components/particles/BioluminescentWakeParticles.js +2 -1
  31. package/dist/components/particles/BrownianDustParticles.d.ts +5 -0
  32. package/dist/components/particles/BrownianDustParticles.d.ts.map +1 -1
  33. package/dist/components/particles/BrownianDustParticles.js +2 -0
  34. package/dist/components/particles/ChladniSandParticles.d.ts +5 -0
  35. package/dist/components/particles/ChladniSandParticles.d.ts.map +1 -1
  36. package/dist/components/particles/ChladniSandParticles.js +2 -1
  37. package/dist/components/particles/ElectronOrbitalsParticles.d.ts +5 -0
  38. package/dist/components/particles/ElectronOrbitalsParticles.d.ts.map +1 -1
  39. package/dist/components/particles/ElectronOrbitalsParticles.js +2 -1
  40. package/dist/components/particles/FaradayPowderParticles.d.ts +5 -0
  41. package/dist/components/particles/FaradayPowderParticles.d.ts.map +1 -1
  42. package/dist/components/particles/FaradayPowderParticles.js +2 -1
  43. package/dist/components/particles/GrinderSparksParticles.d.ts +5 -0
  44. package/dist/components/particles/GrinderSparksParticles.d.ts.map +1 -1
  45. package/dist/components/particles/GrinderSparksParticles.js +2 -1
  46. package/dist/components/particles/MagneticBottleParticles.d.ts +5 -0
  47. package/dist/components/particles/MagneticBottleParticles.d.ts.map +1 -1
  48. package/dist/components/particles/MagneticBottleParticles.js +2 -1
  49. package/dist/components/particles/NeonRainParticles.d.ts +5 -0
  50. package/dist/components/particles/NeonRainParticles.d.ts.map +1 -1
  51. package/dist/components/particles/NeonRainParticles.js +2 -1
  52. package/dist/components/particles/ShapeBurstParticles.d.ts +5 -0
  53. package/dist/components/particles/ShapeBurstParticles.d.ts.map +1 -1
  54. package/dist/components/particles/ShapeBurstParticles.js +2 -1
  55. package/dist/components/particles/ShapeFlowParticles.d.ts +5 -0
  56. package/dist/components/particles/ShapeFlowParticles.d.ts.map +1 -1
  57. package/dist/components/particles/ShapeFlowParticles.js +2 -1
  58. package/dist/components/particles/TidalStreamParticles.d.ts +5 -0
  59. package/dist/components/particles/TidalStreamParticles.d.ts.map +1 -1
  60. package/dist/components/particles/TidalStreamParticles.js +2 -0
  61. package/dist/components/text/PlainText.d.ts +15 -0
  62. package/dist/components/text/PlainText.d.ts.map +1 -0
  63. package/dist/components/text/PlainText.js +4 -0
  64. package/dist/components/text/TextObjectView.d.ts +3 -1
  65. package/dist/components/text/TextObjectView.d.ts.map +1 -1
  66. package/dist/components/text/TextObjectView.js +9 -3
  67. package/dist/components/text/engine/looks/index.d.ts.map +1 -1
  68. package/dist/components/text/engine/looks/index.js +2 -1
  69. package/dist/components/text/engine/looks/plain.d.ts +146 -0
  70. package/dist/components/text/engine/looks/plain.d.ts.map +1 -0
  71. package/dist/components/text/engine/looks/plain.js +104 -0
  72. package/dist/components/text/fonts/bebas-neue.outline.d.ts +3 -0
  73. package/dist/components/text/fonts/bebas-neue.outline.d.ts.map +1 -0
  74. package/dist/components/text/fonts/bebas-neue.outline.js +4 -0
  75. package/dist/components/text/fonts/bebas-neue.strokes.d.ts +3 -0
  76. package/dist/components/text/fonts/bebas-neue.strokes.d.ts.map +1 -0
  77. package/dist/components/text/fonts/bebas-neue.strokes.js +4 -0
  78. package/dist/components/text/fonts/index.d.ts +1 -1
  79. package/dist/components/text/fonts/index.d.ts.map +1 -1
  80. package/dist/components/text/fonts/index.js +15 -1
  81. package/dist/components/text/fonts/inter.outline.d.ts +3 -0
  82. package/dist/components/text/fonts/inter.outline.d.ts.map +1 -0
  83. package/dist/components/text/fonts/inter.outline.js +4 -0
  84. package/dist/components/text/fonts/inter.strokes.d.ts +3 -0
  85. package/dist/components/text/fonts/inter.strokes.d.ts.map +1 -0
  86. package/dist/components/text/fonts/inter.strokes.js +4 -0
  87. package/dist/components/text/fonts/lora.outline.d.ts +3 -0
  88. package/dist/components/text/fonts/lora.outline.d.ts.map +1 -0
  89. package/dist/components/text/fonts/lora.outline.js +4 -0
  90. package/dist/components/text/fonts/lora.strokes.d.ts +3 -0
  91. package/dist/components/text/fonts/lora.strokes.d.ts.map +1 -0
  92. package/dist/components/text/fonts/lora.strokes.js +4 -0
  93. package/dist/components/text/fonts/montserrat.outline.d.ts +3 -0
  94. package/dist/components/text/fonts/montserrat.outline.d.ts.map +1 -0
  95. package/dist/components/text/fonts/montserrat.outline.js +4 -0
  96. package/dist/components/text/fonts/montserrat.strokes.d.ts +3 -0
  97. package/dist/components/text/fonts/montserrat.strokes.d.ts.map +1 -0
  98. package/dist/components/text/fonts/montserrat.strokes.js +4 -0
  99. package/dist/components/text/fonts/oswald.outline.d.ts +3 -0
  100. package/dist/components/text/fonts/oswald.outline.d.ts.map +1 -0
  101. package/dist/components/text/fonts/oswald.outline.js +4 -0
  102. package/dist/components/text/fonts/oswald.strokes.d.ts +3 -0
  103. package/dist/components/text/fonts/oswald.strokes.d.ts.map +1 -0
  104. package/dist/components/text/fonts/oswald.strokes.js +4 -0
  105. package/dist/components/text/fonts/playfair-display.outline.d.ts +3 -0
  106. package/dist/components/text/fonts/playfair-display.outline.d.ts.map +1 -0
  107. package/dist/components/text/fonts/playfair-display.outline.js +4 -0
  108. package/dist/components/text/fonts/playfair-display.strokes.d.ts +3 -0
  109. package/dist/components/text/fonts/playfair-display.strokes.d.ts.map +1 -0
  110. package/dist/components/text/fonts/playfair-display.strokes.js +4 -0
  111. package/dist/components/text/fonts/roboto.outline.d.ts +3 -0
  112. package/dist/components/text/fonts/roboto.outline.d.ts.map +1 -0
  113. package/dist/components/text/fonts/roboto.outline.js +4 -0
  114. package/dist/components/text/fonts/roboto.strokes.d.ts +3 -0
  115. package/dist/components/text/fonts/roboto.strokes.d.ts.map +1 -0
  116. package/dist/components/text/fonts/roboto.strokes.js +4 -0
  117. package/dist/components/text/index.d.ts +2 -0
  118. package/dist/components/text/index.d.ts.map +1 -1
  119. package/dist/components/text/index.js +1 -0
  120. package/dist/components/text/useTextObject.d.ts +1 -0
  121. package/dist/components/text/useTextObject.d.ts.map +1 -1
  122. package/dist/components/text/useTextObject.js +23 -11
  123. package/dist/constants.d.ts.map +1 -1
  124. package/dist/constants.js +2 -0
  125. package/dist/index.d.ts +1 -1
  126. package/dist/index.d.ts.map +1 -1
  127. package/dist/index.js +1 -1
  128. package/dist/operations/catalogue/flock.d.ts.map +1 -1
  129. package/dist/operations/catalogue/flock.js +44 -9
  130. package/dist/operations/catalogue/inkBloom.d.ts.map +1 -1
  131. package/dist/operations/catalogue/inkBloom.js +46 -9
  132. package/dist/operations/catalogue/liquidMetal.d.ts.map +1 -1
  133. package/dist/operations/catalogue/liquidMetal.js +41 -12
  134. package/dist/operations/catalogue/particleSwarm.d.ts.map +1 -1
  135. package/dist/operations/catalogue/particleSwarm.js +29 -9
  136. package/dist/operations/catalogue/petalPeel.d.ts.map +1 -1
  137. package/dist/operations/catalogue/petalPeel.js +33 -9
  138. package/dist/operations/catalogue/portal.d.ts.map +1 -1
  139. package/dist/operations/catalogue/portal.js +33 -11
  140. package/dist/operations/catalogue/sandErosion.d.ts.map +1 -1
  141. package/dist/operations/catalogue/sandErosion.js +36 -14
  142. package/dist/operations/catalogue/teleport.d.ts.map +1 -1
  143. package/dist/operations/catalogue/teleport.js +43 -12
  144. package/dist/operations/catalogue/unweave.d.ts.map +1 -1
  145. package/dist/operations/catalogue/unweave.js +34 -15
  146. package/dist/operations/catalogue/voxelize.d.ts.map +1 -1
  147. package/dist/operations/catalogue/voxelize.js +37 -10
  148. package/dist/triggers/engine.d.ts +1 -1
  149. package/dist/triggers/engine.d.ts.map +1 -1
  150. package/dist/triggers/engine.js +3 -0
  151. package/dist/triggers/hosts/actions.d.ts.map +1 -1
  152. package/dist/triggers/hosts/actions.js +1 -1
  153. package/dist/triggers/hosts/interactions.d.ts.map +1 -1
  154. package/dist/triggers/hosts/interactions.js +1 -1
  155. package/dist/utils/pickProxy.d.ts +11 -0
  156. package/dist/utils/pickProxy.d.ts.map +1 -0
  157. package/dist/utils/pickProxy.js +36 -0
  158. package/dist/utils/shapes.d.ts +4 -0
  159. package/dist/utils/shapes.d.ts.map +1 -1
  160. package/dist/utils/shapes.js +4 -1
  161. package/dist/utils/textShape.d.ts +24 -0
  162. package/dist/utils/textShape.d.ts.map +1 -0
  163. package/dist/utils/textShape.js +102 -0
  164. package/package.json +2 -2
@@ -9,10 +9,12 @@ import { createPlayback } from './engine/playback.js';
9
9
  import { VARIANTS } from './engine/variants.js';
10
10
  import { layoutOutline, layoutStrokeFont } from './engine/glyphs.js';
11
11
  import { loadOutline, loadStrokes } from './fonts/index.js';
12
+ import { extentOf } from './engine/methods.js';
13
+ import { stopRaycast } from '../../utils/pickProxy.js';
12
14
  /** One em of the font in world units: the object's own size, so a text of 0.5 m high default. The parent transform does the rest. */
13
15
  export const TEXT_EM = 0.5;
14
16
  // Keys that are not look knobs.
15
- const BASE_KEYS = new Set(['type', 'enabled', 'text', 'font', 'drawMethod', 'drawDuration', 'drawEase', 'drawStagger', 'drawAngle',
17
+ const BASE_KEYS = new Set(['type', 'shown', 'text', 'font', 'drawMethod', 'drawDuration', 'drawEase', 'drawStagger', 'drawAngle',
16
18
  'eraseMethod', 'eraseDuration', 'eraseEase', 'eraseStagger', 'eraseAngle', 'autoPlay', 'loop', 'yoyo', 'holdTime', 'hiddenTime']);
17
19
  /** Animation keys that bake a schedule into the geometry: a change rebuilds. */
18
20
  const ANIM_STRUCTURAL = ['drawMethod', 'drawStagger', 'drawAngle', 'eraseMethod', 'eraseStagger', 'eraseAngle'];
@@ -36,6 +38,8 @@ export function useTextObject(look, props) {
36
38
  const S = staticOf(look);
37
39
  const rootRef = useRef(null);
38
40
  const innerRef = useRef(null);
41
+ // Hover / click land on one plane over the text's box (em units, inside the em-scaled group), never on a letter.
42
+ const proxyRef = useRef(null);
39
43
  const cfg = useMemo(() => withConfigDefaults(DEFAULT_TEXT_OBJECTS[look], config), [config, look]);
40
44
  useObjectReady(rootRef, cfg, onObjectReady, onObjectDispose);
41
45
  const cfgRef = useRef(cfg);
@@ -71,10 +75,18 @@ export function useTextObject(look, props) {
71
75
  const now = cfgRef.current;
72
76
  syncAnim(now);
73
77
  built = V.build(layout, lookCfg(now), anim.current);
78
+ stopRaycast(built.object);
74
79
  innerRef.current.add(built.object);
75
80
  inst.current = built;
81
+ const e = extentOf(layout.strokes);
82
+ const pad = 0.12; // em: a little air around the glyphs
83
+ const proxy = proxyRef.current;
84
+ if (proxy) {
85
+ proxy.position.set((e.x0 + e.x1) / 2, (e.y0 + e.y1) / 2, 0);
86
+ proxy.scale.set(e.w + 2 * pad, e.h + 2 * pad, 1);
87
+ }
76
88
  pb.current.reset(anim.current);
77
- if (now.enabled === false)
89
+ if (now.shown === false)
78
90
  pb.current.hide();
79
91
  });
80
92
  return () => { dead = true; if (built) {
@@ -84,24 +96,24 @@ export function useTextObject(look, props) {
84
96
  inst.current = null;
85
97
  } };
86
98
  }, [look, rebuildKey, lookCfg, syncAnim]);
87
- // `enabled` is the trigger: on draws, off erases (then stays hidden). Loop / yoyo restart the cycle.
88
- const prev = useRef({ enabled: cfg.enabled, loop: cfg.loop, yoyo: cfg.yoyo });
99
+ // `shown` is the trigger: on draws, off erases (then stays hidden). Loop / yoyo restart the cycle.
100
+ const prev = useRef({ shown: cfg.shown, loop: cfg.loop, yoyo: cfg.yoyo });
89
101
  useEffect(() => {
90
102
  const p = prev.current;
91
- const changedEnabled = p.enabled !== cfg.enabled;
103
+ const changedShown = p.shown !== cfg.shown;
92
104
  const changedPlay = p.loop !== cfg.loop || p.yoyo !== cfg.yoyo;
93
- prev.current = { enabled: cfg.enabled, loop: cfg.loop, yoyo: cfg.yoyo };
105
+ prev.current = { shown: cfg.shown, loop: cfg.loop, yoyo: cfg.yoyo };
94
106
  if (!inst.current)
95
107
  return;
96
- if (changedEnabled) {
97
- if (cfg.enabled === false)
108
+ if (changedShown) {
109
+ if (cfg.shown === false)
98
110
  pb.current.playErase(anim.current);
99
111
  else
100
112
  pb.current.restart(anim.current);
101
113
  }
102
- else if (changedPlay && cfg.enabled !== false)
114
+ else if (changedPlay && cfg.shown !== false)
103
115
  pb.current.restart(anim.current);
104
- }, [cfg.enabled, cfg.loop, cfg.yoyo]);
116
+ }, [cfg.shown, cfg.loop, cfg.yoyo]);
105
117
  const scale = useMemo(() => new THREE.Vector3(), []);
106
118
  useFrame((state, dt) => {
107
119
  const i = inst.current;
@@ -114,5 +126,5 @@ export function useTextObject(look, props) {
114
126
  root.getWorldScale(scale);
115
127
  i.update(st, state.clock.elapsedTime, px * Math.abs(scale.x) * TEXT_EM);
116
128
  });
117
- return { rootRef, innerRef };
129
+ return { rootRef, innerRef, proxyRef };
118
130
  }
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,gBAAgB,gCAAgC,CAAC;AAC9D,eAAO,MAAM,qBAAqB,aAmBhC,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,gBAAgB,gCAAgC,CAAC;AAC9D,eAAO,MAAM,qBAAqB,aAqBhC,CAAC"}
package/dist/constants.js CHANGED
@@ -17,6 +17,8 @@ export const SELF_MANAGED_MATERIAL = new Set([
17
17
  'environment',
18
18
  // Land objects draw with the terrain material (TERRAIN_PATCH).
19
19
  'land',
20
+ // Text looks (plain, handwriting, neon…) draw with their own shaders; 3d text is a mesh and takes a material.
21
+ 'text',
20
22
  // The water surface owns its shader — one material for both hosts, built
21
23
  // around the wave field. Left out of this set, useMaterialApplication
22
24
  // overwrites it with whatever the registry has under materialType, which for
package/dist/index.d.ts CHANGED
@@ -15,13 +15,13 @@ export { cameraManager } from './utils/CameraSingleton';
15
15
  export { applyAnchorSettings, createCurves, createSunPosition, randomRange, } from './utils/utils3d';
16
16
  export { applyAnchorSettings as applyAnchorSettingsLegacy } from './utils/anchorUtils';
17
17
  export * from './utils/shapes';
18
+ export * from './utils/pickProxy';
18
19
  export { hasHoverAnimation } from './utils/hasHoverAnimation';
19
20
  export { findById } from './utils/sceneQuery';
20
21
  export { hasFrameRenderer, registerFrameRenderer, renderFrame } from './utils/frameRenderer';
21
22
  export type { FrameRenderer } from './utils/frameRenderer';
22
23
  export { resolveObject3D } from './utils/resolveObject3D';
23
24
  export type { Object3DRefLike, Object3DSource } from './utils/resolveObject3D';
24
- export * from './utils/textEffects';
25
25
  export { applyTerrainUniforms, applyTerrainTextures } from './utils/terrainUniforms';
26
26
  export { useTerrainTextures } from './hooks/useTerrainTextures';
27
27
  export { terrainHeightAt } from './utils/terrainHeight';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -18,11 +18,11 @@ export { cameraManager } from './utils/CameraSingleton.js';
18
18
  export { applyAnchorSettings, createCurves, createSunPosition, randomRange, } from './utils/utils3d.js';
19
19
  export { applyAnchorSettings as applyAnchorSettingsLegacy } from './utils/anchorUtils.js';
20
20
  export * from './utils/shapes.js';
21
+ export * from './utils/pickProxy.js';
21
22
  export { hasHoverAnimation } from './utils/hasHoverAnimation.js';
22
23
  export { findById } from './utils/sceneQuery.js';
23
24
  export { hasFrameRenderer, registerFrameRenderer, renderFrame } from './utils/frameRenderer.js';
24
25
  export { resolveObject3D } from './utils/resolveObject3D.js';
25
- export * from './utils/textEffects.js';
26
26
  export { applyTerrainUniforms, applyTerrainTextures } from './utils/terrainUniforms.js';
27
27
  export { useTerrainTextures } from './hooks/useTerrainTextures.js';
28
28
  export { terrainHeightAt } from './utils/terrainHeight.js';
@@ -1 +1 @@
1
- {"version":3,"file":"flock.d.ts","sourceRoot":"","sources":["../../../src/operations/catalogue/flock.ts"],"names":[],"mappings":";AA6CA,wBAwHG"}
1
+ {"version":3,"file":"flock.d.ts","sourceRoot":"","sources":["../../../src/operations/catalogue/flock.ts"],"names":[],"mappings":";AA+CA,wBAyJG"}
@@ -4,6 +4,8 @@
4
4
  // streams — out and up, or all toward one side of the screen — accelerating
5
5
  // until they are gone. Birds always face the way they fly, so on revert they
6
6
  // fly back in (head first) and settle into place as the surface re-forms.
7
+ // With Disappear off (an object effect's default) the birds stay perched on every
8
+ // spot of the surface (the surface itself gone): a flutter now and then, heads turning.
7
9
  import { BufferAttribute, BufferGeometry, Vector3 } from 'three';
8
10
  import { defineOperation } from '../types.js';
9
11
  import { addLayer, forEachMeshMaterial, removeLayer } from './shaderLayers.js';
@@ -36,17 +38,19 @@ function birdGeometry() {
36
38
  export default defineOperation({
37
39
  value: 'flock',
38
40
  label: 'Flock',
39
- description: 'Turn the targets into a flock of birds that takes off and flies away — reverting flies them home',
41
+ description: 'Turn the targets into a flock of birds that takes off and flies away (or, Disappear off, stays perched) — reverting flies them home',
40
42
  channel: 'visual',
41
43
  multi: true,
42
44
  toggles: true,
43
- usableIn: ['interaction', 'action'],
45
+ usableIn: ['interaction', 'action', 'object'],
46
+ objectParams: { disappear: false },
44
47
  params: {
45
48
  direction: { type: 'select', label: 'Take-off order', default: 'down', options: DIRECTION_OPTIONS },
46
- exit: { type: 'select', label: 'Fly', default: 'away', options: [{ value: 'away', label: 'Out and up' }, { value: 'left', label: 'To the right' }, { value: 'right', label: 'To the left' }, { value: 'up', label: 'Straight up' }] },
49
+ disappear: { type: 'boolean', label: 'Disappear', default: true, hint: 'Off: the birds stay perched on the object' },
50
+ exit: { type: 'select', label: 'Fly', default: 'away', showWhen: { disappear: ['true'] }, options: [{ value: 'away', label: 'Out and up' }, { value: 'left', label: 'To the right' }, { value: 'right', label: 'To the left' }, { value: 'up', label: 'Straight up' }] },
47
51
  birds: { type: 'number', label: 'Birds', default: 1400, min: 100, max: 12000, step: 100 },
48
52
  birdSize: { type: 'number', label: 'Bird size', default: 1, min: 0.3, max: 4, step: 0.05, unit: '×' },
49
- speed: { type: 'number', label: 'Speed', default: 1, min: 0.2, max: 4, step: 0.05, unit: '×' },
53
+ speed: { type: 'number', label: 'Speed', default: 1, min: 0.2, max: 4, step: 0.05, unit: '×', showWhen: { disappear: ['true'] } },
50
54
  edgeColor: { type: 'color', label: 'Edge colour', default: '#ffffff' },
51
55
  scale: { type: 'number', label: 'Pattern', default: 2.5, min: 0.5, max: 12, step: 0.1 },
52
56
  duration: { type: 'number', label: 'Duration', default: 3.4, min: 0.3, max: 14, step: 0.1, unit: 's' },
@@ -57,7 +61,7 @@ export default defineOperation({
57
61
  const field = createFieldState(target);
58
62
  field.edgeWidth.value = 0.04;
59
63
  const own = {
60
- field, exit: { value: new Vector3(1, 0, 0) }, mode: { value: 0 }, speed: { value: 1 }, dirSign: { value: 1 }, bird: { value: 1 }, mats: new Set(),
64
+ field, exit: { value: new Vector3(1, 0, 0) }, mode: { value: 0 }, speed: { value: 1 }, dirSign: { value: 1 }, bird: { value: 1 }, keep: { value: 0 }, mats: new Set(),
61
65
  };
62
66
  const sample = sampleSurfaceCached(target, Math.round(Number(p.birds)), 53);
63
67
  const n = sample.count;
@@ -68,7 +72,7 @@ export default defineOperation({
68
72
  attributes: { aFlkP: [pos, 3], aFlkN: [sample.normal.slice(0, n * 3), 3], aFlkR: [sample.random.slice(0, n * 4), 4] },
69
73
  layerKey: 'flockInstances', roughness: 0.65, metalness: 0, doubleSided: true,
70
74
  layer: {
71
- uniforms: { ...fieldUniforms(P, field), uFlkExit: own.exit, uFlkMode: own.mode, uFlkSpeed: own.speed, uFlkDirSign: own.dirSign, uFlkBird: own.bird },
75
+ uniforms: { ...fieldUniforms(P, field), uFlkExit: own.exit, uFlkMode: own.mode, uFlkSpeed: own.speed, uFlkDirSign: own.dirSign, uFlkBird: own.bird, uFlkKeep: own.keep },
72
76
  common: fieldGlsl(P),
73
77
  vertexPars: /* glsl */ `
74
78
  attribute float aWing; attribute vec3 aFlkP; attribute vec3 aFlkN; attribute vec4 aFlkR;
@@ -98,12 +102,33 @@ export default defineOperation({
98
102
  vec3 right = normalize(cross(fwd, vec3(0.0, 1.0, 0.0)) + vec3(1e-4));
99
103
  vec3 up = cross(right, fwd);
100
104
  return mat3(right, up, fwd);
101
- }`,
102
- normal: `{ objectNormal = flkFrame(clamp(flkAge(), 0.0, 1.0)) * objectNormal; }`,
105
+ }
106
+ // perched: sitting on its spot, up along the surface normal, turning its head now and then
107
+ mat3 flkPerch() {
108
+ vec3 up = normalize(aFlkN + vec3(1e-4));
109
+ vec3 any = abs(up.y) < 0.9 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
110
+ vec3 right0 = normalize(cross(up, any));
111
+ float yaw = aFlkR.z * 6.2832 + step(0.6, aFlkR.y) * 0.35 * sin(uFlkTime * (0.4 + 0.5 * aFlkR.w) + aFlkR.x * 30.0);
112
+ vec3 right = right0 * cos(yaw) + cross(up, right0) * sin(yaw);
113
+ vec3 fwd = cross(right, up);
114
+ return mat3(right, up, fwd);
115
+ }
116
+ float flkHeld() { return uFlkKeep * step(0.9999, uFlkProgress); }`,
117
+ normal: `{ objectNormal = (uFlkKeep > 0.5 ? flkPerch() : flkFrame(clamp(flkAge(), 0.0, 1.0))) * objectNormal; }`,
103
118
  vertex: /* glsl */ `
104
119
  {
105
120
  float a = flkAge();
106
121
  if (a <= 0.0) { transformed *= 0.0; }
122
+ else if (uFlkKeep > 0.5) {
123
+ // perched on its spot; a brief flutter now and then
124
+ vec3 v = transformed;
125
+ float flutter = smoothstep(0.93, 1.0, sin(uFlkTime * (0.5 + 0.4 * aFlkR.x) + aFlkR.z * 60.0)) * flkHeld();
126
+ v.y += abs(aWing) * sin(uFlkTime * 22.0 + aFlkR.y * 6.28) * 0.38 * flutter;
127
+ v.y += abs(aWing) * 0.12; // wings held slightly up at rest
128
+ float size = uFlkExtent * 0.03 * uFlkBird * (0.8 + 0.4 * aFlkR.w);
129
+ float sc = smoothstep(0.0, 0.06, a);
130
+ transformed = flkPerch() * (v * size * sc) + aFlkN * uFlkExtent * 0.008;
131
+ }
107
132
  else {
108
133
  a = min(a, 1.0);
109
134
  vec3 v = transformed;
@@ -139,6 +164,15 @@ export default defineOperation({
139
164
  s.exit.value.copy(p.exit === 'left' ? r : r.negate());
140
165
  }
141
166
  s.mesh.visible = true;
167
+ s.keep.value = p.disappear === false ? 1 : 0;
168
+ if (s.keep.value) {
169
+ // keep: the surface becomes birds (0 → 1), perched, and holds
170
+ return runProgress(target, s.field, 1, {
171
+ duration: Number(p.duration) / (1 + LIFE), ease: 'none', hold: true,
172
+ onComplete: () => hideMeshes(target, KEY, true),
173
+ });
174
+ }
175
+ hideMeshes(target, KEY, false);
142
176
  return runProgress(target, s.field, 1 + LIFE, {
143
177
  duration: Number(p.duration), ease: 'power1.in',
144
178
  onComplete: () => { hideMeshes(target, KEY, true); s.mesh.visible = false; },
@@ -151,8 +185,9 @@ export default defineOperation({
151
185
  hideMeshes(target, KEY, false);
152
186
  s.mesh.visible = true;
153
187
  s.dirSign.value = -1;
188
+ // from a hold it only has part of the way to go: keep the same speed
154
189
  return runProgress(target, s.field, 0, {
155
- duration: Number(p.duration), ease: 'power1.out',
190
+ duration: Number(p.duration) * Math.min(1, Math.max(0.1, s.field.progress.value / (1 + LIFE))), ease: s.keep.value ? 'none' : 'power1.out',
156
191
  onComplete: () => {
157
192
  s.mats.forEach(m => removeLayer(m, KEY));
158
193
  disposeFxInstances(s.mesh);
@@ -1 +1 @@
1
- {"version":3,"file":"inkBloom.d.ts","sourceRoot":"","sources":["../../../src/operations/catalogue/inkBloom.ts"],"names":[],"mappings":";AAmDA,wBA4GG"}
1
+ {"version":3,"file":"inkBloom.d.ts","sourceRoot":"","sources":["../../../src/operations/catalogue/inkBloom.ts"],"names":[],"mappings":";AA4EA,wBAyHG"}
@@ -4,10 +4,12 @@
4
4
  // so the ink reads as clean curling threads that fray, sink and thin out, not
5
5
  // as soft blobs. Normal blending (dark on light), so it works on light scenes.
6
6
  // Reverting draws the ink back into the object.
7
+ // With Disappear off (an object effect's default) the object stays stained with ink,
8
+ // filaments hanging in the water around it, swaying but never thinning out.
7
9
  import { BufferAttribute, BufferGeometry, LineSegments, NormalBlending, ShaderMaterial } from 'three';
8
10
  import { defineOperation } from '../types.js';
9
11
  import { addLayer, forEachMeshMaterial, removeLayer, uniformDecl } from './shaderLayers.js';
10
- import { DIRECTION_OPTIONS, createFieldState, fieldGlsl, fieldUniforms, hideMeshes, runProgress, setFieldParams, surfaceLayer, } from './transitionField.js';
12
+ import { DIRECTION_OPTIONS, createFieldState, fieldGlsl, fieldUniforms, hideMeshes, runProgress, setFieldParams, setReveal, surfaceLayer, } from './transitionField.js';
11
13
  import { sampleSurfaceCached } from './surfaceSample.js';
12
14
  import { hideUntilWarm } from './warmup.js';
13
15
  const P = 'Ink';
@@ -16,6 +18,28 @@ const STATE = '__opInk';
16
18
  const LIFE = 0.9;
17
19
  /** Points per filament (its trail through the water). */
18
20
  const TRAIL = 12;
21
+ /** Keep: a wisp hangs this far along its path. */
22
+ const KEEP_AGE = 0.35;
23
+ /** The surface: stained along the front and gone behind it — or, keeping, stained all over and staying. */
24
+ function inkLayer(field, mixC, keep) {
25
+ const base = surfaceLayer(P, field, 'stain');
26
+ return {
27
+ ...base,
28
+ uniforms: { ...base.uniforms, uInkMix: mixC, uInkKeep: keep },
29
+ fragment: /* glsl */ `
30
+ if (uInkKeep > 0.5) {
31
+ float InkD = InkField(vInkQ) - InkThreshold();
32
+ float stained = smoothstep(0.0, uInkEdgeWidth, -InkD);
33
+ // painting the object in: nothing ahead of the front, full ink right behind it
34
+ if (uInkReveal > 0.5) { if (InkD > 0.0) discard; stained = 1.0; }
35
+ // ink: a slow swirl of slightly lighter ink moving through it
36
+ float swirl = 0.8 + 0.4 * fxNoise(vInkQ * 5.0 + vec3(uInkTime * 0.12, -uInkTime * 0.08, 0.0));
37
+ vec3 ink = mix(uInkEdgeColor, outgoingLight, uInkMix) * swirl;
38
+ outgoingLight = mix(outgoingLight, ink, stained);
39
+ } else {${base.fragment}
40
+ }`,
41
+ };
42
+ }
19
43
  function buildFilaments(target, count) {
20
44
  const s = sampleSurfaceCached(target, count, 29);
21
45
  const segs = TRAIL - 1;
@@ -45,18 +69,21 @@ function buildFilaments(target, count) {
45
69
  export default defineOperation({
46
70
  value: 'inkBloom',
47
71
  label: 'Ink bloom',
48
- description: 'Bleed the targets into fine curling ink filaments, like a drop in water — reverting draws the ink back in',
72
+ description: 'Bleed the targets into fine curling ink filaments, like a drop in water (or, Disappear off, keep them inked) — reverting draws the ink back in',
49
73
  channel: 'visual',
50
74
  multi: true,
51
75
  toggles: true,
52
- usableIn: ['interaction', 'action'],
76
+ usableIn: ['interaction', 'action', 'object'],
77
+ objectParams: { disappear: false },
78
+ presence: 'paint',
53
79
  params: {
54
80
  direction: { type: 'select', label: 'Direction', default: 'noise', options: DIRECTION_OPTIONS },
81
+ disappear: { type: 'boolean', label: 'Disappear', default: true, hint: 'Off: the object stays inked, filaments hanging around it' },
55
82
  edgeColor: { type: 'color', label: 'Ink colour', default: '#16203a' },
56
83
  tint: { type: 'number', label: 'Object colour', default: 0.15, min: 0, max: 1, step: 0.05, hint: 'How much of the object’s own colour the ink keeps' },
57
84
  filaments: { type: 'number', label: 'Filaments', default: 9000, min: 1000, max: 40000, step: 500 },
58
85
  opacity: { type: 'number', label: 'Density', default: 0.55, min: 0.05, max: 1, step: 0.05 },
59
- sink: { type: 'number', label: 'Sink', default: 1, min: -2, max: 3, step: 0.05, unit: '×', hint: 'Negative rises like smoke' },
86
+ sink: { type: 'number', label: 'Sink', default: 1, min: -2, max: 3, step: 0.05, unit: '×', hint: 'Negative rises like smoke', showWhen: { disappear: ['true'] } },
60
87
  scale: { type: 'number', label: 'Pattern', default: 2.5, min: 0.5, max: 12, step: 0.1 },
61
88
  duration: { type: 'number', label: 'Duration', default: 3.6, min: 0.3, max: 14, step: 0.1, unit: 's' },
62
89
  },
@@ -65,8 +92,8 @@ export default defineOperation({
65
92
  if (!st) {
66
93
  const field = createFieldState(target);
67
94
  field.edgeWidth.value = 0.12;
68
- const mixC = { value: 0.15 }, sink = { value: 1 }, opacity = { value: 0.55 };
69
- const uniforms = { ...fieldUniforms(P, field), uInkMix: mixC, uInkSink: sink, uInkOpacity: opacity };
95
+ const mixC = { value: 0.15 }, sink = { value: 1 }, opacity = { value: 0.55 }, keep = { value: 0 };
96
+ const uniforms = { ...fieldUniforms(P, field), uInkMix: mixC, uInkSink: sink, uInkOpacity: opacity, uInkKeep: keep };
70
97
  const decl = Object.keys(uniforms).map(n => uniformDecl(n, uniforms[n].value)).join('\n');
71
98
  const mat = new ShaderMaterial({
72
99
  uniforms: uniforms,
@@ -88,6 +115,8 @@ vec3 inkAt(vec3 p0, vec3 n0, vec4 r, float a) {
88
115
  void main() {
89
116
  float birth = InkBirthOf(InkField(InkNorm(position)));
90
117
  float a = (uInkProgress - birth) / ${LIFE.toFixed(2)};
118
+ // keep: the wisp hangs part-way along its path, swaying along it
119
+ a = mix(a, min(a, ${KEEP_AGE} * (0.92 + 0.08 * sin(uInkTime * 0.7 + aR.w * 6.2832))), uInkKeep);
91
120
  // the trail: this vertex sits a little back along the wisp's own path
92
121
  float trail = 0.22 * (0.6 + 0.8 * aR.w);
93
122
  float ak = clamp(a - aK * trail, 0.0, 1.0);
@@ -114,9 +143,9 @@ void main() {
114
143
  lines.renderOrder = 10;
115
144
  target.add(lines);
116
145
  hideUntilWarm(lines);
117
- st = { field, mixC, sink, opacity, lines, mats: new Set() };
146
+ st = { field, mixC, sink, opacity, keep, lines, mats: new Set() };
118
147
  const own = st;
119
- const layer = surfaceLayer(P, field, 'stain');
148
+ const layer = inkLayer(field, mixC, keep);
120
149
  forEachMeshMaterial(target, (m, mesh) => {
121
150
  if (mesh.userData?.__opFxHelper)
122
151
  return;
@@ -131,6 +160,12 @@ void main() {
131
160
  s.sink.value = Number(p.sink);
132
161
  s.opacity.value = Number(p.opacity);
133
162
  s.lines.visible = true;
163
+ s.keep.value = p.disappear === false ? 1 : 0;
164
+ hideMeshes(target, KEY, false);
165
+ if (s.keep.value) {
166
+ // keep: the ink spreads over the whole object (0 → 1) and holds
167
+ return runProgress(target, s.field, 1, { duration: Number(p.duration) / (1 + LIFE), ease: 'none', hold: true });
168
+ }
134
169
  return runProgress(target, s.field, 1 + LIFE, {
135
170
  duration: Number(p.duration), ease: 'none',
136
171
  onComplete: () => { hideMeshes(target, KEY, true); s.lines.visible = false; },
@@ -141,9 +176,11 @@ void main() {
141
176
  if (!s)
142
177
  return;
143
178
  hideMeshes(target, KEY, false);
179
+ setReveal(s.field, p);
144
180
  s.lines.visible = true;
181
+ // from a hold it only has part of the way to go: keep the same speed
145
182
  return runProgress(target, s.field, 0, {
146
- duration: Number(p.duration), ease: 'none',
183
+ duration: Number(p.duration) * Math.min(1, Math.max(0.1, s.field.progress.value / (1 + LIFE))), ease: 'none',
147
184
  onComplete: () => {
148
185
  s.mats.forEach(m => removeLayer(m, KEY));
149
186
  s.lines.removeFromParent();
@@ -1 +1 @@
1
- {"version":3,"file":"liquidMetal.d.ts","sourceRoot":"","sources":["../../../src/operations/catalogue/liquidMetal.ts"],"names":[],"mappings":";AA6EA,wBA8CG"}
1
+ {"version":3,"file":"liquidMetal.d.ts","sourceRoot":"","sources":["../../../src/operations/catalogue/liquidMetal.ts"],"names":[],"mappings":";AAgGA,wBAwDG"}
@@ -2,6 +2,9 @@
2
2
  // from the top down and flows out into a puddle on the ground (higher parts
3
3
  // travel further), with slow ripples running across the pool while it lies
4
4
  // there. Reverting pulls it back up into shape and the chrome fades off.
5
+ // With Disappear off (an object effect's default) the object turns to chrome and
6
+ // stays, unmelted: the studio reflection turns slowly, a soft ripple runs over it.
7
+ // The chrome comes in along a front (Direction) so it can paint the object in.
5
8
  //
6
9
  // The chrome is a procedural studio environment (sky gradient, horizon band,
7
10
  // two soft boxes) reflected about the surface normal, so it reads as metal in
@@ -11,22 +14,25 @@ import { gsap } from 'gsap';
11
14
  import { Color } from 'three';
12
15
  import { defineOperation } from '../types.js';
13
16
  import { addLayer, forEachMeshMaterial, removeLayer } from './shaderLayers.js';
14
- import { createFieldState, fieldGlsl, fieldUniforms, runProgress, syncRoot } from './transitionField.js';
17
+ import { DIRECTION_OPTIONS, createFieldState, fieldGlsl, fieldUniforms, runProgress, setFieldParams, setReveal, syncRoot } from './transitionField.js';
15
18
  const P = 'Liq';
16
19
  const KEY = 'liquidMetal';
17
20
  const STATE = '__opLiquid';
21
+ /** The chrome comes in over this much of the progress; the melt runs after it. */
22
+ const CHROME = 0.2;
18
23
  function layerFor(st) {
19
24
  return {
20
- uniforms: { ...fieldUniforms(P, st.field), uLiqTint: st.tint, uLiqSpread: st.spread },
21
- common: `varying float vLiqM;\nvarying vec3 vLiqView;\n${fieldGlsl(P)}`,
25
+ uniforms: { ...fieldUniforms(P, st.field), uLiqTint: st.tint, uLiqSpread: st.spread, uLiqKeep: st.keep },
26
+ common: `varying float vLiqM;\nvarying vec3 vLiqView;\nvarying vec3 vLiqQ;\n${fieldGlsl(P)}`,
22
27
  vertex: /* glsl */ `
23
28
  {
24
29
  mat4 toRoot = uLiqRootInv * modelMatrix;
25
30
  vec3 rl = (toRoot * vec4(transformed, 1.0)).xyz;
26
31
  vec3 q = LiqNorm(rl);
32
+ vLiqQ = q;
27
33
  float E = uLiqExtent;
28
- // melt runs after the chrome has come in; the top gives way first
29
- float mp = clamp((uLiqProgress - 0.18) / 0.82, 0.0, 1.0);
34
+ // melt runs after the chrome has come in; the top gives way first (never in keep)
35
+ float mp = clamp((uLiqProgress - 0.18) / 0.82, 0.0, 1.0) * (1.0 - uLiqKeep);
30
36
  float wob = 0.12 * (fxNoise(q * 4.0) - 0.5);
31
37
  float m = clamp(mp * 1.3 - (1.0 - q.y) * 0.3 + wob, 0.0, 1.0);
32
38
  m = m * m; // slow start: it sags before it runs
@@ -48,7 +54,13 @@ function layerFor(st) {
48
54
  }`,
49
55
  fragment: /* glsl */ `
50
56
  {
51
- float chrome = smoothstep(0.0, 0.2, uLiqProgress);
57
+ // the chrome comes in along a front over the first part of the progress
58
+ float tc = clamp(uLiqProgress / ${CHROME}, 0.0, 1.0) * (1.0 + 2.0 * uLiqEdgeWidth) - uLiqEdgeWidth;
59
+ float d = LiqField(vLiqQ) - tc;
60
+ float chrome = smoothstep(0.0, 0.08, -d);
61
+ // painting the object in: nothing ahead of the front, full chrome right behind it
62
+ if (uLiqReveal > 0.5) { if (d > 0.0) discard; chrome = 1.0; }
63
+ float held = uLiqKeep * step(0.9999, uLiqProgress);
52
64
  vec3 nv = normalize(cross(dFdx(vLiqView), dFdy(vLiqView)));
53
65
  #if defined( STANDARD ) || defined( PHONG ) || defined( LAMBERT ) || defined( TOON )
54
66
  vec3 n = normalize(mix(normal, nv, smoothstep(0.05, 0.5, vLiqM)));
@@ -56,8 +68,12 @@ function layerFor(st) {
56
68
  vec3 n = nv;
57
69
  #endif
58
70
  vec3 v = normalize(vLiqView);
71
+ // holding as chrome: a soft ripple runs over the surface, the studio turns slowly
72
+ n = normalize(n + held * 0.04 * vec3(sin(vLiqQ.y * 22.0 - uLiqTime * 1.6), 0.0, cos(vLiqQ.x * 19.0 - uLiqTime * 1.3)));
59
73
  vec3 rv = reflect(v, n);
60
74
  vec3 rw = normalize((vec4(rv, 0.0) * viewMatrix).xyz); // view → world direction
75
+ float ra = held * uLiqTime * 0.25;
76
+ rw = vec3(rw.x * cos(ra) - rw.z * sin(ra), rw.y, rw.x * sin(ra) + rw.z * cos(ra));
61
77
  // studio: dark floor, bright sky, a crisp horizon band and two soft boxes
62
78
  float up = rw.y;
63
79
  vec3 env = mix(vec3(0.05), vec3(0.85, 0.9, 1.0), smoothstep(-0.15, 0.6, up));
@@ -66,6 +82,8 @@ function layerFor(st) {
66
82
  env += vec3(1.2) * smoothstep(0.9, 0.98, dot(rw, normalize(vec3(-0.7, 0.35, -0.3))));
67
83
  float fres = 0.65 + 0.35 * pow(1.0 - abs(dot(n, -v)), 3.0);
68
84
  vec3 metal = uLiqTint * env * fres;
85
+ // a bright lip right at the front
86
+ metal *= 1.0 + 1.5 * (1.0 - smoothstep(0.0, 0.05, abs(d))) * step(0.0001, uLiqProgress) * step(uLiqProgress, ${CHROME} - 0.0001);
69
87
  outgoingLight = mix(outgoingLight, metal, chrome);
70
88
  }`,
71
89
  };
@@ -73,21 +91,26 @@ function layerFor(st) {
73
91
  export default defineOperation({
74
92
  value: 'liquidMetal',
75
93
  label: 'Liquid metal',
76
- description: 'Turn the targets to chrome and melt them into a rippling pool — reverting pulls them back into shape',
94
+ description: 'Turn the targets to chrome and melt them into a rippling pool (or, Disappear off, keep them as chrome) — reverting pulls them back into shape',
77
95
  channel: 'visual',
78
96
  multi: true,
79
97
  toggles: true,
80
- usableIn: ['interaction', 'action'],
98
+ usableIn: ['interaction', 'action', 'object'],
99
+ objectParams: { disappear: false },
100
+ presence: 'paint',
81
101
  params: {
102
+ direction: { type: 'select', label: 'Direction', default: 'up', options: DIRECTION_OPTIONS, hint: 'How the chrome comes in' },
82
103
  tint: { type: 'color', label: 'Metal', default: '#dfe7ee' },
83
- spread: { type: 'number', label: 'Pool size', default: 1, min: 0.2, max: 3, step: 0.05, unit: '×' },
104
+ disappear: { type: 'boolean', label: 'Disappear', default: true, hint: 'Off: the object turns to chrome and stays' },
105
+ spread: { type: 'number', label: 'Pool size', default: 1, min: 0.2, max: 3, step: 0.05, unit: '×', showWhen: { disappear: ['true'] } },
84
106
  duration: { type: 'number', label: 'Duration', default: 3.4, min: 0.3, max: 12, step: 0.1, unit: 's' },
85
107
  },
86
- apply(target, p) {
108
+ apply(target, p, ctx) {
87
109
  let st = target.userData[STATE];
88
110
  if (!st) {
89
111
  const field = createFieldState(target);
90
- const own = { field, tint: { value: new Color() }, spread: { value: 1 }, mats: new Set() };
112
+ field.edgeWidth.value = 0.04;
113
+ const own = { field, tint: { value: new Color() }, spread: { value: 1 }, keep: { value: 0 }, mats: new Set() };
91
114
  // ripples keep moving while it lies melted
92
115
  own.tick = () => { own.field.time.value += gsap.ticker.deltaRatio() / 60; syncRoot(target, own.field); };
93
116
  const layer = layerFor(own);
@@ -102,16 +125,22 @@ export default defineOperation({
102
125
  target.userData[STATE] = st;
103
126
  }
104
127
  const s = st;
128
+ setFieldParams(s.field, p, target, ctx);
105
129
  s.tint.value.set(String(p.tint));
106
130
  s.spread.value = Number(p.spread);
131
+ s.keep.value = p.disappear === false ? 1 : 0;
132
+ // keep: only the chrome comes in (the melt is off), then it holds — its own ticker keeps the time running
133
+ if (s.keep.value)
134
+ return runProgress(target, s.field, 1, { duration: Number(p.duration) * CHROME * 1.5, ease: 'none', clock: false });
107
135
  return runProgress(target, s.field, 1, { duration: Number(p.duration), ease: 'power1.inOut', clock: false });
108
136
  },
109
137
  revert(target, p) {
110
138
  const s = target.userData[STATE];
111
139
  if (!s)
112
140
  return;
141
+ setReveal(s.field, p);
113
142
  return runProgress(target, s.field, 0, {
114
- duration: Number(p.duration), ease: 'power1.inOut', clock: false,
143
+ duration: Number(p.duration) * (s.keep.value ? CHROME * 1.5 : 1) * Math.min(1, Math.max(0.1, s.field.progress.value)), ease: s.keep.value ? 'none' : 'power1.inOut', clock: false,
115
144
  onComplete: () => {
116
145
  gsap.ticker.remove(s.tick);
117
146
  s.mats.forEach(m => removeLayer(m, KEY));
@@ -1 +1 @@
1
- {"version":3,"file":"particleSwarm.d.ts","sourceRoot":"","sources":["../../../src/operations/catalogue/particleSwarm.ts"],"names":[],"mappings":";AAiCA,wBAsGG"}
1
+ {"version":3,"file":"particleSwarm.d.ts","sourceRoot":"","sources":["../../../src/operations/catalogue/particleSwarm.ts"],"names":[],"mappings":";AAoCA,wBAwHG"}