glyphdust 0.2.0 → 0.2.1

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.
package/dist/index.d.cts CHANGED
@@ -137,7 +137,7 @@ declare function buildVertexShader(keyframeCount: number): string;
137
137
  * フラグメントシェーダ。キーフレーム数に依存しないため定数。
138
138
  * 円形ソフト点 + アクセント色 + きらめき + 奥行き濃淡。
139
139
  */
140
- declare const FRAGMENT_SHADER = "\n uniform vec3 uColorInk;\n uniform vec3 uColorAccent;\n\n varying float vSeed;\n varying float vAccent;\n varying float vDepth;\n varying float vForm;\n varying float vAlpha;\n varying float vSettle;\n\n void main() {\n // \u5186\u5F62\u306E\u30BD\u30D5\u30C8\u306A\u70B9\u3002\u4E2D\u5FC3\u3067 1\u3001\u7E01\u3067 0\uFF08smoothstep \u306F edge0<edge1 \u5FC5\u9808\u306A\u306E\u3067\u53CD\u8EE2\u3057\u3066\u4F7F\u3046\uFF09\u3002\n vec2 uv = gl_PointCoord - 0.5;\n float r = length(uv);\n float alpha = 1.0 - smoothstep(0.12, 0.5, r);\n if (alpha < 0.02) discard;\n\n // \u4E3B\u4F53\u306F\u30A4\u30F3\u30AF\u3001\u4E00\u90E8\u306E\u7C92\u3060\u3051\u30A2\u30AF\u30BB\u30F3\u30C8\u8272\u3002\u5B57\u5F62\u53CE\u675F\u6642\u306F\u3084\u3084\u63A7\u3048\u3081\u306B\u3002\n float accentAmt = vAccent * mix(0.85, 0.55, vForm);\n vec3 col = mix(uColorInk, uColorAccent, accentAmt);\n\n // \u4E00\u90E8\u306E\u7C92\u306B\u660E\u308B\u3044\u304D\u3089\u3081\u304D\uFF08\u98DB\u6563\u6642\u306B\u6620\u3048\u308B\uFF09\u3002\u6574\u5217\u6642\u306F\u63A7\u3048\u3081\u3002\n float spark = step(0.94, vSeed);\n col = mix(col, uColorAccent, spark * mix(0.45, 0.15, vSettle));\n\n // \u5965\u884C\u304D\u3067\u6FC3\u6DE1\uFF08\u660E\u80CC\u666F\u3067\u306E\u8996\u8A8D\u6027\u78BA\u4FDD\u306E\u305F\u3081\u4E0B\u9650\u3092\u6301\u305F\u305B\u308B\uFF09\u3002\n float floorFade = mix(0.45, 0.78, vSettle);\n float depthFade = clamp(1.0 - (vDepth - 3.0) * 0.10, floorFade, 1.0);\n\n // \u6574\u5217\u6642\u306F\u4E0D\u900F\u660E\u5BC4\u308A\u306B\u3057\u3066\u30A8\u30C3\u30B8\u3092\u7DE0\u3081\u308B\u3002\n float a = alpha * depthFade * vAlpha;\n a = mix(a, clamp(a * 1.3, 0.0, 1.0), vSettle);\n\n gl_FragColor = vec4(col, a);\n }\n";
140
+ declare const FRAGMENT_SHADER = "\n uniform vec3 uColorInk;\n uniform vec3 uColorAccent;\n uniform float uSparkle;\n\n varying float vSeed;\n varying float vAccent;\n varying float vDepth;\n varying float vForm;\n varying float vAlpha;\n varying float vSettle;\n\n void main() {\n // \u5186\u5F62\u306E\u30BD\u30D5\u30C8\u306A\u70B9\u3002\u4E2D\u5FC3\u3067 1\u3001\u7E01\u3067 0\uFF08smoothstep \u306F edge0<edge1 \u5FC5\u9808\u306A\u306E\u3067\u53CD\u8EE2\u3057\u3066\u4F7F\u3046\uFF09\u3002\n vec2 uv = gl_PointCoord - 0.5;\n float r = length(uv);\n float alpha = 1.0 - smoothstep(0.12, 0.5, r);\n if (alpha < 0.02) discard;\n\n // \u4E3B\u4F53\u306F\u30A4\u30F3\u30AF\u3001\u4E00\u90E8\u306E\u7C92\u3060\u3051\u30A2\u30AF\u30BB\u30F3\u30C8\u8272\u3002\u5B57\u5F62\u53CE\u675F\u6642\u306F\u3084\u3084\u63A7\u3048\u3081\u306B\u3002\n float accentAmt = vAccent * mix(0.85, 0.55, vForm);\n vec3 col = mix(uColorInk, uColorAccent, accentAmt);\n\n // \u4E00\u90E8\u306E\u7C92\u306B\u660E\u308B\u3044\u304D\u3089\u3081\u304D\uFF08\u98DB\u6563\u6642\u306B\u6620\u3048\u308B\uFF09\u3002\u6574\u5217\u6642\u306F\u63A7\u3048\u3081\u3002\n float spark = step(0.94, vSeed);\n col = mix(col, uColorAccent, spark * mix(0.45, 0.15, vSettle) * uSparkle);\n\n // \u5965\u884C\u304D\u3067\u6FC3\u6DE1\uFF08\u660E\u80CC\u666F\u3067\u306E\u8996\u8A8D\u6027\u78BA\u4FDD\u306E\u305F\u3081\u4E0B\u9650\u3092\u6301\u305F\u305B\u308B\uFF09\u3002\n float floorFade = mix(0.45, 0.78, vSettle);\n float depthFade = clamp(1.0 - (vDepth - 3.0) * 0.10, floorFade, 1.0);\n\n // \u6574\u5217\u6642\u306F\u4E0D\u900F\u660E\u5BC4\u308A\u306B\u3057\u3066\u30A8\u30C3\u30B8\u3092\u7DE0\u3081\u308B\u3002\n float a = alpha * depthFade * vAlpha;\n a = mix(a, clamp(a * 1.3, 0.0, 1.0), vSettle);\n\n gl_FragColor = vec4(col, a);\n }\n";
141
141
 
142
142
  /**
143
143
  * dom-overlay.ts — 実 DOM 文字との重ね合わせ/受け渡しのための幾何ユーティリティ。
@@ -248,8 +248,31 @@ interface ManualDriverConfig {
248
248
  /** 0..1 の進捗。 */
249
249
  progress: number;
250
250
  }
251
+ /**
252
+ * 自動再生ドライバ設定。スクロール不要で、時間ベースに進捗 0→1 を進める。
253
+ * 「普通にテキストとして、どんな箱にも置いて勝手に動かす」用途の標準。
254
+ * 既定では画面内に入った瞬間に再生開始(`playOnView`)。
255
+ */
256
+ interface AutoplayDriverConfig {
257
+ type: "autoplay";
258
+ /** 0→1 にかける秒数。既定 4。 */
259
+ duration?: number;
260
+ /** 再生開始までの遅延秒。既定 0。 */
261
+ delay?: number;
262
+ /** ループ再生。既定 false(1 回で 1.0 に張り付く)。 */
263
+ loop?: boolean;
264
+ /** ループ時に 0→1→0 を往復する(loop 必須)。既定 false。 */
265
+ pingpong?: boolean;
266
+ /** 画面内に入ってから再生開始(IntersectionObserver)。既定 true。 */
267
+ playOnView?: boolean;
268
+ }
251
269
  /** ドライバ設定の合併型。 */
252
- type DriverConfig = ScrollDriverConfig | ManualDriverConfig;
270
+ type DriverConfig = ScrollDriverConfig | ManualDriverConfig | AutoplayDriverConfig;
271
+ /**
272
+ * 自動再生ドライバの進捗を、経過秒から純粋に算出する。
273
+ * `loop`/`pingpong`/`delay` を解決し 0..1 を返す。SSR セーフ(時計は呼び出し側が渡す)。
274
+ */
275
+ declare function computeAutoplayProgress(elapsedSec: number, cfg: Pick<AutoplayDriverConfig, "duration" | "delay" | "loop" | "pingpong">): number;
253
276
  /**
254
277
  * 要素の sticky スクロール進捗 0→1 を返すゲッターを作る。
255
278
  * 進捗 = `-rect.top / (rect.height - innerHeight)`(要素上端が viewport 上端を通過し切るまでで 0→1)。
@@ -340,6 +363,32 @@ interface GlyphCamera {
340
363
  /** 縦 fov(度)。既定 42。 */
341
364
  fov?: number;
342
365
  }
366
+ /**
367
+ * 粒子の見た目・モーションの質感。
368
+ * すべて省略可。{@link GlyphPreset} の上に個別上書きできる(プリセット+上書き)。
369
+ */
370
+ interface GlyphStyle {
371
+ /** 点サイズ倍率。既定 1。大きいほど太い粒(可読性↓・密度感↑)。 */
372
+ size?: number;
373
+ /**
374
+ * 合成モード。
375
+ * - `"normal"`(既定)… 明背景で可読性が高い。
376
+ * - `"additive"` … 重なりで発光する。暗背景のアンビエント/グロー向け。
377
+ */
378
+ blend?: "normal" | "additive";
379
+ /** アイドル/飛散時の漂い量 0..1。既定 1。0 で静止(端正)。 */
380
+ drift?: number;
381
+ /** きらめく粒の強さ 0..1。既定 1。0 で無効(ミニマル)。 */
382
+ sparkle?: number;
383
+ }
384
+ /**
385
+ * 質感プリセット。`style` で部分上書きできる。
386
+ * - `"default"` … 現行の標準(バランス型)。
387
+ * - `"minimal"` … 漂い・きらめき控えめで端正。明背景の本文向け。
388
+ * - `"lively"` … 漂い・きらめき強めで躍動的。
389
+ * - `"glow"` … additive 合成の発光。暗背景のヒーロー/アンビエント向け。
390
+ */
391
+ type GlyphPreset = "default" | "minimal" | "lively" | "glow";
343
392
  /** インタラクション設定。 */
344
393
  interface GlyphInteraction {
345
394
  /** ポインタ追従(近傍反発)。既定 true。 */
@@ -353,6 +402,10 @@ interface GlyphDustProps {
353
402
  keyframes: Keyframe[];
354
403
  /** 進捗ドライバ。既定 `{ type: "scroll" }`。 */
355
404
  driver?: DriverConfig;
405
+ /** 質感プリセット。既定 `"default"`。`style` で部分上書き可。 */
406
+ preset?: GlyphPreset;
407
+ /** 粒子の見た目・モーションの個別上書き(プリセットより優先)。 */
408
+ style?: GlyphStyle;
356
409
  /** 配色。 */
357
410
  colors?: GlyphColors;
358
411
  /** デバイス別粒子数。 */
@@ -380,10 +433,9 @@ declare function GlyphDust(props: GlyphDustProps): react.JSX.Element;
380
433
  * glyphdust — scroll-driven text → particles → glyph → real-text resolve
381
434
  * for react-three-fiber.
382
435
  *
383
- * 公開 API はこのファイルから re-export する(Task-007)。
384
- * 雛形段階のプレースホルダ。実装は Phase 1 (Task-002〜) で追加する。
436
+ * 公開 API はこのファイルから re-export する。
385
437
  */
386
438
  /** ライブラリのバージョン(package.json と一致させる)。 */
387
- declare const VERSION = "0.1.0";
439
+ declare const VERSION = "0.2.1";
388
440
 
389
- export { DEFAULT_TRIGGER_HEIGHT, type DenseTextTargetOptions, type DomGlyphOptions, type DriverConfig, FRAGMENT_SHADER, GLYPH_POSITION_ATTRIBUTE_PREFIX, type GlyphCamera, type GlyphColors, type GlyphCount, GlyphDust, type GlyphDustProps, type GlyphInteraction, type GlyphScreenRect, type Keyframe, type ManualDriverConfig, type Random, type ScatterKeyframe, type ScrollDriverConfig, type TextKeyframe, type TextTargetOptions, VERSION, type ViewSize, buildDenseTextTargets, buildGlyphFromDOM, buildTextTargets, buildVertexShader, computeScreenRect, createScrollProgress, glyphPositionAttribute, prefersReducedMotion, useReducedMotion, useScrollProgress, viewSizeAtZ0 };
441
+ export { type AutoplayDriverConfig, DEFAULT_TRIGGER_HEIGHT, type DenseTextTargetOptions, type DomGlyphOptions, type DriverConfig, FRAGMENT_SHADER, GLYPH_POSITION_ATTRIBUTE_PREFIX, type GlyphCamera, type GlyphColors, type GlyphCount, GlyphDust, type GlyphDustProps, type GlyphInteraction, type GlyphPreset, type GlyphScreenRect, type GlyphStyle, type Keyframe, type ManualDriverConfig, type Random, type ScatterKeyframe, type ScrollDriverConfig, type TextKeyframe, type TextTargetOptions, VERSION, type ViewSize, buildDenseTextTargets, buildGlyphFromDOM, buildTextTargets, buildVertexShader, computeAutoplayProgress, computeScreenRect, createScrollProgress, glyphPositionAttribute, prefersReducedMotion, useReducedMotion, useScrollProgress, viewSizeAtZ0 };
package/dist/index.d.ts CHANGED
@@ -137,7 +137,7 @@ declare function buildVertexShader(keyframeCount: number): string;
137
137
  * フラグメントシェーダ。キーフレーム数に依存しないため定数。
138
138
  * 円形ソフト点 + アクセント色 + きらめき + 奥行き濃淡。
139
139
  */
140
- declare const FRAGMENT_SHADER = "\n uniform vec3 uColorInk;\n uniform vec3 uColorAccent;\n\n varying float vSeed;\n varying float vAccent;\n varying float vDepth;\n varying float vForm;\n varying float vAlpha;\n varying float vSettle;\n\n void main() {\n // \u5186\u5F62\u306E\u30BD\u30D5\u30C8\u306A\u70B9\u3002\u4E2D\u5FC3\u3067 1\u3001\u7E01\u3067 0\uFF08smoothstep \u306F edge0<edge1 \u5FC5\u9808\u306A\u306E\u3067\u53CD\u8EE2\u3057\u3066\u4F7F\u3046\uFF09\u3002\n vec2 uv = gl_PointCoord - 0.5;\n float r = length(uv);\n float alpha = 1.0 - smoothstep(0.12, 0.5, r);\n if (alpha < 0.02) discard;\n\n // \u4E3B\u4F53\u306F\u30A4\u30F3\u30AF\u3001\u4E00\u90E8\u306E\u7C92\u3060\u3051\u30A2\u30AF\u30BB\u30F3\u30C8\u8272\u3002\u5B57\u5F62\u53CE\u675F\u6642\u306F\u3084\u3084\u63A7\u3048\u3081\u306B\u3002\n float accentAmt = vAccent * mix(0.85, 0.55, vForm);\n vec3 col = mix(uColorInk, uColorAccent, accentAmt);\n\n // \u4E00\u90E8\u306E\u7C92\u306B\u660E\u308B\u3044\u304D\u3089\u3081\u304D\uFF08\u98DB\u6563\u6642\u306B\u6620\u3048\u308B\uFF09\u3002\u6574\u5217\u6642\u306F\u63A7\u3048\u3081\u3002\n float spark = step(0.94, vSeed);\n col = mix(col, uColorAccent, spark * mix(0.45, 0.15, vSettle));\n\n // \u5965\u884C\u304D\u3067\u6FC3\u6DE1\uFF08\u660E\u80CC\u666F\u3067\u306E\u8996\u8A8D\u6027\u78BA\u4FDD\u306E\u305F\u3081\u4E0B\u9650\u3092\u6301\u305F\u305B\u308B\uFF09\u3002\n float floorFade = mix(0.45, 0.78, vSettle);\n float depthFade = clamp(1.0 - (vDepth - 3.0) * 0.10, floorFade, 1.0);\n\n // \u6574\u5217\u6642\u306F\u4E0D\u900F\u660E\u5BC4\u308A\u306B\u3057\u3066\u30A8\u30C3\u30B8\u3092\u7DE0\u3081\u308B\u3002\n float a = alpha * depthFade * vAlpha;\n a = mix(a, clamp(a * 1.3, 0.0, 1.0), vSettle);\n\n gl_FragColor = vec4(col, a);\n }\n";
140
+ declare const FRAGMENT_SHADER = "\n uniform vec3 uColorInk;\n uniform vec3 uColorAccent;\n uniform float uSparkle;\n\n varying float vSeed;\n varying float vAccent;\n varying float vDepth;\n varying float vForm;\n varying float vAlpha;\n varying float vSettle;\n\n void main() {\n // \u5186\u5F62\u306E\u30BD\u30D5\u30C8\u306A\u70B9\u3002\u4E2D\u5FC3\u3067 1\u3001\u7E01\u3067 0\uFF08smoothstep \u306F edge0<edge1 \u5FC5\u9808\u306A\u306E\u3067\u53CD\u8EE2\u3057\u3066\u4F7F\u3046\uFF09\u3002\n vec2 uv = gl_PointCoord - 0.5;\n float r = length(uv);\n float alpha = 1.0 - smoothstep(0.12, 0.5, r);\n if (alpha < 0.02) discard;\n\n // \u4E3B\u4F53\u306F\u30A4\u30F3\u30AF\u3001\u4E00\u90E8\u306E\u7C92\u3060\u3051\u30A2\u30AF\u30BB\u30F3\u30C8\u8272\u3002\u5B57\u5F62\u53CE\u675F\u6642\u306F\u3084\u3084\u63A7\u3048\u3081\u306B\u3002\n float accentAmt = vAccent * mix(0.85, 0.55, vForm);\n vec3 col = mix(uColorInk, uColorAccent, accentAmt);\n\n // \u4E00\u90E8\u306E\u7C92\u306B\u660E\u308B\u3044\u304D\u3089\u3081\u304D\uFF08\u98DB\u6563\u6642\u306B\u6620\u3048\u308B\uFF09\u3002\u6574\u5217\u6642\u306F\u63A7\u3048\u3081\u3002\n float spark = step(0.94, vSeed);\n col = mix(col, uColorAccent, spark * mix(0.45, 0.15, vSettle) * uSparkle);\n\n // \u5965\u884C\u304D\u3067\u6FC3\u6DE1\uFF08\u660E\u80CC\u666F\u3067\u306E\u8996\u8A8D\u6027\u78BA\u4FDD\u306E\u305F\u3081\u4E0B\u9650\u3092\u6301\u305F\u305B\u308B\uFF09\u3002\n float floorFade = mix(0.45, 0.78, vSettle);\n float depthFade = clamp(1.0 - (vDepth - 3.0) * 0.10, floorFade, 1.0);\n\n // \u6574\u5217\u6642\u306F\u4E0D\u900F\u660E\u5BC4\u308A\u306B\u3057\u3066\u30A8\u30C3\u30B8\u3092\u7DE0\u3081\u308B\u3002\n float a = alpha * depthFade * vAlpha;\n a = mix(a, clamp(a * 1.3, 0.0, 1.0), vSettle);\n\n gl_FragColor = vec4(col, a);\n }\n";
141
141
 
142
142
  /**
143
143
  * dom-overlay.ts — 実 DOM 文字との重ね合わせ/受け渡しのための幾何ユーティリティ。
@@ -248,8 +248,31 @@ interface ManualDriverConfig {
248
248
  /** 0..1 の進捗。 */
249
249
  progress: number;
250
250
  }
251
+ /**
252
+ * 自動再生ドライバ設定。スクロール不要で、時間ベースに進捗 0→1 を進める。
253
+ * 「普通にテキストとして、どんな箱にも置いて勝手に動かす」用途の標準。
254
+ * 既定では画面内に入った瞬間に再生開始(`playOnView`)。
255
+ */
256
+ interface AutoplayDriverConfig {
257
+ type: "autoplay";
258
+ /** 0→1 にかける秒数。既定 4。 */
259
+ duration?: number;
260
+ /** 再生開始までの遅延秒。既定 0。 */
261
+ delay?: number;
262
+ /** ループ再生。既定 false(1 回で 1.0 に張り付く)。 */
263
+ loop?: boolean;
264
+ /** ループ時に 0→1→0 を往復する(loop 必須)。既定 false。 */
265
+ pingpong?: boolean;
266
+ /** 画面内に入ってから再生開始(IntersectionObserver)。既定 true。 */
267
+ playOnView?: boolean;
268
+ }
251
269
  /** ドライバ設定の合併型。 */
252
- type DriverConfig = ScrollDriverConfig | ManualDriverConfig;
270
+ type DriverConfig = ScrollDriverConfig | ManualDriverConfig | AutoplayDriverConfig;
271
+ /**
272
+ * 自動再生ドライバの進捗を、経過秒から純粋に算出する。
273
+ * `loop`/`pingpong`/`delay` を解決し 0..1 を返す。SSR セーフ(時計は呼び出し側が渡す)。
274
+ */
275
+ declare function computeAutoplayProgress(elapsedSec: number, cfg: Pick<AutoplayDriverConfig, "duration" | "delay" | "loop" | "pingpong">): number;
253
276
  /**
254
277
  * 要素の sticky スクロール進捗 0→1 を返すゲッターを作る。
255
278
  * 進捗 = `-rect.top / (rect.height - innerHeight)`(要素上端が viewport 上端を通過し切るまでで 0→1)。
@@ -340,6 +363,32 @@ interface GlyphCamera {
340
363
  /** 縦 fov(度)。既定 42。 */
341
364
  fov?: number;
342
365
  }
366
+ /**
367
+ * 粒子の見た目・モーションの質感。
368
+ * すべて省略可。{@link GlyphPreset} の上に個別上書きできる(プリセット+上書き)。
369
+ */
370
+ interface GlyphStyle {
371
+ /** 点サイズ倍率。既定 1。大きいほど太い粒(可読性↓・密度感↑)。 */
372
+ size?: number;
373
+ /**
374
+ * 合成モード。
375
+ * - `"normal"`(既定)… 明背景で可読性が高い。
376
+ * - `"additive"` … 重なりで発光する。暗背景のアンビエント/グロー向け。
377
+ */
378
+ blend?: "normal" | "additive";
379
+ /** アイドル/飛散時の漂い量 0..1。既定 1。0 で静止(端正)。 */
380
+ drift?: number;
381
+ /** きらめく粒の強さ 0..1。既定 1。0 で無効(ミニマル)。 */
382
+ sparkle?: number;
383
+ }
384
+ /**
385
+ * 質感プリセット。`style` で部分上書きできる。
386
+ * - `"default"` … 現行の標準(バランス型)。
387
+ * - `"minimal"` … 漂い・きらめき控えめで端正。明背景の本文向け。
388
+ * - `"lively"` … 漂い・きらめき強めで躍動的。
389
+ * - `"glow"` … additive 合成の発光。暗背景のヒーロー/アンビエント向け。
390
+ */
391
+ type GlyphPreset = "default" | "minimal" | "lively" | "glow";
343
392
  /** インタラクション設定。 */
344
393
  interface GlyphInteraction {
345
394
  /** ポインタ追従(近傍反発)。既定 true。 */
@@ -353,6 +402,10 @@ interface GlyphDustProps {
353
402
  keyframes: Keyframe[];
354
403
  /** 進捗ドライバ。既定 `{ type: "scroll" }`。 */
355
404
  driver?: DriverConfig;
405
+ /** 質感プリセット。既定 `"default"`。`style` で部分上書き可。 */
406
+ preset?: GlyphPreset;
407
+ /** 粒子の見た目・モーションの個別上書き(プリセットより優先)。 */
408
+ style?: GlyphStyle;
356
409
  /** 配色。 */
357
410
  colors?: GlyphColors;
358
411
  /** デバイス別粒子数。 */
@@ -380,10 +433,9 @@ declare function GlyphDust(props: GlyphDustProps): react.JSX.Element;
380
433
  * glyphdust — scroll-driven text → particles → glyph → real-text resolve
381
434
  * for react-three-fiber.
382
435
  *
383
- * 公開 API はこのファイルから re-export する(Task-007)。
384
- * 雛形段階のプレースホルダ。実装は Phase 1 (Task-002〜) で追加する。
436
+ * 公開 API はこのファイルから re-export する。
385
437
  */
386
438
  /** ライブラリのバージョン(package.json と一致させる)。 */
387
- declare const VERSION = "0.1.0";
439
+ declare const VERSION = "0.2.1";
388
440
 
389
- export { DEFAULT_TRIGGER_HEIGHT, type DenseTextTargetOptions, type DomGlyphOptions, type DriverConfig, FRAGMENT_SHADER, GLYPH_POSITION_ATTRIBUTE_PREFIX, type GlyphCamera, type GlyphColors, type GlyphCount, GlyphDust, type GlyphDustProps, type GlyphInteraction, type GlyphScreenRect, type Keyframe, type ManualDriverConfig, type Random, type ScatterKeyframe, type ScrollDriverConfig, type TextKeyframe, type TextTargetOptions, VERSION, type ViewSize, buildDenseTextTargets, buildGlyphFromDOM, buildTextTargets, buildVertexShader, computeScreenRect, createScrollProgress, glyphPositionAttribute, prefersReducedMotion, useReducedMotion, useScrollProgress, viewSizeAtZ0 };
441
+ export { type AutoplayDriverConfig, DEFAULT_TRIGGER_HEIGHT, type DenseTextTargetOptions, type DomGlyphOptions, type DriverConfig, FRAGMENT_SHADER, GLYPH_POSITION_ATTRIBUTE_PREFIX, type GlyphCamera, type GlyphColors, type GlyphCount, GlyphDust, type GlyphDustProps, type GlyphInteraction, type GlyphPreset, type GlyphScreenRect, type GlyphStyle, type Keyframe, type ManualDriverConfig, type Random, type ScatterKeyframe, type ScrollDriverConfig, type TextKeyframe, type TextTargetOptions, VERSION, type ViewSize, buildDenseTextTargets, buildGlyphFromDOM, buildTextTargets, buildVertexShader, computeAutoplayProgress, computeScreenRect, createScrollProgress, glyphPositionAttribute, prefersReducedMotion, useReducedMotion, useScrollProgress, viewSizeAtZ0 };
package/dist/index.js CHANGED
@@ -160,6 +160,8 @@ function buildVertexShader(keyframeCount) {
160
160
  uniform vec3 uPointer;
161
161
  uniform float uPointerActive;
162
162
  uniform float uSize;
163
+ uniform float uSizeScale;
164
+ uniform float uDrift;
163
165
  uniform float uPixelRatio;
164
166
 
165
167
  ${attributeDecls}
@@ -195,7 +197,7 @@ ${mixChain}
195
197
 
196
198
  // \u30A2\u30A4\u30C9\u30EB\u306E\u6F02\u3044\uFF08\u6574\u5217\u6642 settle / \u5B57\u5F62\u6642 form \u3067\u5F31\u3081\u308B\uFF09\u3002
197
199
  vSettle = uSettle;
198
- float drift = (1.0 - uReduced) * (1.0 - uSettle * 0.9) * (1.0 - uForm);
200
+ float drift = (1.0 - uReduced) * (1.0 - uSettle * 0.9) * (1.0 - uForm) * uDrift;
199
201
  pos.x += sin(uTime * 0.35 + ph) * 0.06 * drift;
200
202
  pos.y += cos(uTime * 0.30 + ph * 1.7) * 0.06 * drift;
201
203
  pos.z += sin(uTime * 0.27 + ph * 2.3) * 0.06 * drift;
@@ -223,9 +225,11 @@ ${mixChain}
223
225
  float sizeVar = mix(0.55 + aSeed * 0.9, 0.72 + aSeed * 0.35, uSettle);
224
226
  // \u5B57\u5F62\u53CE\u675F\u6642\u306F\u96A3\u63A5\u7C92\u5B50\u3067\u9699\u9593\u3092\u57CB\u3081\u308B\u305F\u3081\u308F\u305A\u304B\u306B\u5927\u304D\u3081\uFF06\u5747\u4E00\u306B\u3002
225
227
  sizeVar = mix(sizeVar, 0.95 + aSeed * 0.18, uForm);
226
- float s = uSize * sizeVar;
228
+ // \u9AD8 dpr \u74B0\u5883\u3067\u306F\u5C0F\u7C92\u30FB\u4E0A\u9650\u4F4E\u3081\u306E\u65B9\u304C\u30A8\u30C3\u30B8\u304C\u7DE0\u307E\u308A\u9AD8\u7CBE\u7D30\u306B\u898B\u3048\u308B
229
+ // \uFF08\u30B3\u30FC\u30DD\u30EC\u30FC\u30C8\u30B5\u30A4\u30C8\u5B9F\u88C5\u3067\u5B9F\u8A3C\u30020.62 \u3068 clamp 4\u301C5 \u304C\u6700\u3082\u300C\u971E\u307E\u306A\u3044\u300D\uFF09\u3002
230
+ float s = uSize * sizeVar * 0.62 * uSizeScale;
227
231
  gl_PointSize = s * uPixelRatio * (1.0 / -mvPosition.z);
228
- gl_PointSize = clamp(gl_PointSize, 1.0, mix(7.0, 9.0, uForm) * uPixelRatio);
232
+ gl_PointSize = clamp(gl_PointSize, 1.0, mix(4.0, 5.0, uForm) * uPixelRatio);
229
233
  }
230
234
  `
231
235
  );
@@ -235,6 +239,7 @@ var FRAGMENT_SHADER = (
235
239
  `
236
240
  uniform vec3 uColorInk;
237
241
  uniform vec3 uColorAccent;
242
+ uniform float uSparkle;
238
243
 
239
244
  varying float vSeed;
240
245
  varying float vAccent;
@@ -256,7 +261,7 @@ var FRAGMENT_SHADER = (
256
261
 
257
262
  // \u4E00\u90E8\u306E\u7C92\u306B\u660E\u308B\u3044\u304D\u3089\u3081\u304D\uFF08\u98DB\u6563\u6642\u306B\u6620\u3048\u308B\uFF09\u3002\u6574\u5217\u6642\u306F\u63A7\u3048\u3081\u3002
258
263
  float spark = step(0.94, vSeed);
259
- col = mix(col, uColorAccent, spark * mix(0.45, 0.15, vSettle));
264
+ col = mix(col, uColorAccent, spark * mix(0.45, 0.15, vSettle) * uSparkle);
260
265
 
261
266
  // \u5965\u884C\u304D\u3067\u6FC3\u6DE1\uFF08\u660E\u80CC\u666F\u3067\u306E\u8996\u8A8D\u6027\u78BA\u4FDD\u306E\u305F\u3081\u4E0B\u9650\u3092\u6301\u305F\u305B\u308B\uFF09\u3002
262
267
  float floorFade = mix(0.45, 0.78, vSettle);
@@ -390,6 +395,21 @@ function computeScreenRect(targets, viewportW, viewportH, visibleWorldW) {
390
395
  };
391
396
  }
392
397
  var DEFAULT_TRIGGER_HEIGHT = 2;
398
+ function triangle(x) {
399
+ const t = x % 2;
400
+ return t <= 1 ? t : 2 - t;
401
+ }
402
+ function computeAutoplayProgress(elapsedSec, cfg) {
403
+ const duration = cfg.duration && cfg.duration > 0 ? cfg.duration : 4;
404
+ const delay = cfg.delay && cfg.delay > 0 ? cfg.delay : 0;
405
+ const t = elapsedSec - delay;
406
+ if (t <= 0) return 0;
407
+ const raw = t / duration;
408
+ if (cfg.loop) {
409
+ return cfg.pingpong ? triangle(raw) : raw % 1;
410
+ }
411
+ return clamp01(raw);
412
+ }
393
413
  function clamp01(x) {
394
414
  return x < 0 ? 0 : x > 1 ? 1 : x;
395
415
  }
@@ -501,6 +521,7 @@ function GlyphPoints(props) {
501
521
  keyframes,
502
522
  count,
503
523
  colors,
524
+ style,
504
525
  cameraZ,
505
526
  cameraFov,
506
527
  pointer: pointerEnabled,
@@ -584,6 +605,9 @@ function GlyphPoints(props) {
584
605
  uPointer: { value: new THREE.Vector3(0, 0, 0) },
585
606
  uPointerActive: { value: 0 },
586
607
  uSize: { value: 1 },
608
+ uSizeScale: { value: style.size },
609
+ uDrift: { value: style.drift },
610
+ uSparkle: { value: style.sparkle },
587
611
  uPixelRatio: { value: 1 },
588
612
  uColorInk: { value: colors.ink.clone() },
589
613
  uColorAccent: { value: colors.accent.clone() }
@@ -781,9 +805,19 @@ function GlyphPoints(props) {
781
805
  const mat = matRef.current;
782
806
  if (!mat) return;
783
807
  const u = mat.uniforms;
784
- u.uPixelRatio.value = Math.min(window.devicePixelRatio || 1, 2);
808
+ u.uPixelRatio.value = Math.min(window.devicePixelRatio || 1, 3);
785
809
  u.uSize.value = Math.min(size.height / 18, 26);
786
810
  }, [size]);
811
+ useEffect(() => {
812
+ const mat = matRef.current;
813
+ if (!mat) return;
814
+ const u = mat.uniforms;
815
+ u.uSizeScale.value = style.size;
816
+ u.uDrift.value = style.drift;
817
+ u.uSparkle.value = style.sparkle;
818
+ mat.blending = style.blend === "additive" ? THREE.AdditiveBlending : THREE.NormalBlending;
819
+ mat.needsUpdate = true;
820
+ }, [style.size, style.drift, style.sparkle, style.blend]);
787
821
  useFrame((state, delta) => {
788
822
  const p = pointsRef.current;
789
823
  const mat = matRef.current;
@@ -791,7 +825,7 @@ function GlyphPoints(props) {
791
825
  const u = mat.uniforms;
792
826
  const d = Math.min(delta, 0.05);
793
827
  const raw = THREE.MathUtils.clamp(getProgress(), 0, 1);
794
- stage.current = THREE.MathUtils.lerp(stage.current, raw, 0.1);
828
+ stage.current = raw;
795
829
  const s = stage.current;
796
830
  let settle = 0;
797
831
  let burst = 0;
@@ -808,6 +842,11 @@ function GlyphPoints(props) {
808
842
  if (lastIsText && n >= 2) {
809
843
  form = smooth(times[n - 2] ?? 0, times[n - 1] ?? 1, s);
810
844
  }
845
+ const firstIsText = timeline.isText[0] === true;
846
+ if (firstIsText && n >= 2) {
847
+ const formStart = 1 - smooth(times[0] ?? 0, times[1] ?? 1, s);
848
+ form = Math.max(form, formStart);
849
+ }
811
850
  const guard = THREE.MathUtils.clamp(Math.max(settle, form), 0, 1);
812
851
  guardRef.current = guard;
813
852
  const swapped = raw >= timeline.swapAt ? 1 : 0;
@@ -858,7 +897,7 @@ function GlyphPoints(props) {
858
897
  uniforms,
859
898
  transparent: true,
860
899
  depthWrite: false,
861
- blending: THREE.NormalBlending,
900
+ blending: style.blend === "additive" ? THREE.AdditiveBlending : THREE.NormalBlending,
862
901
  vertexShader,
863
902
  fragmentShader: FRAGMENT_SHADER
864
903
  }
@@ -872,6 +911,12 @@ var DEFAULT_COUNT_MOBILE = 5200;
872
911
  var DEFAULT_CAMERA_Z = 7;
873
912
  var DEFAULT_CAMERA_FOV = 42;
874
913
  var DEFAULT_DPR = [1, 1.75];
914
+ var PRESETS = {
915
+ default: { size: 1, blend: "normal", drift: 1, sparkle: 1 },
916
+ minimal: { size: 0.92, blend: "normal", drift: 0.35, sparkle: 0 },
917
+ lively: { size: 1.05, blend: "normal", drift: 1.4, sparkle: 1.4 },
918
+ glow: { size: 1.1, blend: "additive", drift: 1.1, sparkle: 1.5 }
919
+ };
875
920
  function clamp012(x) {
876
921
  return x < 0 ? 0 : x > 1 ? 1 : x;
877
922
  }
@@ -890,6 +935,8 @@ function GlyphDust(props) {
890
935
  const {
891
936
  keyframes,
892
937
  driver = { type: "scroll" },
938
+ preset = "default",
939
+ style,
893
940
  colors,
894
941
  count,
895
942
  dpr = DEFAULT_DPR,
@@ -913,15 +960,62 @@ function GlyphDust(props) {
913
960
  const resolveRef = useRef(null);
914
961
  const manualRef = useRef(0);
915
962
  if (driver.type === "manual") manualRef.current = clamp012(driver.progress);
963
+ const autoplay = driver.type === "autoplay" ? driver : null;
964
+ const playingRef = useRef(false);
965
+ const startMsRef = useRef(null);
966
+ const lastAutoRef = useRef(0);
967
+ useEffect(() => {
968
+ if (!autoplay) return;
969
+ if (autoplay.playOnView === false) {
970
+ playingRef.current = true;
971
+ return;
972
+ }
973
+ const el = wrapperRef.current;
974
+ if (el === null || typeof IntersectionObserver === "undefined") {
975
+ playingRef.current = true;
976
+ return;
977
+ }
978
+ const io = new IntersectionObserver(
979
+ (entries) => {
980
+ for (const e of entries) {
981
+ if (e.isIntersecting && !playingRef.current) {
982
+ playingRef.current = true;
983
+ startMsRef.current = null;
984
+ }
985
+ }
986
+ },
987
+ { threshold: 0.25 }
988
+ );
989
+ io.observe(el);
990
+ return () => io.disconnect();
991
+ }, [autoplay?.playOnView]);
916
992
  const getProgress = useCallback(() => {
917
993
  if (driver.type === "manual") return manualRef.current;
994
+ if (driver.type === "autoplay") {
995
+ if (!playingRef.current || typeof performance === "undefined") {
996
+ return lastAutoRef.current;
997
+ }
998
+ if (startMsRef.current === null) startMsRef.current = performance.now();
999
+ const elapsed = (performance.now() - startMsRef.current) / 1e3;
1000
+ lastAutoRef.current = computeAutoplayProgress(elapsed, driver);
1001
+ return lastAutoRef.current;
1002
+ }
918
1003
  const el = wrapperRef.current;
919
1004
  if (el === null || typeof window === "undefined") return 0;
920
1005
  const rect = el.getBoundingClientRect();
921
1006
  const total = rect.height - window.innerHeight;
922
1007
  if (total <= 0) return 0;
923
1008
  return clamp012(-rect.top / total);
924
- }, [driver.type]);
1009
+ }, [driver]);
1010
+ const resolvedStyle = useMemo(() => {
1011
+ const base = PRESETS[preset] ?? PRESETS.default;
1012
+ return {
1013
+ size: style?.size ?? base.size,
1014
+ blend: style?.blend ?? base.blend,
1015
+ drift: style?.drift ?? base.drift,
1016
+ sparkle: style?.sparkle ?? base.sparkle
1017
+ };
1018
+ }, [preset, style?.size, style?.blend, style?.drift, style?.sparkle]);
925
1019
  const resolvedColors = useMemo(
926
1020
  () => ({
927
1021
  ink: new THREE.Color(colors?.ink ?? DEFAULT_INK),
@@ -958,6 +1052,7 @@ function GlyphDust(props) {
958
1052
  keyframes,
959
1053
  count: particleCount,
960
1054
  colors: resolvedColors,
1055
+ style: resolvedStyle,
961
1056
  cameraZ,
962
1057
  cameraFov,
963
1058
  pointer: pointerEnabled,
@@ -991,10 +1086,11 @@ function GlyphDust(props) {
991
1086
  }
992
1087
  ) : null
993
1088
  ] });
994
- if (driver.type === "manual") {
1089
+ if (driver.type === "manual" || driver.type === "autoplay") {
995
1090
  return /* @__PURE__ */ jsx(
996
1091
  "div",
997
1092
  {
1093
+ ref: wrapperRef,
998
1094
  className,
999
1095
  style: { position: "relative", width: "100%", height: "100%" },
1000
1096
  children: scene
@@ -1026,8 +1122,8 @@ function GlyphDust(props) {
1026
1122
  }
1027
1123
 
1028
1124
  // src/index.ts
1029
- var VERSION = "0.1.0";
1125
+ var VERSION = "0.2.1";
1030
1126
 
1031
- export { DEFAULT_TRIGGER_HEIGHT, FRAGMENT_SHADER, GLYPH_POSITION_ATTRIBUTE_PREFIX, GlyphDust, VERSION, buildDenseTextTargets, buildGlyphFromDOM, buildTextTargets, buildVertexShader, computeScreenRect, createScrollProgress, glyphPositionAttribute, prefersReducedMotion, useReducedMotion, useScrollProgress, viewSizeAtZ0 };
1127
+ export { DEFAULT_TRIGGER_HEIGHT, FRAGMENT_SHADER, GLYPH_POSITION_ATTRIBUTE_PREFIX, GlyphDust, VERSION, buildDenseTextTargets, buildGlyphFromDOM, buildTextTargets, buildVertexShader, computeAutoplayProgress, computeScreenRect, createScrollProgress, glyphPositionAttribute, prefersReducedMotion, useReducedMotion, useScrollProgress, viewSizeAtZ0 };
1032
1128
  //# sourceMappingURL=index.js.map
1033
1129
  //# sourceMappingURL=index.js.map