pixel-daylight-switch-gsvegetable 3.0.0 → 3.1.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/README.ja.md CHANGED
@@ -12,22 +12,22 @@
12
12
 
13
13
  Pixel Daylight Switch は、ドラッグ操作で昼・夕暮れ・夜を切り替えられる UI コンポーネントです。ガラスレンズの中のピクセルアートの世界が、スイッチの動きに合わせて変化します。Vanilla JavaScript、WebGL2、Canvas 2D フォールバックで構成され、実行時にサードパーティ製ライブラリを必要としません。
14
14
 
15
- **[ライブデモ](https://gsvegetable.github.io/pixel-daylight-switch/)** · **[パラメーター Playground](https://gsvegetable.github.io/pixel-daylight-switch/playground.html)** · **[npm パッケージ](https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable)**
15
+ **[ライブデモ](https://gsvegetable.github.io/pixel-daylight-switch/)** · **[パラメーター Playground](https://gsvegetable.github.io/pixel-daylight-switch/demos/playground.html)** · **[npm パッケージ](https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable)**
16
16
 
17
17
  ## 目的別リンク
18
18
 
19
19
  | 目的 | リンク |
20
20
  | --- | --- |
21
21
  | スイッチを体験する | [ライブデモ](https://gsvegetable.github.io/pixel-daylight-switch/) |
22
- | シーンや表示を調整する | [パラメーター Playground](https://gsvegetable.github.io/pixel-daylight-switch/playground.html) |
23
- | コンポーネントを見る | [Preview](https://gsvegetable.github.io/pixel-daylight-switch/preview.html) |
22
+ | シーンや表示を調整する | [パラメーター Playground](https://gsvegetable.github.io/pixel-daylight-switch/demos/playground.html) |
23
+ | コンポーネントを見る | [Preview](https://gsvegetable.github.io/pixel-daylight-switch/demos/preview.html) |
24
24
  | Web サイトに埋め込む | [embed.js](embed.js) |
25
25
  | JavaScript プロジェクトで使う | [npm](https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable) |
26
- | React / Vue の導入例 | [フレームワーク統合ガイド](INTEGRATIONS.md) |
26
+ | React / Vue の導入例 | [フレームワーク統合ガイド](docs/INTEGRATIONS.md) |
27
27
  | 実装を読む | [app.js](app.js) · [style.css](style.css) |
28
28
  | モバイルビルド | [ビルド情報](builds/README.md) · [GitHub Actions](https://github.com/GSvegetable/pixel-daylight-switch/actions/workflows/mobile-build.yml) |
29
29
  | テスト結果 | [CI ワークフロー](https://github.com/GSvegetable/pixel-daylight-switch/actions/workflows/ci.yml) |
30
- | 更新履歴 | [CHANGELOG](CHANGELOG.md) |
30
+ | 更新履歴 | [CHANGELOG](docs/CHANGELOG.md) |
31
31
  | 不具合を報告する | [Issues](https://github.com/GSvegetable/pixel-daylight-switch/issues) |
32
32
 
33
33
  ## 主な機能
@@ -96,40 +96,61 @@ const PixelDaylightSwitch = require('pixel-daylight-switch-gsvegetable');
96
96
 
97
97
  ESM と CommonJS の両方を提供します。SSR 環境でもインポートできますが、`mount()` はブラウザー上で対象要素が存在してから呼び出してください。
98
98
 
99
- ## コミュニティシーンプラグイン(v3.0.0)
100
99
 
101
- 3.0.0 では JSON Manifest によるカスタムピクセルワールドの登録に対応しました。初めての方はピクセルマップをそのまま使い、昼夜パレットの色だけを変更できます。上級者はピクセルマップと時間パレットを自由に設計できます。プラグインは宣言型データのみを受け取り、レンダラー内で第三者 JavaScript を実行しません。
100
+ ## 独立ガラス屈折エンジン(v3.1.1)
102
101
 
103
- **ブラウザーで使用:**
102
+ ガラスレンズを昼夜スイッチから独立して利用できるようになりました。固定アスペクト比に依存せず、幅・高さ・位置・角丸・屈折・色分散・縁の厚さ・ハイライトを個別に設定できます。Canvas、画像、動画、ImageBitmap、またはそれらを返す関数を背景ソースとして指定します。[ガイド](docs/GLASS_ENGINE.md) と [ライブデモ](https://gsvegetable.github.io/pixel-daylight-switch/demos/glass-engine.html) を参照してください。
104
103
 
105
- ```html
106
- <script src="https://gsvegetable.github.io/pixel-daylight-switch/scene-plugin.js"></script>
107
- <script src="https://gsvegetable.github.io/pixel-daylight-switch/app.js"></script>
108
- <div data-ld-part="toggle-daylight" data-scene="sakura-evening"></div>
109
- <script>
110
- PixelDaylightSwitch.registerScene(sceneManifest);
111
- </script>
104
+ ```js
105
+ import { createGlass } from 'pixel-daylight-switch-gsvegetable/glass-engine';
106
+ const glass = createGlass(container, { source: backgroundCanvas, width: '72%', height: 180, radius: 28 });
107
+ glass.setOptions({ refraction: 1.3, dispersion: 0.08 });
108
+ glass.destroy();
112
109
  ```
113
110
 
114
- コンポーネントをマウントする前に Manifest を登録してください。npm の場合:
111
+ WebGL は任意の DOM ピクセルを直接読み取れません。Web ページの背景を屈折させるには、ホストアプリが Canvas などの描画ソースを提供する必要があります。クロスオリジンメディアには CORS 制約があります。
112
+
113
+ ## コミュニティシーン
114
+
115
+ コミュニティシーンは宣言型 JSON Manifest で定義し、コンポーネントのマウント前に登録します。現在の形式は **Manifest v2** です。再利用可能なピクセル素材、順序付きレイヤー、制限付きモーショントラックに対応し、既存の Manifest v1 シーンも引き続き利用できます。
116
+
117
+ ### シーンの登録
118
+
119
+ npm プロジェクトの場合:
115
120
 
116
121
  ```js
117
122
  import PixelDaylightSwitch, { registerScene } from 'pixel-daylight-switch-gsvegetable';
118
- import myScene from './my-scene.json';
123
+ import scene from './floating-island.v2.json';
119
124
 
120
- const unregister = registerScene(myScene);
125
+ const unregister = registerScene(scene);
121
126
  const root = document.querySelector('[data-ld-part="toggle-daylight"]');
122
- root.dataset.scene = myScene.id;
127
+ root.dataset.scene = scene.id;
123
128
  PixelDaylightSwitch.mount(root);
124
129
  ```
125
130
 
126
- Manifest v1 はシーン ID、作者・ライセンス、ピクセルキャンバス、文字ベースのピクセルマップ、複数の昼夜パレット、24 時間のタイムラインで構成されます。キャンバスは最大 192 × 112 ピクセルです。すべてのピクセル記号に各パレットの色を定義する必要があります。時間ノード間の色はレンダラーが補間します。
131
+ ブラウザーで直接使用する場合は、`scene-plugin.js` を `app.js` より先に読み込み、マウント前に Manifest を登録します。
132
+
133
+ ```html
134
+ <script src="https://gsvegetable.github.io/pixel-daylight-switch/scene-plugin.js"></script>
135
+ <script src="https://gsvegetable.github.io/pixel-daylight-switch/app.js"></script>
136
+ <div data-ld-part="toggle-daylight" data-scene="my-community-scene"></div>
137
+ <script>
138
+ PixelDaylightSwitchScenePlugins.registerScene(sceneManifest);
139
+ </script>
140
+ ```
141
+
142
+ ### Manifest v2 の機能
143
+
144
+ - 最大 24 個の再利用可能な文字ベースのピクセル素材と、最大 16 個の順序付きレイヤー。
145
+ - 整数のシーン座標を使用し、後のレイヤーほど手前に合成されます。
146
+ - オーバーレイ素材のドット文字 `.` は透明ピクセルとして扱われます。
147
+ - `drift` と `bob` の決定論的なモーショントラックに対応し、X/Y 軸上で動作します。
148
+ - 既存の Manifest v1 と 5 つの組み込みシーンは引き続きサポートされます。
149
+
150
+ [Manifest v2 Schema](schemas/scene-manifest.v2.schema.json)、[Floating Island サンプル](examples/scenes/floating-island.v2.json)、[シーン制作ガイド](docs/SCENE_PLUGINS.md)を参照してください。互換性のため [v1 Schema](schemas/scene-manifest.v1.schema.json) も維持されています。
127
151
 
128
- - [プラグイン開発ガイド](SCENE_PLUGINS.md)
129
- - [Manifest v1 JSON Schema](schemas/scene-manifest.v1.schema.json)
130
- - [Sakura Evening サンプル](examples/scenes/sakura-evening.json)
152
+ 現在の素材形式は文字ベースのピクセルマトリックスです。PNG/JPEG のアップロードや任意の実行コードには対応していません。
131
153
 
132
- v1 は外部画像/Sprite、レイヤー式 Tile Map、独自アニメーショントラックにはまだ対応していません。これらは任意コードの実行を許可せず、将来の独立した Schema 拡張として追加します。5 つの組み込みシーンは従来の描画・アニメーション実装を維持します。
133
154
 
134
155
  ## 設定属性
135
156
 
@@ -238,35 +259,27 @@ iOS は初回に `npm run mobile:ios:init`、以降は `npm run mobile:ios` を
238
259
  ```text
239
260
  pixel-daylight-switch/
240
261
  ├── .github/workflows/ # CI、モバイルビルド、npm 公開
241
- ├── assets/
242
- │ └── scene-gallery.png # 5 シーンの一覧画像
243
- ├── builds/
244
- │ └── README.md # モバイル成果物の説明(バイナリは Actions が生成)
245
- ├── mobile/
246
- │ ├── index.html # Capacitor アプリの入口
247
- │ └── mobile.css # モバイル専用レイアウト
248
- ├── scripts/
249
- │ └── prepare-mobile.cjs # Capacitor 用 Web ランタイム準備
250
- ├── tests/
251
- │ ├── browser/ # Playwright 操作・モバイル回帰テスト
252
- │ ├── fixture.html
253
- │ ├── mobile-smoke.cjs
254
- │ ├── package-smoke.cjs
255
- │ └── server.cjs
256
- ├── app.js # コンポーネント、シーン、入力、描画
257
- ├── style.css # コンポーネントのスタイル
258
- ├── embed.js # 1 スクリプト埋め込みローダー
259
- ├── index.html # ライブデモ
260
- ├── preview.html # コンポーネントプレビュー
261
- ├── playground.html # パラメーター Playground
262
- ├── index.mjs / index.cjs # ESM / CommonJS エントリー
263
- ├── package.json # npm メタデータとスクリプト
264
- ├── capacitor.config.json # Capacitor 設定
265
- ├── playwright.config.cjs # ブラウザーテスト設定
266
- ├── CHANGELOG.md
267
- ├── llms.txt
262
+ ├── assets/ # シーンギャラリー画像
263
+ ├── builds/ # モバイルビルド情報
264
+ ├── docs/ # 統合ガイド、シーン制作ガイド、変更履歴
265
+ ├── examples/scenes/ # コミュニティシーン Manifest
266
+ ├── mobile/ # Capacitor アプリシェル
267
+ ├── schemas/ # Manifest v1 / v2 JSON Schema
268
+ ├── scripts/ # モバイル準備・バージョン設定
269
+ ├── tests/ # Node / Chromium 回帰テスト
270
+ ├── app.js / style.css # ランタイムとコンポーネントスタイル
271
+ ├── scene-plugin.js # シーン登録・検証 API
272
+ ├── embed.js # Web サイト埋め込みローダー
273
+ ├── index.html # ライブデモ
274
+ ├── demos/ # パラメーター Playground と Preview
275
+ ├── config/ # 開発ツール設定
276
+ ├── index.mjs / index.cjs # ESM / CommonJS エントリー
277
+ ├── package.json # パッケージ設定とスクリプト
278
+ ├── capacitor.config.json # Capacitor 設定
279
+ ├── playwright.config.cjs # ブラウザーテスト設定
280
+ ├── llms.txt # AI 向けプロジェクトガイド
268
281
  ├── LICENSE
269
- └── README*.md # 英語・簡体字中国語・日本語の README
282
+ └── README*.md # 英語・簡体字中国語・日本語 README
270
283
  ```
271
284
 
272
285
  ## ライセンス
package/README.md CHANGED
@@ -12,23 +12,23 @@
12
12
 
13
13
  Pixel Daylight Switch is a draggable day/night UI component. Move its glass control to travel between day, dusk and night while a miniature pixel-art world changes inside the lens. It uses vanilla JavaScript, WebGL2 and a Canvas 2D fallback, with no third-party runtime dependency.
14
14
 
15
- **[Open the live demo](https://gsvegetable.github.io/pixel-daylight-switch/)** · **[Launch the parameter lab](https://gsvegetable.github.io/pixel-daylight-switch/playground.html)** · **[Install from npm](https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable)**
15
+ **[Open the live demo](https://gsvegetable.github.io/pixel-daylight-switch/)** · **[Launch the parameter lab](https://gsvegetable.github.io/pixel-daylight-switch/demos/playground.html)** · **[Install from npm](https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable)**
16
16
 
17
17
  ## Start here
18
18
 
19
19
  | Goal | Direct link |
20
20
  | --- | --- |
21
21
  | Experience the switch | [Live demo](https://gsvegetable.github.io/pixel-daylight-switch/) |
22
- | Customize scenes and visual behavior | [Interactive parameter lab](https://gsvegetable.github.io/pixel-daylight-switch/playground.html) |
23
- | Inspect the component | [Preview page](https://gsvegetable.github.io/pixel-daylight-switch/preview.html) |
22
+ | Customize scenes and visual behavior | [Interactive parameter lab](https://gsvegetable.github.io/pixel-daylight-switch/demos/playground.html) |
23
+ | Inspect the component | [Preview page](https://gsvegetable.github.io/pixel-daylight-switch/demos/preview.html) |
24
24
  | Add it to a website with one script | [Embed loader source](embed.js) |
25
25
  | Use it in a JavaScript project | [npm package](https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable) |
26
- | Framework integration recipes | [React and Vue examples](INTEGRATIONS.md) |
27
- | Scene plugin authoring guide | [Scene plugin guide](SCENE_PLUGINS.md) |
26
+ | Framework integration recipes | [React and Vue examples](docs/INTEGRATIONS.md) |\n| Standalone glass refraction | [Glass engine guide](docs/GLASS_ENGINE.md) · [Live demo](https://gsvegetable.github.io/pixel-daylight-switch/demos/glass-engine.html) |
27
+ | Scene plugin authoring guide | [Scene plugin guide](docs/SCENE_PLUGINS.md) |
28
28
  | Read implementation details | [Runtime](app.js) · [Styles](style.css) |
29
29
  | Download mobile builds | [Build notes and limitations](builds/README.md) · [GitHub Actions artifacts](https://github.com/GSvegetable/pixel-daylight-switch/actions/workflows/mobile-build.yml) |
30
30
  | Check tests | [CI workflow](https://github.com/GSvegetable/pixel-daylight-switch/actions/workflows/ci.yml) |
31
- | Review changes | [Changelog](CHANGELOG.md) |
31
+ | Review changes | [Changelog](docs/CHANGELOG.md) |
32
32
  | Report a bug | [Issue tracker](https://github.com/GSvegetable/pixel-daylight-switch/issues) |
33
33
 
34
34
  ## Features
@@ -97,52 +97,61 @@ const PixelDaylightSwitch = require('pixel-daylight-switch-gsvegetable');
97
97
 
98
98
  The package exposes ESM and CommonJS entry points. Importing is safe in an SSR environment; call `mount()` only in a browser after the target element exists. The package's published file list is defined in [package.json](package.json).
99
99
 
100
- ## Community scene plugins (v3.0.0)
101
100
 
102
- Version 3 introduces a data-driven scene plugin API. Community scenes use a JSON manifest; they do not execute third-party JavaScript inside the renderer.
101
+ ## Standalone glass engine (v3.1.1)
103
102
 
104
- ### Register a scene
105
-
106
- Load the registry before the renderer when using script tags:
103
+ The glass lens is also available independently of the day/night switch. It accepts a canvas, image, video, bitmap, or source callback and lets you set any width, height, position, radius, refraction, dispersion, bezel and highlight values. See the [glass engine guide](docs/GLASS_ENGINE.md) and [live demo](https://gsvegetable.github.io/pixel-daylight-switch/demos/glass-engine.html).
107
104
 
108
- ```html
109
- <script src="https://gsvegetable.github.io/pixel-daylight-switch/scene-plugin.js"></script>
110
- <script src="https://gsvegetable.github.io/pixel-daylight-switch/app.js"></script>
111
- <div data-ld-part="toggle-daylight" data-scene="sakura-evening"></div>
105
+ ```js
106
+ import { createGlass } from 'pixel-daylight-switch-gsvegetable/glass-engine';
107
+ const glass = createGlass(container, { source: backgroundCanvas, width: '72%', height: 180, radius: 28 });
108
+ glass.setOptions({ refraction: 1.3, dispersion: 0.08 });
109
+ glass.destroy();
112
110
  ```
113
111
 
114
- Register a manifest before mounting the component:
112
+ WebGL cannot sample arbitrary DOM pixels directly. To refract a live page background, provide an origin-clean canvas/render target from your application. Cross-origin media is subject to browser CORS rules.
115
113
 
116
- ```js
117
- const unregister = PixelDaylightSwitchScenePlugins.registerScene(sceneManifest);
118
- PixelDaylightSwitch.mount(document.querySelector('[data-ld-part="toggle-daylight"]'));
119
- ```
114
+ ## Community scenes
120
115
 
121
- With npm:
116
+ Community scenes are declarative JSON manifests registered before the component is mounted. The current format is **Manifest v2**, which supports reusable pixel-matrix assets, ordered layers and bounded motion tracks. Existing Manifest v1 scenes remain compatible.
117
+
118
+ ### Register a scene
119
+
120
+ For npm projects:
122
121
 
123
122
  ```js
124
123
  import PixelDaylightSwitch, { registerScene } from 'pixel-daylight-switch-gsvegetable';
125
- import sakura from './sakura-evening.json';
124
+ import scene from './floating-island.v2.json';
125
+
126
+ const unregister = registerScene(scene);
127
+ const root = document.querySelector('[data-ld-part="toggle-daylight"]');
128
+ root.dataset.scene = scene.id;
129
+ PixelDaylightSwitch.mount(root);
130
+ ```
126
131
 
127
- registerScene(sakura);
128
- PixelDaylightSwitch.mount(document.querySelector('[data-ld-part="toggle-daylight"]'));
132
+ For direct browser usage, load `scene-plugin.js` before `app.js`, then register the manifest before mounting:
133
+
134
+ ```html
135
+ <script src="https://gsvegetable.github.io/pixel-daylight-switch/scene-plugin.js"></script>
136
+ <script src="https://gsvegetable.github.io/pixel-daylight-switch/app.js"></script>
137
+ <div data-ld-part="toggle-daylight" data-scene="my-community-scene"></div>
138
+ <script>
139
+ PixelDaylightSwitchScenePlugins.registerScene(sceneManifest);
140
+ </script>
129
141
  ```
130
142
 
131
- ### Manifest v1
143
+ ### Manifest v2 capabilities
132
144
 
133
- A manifest contains a fixed-size character pixel map, named color palettes and a 24-hour palette timeline. The renderer interpolates colors between timeline stops while keeping the same scene geometry.
145
+ - Up to 24 reusable pixel-matrix assets and 16 ordered layers.
146
+ - Integer scene coordinates; later layers are composited above earlier layers.
147
+ - The dot character (`.`) is transparent in overlay assets.
148
+ - Optional deterministic `drift` and `bob` motion tracks on either axis.
149
+ - Existing v1 manifests and all five built-in scenes remain supported and unchanged.
134
150
 
135
- - Canvas limit: 192 × 112 pixels.
136
- - Pixel rows must match the declared width and height.
137
- - Every symbol used by the pixel map must have a color in every palette.
138
- - Colors use `#RGB` or `#RRGGBB`.
139
- - Timeline stops are strictly increasing and range from 0 to 24 hours.
140
- - Built-in scene IDs are reserved; community IDs must be unique.
141
- - The runtime accepts declarative data only. Manifest data is validated and copied on registration; it cannot run scripts.
151
+ See the [Manifest v2 schema](schemas/scene-manifest.v2.schema.json), [Floating Island example](examples/scenes/floating-island.v2.json) and [scene authoring guide](docs/SCENE_PLUGINS.md). The v1 schema remains available for compatibility at [schemas/scene-manifest.v1.schema.json](schemas/scene-manifest.v1.schema.json).
142
152
 
143
- See the [v1 JSON Schema](schemas/scene-manifest.v1.schema.json) and [Sakura Evening example](examples/scenes/sakura-evening.json). The public API is `registerScene(manifest)`, `validateScene(manifest)`, `getScene(id)`, and `listScenes()`. The returned unregister function removes that registration.
153
+ Assets currently use native character pixel matrices; PNG/JPEG uploads and arbitrary executable plugin callbacks are not part of the current format.
144
154
 
145
- The first manifest release focuses on palette-driven pixel worlds. Sprite assets, layered tile maps and declarative animation tracks are planned as compatible schema extensions rather than arbitrary executable plugin code.
146
155
 
147
156
  ## Configuration
148
157
 
@@ -251,35 +260,27 @@ For iOS, use `npm run mobile:ios:init` once and `npm run mobile:ios` for later s
251
260
  ```text
252
261
  pixel-daylight-switch/
253
262
  ├── .github/workflows/ # CI, native mobile builds, npm publishing
254
- ├── assets/
255
- │ └── scene-gallery.png # Five-scene overview image
256
- ├── builds/
257
- │ └── README.md # Mobile artifact notes; binaries are workflow outputs
258
- ├── mobile/
259
- │ ├── index.html # Capacitor app entry
260
- │ └── mobile.css # Mobile-only layout
261
- ├── scripts/
262
- │ └── prepare-mobile.cjs # Prepare the shared web runtime for Capacitor
263
- ├── tests/
264
- │ ├── browser/ # Playwright interaction and mobile regressions
265
- │ ├── fixture.html
266
- │ ├── mobile-smoke.cjs
267
- │ ├── package-smoke.cjs
268
- │ └── server.cjs
269
- ├── app.js # Component runtime, scenes, input and rendering
270
- ├── style.css # Component styles
271
- ├── embed.js # One-script website embed loader
272
- ├── index.html # Live demo page
273
- ├── preview.html # Component preview
274
- ├── playground.html # Interactive parameter lab
275
- ├── index.mjs / index.cjs # ESM and CommonJS package entry points
276
- ├── package.json # Package metadata and scripts
277
- ├── capacitor.config.json # Capacitor configuration
278
- ├── playwright.config.cjs # Browser test configuration
279
- ├── CHANGELOG.md
280
- ├── llms.txt
263
+ ├── assets/ # Gallery artwork
264
+ ├── builds/ # Mobile build notes
265
+ ├── docs/ # Integration guides, scene guide, changelog
266
+ ├── examples/scenes/ # Community scene manifests
267
+ ├── mobile/ # Capacitor app shell
268
+ ├── schemas/ # Manifest v1 and v2 JSON Schemas
269
+ ├── scripts/ # Mobile preparation/version scripts
270
+ ├── tests/ # Node and Chromium regression tests
271
+ ├── app.js / style.css # Runtime and component styles
272
+ ├── scene-plugin.js # Community scene registry and validator
273
+ ├── embed.js # One-script website embed loader
274
+ ├── index.html # Live demo
275
+ ├── demos/ # Public parameter lab and preview pages
276
+ ├── config/ # Tooling configuration
277
+ ├── index.mjs / index.cjs # ESM and CommonJS package entry points
278
+ ├── package.json # Package metadata and scripts
279
+ ├── capacitor.config.json # Capacitor configuration
280
+ ├── playwright.config.cjs # Browser test configuration
281
+ ├── llms.txt # AI-readable project guide
281
282
  ├── LICENSE
282
- └── README*.md # English, Simplified Chinese and Japanese docs
283
+ └── README*.md # English, Simplified Chinese and Japanese
283
284
  ```
284
285
 
285
286
  ## License
package/README.zh-CN.md CHANGED
@@ -12,22 +12,22 @@
12
12
 
13
13
  Pixel Daylight Switch 是一个可拖动的昼夜切换 UI 组件。拖动玻璃开关,可以让内部的像素世界从白天经过黄昏切换到夜晚。项目使用原生 JavaScript、WebGL2 和 Canvas 2D 回退渲染,不依赖第三方运行时库。
14
14
 
15
- **[直接体验在线演示](https://gsvegetable.github.io/pixel-daylight-switch/)** · **[打开参数实验室](https://gsvegetable.github.io/pixel-daylight-switch/playground.html)** · **[npm 安装](https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable)**
15
+ **[直接体验在线演示](https://gsvegetable.github.io/pixel-daylight-switch/)** · **[打开参数实验室](https://gsvegetable.github.io/pixel-daylight-switch/demos/playground.html)** · **[npm 安装](https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable)**
16
16
 
17
17
  ## 快速入口
18
18
 
19
19
  | 你想做什么 | 直接打开 |
20
20
  | --- | --- |
21
21
  | 体验昼夜开关 | [在线演示](https://gsvegetable.github.io/pixel-daylight-switch/) |
22
- | 调整场景与视觉参数 | [交互式参数实验室](https://gsvegetable.github.io/pixel-daylight-switch/playground.html) |
23
- | 查看组件预览 | [Preview 页面](https://gsvegetable.github.io/pixel-daylight-switch/preview.html) |
22
+ | 调整场景与视觉参数 | [交互式参数实验室](https://gsvegetable.github.io/pixel-daylight-switch/demos/playground.html) |
23
+ | 查看组件预览 | [Preview 页面](https://gsvegetable.github.io/pixel-daylight-switch/demos/preview.html) |
24
24
  | 用一个脚本嵌入网站 | [嵌入加载器源码](embed.js) |
25
25
  | 在 JavaScript 项目中使用 | [npm 包](https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable) |
26
- | React / Vue 接入示例 | [框架集成指南](INTEGRATIONS.md) |
26
+ | React / Vue 接入示例 | [框架集成指南](docs/INTEGRATIONS.md) |\n| 独立玻璃折射引擎 | [引擎文档](docs/GLASS_ENGINE.md) · [在线演示](https://gsvegetable.github.io/pixel-daylight-switch/demos/glass-engine.html) |
27
27
  | 阅读核心实现 | [运行时 app.js](app.js) · [样式 style.css](style.css) |
28
28
  | 获取移动端构建 | [构建说明与限制](builds/README.md) · [GitHub Actions 构建任务](https://github.com/GSvegetable/pixel-daylight-switch/actions/workflows/mobile-build.yml) |
29
29
  | 查看自动化测试 | [CI 工作流](https://github.com/GSvegetable/pixel-daylight-switch/actions/workflows/ci.yml) |
30
- | 查看版本变化 | [更新日志](CHANGELOG.md) |
30
+ | 查看版本变化 | [更新日志](docs/CHANGELOG.md) |
31
31
  | 反馈问题 | [GitHub Issues](https://github.com/GSvegetable/pixel-daylight-switch/issues) |
32
32
 
33
33
  ## 功能特点
@@ -96,40 +96,61 @@ const PixelDaylightSwitch = require('pixel-daylight-switch-gsvegetable');
96
96
 
97
97
  npm 包提供 ESM 和 CommonJS 入口,可在 SSR 环境中安全导入;只有在浏览器中且目标元素已经存在时,才能调用 `mount()`。npm 发布文件清单以 [package.json](package.json) 为准。
98
98
 
99
- ## 社区场景插件(v3.0.0)
100
99
 
101
- 3.0.0 开始支持通过 JSON Manifest 注册自定义像素世界。普通创作者可以保留像素矩阵、只修改昼夜色板;进阶创作者可以自己设计像素矩阵和时间色板。插件只提供声明式数据,不会在渲染器中执行第三方 JavaScript。
100
+ ## 独立玻璃折射引擎(v3.1.1)
102
101
 
103
- **浏览器使用:**
102
+ 这次升级把玻璃镜片从昼夜开关中独立出来。现在可以单独创建一层玻璃,不再受原开关的固定宽高比和尺寸限制;可分别设置宽度、高度、位置、圆角、折射、色散、边缘厚度和高光。支持 Canvas、图片、视频、ImageBitmap 或返回这些对象的函数作为背景源。查看[完整接入文档](docs/GLASS_ENGINE.md)和[在线调参演示](https://gsvegetable.github.io/pixel-daylight-switch/demos/glass-engine.html)。
104
103
 
105
- ```html
106
- <script src="https://gsvegetable.github.io/pixel-daylight-switch/scene-plugin.js"></script>
107
- <script src="https://gsvegetable.github.io/pixel-daylight-switch/app.js"></script>
108
- <div data-ld-part="toggle-daylight" data-scene="sakura-evening"></div>
109
- <script>
110
- PixelDaylightSwitch.registerScene(sceneManifest);
111
- </script>
104
+ ```js
105
+ import { createGlass } from 'pixel-daylight-switch-gsvegetable/glass-engine';
106
+ const glass = createGlass(container, { source: backgroundCanvas, width: '72%', height: 180, radius: 28 });
107
+ glass.setOptions({ refraction: 1.3, dispersion: 0.08 });
108
+ glass.destroy();
112
109
  ```
113
110
 
114
- 请在组件挂载前注册 Manifest。使用 npm 时:
111
+ 注意:浏览器不允许 WebGL 直接读取任意网页 DOM 像素。若要折射实时网页背景,宿主应用必须提供可采样的 Canvas / 渲染目标;跨域媒体还必须满足 CORS 要求。
112
+
113
+ ## 社区场景
114
+
115
+ 社区场景使用声明式 JSON Manifest,在组件挂载前注册。当前采用 **Manifest v2**,支持可复用像素素材、顺序图层和受限动画轨道,同时继续兼容已有的 Manifest v1 场景。
116
+
117
+ ### 注册场景
118
+
119
+ 在 npm 项目中:
115
120
 
116
121
  ```js
117
122
  import PixelDaylightSwitch, { registerScene } from 'pixel-daylight-switch-gsvegetable';
118
- import myScene from './my-scene.json';
123
+ import scene from './floating-island.v2.json';
119
124
 
120
- const unregister = registerScene(myScene);
125
+ const unregister = registerScene(scene);
121
126
  const root = document.querySelector('[data-ld-part="toggle-daylight"]');
122
- root.dataset.scene = myScene.id;
127
+ root.dataset.scene = scene.id;
123
128
  PixelDaylightSwitch.mount(root);
124
129
  ```
125
130
 
126
- Manifest v1 包含场景 ID、作者与许可证、像素画布、字符像素矩阵、多个昼夜色板和 24 小时时间节点。画布最大为 192 × 112;每个像素符号都必须在每套色板中定义颜色。渲染器会在相邻时间节点之间插值颜色。
131
+ 直接在浏览器中使用时,先加载 `scene-plugin.js`,再加载 `app.js`,并在挂载组件前注册 Manifest:
132
+
133
+ ```html
134
+ <script src="https://gsvegetable.github.io/pixel-daylight-switch/scene-plugin.js"></script>
135
+ <script src="https://gsvegetable.github.io/pixel-daylight-switch/app.js"></script>
136
+ <div data-ld-part="toggle-daylight" data-scene="my-community-scene"></div>
137
+ <script>
138
+ PixelDaylightSwitchScenePlugins.registerScene(sceneManifest);
139
+ </script>
140
+ ```
141
+
142
+ ### Manifest v2 能力
143
+
144
+ - 最多 24 个可复用字符像素素材、最多 16 个顺序合成图层。
145
+ - 图层使用整数场景坐标,后声明的图层显示在前面图层之上。
146
+ - 叠加素材中的点号 `.` 表示透明像素。
147
+ - 支持 `drift`(往返漂移)和 `bob`(正弦浮动)两种受限动画轨道,可沿水平或垂直方向运行。
148
+ - 继续兼容已有 v1 Manifest,原有五个内置场景保持不变。
149
+
150
+ 查看 [Manifest v2 Schema](schemas/scene-manifest.v2.schema.json)、[Floating Island 分层场景示例](examples/scenes/floating-island.v2.json) 和[场景创作指南](docs/SCENE_PLUGINS.md)。为保证兼容性,原始 [v1 Schema](schemas/scene-manifest.v1.schema.json) 仍然保留。
127
151
 
128
- - [完整插件开发指南](SCENE_PLUGINS.md)
129
- - [Manifest v1 JSON Schema](schemas/scene-manifest.v1.schema.json)
130
- - [樱花黄昏示例](examples/scenes/sakura-evening.json)
152
+ 当前素材采用字符像素矩阵,不支持 PNG/JPEG 上传,也不会执行社区提交的任意脚本。
131
153
 
132
- v1 暂不支持外部图片/Sprite、分层 Tile Map 和自定义动画轨道;这些能力会通过后续独立版本的 Schema 扩展,不会开放任意代码执行。五个内置场景仍保留原有绘制与动态逻辑。
133
154
 
134
155
  ## 配置参数
135
156
 
@@ -238,35 +259,27 @@ iOS 首次创建使用 `npm run mobile:ios:init`,后续使用 `npm run mobile:
238
259
  ```text
239
260
  pixel-daylight-switch/
240
261
  ├── .github/workflows/ # CI、原生移动端构建、npm 发布
241
- ├── assets/
242
- │ └── scene-gallery.png # 五种场景总览图
243
- ├── builds/
244
- │ └── README.md # 移动端产物说明;二进制由工作流生成
245
- ├── mobile/
246
- │ ├── index.html # Capacitor 应用入口
247
- │ └── mobile.css # 移动端专用布局
248
- ├── scripts/
249
- │ └── prepare-mobile.cjs # 为 Capacitor 准备共用 Web 运行时
250
- ├── tests/
251
- │ ├── browser/ # Playwright 交互与移动端回归
252
- │ ├── fixture.html
253
- │ ├── mobile-smoke.cjs
254
- │ ├── package-smoke.cjs
255
- │ └── server.cjs
256
- ├── app.js # 组件运行时、场景、交互与渲染
257
- ├── style.css # 组件样式
258
- ├── embed.js # 单脚本网站嵌入加载器
259
- ├── index.html # 在线演示主页
260
- ├── preview.html # 组件预览
261
- ├── playground.html # 交互式参数实验室
262
- ├── index.mjs / index.cjs # ESM 与 CommonJS 包入口
263
- ├── package.json # npm 元数据与脚本
264
- ├── capacitor.config.json # Capacitor 配置
265
- ├── playwright.config.cjs # 浏览器测试配置
266
- ├── CHANGELOG.md
267
- ├── llms.txt
262
+ ├── assets/ # 场景展示图片
263
+ ├── builds/ # 移动端构建说明
264
+ ├── docs/ # 集成指南、场景创作指南、更新日志
265
+ ├── examples/scenes/ # 社区场景 Manifest 示例
266
+ ├── mobile/ # Capacitor 应用外壳
267
+ ├── schemas/ # Manifest v1 / v2 JSON Schema
268
+ ├── scripts/ # 移动端准备与版本脚本
269
+ ├── tests/ # Node 与 Chromium 回归测试
270
+ ├── app.js / style.css # 运行时与组件样式
271
+ ├── scene-plugin.js # 场景注册与校验 API
272
+ ├── embed.js # 网站单脚本嵌入加载器
273
+ ├── index.html # 在线演示
274
+ ├── demos/ # 参数实验室与组件预览页面
275
+ ├── config/ # 工具配置
276
+ ├── index.mjs / index.cjs # ESM / CommonJS 入口
277
+ ├── package.json # 包元数据与脚本
278
+ ├── capacitor.config.json # Capacitor 配置
279
+ ├── playwright.config.cjs # 浏览器测试配置
280
+ ├── llms.txt # 面向 AI 的项目说明
268
281
  ├── LICENSE
269
- └── README*.md # 英文、简体中文、日文文档
282
+ └── README*.md # 英文、简体中文和日文 README
270
283
  ```
271
284
 
272
285
  ## 许可证
package/app.js CHANGED
@@ -931,6 +931,7 @@ function mount(root) {
931
931
  Object.keys(palette).forEach((symbol) => { colors[symbol.charCodeAt(0)] = parsePluginColor(palette[symbol]); });
932
932
  return [name, colors];
933
933
  })) : null;
934
+ let pluginAnimClock = 0;
934
935
  const renderPluginWorld = (hour) => {
935
936
  const stops = pluginScene.timeCycle;
936
937
  let left = stops[0]; let right = stops[stops.length - 1]; let sampleHour = hour;
@@ -945,14 +946,43 @@ function mount(root) {
945
946
  const aPalette = pluginPaletteRgb[left.palette];
946
947
  const bPalette = pluginPaletteRgb[right.palette];
947
948
  const width = pluginScene.canvas.width; const height = pluginScene.canvas.height;
949
+ let scenePixels = pluginScene.pixels;
950
+ if (pluginScene.schemaVersion === 2 && Array.isArray(pluginScene.layers)) {
951
+ const composed = pluginScene.pixels.map(row => row.split(''));
952
+ for (const layer of pluginScene.layers) {
953
+ const asset = pluginScene.assets[layer.asset];
954
+ const motion = layer.animation;
955
+ let dx = 0, dy = 0;
956
+ if (motion) {
957
+ const phase = pluginAnimClock * motion.speed + (motion.phase || 0);
958
+ const wave = motion.type === 'drift' ? (2 / Math.PI) * Math.asin(Math.sin(phase)) : Math.sin(phase);
959
+ const offset = wave * motion.distance;
960
+ if (motion.axis === 'x') dx = Math.round(offset);
961
+ else dy = Math.round(offset);
962
+ }
963
+ const originX = layer.x + dx, originY = layer.y + dy;
964
+ for (let sy = 0; sy < asset.height; sy++) {
965
+ const targetY = originY + sy;
966
+ if (targetY < 0 || targetY >= height) continue;
967
+ const row = asset.pixels[sy];
968
+ for (let sx = 0; sx < asset.width; sx++) {
969
+ const targetX = originX + sx;
970
+ if (targetX < 0 || targetX >= width) continue;
971
+ const symbol = row[sx];
972
+ if (symbol !== '.') composed[targetY][targetX] = symbol;
973
+ }
974
+ }
975
+ }
976
+ scenePixels = composed.map(row => row.join(''));
977
+ }
948
978
  const scale = Math.min(bw / width, bh / height);
949
979
  const drawW = Math.max(1, Math.round(width * scale)); const drawH = Math.max(1, Math.round(height * scale));
950
980
  const offsetX = Math.floor((bw - drawW) / 2); const offsetY = Math.floor((bh - drawH) / 2);
951
- const background = pluginScene.pixels[0].charCodeAt(0);
981
+ const background = scenePixels[0].charCodeAt(0);
952
982
  for (let py = 0; py < bh; py++) {
953
983
  const insideY = py >= offsetY && py < offsetY + drawH;
954
984
  const sy = insideY ? clamp(Math.floor(((py - offsetY) / drawH) * height), 0, height - 1) : 0;
955
- const row = pluginScene.pixels[sy];
985
+ const row = scenePixels[sy];
956
986
  for (let px = 0; px < bw; px++) {
957
987
  const inside = insideY && px >= offsetX && px < offsetX + drawW;
958
988
  const sx = inside ? clamp(Math.floor(((px - offsetX) / drawW) * width), 0, width - 1) : 0;
@@ -1083,7 +1113,7 @@ function mount(root) {
1083
1113
  const pickKeys = (h) => { for (let i = 0; i < KEYS.length - 1; i++) { if (h >= KEYS[i][0] && h < KEYS[i + 1][0]) { keyA = i; keyB = i + 1; keyP = clamp(((h - KEYS[i][0]) / (KEYS[i + 1][0] - KEYS[i][0]) - 0.32) / 0.36); return; } } keyA = 0; keyB = 1; keyP = 0; };
1084
1114
  const renderWorld = (clock, dt, hurry) => {
1085
1115
  const h = hourOf(clock);
1086
- if (pluginScene) { renderPluginWorld(h); return; }
1116
+ if (pluginScene) { if (!calm) pluginAnimClock += dt; renderPluginWorld(h); return; }
1087
1117
  if (!calm) anim += dt; matBuf.set(baseBuf); varBuf.fill(0); emitList.length = 0;
1088
1118
  for (const c of sources) { c.w = 0; c.x = 0; c.y = 0; }
1089
1119
  lampFlick = calm ? 1 : 0.95 + 0.05 * Math.sin(anim * 13) * Math.sin(anim * 5.3);
package/browser.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import './glass-engine.js';
1
2
  import './scene-plugin.js';
2
3
 
3
4
  // Browser ESM consumers need to register their scene before mounting.
@@ -10,6 +11,7 @@ try {
10
11
  }
11
12
 
12
13
  const api = globalThis.PixelDaylightSwitch;
14
+ api.createGlass = globalThis.PixelGlassEngine.createGlass;
13
15
  const scenes = globalThis.PixelDaylightSwitchScenePlugins;
14
16
  export const mount = api.mount;
15
17
  export const unmount = api.unmount;
@@ -18,3 +20,5 @@ export const validateScene = scenes.validateScene;
18
20
  export const getScene = scenes.getScene;
19
21
  export const listScenes = scenes.listScenes;
20
22
  export default api;
23
+
24
+ export const createGlass = globalThis.PixelGlassEngine.createGlass;