@wolsok/thanos 17.0.37 → 18.0.0

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 (48) hide show
  1. package/README.md +73 -12
  2. package/esm2022/lib/canvas-particle-renderer.js +98 -0
  3. package/esm2022/lib/canvas-particle-renderer.js.map +1 -0
  4. package/esm2022/lib/capture-scale.js +13 -0
  5. package/esm2022/lib/capture-scale.js.map +1 -0
  6. package/esm2022/lib/choose-particle-renderer.js +78 -0
  7. package/esm2022/lib/choose-particle-renderer.js.map +1 -0
  8. package/esm2022/lib/create-ws-thanos-options.js +4 -1
  9. package/esm2022/lib/create-ws-thanos-options.js.map +1 -1
  10. package/esm2022/lib/crumble-shape.js +24 -0
  11. package/esm2022/lib/crumble-shape.js.map +1 -0
  12. package/esm2022/lib/particle-renderer.js +1 -0
  13. package/esm2022/lib/particle-renderer.js.map +1 -0
  14. package/esm2022/lib/particles.js +84 -0
  15. package/esm2022/lib/particles.js.map +1 -0
  16. package/esm2022/lib/snap-sound-envelopes.js +38 -0
  17. package/esm2022/lib/snap-sound-envelopes.js.map +1 -0
  18. package/esm2022/lib/snap-sound.service.js +287 -0
  19. package/esm2022/lib/snap-sound.service.js.map +1 -0
  20. package/esm2022/lib/vaporizing-front.js +48 -0
  21. package/esm2022/lib/vaporizing-front.js.map +1 -0
  22. package/esm2022/lib/webgl/simplex-noise.glsl.js +69 -0
  23. package/esm2022/lib/webgl/simplex-noise.glsl.js.map +1 -0
  24. package/esm2022/lib/webgl/webgl-particle-renderer.js +308 -0
  25. package/esm2022/lib/webgl/webgl-particle-renderer.js.map +1 -0
  26. package/esm2022/lib/webgl/webgl-shaders.js +323 -0
  27. package/esm2022/lib/webgl/webgl-shaders.js.map +1 -0
  28. package/esm2022/lib/ws-thanos.directive.js +8 -6
  29. package/esm2022/lib/ws-thanos.directive.js.map +1 -1
  30. package/esm2022/lib/ws-thanos.options.js.map +1 -1
  31. package/esm2022/lib/ws-thanos.service.js +103 -220
  32. package/esm2022/lib/ws-thanos.service.js.map +1 -1
  33. package/lib/canvas-particle-renderer.d.ts +29 -0
  34. package/lib/capture-scale.d.ts +14 -0
  35. package/lib/choose-particle-renderer.d.ts +31 -0
  36. package/lib/crumble-shape.d.ts +11 -0
  37. package/lib/particle-renderer.d.ts +26 -0
  38. package/lib/particles.d.ts +35 -0
  39. package/lib/snap-sound-envelopes.d.ts +15 -0
  40. package/lib/snap-sound.service.d.ts +27 -0
  41. package/lib/vaporizing-front.d.ts +34 -0
  42. package/lib/webgl/simplex-noise.glsl.d.ts +5 -0
  43. package/lib/webgl/webgl-particle-renderer.d.ts +46 -0
  44. package/lib/webgl/webgl-shaders.d.ts +27 -0
  45. package/lib/ws-thanos.directive.d.ts +4 -2
  46. package/lib/ws-thanos.options.d.ts +14 -0
  47. package/lib/ws-thanos.service.d.ts +11 -8
  48. package/package.json +1 -1
@@ -0,0 +1,323 @@
1
+ import { MIN_PARTICLE_ALPHA } from '../particles';
2
+ import { FLOW_FIELD, FRONT_FRAY } from '../vaporizing-front';
3
+ import { SIMPLEX_NOISE_3D } from './simplex-noise.glsl';
4
+ /** attribute locations shared by all programs */
5
+ export const AttributeLocation = {
6
+ POSITION: 0,
7
+ VELOCITY: 1,
8
+ ACCELERATION: 2,
9
+ ALPHA: 3,
10
+ COLOR: 4,
11
+ RELEASED_AT: 5,
12
+ CRACK: 6,
13
+ CELL_RANDOM: 7,
14
+ };
15
+ /** crumble mode as passed to the shaders */
16
+ export const CRUMBLE_MODE = {
17
+ dust: 0,
18
+ cracks: 1,
19
+ shards: 2,
20
+ chunks: 3,
21
+ };
22
+ export const UPDATE_FEEDBACK_VARYINGS = [
23
+ 'vPosition',
24
+ 'vVelocity',
25
+ 'vAcceleration',
26
+ 'vAlpha',
27
+ 'vReleasedAt',
28
+ ];
29
+ export const CRACK_FEEDBACK_VARYINGS = ['vCrack', 'vCellRandom'];
30
+ /** floats per particle written by the crack program */
31
+ export const CRACK_LENGTH = 5;
32
+ const L = AttributeLocation;
33
+ /** GLSL float literal */
34
+ const f = (value) => Number.isInteger(value) ? value.toFixed(1) : String(value);
35
+ const IS_VISIBLE_INSIDE = /* glsl */ `
36
+ bool isVisibleInside(vec2 position, float alpha, vec2 size) {
37
+ return position.x <= size.x && position.x >= 0.0 &&
38
+ position.y <= size.y && position.y >= 0.0 &&
39
+ alpha >= ${f(MIN_PARTICLE_ALPHA)};
40
+ }
41
+ `;
42
+ const HASH = /* glsl */ `
43
+ uint hash(uint x) {
44
+ x ^= x >> 16;
45
+ x *= 0x7feb352du;
46
+ x ^= x >> 15;
47
+ x *= 0x846ca68bu;
48
+ x ^= x >> 16;
49
+ return x;
50
+ }
51
+
52
+ float toUnit(uint x) {
53
+ return float(x) / 4294967295.0;
54
+ }
55
+ `;
56
+ /**
57
+ * Shards shift and tilt around their center while uShardGrow rises.
58
+ * aCrack: shard center (xy), distance to the next coarse (z) and fine (w) crack, in device pixels.
59
+ */
60
+ const SHARD_OFFSET = /* glsl */ `
61
+ vec2 shardOffset(vec2 position, vec4 crack, float cellRandom) {
62
+ if (uShardGrow == 0.0) {
63
+ return vec2(0.0);
64
+ }
65
+ float angle = (cellRandom - 0.5) * 0.14 * uShardGrow;
66
+ vec2 relative = position - crack.xy;
67
+ vec2 rotated = mat2(cos(angle), sin(angle), -sin(angle), cos(angle)) * relative;
68
+ float direction = fract(cellRandom * 7.31) * 6.2831853;
69
+ vec2 drift = vec2(cos(direction), sin(direction) * 0.6 - 0.4) * 5.0 * uPixelScale * uShardGrow;
70
+ return rotated - relative + drift;
71
+ }
72
+ `;
73
+ /** runs once: which shard each particle belongs to and how far it is from the next crack */
74
+ export const CRACK_VERTEX_SHADER = /* glsl */ `#version 300 es
75
+ precision highp float;
76
+ precision highp int;
77
+
78
+ layout(location = ${L.POSITION}) in vec2 aPosition;
79
+
80
+ uniform float uCellSize;
81
+ uniform float uSeed;
82
+
83
+ out vec4 vCrack;
84
+ out float vCellRandom;
85
+
86
+ ${HASH}
87
+
88
+ vec2 cellRandom(vec2 cell, uint saltedSeed) {
89
+ uvec2 c = uvec2(ivec2(cell) + 32768);
90
+ uint h = hash(c.x ^ hash(c.y ^ saltedSeed));
91
+ return vec2(toUnit(h), toUnit(hash(h)));
92
+ }
93
+
94
+ /** voronoi cell of p: center (xy), distance to the cell border (z), random (w), in cell units */
95
+ vec4 voronoi(vec2 p, uint salt) {
96
+ uint saltedSeed = hash(floatBitsToUint(uSeed) ^ salt);
97
+ vec2 n = floor(p);
98
+ vec2 f = fract(p);
99
+ vec2 nearestCell = vec2(0.0);
100
+ vec2 nearest = vec2(0.0);
101
+ float nearestDistance = 8.0;
102
+ for (int j = -1; j <= 1; j++) {
103
+ for (int i = -1; i <= 1; i++) {
104
+ vec2 g = vec2(float(i), float(j));
105
+ vec2 r = g + cellRandom(n + g, saltedSeed) - f;
106
+ float d = dot(r, r);
107
+ if (d < nearestDistance) {
108
+ nearestDistance = d;
109
+ nearest = r;
110
+ nearestCell = g;
111
+ }
112
+ }
113
+ }
114
+ // exact distance to the border between the nearest and its neighbour cells
115
+ float border = 8.0;
116
+ for (int j = -2; j <= 2; j++) {
117
+ for (int i = -2; i <= 2; i++) {
118
+ vec2 g = nearestCell + vec2(float(i), float(j));
119
+ vec2 r = g + cellRandom(n + g, saltedSeed) - f;
120
+ vec2 between = r - nearest;
121
+ if (dot(between, between) > 0.00001) {
122
+ border = min(border, dot(0.5 * (nearest + r), normalize(between)));
123
+ }
124
+ }
125
+ }
126
+ uint randomSeed = hash(floatBitsToUint(uSeed) ^ (salt + 7u));
127
+ return vec4(p + nearest, border, cellRandom(n + nearestCell, randomSeed).x);
128
+ }
129
+
130
+ void main() {
131
+ vec4 coarse = voronoi(aPosition / uCellSize, 1u);
132
+ float fineSize = uCellSize * 0.42;
133
+ vec4 fine = voronoi(aPosition / fineSize, 2u);
134
+ vCrack = vec4(coarse.xy * uCellSize, coarse.z * uCellSize, fine.z * fineSize);
135
+ vCellRandom = coarse.w;
136
+ }
137
+ `;
138
+ /** moves the particles, the GPU port of updateParticlesOnCpu plus the crumble modes */
139
+ export const UPDATE_VERTEX_SHADER = /* glsl */ `#version 300 es
140
+ precision highp float;
141
+ precision highp int;
142
+
143
+ layout(location = ${L.POSITION}) in vec2 aPosition;
144
+ layout(location = ${L.VELOCITY}) in vec2 aVelocity;
145
+ layout(location = ${L.ACCELERATION}) in vec2 aAcceleration;
146
+ layout(location = ${L.ALPHA}) in float aAlpha;
147
+ layout(location = ${L.RELEASED_AT}) in float aReleasedAt;
148
+ layout(location = ${L.CRACK}) in vec4 aCrack;
149
+ layout(location = ${L.CELL_RANDOM}) in float aCellRandom;
150
+
151
+ uniform vec2 uSize;
152
+ uniform float uDeltaTSec;
153
+ uniform float uAnimationT;
154
+ uniform float uParticleAcceleration;
155
+ uniform float uSeed;
156
+ uniform int uCrumble;
157
+ uniform float uPixelScale;
158
+ /** see vaporizingFront() */
159
+ uniform float uFrontTime;
160
+ uniform float uFrontRadiusPow;
161
+ uniform float uFade;
162
+ /** see crumbleShape(), in device pixels */
163
+ uniform float uCoarseCrack;
164
+ uniform float uFineCrack;
165
+ uniform float uShardGrow;
166
+
167
+ out vec2 vPosition;
168
+ out vec2 vVelocity;
169
+ out vec2 vAcceleration;
170
+ out float vAlpha;
171
+ /** 0 while attached, < 0 while a chunk falls, > 0 once it is dust */
172
+ out float vReleasedAt;
173
+
174
+ const int CHUNKS = ${CRUMBLE_MODE.chunks};
175
+ /** part of the animation a chunk flies before it crumbles */
176
+ const float CHUNK_LIFE = 0.12;
177
+ /** weak, the effect canvas ends at the bottom of the element, so chunks jump up and arc */
178
+ const float CHUNK_GRAVITY = 4.0;
179
+
180
+ ${IS_VISIBLE_INSIDE}
181
+ ${SIMPLEX_NOISE_3D}
182
+ ${HASH}
183
+ ${SHARD_OFFSET}
184
+
185
+ /** simplex noise mapped to [0, 1] like SimplexNoise({ min: 0 }) */
186
+ float noise01(vec3 position) {
187
+ return snoise(position * ${f(FLOW_FIELD.frequency)}) * 0.5 + 0.5;
188
+ }
189
+
190
+ /** random number in [0, 1] per particle, frame and salt */
191
+ float random(uint salt) {
192
+ uint frame = floatBitsToUint(uAnimationT) ^ floatBitsToUint(uSeed);
193
+ return toUnit(hash(uint(gl_VertexID) ^ hash(frame ^ salt)));
194
+ }
195
+
196
+ vec2 dustAcceleration() {
197
+ return vec2(random(1u), -random(2u));
198
+ }
199
+
200
+ /** has the vaporizing front passed this position, see isBehindFront() */
201
+ bool isBehindFront(vec2 position) {
202
+ float pXLength = position.x + mod(position.x, uDeltaTSec) * 0.5;
203
+ float pYLength = uSize.y - position.y;
204
+ pXLength += sin((pXLength / ${f(FRONT_FRAY.xWave)} + ${f(FRONT_FRAY.xPhase)}) * uFrontTime
205
+ + uSeed * ${f(FRONT_FRAY.xSeed)}) * ${f(FRONT_FRAY.xAmplitude)};
206
+ pYLength += cos((pYLength / ${f(FRONT_FRAY.yWave)} + ${f(FRONT_FRAY.yPhase)}) * uFrontTime
207
+ + uSeed * ${f(FRONT_FRAY.ySeed)}) * ${f(FRONT_FRAY.yAmplitude)};
208
+ return pXLength * pXLength + pYLength * pYLength > uFrontRadiusPow;
209
+ }
210
+
211
+ void main() {
212
+ vPosition = aPosition;
213
+ vVelocity = aVelocity;
214
+ vAcceleration = aAcceleration;
215
+ vAlpha = aAlpha;
216
+ vReleasedAt = aReleasedAt;
217
+
218
+ // only update particles that are inside view and visible
219
+ if (!isVisibleInside(aPosition, aAlpha, uSize)) {
220
+ return;
221
+ }
222
+
223
+ float releaseT = max(uAnimationT, 0.0001);
224
+
225
+ if (aReleasedAt == 0.0) {
226
+ bool inCrack = aCrack.z < uCoarseCrack || aCrack.w < uFineCrack;
227
+ // chunks break off as a whole, so the shard center decides
228
+ vec2 frontPosition = uCrumble == CHUNKS ? aCrack.xy : aPosition;
229
+
230
+ if (inCrack || isBehindFront(frontPosition)) {
231
+ vPosition += shardOffset(aPosition, aCrack, aCellRandom);
232
+ vReleasedAt = releaseT;
233
+ if (inCrack) {
234
+ // the crack crumbles to dust that trickles out gently
235
+ vAcceleration = dustAcceleration() * 0.3;
236
+ } else if (uCrumble == CHUNKS) {
237
+ // the whole chunk shares velocity and gravity, so it flies in one piece
238
+ float side = fract(aCellRandom * 13.7) - 0.5;
239
+ vVelocity = vec2(side * 90.0, -60.0 - aCellRandom * 70.0) * uPixelScale;
240
+ vAcceleration = vec2(0.0, CHUNK_GRAVITY);
241
+ vReleasedAt = -releaseT;
242
+ } else {
243
+ vAcceleration = dustAcceleration();
244
+ }
245
+ }
246
+ } else if (aReleasedAt < 0.0) {
247
+ // a flying chunk crumbles to dust after a while
248
+ if (uAnimationT + aReleasedAt > CHUNK_LIFE + aCellRandom * 0.08) {
249
+ vAcceleration = dustAcceleration();
250
+ vReleasedAt = releaseT;
251
+ }
252
+ } else {
253
+ // flow along the noise velocity field
254
+ vAcceleration += vec2(
255
+ noise01(vec3(aPosition.x, aPosition.y, uSeed + ${f(FLOW_FIELD.xSeedOffset)})),
256
+ -noise01(vec3(aPosition.x, uSeed / ${f(FLOW_FIELD.ySeedDivisor)}, aPosition.y))
257
+ );
258
+ }
259
+
260
+ vVelocity += vAcceleration * uParticleAcceleration * uDeltaTSec;
261
+ vPosition += vVelocity * uDeltaTSec;
262
+ vAlpha *= uFade;
263
+ }
264
+ `;
265
+ /** transform feedback only, nothing is rasterized */
266
+ export const FEEDBACK_FRAGMENT_SHADER = /* glsl */ `#version 300 es
267
+ precision mediump float;
268
+ out vec4 outColor;
269
+ void main() {
270
+ outColor = vec4(0.0);
271
+ }
272
+ `;
273
+ /** draws every particle as one device pixel */
274
+ export const DRAW_VERTEX_SHADER = /* glsl */ `#version 300 es
275
+ precision highp float;
276
+ precision highp int;
277
+
278
+ layout(location = ${L.POSITION}) in vec2 aPosition;
279
+ layout(location = ${L.ALPHA}) in float aAlpha;
280
+ layout(location = ${L.COLOR}) in vec4 aColor;
281
+ layout(location = ${L.RELEASED_AT}) in float aReleasedAt;
282
+ layout(location = ${L.CRACK}) in vec4 aCrack;
283
+ layout(location = ${L.CELL_RANDOM}) in float aCellRandom;
284
+
285
+ uniform vec2 uSize;
286
+ uniform float uPixelScale;
287
+ uniform float uShardGrow;
288
+
289
+ out vec4 vColor;
290
+
291
+ ${IS_VISIBLE_INSIDE}
292
+ ${SHARD_OFFSET}
293
+
294
+ void main() {
295
+ vec2 position = aPosition;
296
+ if (aReleasedAt == 0.0) {
297
+ // attached shards shift and tilt before they break off
298
+ position += shardOffset(aPosition, aCrack, aCellRandom);
299
+ }
300
+ if (!isVisibleInside(position, aAlpha, uSize)) {
301
+ // move outside of clip space
302
+ gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
303
+ gl_PointSize = 1.0;
304
+ vColor = vec4(0.0);
305
+ return;
306
+ }
307
+ vec2 clip = (floor(position) + 0.5) / uSize * 2.0 - 1.0;
308
+ gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);
309
+ gl_PointSize = 1.0;
310
+ float alpha = aAlpha / 255.0;
311
+ // premultiplied alpha
312
+ vColor = vec4(aColor.rgb * alpha, alpha);
313
+ }
314
+ `;
315
+ export const DRAW_FRAGMENT_SHADER = /* glsl */ `#version 300 es
316
+ precision mediump float;
317
+ in vec4 vColor;
318
+ out vec4 outColor;
319
+ void main() {
320
+ outColor = vColor;
321
+ }
322
+ `;
323
+ //# sourceMappingURL=webgl-shaders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webgl-shaders.js","sourceRoot":"","sources":["../../../../../../../libs/public/ws-thanos/src/lib/webgl/webgl-shaders.ts"],"names":[],"mappings":"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","sourcesContent":["import { MIN_PARTICLE_ALPHA } from '../particles';\nimport { FLOW_FIELD, FRONT_FRAY } from '../vaporizing-front';\nimport { WsThanosCrumble } from '../ws-thanos.options';\nimport { SIMPLEX_NOISE_3D } from './simplex-noise.glsl';\n\n/** attribute locations shared by all programs */\nexport const AttributeLocation = {\n POSITION: 0,\n VELOCITY: 1,\n ACCELERATION: 2,\n ALPHA: 3,\n COLOR: 4,\n RELEASED_AT: 5,\n CRACK: 6,\n CELL_RANDOM: 7,\n} as const;\n\n/** crumble mode as passed to the shaders */\nexport const CRUMBLE_MODE: Record<WsThanosCrumble, number> = {\n dust: 0,\n cracks: 1,\n shards: 2,\n chunks: 3,\n};\n\nexport const UPDATE_FEEDBACK_VARYINGS = [\n 'vPosition',\n 'vVelocity',\n 'vAcceleration',\n 'vAlpha',\n 'vReleasedAt',\n];\n\nexport const CRACK_FEEDBACK_VARYINGS = ['vCrack', 'vCellRandom'];\n\n/** floats per particle written by the crack program */\nexport const CRACK_LENGTH = 5;\n\nconst L = AttributeLocation;\n\n/** GLSL float literal */\nconst f = (value: number): string =>\n Number.isInteger(value) ? value.toFixed(1) : String(value);\n\nconst IS_VISIBLE_INSIDE = /* glsl */ `\nbool isVisibleInside(vec2 position, float alpha, vec2 size) {\n return position.x <= size.x && position.x >= 0.0 &&\n position.y <= size.y && position.y >= 0.0 &&\n alpha >= ${f(MIN_PARTICLE_ALPHA)};\n}\n`;\n\nconst HASH = /* glsl */ `\nuint hash(uint x) {\n x ^= x >> 16;\n x *= 0x7feb352du;\n x ^= x >> 15;\n x *= 0x846ca68bu;\n x ^= x >> 16;\n return x;\n}\n\nfloat toUnit(uint x) {\n return float(x) / 4294967295.0;\n}\n`;\n\n/**\n * Shards shift and tilt around their center while uShardGrow rises.\n * aCrack: shard center (xy), distance to the next coarse (z) and fine (w) crack, in device pixels.\n */\nconst SHARD_OFFSET = /* glsl */ `\nvec2 shardOffset(vec2 position, vec4 crack, float cellRandom) {\n if (uShardGrow == 0.0) {\n return vec2(0.0);\n }\n float angle = (cellRandom - 0.5) * 0.14 * uShardGrow;\n vec2 relative = position - crack.xy;\n vec2 rotated = mat2(cos(angle), sin(angle), -sin(angle), cos(angle)) * relative;\n float direction = fract(cellRandom * 7.31) * 6.2831853;\n vec2 drift = vec2(cos(direction), sin(direction) * 0.6 - 0.4) * 5.0 * uPixelScale * uShardGrow;\n return rotated - relative + drift;\n}\n`;\n\n/** runs once: which shard each particle belongs to and how far it is from the next crack */\nexport const CRACK_VERTEX_SHADER = /* glsl */ `#version 300 es\nprecision highp float;\nprecision highp int;\n\nlayout(location = ${L.POSITION}) in vec2 aPosition;\n\nuniform float uCellSize;\nuniform float uSeed;\n\nout vec4 vCrack;\nout float vCellRandom;\n\n${HASH}\n\nvec2 cellRandom(vec2 cell, uint saltedSeed) {\n uvec2 c = uvec2(ivec2(cell) + 32768);\n uint h = hash(c.x ^ hash(c.y ^ saltedSeed));\n return vec2(toUnit(h), toUnit(hash(h)));\n}\n\n/** voronoi cell of p: center (xy), distance to the cell border (z), random (w), in cell units */\nvec4 voronoi(vec2 p, uint salt) {\n uint saltedSeed = hash(floatBitsToUint(uSeed) ^ salt);\n vec2 n = floor(p);\n vec2 f = fract(p);\n vec2 nearestCell = vec2(0.0);\n vec2 nearest = vec2(0.0);\n float nearestDistance = 8.0;\n for (int j = -1; j <= 1; j++) {\n for (int i = -1; i <= 1; i++) {\n vec2 g = vec2(float(i), float(j));\n vec2 r = g + cellRandom(n + g, saltedSeed) - f;\n float d = dot(r, r);\n if (d < nearestDistance) {\n nearestDistance = d;\n nearest = r;\n nearestCell = g;\n }\n }\n }\n // exact distance to the border between the nearest and its neighbour cells\n float border = 8.0;\n for (int j = -2; j <= 2; j++) {\n for (int i = -2; i <= 2; i++) {\n vec2 g = nearestCell + vec2(float(i), float(j));\n vec2 r = g + cellRandom(n + g, saltedSeed) - f;\n vec2 between = r - nearest;\n if (dot(between, between) > 0.00001) {\n border = min(border, dot(0.5 * (nearest + r), normalize(between)));\n }\n }\n }\n uint randomSeed = hash(floatBitsToUint(uSeed) ^ (salt + 7u));\n return vec4(p + nearest, border, cellRandom(n + nearestCell, randomSeed).x);\n}\n\nvoid main() {\n vec4 coarse = voronoi(aPosition / uCellSize, 1u);\n float fineSize = uCellSize * 0.42;\n vec4 fine = voronoi(aPosition / fineSize, 2u);\n vCrack = vec4(coarse.xy * uCellSize, coarse.z * uCellSize, fine.z * fineSize);\n vCellRandom = coarse.w;\n}\n`;\n\n/** moves the particles, the GPU port of updateParticlesOnCpu plus the crumble modes */\nexport const UPDATE_VERTEX_SHADER = /* glsl */ `#version 300 es\nprecision highp float;\nprecision highp int;\n\nlayout(location = ${L.POSITION}) in vec2 aPosition;\nlayout(location = ${L.VELOCITY}) in vec2 aVelocity;\nlayout(location = ${L.ACCELERATION}) in vec2 aAcceleration;\nlayout(location = ${L.ALPHA}) in float aAlpha;\nlayout(location = ${L.RELEASED_AT}) in float aReleasedAt;\nlayout(location = ${L.CRACK}) in vec4 aCrack;\nlayout(location = ${L.CELL_RANDOM}) in float aCellRandom;\n\nuniform vec2 uSize;\nuniform float uDeltaTSec;\nuniform float uAnimationT;\nuniform float uParticleAcceleration;\nuniform float uSeed;\nuniform int uCrumble;\nuniform float uPixelScale;\n/** see vaporizingFront() */\nuniform float uFrontTime;\nuniform float uFrontRadiusPow;\nuniform float uFade;\n/** see crumbleShape(), in device pixels */\nuniform float uCoarseCrack;\nuniform float uFineCrack;\nuniform float uShardGrow;\n\nout vec2 vPosition;\nout vec2 vVelocity;\nout vec2 vAcceleration;\nout float vAlpha;\n/** 0 while attached, < 0 while a chunk falls, > 0 once it is dust */\nout float vReleasedAt;\n\nconst int CHUNKS = ${CRUMBLE_MODE.chunks};\n/** part of the animation a chunk flies before it crumbles */\nconst float CHUNK_LIFE = 0.12;\n/** weak, the effect canvas ends at the bottom of the element, so chunks jump up and arc */\nconst float CHUNK_GRAVITY = 4.0;\n\n${IS_VISIBLE_INSIDE}\n${SIMPLEX_NOISE_3D}\n${HASH}\n${SHARD_OFFSET}\n\n/** simplex noise mapped to [0, 1] like SimplexNoise({ min: 0 }) */\nfloat noise01(vec3 position) {\n return snoise(position * ${f(FLOW_FIELD.frequency)}) * 0.5 + 0.5;\n}\n\n/** random number in [0, 1] per particle, frame and salt */\nfloat random(uint salt) {\n uint frame = floatBitsToUint(uAnimationT) ^ floatBitsToUint(uSeed);\n return toUnit(hash(uint(gl_VertexID) ^ hash(frame ^ salt)));\n}\n\nvec2 dustAcceleration() {\n return vec2(random(1u), -random(2u));\n}\n\n/** has the vaporizing front passed this position, see isBehindFront() */\nbool isBehindFront(vec2 position) {\n float pXLength = position.x + mod(position.x, uDeltaTSec) * 0.5;\n float pYLength = uSize.y - position.y;\n pXLength += sin((pXLength / ${f(FRONT_FRAY.xWave)} + ${f(FRONT_FRAY.xPhase)}) * uFrontTime\n + uSeed * ${f(FRONT_FRAY.xSeed)}) * ${f(FRONT_FRAY.xAmplitude)};\n pYLength += cos((pYLength / ${f(FRONT_FRAY.yWave)} + ${f(FRONT_FRAY.yPhase)}) * uFrontTime\n + uSeed * ${f(FRONT_FRAY.ySeed)}) * ${f(FRONT_FRAY.yAmplitude)};\n return pXLength * pXLength + pYLength * pYLength > uFrontRadiusPow;\n}\n\nvoid main() {\n vPosition = aPosition;\n vVelocity = aVelocity;\n vAcceleration = aAcceleration;\n vAlpha = aAlpha;\n vReleasedAt = aReleasedAt;\n\n // only update particles that are inside view and visible\n if (!isVisibleInside(aPosition, aAlpha, uSize)) {\n return;\n }\n\n float releaseT = max(uAnimationT, 0.0001);\n\n if (aReleasedAt == 0.0) {\n bool inCrack = aCrack.z < uCoarseCrack || aCrack.w < uFineCrack;\n // chunks break off as a whole, so the shard center decides\n vec2 frontPosition = uCrumble == CHUNKS ? aCrack.xy : aPosition;\n\n if (inCrack || isBehindFront(frontPosition)) {\n vPosition += shardOffset(aPosition, aCrack, aCellRandom);\n vReleasedAt = releaseT;\n if (inCrack) {\n // the crack crumbles to dust that trickles out gently\n vAcceleration = dustAcceleration() * 0.3;\n } else if (uCrumble == CHUNKS) {\n // the whole chunk shares velocity and gravity, so it flies in one piece\n float side = fract(aCellRandom * 13.7) - 0.5;\n vVelocity = vec2(side * 90.0, -60.0 - aCellRandom * 70.0) * uPixelScale;\n vAcceleration = vec2(0.0, CHUNK_GRAVITY);\n vReleasedAt = -releaseT;\n } else {\n vAcceleration = dustAcceleration();\n }\n }\n } else if (aReleasedAt < 0.0) {\n // a flying chunk crumbles to dust after a while\n if (uAnimationT + aReleasedAt > CHUNK_LIFE + aCellRandom * 0.08) {\n vAcceleration = dustAcceleration();\n vReleasedAt = releaseT;\n }\n } else {\n // flow along the noise velocity field\n vAcceleration += vec2(\n noise01(vec3(aPosition.x, aPosition.y, uSeed + ${f(FLOW_FIELD.xSeedOffset)})),\n -noise01(vec3(aPosition.x, uSeed / ${f(FLOW_FIELD.ySeedDivisor)}, aPosition.y))\n );\n }\n\n vVelocity += vAcceleration * uParticleAcceleration * uDeltaTSec;\n vPosition += vVelocity * uDeltaTSec;\n vAlpha *= uFade;\n}\n`;\n\n/** transform feedback only, nothing is rasterized */\nexport const FEEDBACK_FRAGMENT_SHADER = /* glsl */ `#version 300 es\nprecision mediump float;\nout vec4 outColor;\nvoid main() {\n outColor = vec4(0.0);\n}\n`;\n\n/** draws every particle as one device pixel */\nexport const DRAW_VERTEX_SHADER = /* glsl */ `#version 300 es\nprecision highp float;\nprecision highp int;\n\nlayout(location = ${L.POSITION}) in vec2 aPosition;\nlayout(location = ${L.ALPHA}) in float aAlpha;\nlayout(location = ${L.COLOR}) in vec4 aColor;\nlayout(location = ${L.RELEASED_AT}) in float aReleasedAt;\nlayout(location = ${L.CRACK}) in vec4 aCrack;\nlayout(location = ${L.CELL_RANDOM}) in float aCellRandom;\n\nuniform vec2 uSize;\nuniform float uPixelScale;\nuniform float uShardGrow;\n\nout vec4 vColor;\n\n${IS_VISIBLE_INSIDE}\n${SHARD_OFFSET}\n\nvoid main() {\n vec2 position = aPosition;\n if (aReleasedAt == 0.0) {\n // attached shards shift and tilt before they break off\n position += shardOffset(aPosition, aCrack, aCellRandom);\n }\n if (!isVisibleInside(position, aAlpha, uSize)) {\n // move outside of clip space\n gl_Position = vec4(2.0, 2.0, 2.0, 1.0);\n gl_PointSize = 1.0;\n vColor = vec4(0.0);\n return;\n }\n vec2 clip = (floor(position) + 0.5) / uSize * 2.0 - 1.0;\n gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);\n gl_PointSize = 1.0;\n float alpha = aAlpha / 255.0;\n // premultiplied alpha\n vColor = vec4(aColor.rgb * alpha, alpha);\n}\n`;\n\nexport const DRAW_FRAGMENT_SHADER = /* glsl */ `#version 300 es\nprecision mediump float;\nin vec4 vColor;\nout vec4 outColor;\nvoid main() {\n outColor = vColor;\n}\n`;\n"]}
@@ -1,6 +1,6 @@
1
- import { Directive, ElementRef, Output, inject } from '@angular/core';
1
+ import { Directive, ElementRef, Output, inject, input } from '@angular/core';
2
2
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3
- import { finalize, Subject, Subscription, take, tap } from 'rxjs';
3
+ import { finalize, Subject, take, tap } from 'rxjs';
4
4
  import { WS_THANOS_OPTIONS_TOKEN } from './ws-thanos-options.token';
5
5
  import { WsThanosService } from './ws-thanos.service';
6
6
  import * as i0 from "@angular/core";
@@ -8,8 +8,10 @@ export class WsThanosDirective {
8
8
  constructor() {
9
9
  this.wsThanosCompleteSubject = new Subject();
10
10
  this.untilDestroyed = takeUntilDestroyed();
11
+ /** override the provided options for this element */
12
+ this.wsThanosOptions = input(/* @ts-ignore */
13
+ ...(ngDevMode ? [undefined, { debugName: "wsThanosOptions" }] : /* istanbul ignore next */ []));
11
14
  this.wsThanosComplete = this.wsThanosCompleteSubject.asObservable();
12
- this.subscriptions = new Subscription();
13
15
  this.vaporizeDomElem = inject(ElementRef);
14
16
  this.thanosService = inject(WsThanosService);
15
17
  this.thanosOptions = inject(WS_THANOS_OPTIONS_TOKEN);
@@ -24,7 +26,7 @@ export class WsThanosDirective {
24
26
  */
25
27
  vaporize$(removeElem = true) {
26
28
  const elem = this.vaporizeDomElem.nativeElement;
27
- return this.thanosService.vaporize(elem).pipe(tap({
29
+ return this.thanosService.vaporize(elem, this.wsThanosOptions()).pipe(tap({
28
30
  error: (error) => {
29
31
  console.error('Error vaporizing', error, {
30
32
  elemTovaporize: elem,
@@ -53,7 +55,7 @@ export class WsThanosDirective {
53
55
  return this.wsThanosComplete.pipe(take(1));
54
56
  }
55
57
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: WsThanosDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
56
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.2.1", type: WsThanosDirective, isStandalone: true, selector: "[wsThanos], ws-thanos", outputs: { wsThanosComplete: "wsThanosComplete" }, exportAs: ["thanos"], ngImport: i0 }); }
58
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.2.1", type: WsThanosDirective, isStandalone: true, selector: "[wsThanos], ws-thanos", inputs: { wsThanosOptions: { classPropertyName: "wsThanosOptions", publicName: "wsThanosOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { wsThanosComplete: "wsThanosComplete" }, exportAs: ["thanos"], ngImport: i0 }); }
57
59
  }
58
60
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: WsThanosDirective, decorators: [{
59
61
  type: Directive,
@@ -63,7 +65,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImpor
63
65
  selector: '[wsThanos], ws-thanos',
64
66
  exportAs: 'thanos',
65
67
  }]
66
- }], propDecorators: { wsThanosComplete: [{
68
+ }], propDecorators: { wsThanosOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "wsThanosOptions", required: false }] }], wsThanosComplete: [{
67
69
  type: Output
68
70
  }] } });
69
71
  //# sourceMappingURL=ws-thanos.directive.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ws-thanos.directive.js","sourceRoot":"","sources":["../../../../../../libs/public/ws-thanos/src/lib/ws-thanos.directive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAc,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAE9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;;AAQtD,MAAM,OAAO,iBAAiB;IAN9B;QAOU,4BAAuB,GAAG,IAAI,OAAO,EAAQ,CAAC;QAC9C,mBAAc,GAAG,kBAAkB,EAAE,CAAC;QAGvC,qBAAgB,GACrB,IAAI,CAAC,uBAAuB,CAAC,YAAY,EAAE,CAAC;QACtC,kBAAa,GAAiB,IAAI,YAAY,EAAE,CAAC;QAExC,oBAAe,GAC9B,MAAM,CAAC,UAAU,CAAC,CAAC;QACJ,kBAAa,GAAoB,MAAM,CAAC,eAAe,CAAC,CAAC;QACzD,kBAAa,GAAoB,MAAM,CACtD,uBAAuB,CACxB,CAAC;KA2CH;IAzCC;;;;;;;OAOG;IACI,SAAS,CAAC,UAAU,GAAG,IAAI;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC;QAChD,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAC3C,GAAG,CAAC;YACF,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE;gBACf,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,KAAK,EAAE;oBACvC,cAAc,EAAE,IAAI;oBACpB,UAAU;iBACX,CAAC,CAAC;YACL,CAAC;SACF,CAAC,EACF,QAAQ,CAAC,GAAG,EAAE;YACZ,IAAI,UAAU,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,CAAC;iBAAM,CAAC;gBACN,qBAAqB;gBACrB,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,eAAe,CAAC;gBACxC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE,CAAC;QACtC,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACI,QAAQ,CAAC,UAAU,GAAG,IAAI;QAC/B,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,SAAS,EAAE,CAAC;QACjE,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC;8GAxDU,iBAAiB;kGAAjB,iBAAiB;;2FAAjB,iBAAiB;kBAN7B,SAAS;mBAAC;oBACT,4FAA4F;oBAC5F,8DAA8D;oBAC9D,QAAQ,EAAE,uBAAuB;oBACjC,QAAQ,EAAE,QAAQ;iBACnB;;sBAKE,MAAM","sourcesContent":["import { Directive, ElementRef, Output, inject } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { finalize, Observable, Subject, Subscription, take, tap } from 'rxjs';\nimport { AnimationState } from './animation.state';\nimport { WS_THANOS_OPTIONS_TOKEN } from './ws-thanos-options.token';\nimport type { WsThanosOptions } from './ws-thanos.options';\nimport { WsThanosService } from './ws-thanos.service';\n\n@Directive({\n // Bare prefix is the published public API of @wolsok/thanos; renaming it would be breaking.\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: '[wsThanos], ws-thanos',\n exportAs: 'thanos',\n})\nexport class WsThanosDirective {\n private wsThanosCompleteSubject = new Subject<void>();\n private untilDestroyed = takeUntilDestroyed();\n\n @Output()\n public wsThanosComplete: Observable<void> =\n this.wsThanosCompleteSubject.asObservable();\n private subscriptions: Subscription = new Subscription();\n\n private readonly vaporizeDomElem: ElementRef<HTMLElement> =\n inject(ElementRef);\n private readonly thanosService: WsThanosService = inject(WsThanosService);\n private readonly thanosOptions: WsThanosOptions = inject(\n WS_THANOS_OPTIONS_TOKEN\n );\n\n /**\n *\n * Vaporize the dom element of this directive\n * @param removeElem remove the vaporized dom element or blend back in after a while\n *\n * Subscribe to the returned observable to start vaporization\n *\n */\n public vaporize$(removeElem = true): Observable<AnimationState> {\n const elem = this.vaporizeDomElem.nativeElement;\n return this.thanosService.vaporize(elem).pipe(\n tap({\n error: (error) => {\n console.error('Error vaporizing', error, {\n elemTovaporize: elem,\n removeElem,\n });\n },\n }),\n finalize(() => {\n if (removeElem) {\n elem.remove();\n } else {\n // make visible again\n elem.style.transition = 'opacity 700ms';\n elem.style.opacity = '1';\n }\n this.wsThanosCompleteSubject.next();\n })\n );\n }\n\n /**\n * Vaporize the dom element of this directive\n * @param removeElem remove the vaporized dom element or blend back in after a while\n *\n */\n public vaporize(removeElem = true): Observable<void> {\n this.vaporize$(removeElem).pipe(this.untilDestroyed).subscribe();\n return this.wsThanosComplete.pipe(take(1));\n }\n}\n"]}
1
+ {"version":3,"file":"ws-thanos.directive.js","sourceRoot":"","sources":["../../../../../../libs/public/ws-thanos/src/lib/ws-thanos.directive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAc,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAEhE,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;;AAQtD,MAAM,OAAO,iBAAiB;IAN9B;QAOU,4BAAuB,GAAG,IAAI,OAAO,EAAQ,CAAC;QAC9C,mBAAc,GAAG,kBAAkB,EAAE,CAAC;QAE9C,qDAAqD;QACrC,oBAAe,GAAG,KAAK;uGAA4B,CAAC;QAG7D,qBAAgB,GACrB,IAAI,CAAC,uBAAuB,CAAC,YAAY,EAAE,CAAC;QAE7B,oBAAe,GAC9B,MAAM,CAAC,UAAU,CAAC,CAAC;QACJ,kBAAa,GAAoB,MAAM,CAAC,eAAe,CAAC,CAAC;QACzD,kBAAa,GAAoB,MAAM,CACtD,uBAAuB,CACxB,CAAC;KA2CH;IAzCC;;;;;;;OAOG;IACI,SAAS,CAAC,UAAU,GAAG,IAAI;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC;QAChD,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CACnE,GAAG,CAAC;YACF,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE;gBACf,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,KAAK,EAAE;oBACvC,cAAc,EAAE,IAAI;oBACpB,UAAU;iBACX,CAAC,CAAC;YACL,CAAC;SACF,CAAC,EACF,QAAQ,CAAC,GAAG,EAAE;YACZ,IAAI,UAAU,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,CAAC;iBAAM,CAAC;gBACN,qBAAqB;gBACrB,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,eAAe,CAAC;gBACxC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE,CAAC;QACtC,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACI,QAAQ,CAAC,UAAU,GAAG,IAAI;QAC/B,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,SAAS,EAAE,CAAC;QACjE,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC;8GA1DU,iBAAiB;kGAAjB,iBAAiB;;2FAAjB,iBAAiB;kBAN7B,SAAS;mBAAC;oBACT,4FAA4F;oBAC5F,8DAA8D;oBAC9D,QAAQ,EAAE,uBAAuB;oBACjC,QAAQ,EAAE,QAAQ;iBACnB;;sBAQE,MAAM","sourcesContent":["import { Directive, ElementRef, Output, inject, input } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { finalize, Observable, Subject, take, tap } from 'rxjs';\nimport { AnimationState } from './animation.state';\nimport { WS_THANOS_OPTIONS_TOKEN } from './ws-thanos-options.token';\nimport type { WsThanosOptions } from './ws-thanos.options';\nimport { WsThanosService } from './ws-thanos.service';\n\n@Directive({\n // Bare prefix is the published public API of @wolsok/thanos; renaming it would be breaking.\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: '[wsThanos], ws-thanos',\n exportAs: 'thanos',\n})\nexport class WsThanosDirective {\n private wsThanosCompleteSubject = new Subject<void>();\n private untilDestroyed = takeUntilDestroyed();\n\n /** override the provided options for this element */\n public readonly wsThanosOptions = input<Partial<WsThanosOptions>>();\n\n @Output()\n public wsThanosComplete: Observable<void> =\n this.wsThanosCompleteSubject.asObservable();\n\n private readonly vaporizeDomElem: ElementRef<HTMLElement> =\n inject(ElementRef);\n private readonly thanosService: WsThanosService = inject(WsThanosService);\n private readonly thanosOptions: WsThanosOptions = inject(\n WS_THANOS_OPTIONS_TOKEN\n );\n\n /**\n *\n * Vaporize the dom element of this directive\n * @param removeElem remove the vaporized dom element or blend back in after a while\n *\n * Subscribe to the returned observable to start vaporization\n *\n */\n public vaporize$(removeElem = true): Observable<AnimationState> {\n const elem = this.vaporizeDomElem.nativeElement;\n return this.thanosService.vaporize(elem, this.wsThanosOptions()).pipe(\n tap({\n error: (error) => {\n console.error('Error vaporizing', error, {\n elemTovaporize: elem,\n removeElem,\n });\n },\n }),\n finalize(() => {\n if (removeElem) {\n elem.remove();\n } else {\n // make visible again\n elem.style.transition = 'opacity 700ms';\n elem.style.opacity = '1';\n }\n this.wsThanosCompleteSubject.next();\n })\n );\n }\n\n /**\n * Vaporize the dom element of this directive\n * @param removeElem remove the vaporized dom element or blend back in after a while\n *\n */\n public vaporize(removeElem = true): Observable<void> {\n this.vaporize$(removeElem).pipe(this.untilDestroyed).subscribe();\n return this.wsThanosComplete.pipe(take(1));\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ws-thanos.options.js","sourceRoot":"","sources":["../../../../../../libs/public/ws-thanos/src/lib/ws-thanos.options.ts"],"names":[],"mappings":"","sourcesContent":["export interface WsThanosOptions {\n animationLength: number;\n maxParticleCount: number;\n particleAcceleration: number;\n}\n"]}
1
+ {"version":3,"file":"ws-thanos.options.js","sourceRoot":"","sources":["../../../../../../libs/public/ws-thanos/src/lib/ws-thanos.options.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * How the element breaks apart (needs WebGL2, the canvas fallback always uses 'dust'):\n * - dust: particles blow away along the vaporizing front\n * - cracks: cracks spread through the element before it turns to dust\n * - shards: cracks split the element into shards that shift and tilt before they turn to dust\n * - chunks: whole chunks break off, jump away and then crumble to dust\n */\nexport type WsThanosCrumble = 'dust' | 'cracks' | 'shards' | 'chunks';\n\nexport interface WsThanosOptions {\n animationLength: number;\n maxParticleCount: number;\n particleAcceleration: number;\n /** play a windy, sandy sound while vaporizing */\n sound: boolean;\n /** volume of the snap sound between 0 and 1 */\n soundVolume: number;\n /** how the element breaks apart */\n crumble: WsThanosCrumble;\n}\n"]}