pixel-daylight-switch-gsvegetable 2.0.0 → 2.2.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.
package/README.md CHANGED
@@ -1,505 +1,158 @@
1
1
  # Pixel Daylight Switch
2
2
 
3
- <p align="center">
4
- <strong>A tiny pixel world inside a draggable glass day/night switch.</strong>
5
- </p>
3
+ [![Quality checks](https://github.com/GSvegetable/pixel-daylight-switch/actions/workflows/ci.yml/badge.svg)](https://github.com/GSvegetable/pixel-daylight-switch/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/pixel-daylight-switch-gsvegetable)](https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable)
5
+ [![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](LICENSE)
6
6
 
7
- <p align="center">
8
- <a href="https://gsvegetable.github.io/pixel-daylight-switch/">Live Demo</a> ·
9
- <a href="https://gsvegetable.github.io/pixel-daylight-switch/playground.html">Interactive Playground</a> ·
10
- <a href="https://github.com/GSvegetable/pixel-daylight-switch/releases">Releases</a> ·
11
- <a href="https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable">NPM</a>
12
- </p>
7
+ **Languages:** [English](README.md) · [简体中文](README.zh-CN.md) · [日本語](README.ja.md)
13
8
 
14
- <p align="center">
15
- <img src="https://github.com/user-attachments/assets/f7f248a5-c39c-432c-92f6-73237abdfe6b" width="49%" alt="Pixel Daylight Switch in daytime" />
16
- <img src="https://github.com/user-attachments/assets/b2e24e65-a378-4ac6-be7b-efd3070e4da8" width="49%" alt="Pixel Daylight Switch at night" />
17
- </p>
9
+ ![Five animated pixel-world scenes](assets/scene-gallery.png)
18
10
 
19
- <p align="center">
20
- <em>Drag the glass control. Watch a small world move from day to dusk to night.</em>
21
- </p>
11
+ *A tiny animated world, held inside a refractive glass switch.*
22
12
 
23
- **中文文档在前。** [English documentation](#english)
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.
24
14
 
25
- ---
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)**
26
16
 
27
- ## 目录
17
+ ## Start here
28
18
 
29
- - [项目简介](#项目简介)
30
- - [快速开始](#快速开始)
31
- - [Android 与 iOS 应用](#android-与-ios-应用)
32
- - [NPM 安装](#npm-安装)
33
- - [框架集成](#框架集成)
34
- - [参数实验室 Playground](#参数实验室-playground)
35
- - [配置参数](#配置参数)
36
- - [交互与事件](#交互与事件)
37
- - [自定义主题](#自定义主题)
38
- - [动态挂载与卸载](#动态挂载与卸载)
39
- - [渲染与兼容性](#渲染与兼容性)
40
- - [项目目录结构](#项目目录结构)
41
- - [本地开发与测试](#本地开发与测试)
42
- - [版本记录](#版本记录)
43
- - [许可证](#许可证)
44
-
45
- ## 项目简介
46
-
47
- **Pixel Daylight Switch** 是一个可拖动的昼夜切换组件。它把一个持续变化的像素世界放进玻璃开关中:拖动滑块,场景会经过白天、黄昏并进入夜晚;点击或使用键盘也可以切换昼夜。
48
-
49
- ### 特性
50
-
51
- - **像素世界**:场景中有持续活动的像素角色与环境细节。
52
- - **玻璃质感**:使用 WebGL2 绘制折射玻璃效果。
53
- - **五种场景**:乡村小屋、日本街道、现代公寓、海边灯塔、山间木屋。
54
- - **昼夜过渡**:不是简单的二态换色,而是连续的昼夜变化。
55
- - **可配置**:通过 HTML `data-*` 属性配置运动、玻璃、天气和场景。
56
- - **多实例**:同一页面可以放置多个独立开关。
57
- - **轻量接入**:原生脚本无需构建工具或运行时依赖。
58
- - **无障碍支持**:提供 switch 语义、键盘操作,并尊重减少动态效果偏好。
59
-
60
- ### 五种场景一览
61
-
62
- <p align="center">
63
- <img src="./assets/scene-gallery.png" alt="Pixel Daylight Switch — five pixel-art scenes: countryside, Japanese street, apartment, seaside, and mountain cabin" width="100%" />
64
- </p>
65
-
66
- ## 快速开始
67
-
68
- 不使用 NPM,也不需要安装依赖。把容器和脚本放进 HTML:
19
+ | Goal | Direct link |
20
+ | --- | --- |
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) |
24
+ | Add it to a website with one script | [Embed loader source](embed.js) |
25
+ | Use it in a JavaScript project | [npm package](https://www.npmjs.com/package/pixel-daylight-switch-gsvegetable) |
26
+ | Read implementation details | [Runtime](app.js) · [Styles](style.css) |
27
+ | 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) |
28
+ | Check tests | [CI workflow](https://github.com/GSvegetable/pixel-daylight-switch/actions/workflows/ci.yml) |
29
+ | Review changes | [Changelog](CHANGELOG.md) |
30
+ | Report a bug | [Issue tracker](https://github.com/GSvegetable/pixel-daylight-switch/issues) |
31
+
32
+ ## Features
33
+
34
+ - Five scenes: countryside, Japanese street, modern apartment, seaside lighthouse and mountain cabin.
35
+ - Refractive glass rendering with WebGL2 and a Canvas 2D fallback.
36
+ - Continuous drag interaction, tap/click switching and keyboard operation.
37
+ - Configurable motion, inertia, bounce, refraction, glass details, world speed, weather and scene.
38
+ - Multiple independent instances on one page.
39
+ - Switch semantics, live status announcements and `prefers-reduced-motion` support.
40
+ - `mount()` and `unmount()` lifecycle methods for dynamic applications.
41
+ - Rendering pauses when the document is hidden or the component is outside the viewport.
42
+
43
+ ## Quick start — browser, no build step
44
+
45
+ Place a mount point on your page and load the runtime:
69
46
 
70
47
  ```html
71
- <div data-ld-part="toggle-daylight"></div>
72
-
73
- <script
74
- defer
75
- src="https://gsvegetable.github.io/pixel-daylight-switch/app.js"
76
- ></script>
77
- ```
78
-
79
- 脚本会自动为每个匹配的容器创建 Canvas、交互按钮和状态播报元素。容器宽度会自适应父级,组件保持约 **1.6439:1** 的宽高比。
80
-
81
- 如果需要自行托管,可以下载仓库中的 `app.js`,将脚本地址替换为自己的静态资源地址。
82
-
83
- ## Android 与 iOS 应用
84
-
85
- 从 **v2.0.0** 开始,项目提供 Capacitor 原生应用构建路径。Web 版本继续独立运行;Android 与 iOS 使用同一份核心渲染器,不复制或重写场景、WebGL shader 和交互物理逻辑。
48
+ <div data-ld-part="toggle-daylight" style="width: min(100%, 800px);">
49
+ </div>
86
50
 
87
- ### 技术结构
88
-
89
- - **共享核心**:根目录的 `app.js` 与 `style.css` 是唯一渲染实现。
90
- - **原生容器**:Capacitor 8 将同一套 Web 资源装入 Android WebView 和 iOS WKWebView。
91
- - **移动外壳**:`mobile/` 只负责全屏布局、安全区域和移动端 viewport,不包含第二套场景引擎。
92
- - **构建产物**:`mobile/www/` 由脚本生成并被 Git 忽略,避免提交重复的渲染资源。
93
-
94
- ### 环境要求
95
-
96
- - Node.js 22+
97
- - Android:Android Studio、Android SDK、JDK 21
98
- - iOS:macOS、Xcode 26+;需要在 Mac 上构建和签名发布包
99
-
100
- Capacitor 8 的平台最低版本为 Android 7.0(API 24)和 iOS 15。具体要求以 [Capacitor 环境文档](https://capacitorjs.com/docs/getting-started/environment-setup) 为准。
101
-
102
- ### 初始化原生工程
103
-
104
- 先安装依赖并生成 Web 资源:
105
-
106
- ```bash
107
- npm install
108
- npm run mobile:prepare
51
+ <script defer src="https://gsvegetable.github.io/pixel-daylight-switch/app.js"></script>
109
52
  ```
110
53
 
111
- 首次创建 Android / iOS 工程(每个平台只需初始化一次):
54
+ The browser runtime finds the mount point and creates the component UI. The host element needs a usable width.
112
55
 
113
- ```bash
114
- npm run mobile:android:init
115
- npm run mobile:ios:init
116
- ```
56
+ ### Embed with one script
117
57
 
118
- 同步后续 Web 资源变更:
119
-
120
- ```bash
121
- npm run mobile:android
122
- npm run mobile:ios
123
- ```
58
+ For a self-inserting embed, add this script where you want the component to appear:
124
59
 
125
- 在 Android Studio 或 Xcode 中打开工程:
126
-
127
- ```bash
128
- npm run mobile:open:android
129
- npm run mobile:open:ios
60
+ ```html
61
+ <script src="https://gsvegetable.github.io/pixel-daylight-switch/embed.js"></script>
130
62
  ```
131
63
 
132
- 原生工程目录 `android/` 与 `ios/` 是本地生成目录,不纳入 Git。这样可以避免提交大量 IDE 生成文件;CI 会从锁定的 Capacitor 主版本配置重新生成工程并验证构建。
133
-
134
- ### 自动构建
64
+ The loader inserts its own container, stylesheet and runtime. It currently creates one default instance with a fixed maximum display size; use the mount-point approach above when you need precise layout or custom attributes.
135
65
 
136
- `.github/workflows/mobile-build.yml` 提供 Android Debug APK 与 iOS Simulator 构建。可在 GitHub Actions 手动运行;推送 `v2.*` 标签也会触发构建。Android APK 会作为 Actions artifact 保存。iOS Simulator 构建用于验证编译,不等同于已签名、可提交 App Store 的 IPA。
137
-
138
- **发布边界:** App Store / Google Play 上架仍需要开发者账号、应用图标与商店素材、签名证书及隐私合规信息。此仓库的 CI 不会伪造签名或自动代表开发者提交商店。
139
-
140
- ## NPM 安装
66
+ ## Install from npm
141
67
 
142
68
  ```bash
143
69
  npm install pixel-daylight-switch-gsvegetable
144
70
  ```
145
71
 
146
- ### 原生 JavaScript / ESM
72
+ **ES modules**
147
73
 
148
74
  ```js
149
75
  import PixelDaylightSwitch from 'pixel-daylight-switch-gsvegetable';
150
76
 
151
- const root = document.querySelector(
152
- '[data-ld-part="toggle-daylight"]'
153
- );
154
-
155
- // 页面加载后动态创建的元素需要手动挂载
156
- const destroy = PixelDaylightSwitch.mount(root);
77
+ const root = document.querySelector('[data-ld-part="toggle-daylight"]');
78
+ PixelDaylightSwitch.mount(root);
157
79
 
158
- // 不再使用时,在移除元素前卸载
80
+ // Call before removing the root from the DOM.
159
81
  PixelDaylightSwitch.unmount(root);
160
82
  ```
161
83
 
162
- 也可以使用命名导入:
163
-
164
- ```js
165
- import { mount, unmount } from 'pixel-daylight-switch-gsvegetable';
166
-
167
- mount(root);
168
- unmount(root);
169
- ```
170
-
171
- ### CommonJS
84
+ **CommonJS**
172
85
 
173
86
  ```js
174
87
  const PixelDaylightSwitch = require('pixel-daylight-switch-gsvegetable');
175
-
176
- PixelDaylightSwitch.mount(root);
177
- ```
178
-
179
- 包同时提供 ESM 和 CommonJS 入口。模块可以在 SSR 环境中导入;DOM 挂载只会在浏览器中执行。对于服务端渲染项目,请在客户端生命周期中调用 `mount()`。
180
-
181
- ## 框架集成
182
-
183
- ### React
184
-
185
- ```jsx
186
- import { useEffect, useRef } from 'react';
187
- import PixelDaylightSwitch from 'pixel-daylight-switch-gsvegetable';
188
-
189
- export function DaylightSwitch() {
190
- const rootRef = useRef(null);
191
-
192
- useEffect(() => {
193
- const root = rootRef.current;
194
- if (!root) return;
195
-
196
- PixelDaylightSwitch.mount(root);
197
- return () => PixelDaylightSwitch.unmount(root);
198
- }, []);
199
-
200
- return <div ref={rootRef} data-ld-part="toggle-daylight" />;
201
- }
202
88
  ```
203
89
 
204
- ### Vue 3
90
+ 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).
205
91
 
206
- ```vue
207
- <script setup>
208
- import { ref, onMounted, onBeforeUnmount } from 'vue';
209
- import PixelDaylightSwitch from 'pixel-daylight-switch-gsvegetable';
210
-
211
- const root = ref(null);
92
+ ## Configuration
212
93
 
213
- onMounted(() => {
214
- PixelDaylightSwitch.mount(root.value);
215
- });
94
+ Set optional attributes on the mount element. Defaults apply when an attribute is omitted or invalid.
216
95
 
217
- onBeforeUnmount(() => {
218
- PixelDaylightSwitch.unmount(root.value);
219
- });
220
- </script>
221
-
222
- <template>
223
- <div ref="root" data-ld-part="toggle-daylight"></div>
224
- </template>
225
- ```
226
-
227
- ### Svelte
228
-
229
- ```svelte
230
- <script>
231
- import { onMount } from 'svelte';
232
- import PixelDaylightSwitch from 'pixel-daylight-switch-gsvegetable';
233
-
234
- let root;
235
-
236
- onMount(() => {
237
- PixelDaylightSwitch.mount(root);
238
- return () => PixelDaylightSwitch.unmount(root);
239
- });
240
- </script>
241
-
242
- <div bind:this={root} data-ld-part="toggle-daylight"></div>
243
- ```
244
-
245
- > **生命周期原则:** 对于框架或路由动态创建的容器,在元素进入 DOM 后调用 `mount(element)`;在元素被移除前调用 `unmount(element)`。不要在同一个容器上重复创建自己的实例。
246
-
247
- ## 参数实验室 Playground
248
-
249
- 打开 [Pixel Daylight Playground](https://gsvegetable.github.io/pixel-daylight-switch/playground.html),可以在浏览器中试验组件配置。
250
-
251
- - 拖动预览区中的开关,直接体验交互。
252
- - 按运动反馈、玻璃光学、世界节奏和场景环境调整参数。
253
- - 在五种像素场景之间切换。
254
- - 选择天气、初始昼夜状态与世界时间倍率。
255
- - 先调整参数,再点击「应用参数」更新预览。
256
- - 复制生成的 HTML 配置,放入自己的项目。
257
-
258
- Playground 是独立页面。它用于探索配置,不是组件运行时的依赖。
259
-
260
- ## 配置参数
261
-
262
- 所有配置都写在组件根元素的 HTML `data-*` 属性上。每个实例可以使用不同参数;未填写的参数使用默认值。
263
-
264
- ### 完整参数表
265
-
266
- | HTML 属性 | 默认值 | 说明 |
96
+ | Attribute | Default | Description |
267
97
  | --- | ---: | --- |
268
- | `data-speed` | `9.5` | 开关运动的基础响应速度,必须为正数。 |
269
- | `data-refraction` | `1.24` | 玻璃镜片折射强度,必须为正数。 |
270
- | `data-bezel` | `0.046` | 玻璃边缘厚度系数,必须为正数。 |
271
- | `data-start-night` | `false` | 精确设置为 `true` 时,从夜晚状态开始。 |
272
- | `data-friction` | `0.6` | 松手后的惯性阻尼,范围 `0.1–0.9`;值越低,滑动越持久。 |
273
- | `data-bounce` | `3.2` | 边缘回弹力度,范围 `0–12`。 |
274
- | `data-dispersion` | `0.05` | 玻璃边缘 RGB 色散强度,范围 `0–0.25`;`0` 关闭色散。 |
275
- | `data-shine` | `0.44` | 白天玻璃表面高光强度,范围 `0–1.5`。 |
276
- | `data-time-speed` | `1` | 像素世界时间倍率,范围 `0–8`;`0` 冻结世界动态,`2` 为两倍速度。 |
277
- | `data-weather` | `default` | 天气预设:`default` 或 `snow`。 |
278
- | `data-scene` | `countryside` | 场景:`countryside`、`japanese-street`、`apartment`、`seaside`、`mountain-cabin`。 |
279
-
280
- ### 场景选项
281
-
282
- | 值 | 场景 |
283
- | --- | --- |
284
- | `countryside` | 乡村小屋(默认) |
285
- | `japanese-street` | 日本街道 |
286
- | `apartment` | 现代公寓 |
287
- | `seaside` | 海边灯塔 |
288
- | `mountain-cabin` | 山间木屋 |
98
+ | `data-speed` | `9.5` | Base response speed; greater than zero. |
99
+ | `data-refraction` | `1.24` | Glass lens refraction strength; greater than zero. |
100
+ | `data-bezel` | `0.046` | Relative glass-edge thickness; greater than zero. |
101
+ | `data-start-night` | `false` | Exact string `"true"` starts in night mode. |
102
+ | `data-friction` | `0.6` | Release inertia damping; clamped to `0.1–0.9`. |
103
+ | `data-bounce` | `3.2` | Edge rebound strength; clamped to `0–12`. |
104
+ | `data-dispersion` | `0.05` | RGB dispersion at the glass edge; clamped to `0–0.25`. |
105
+ | `data-shine` | `0.44` | Daylight glass highlight; clamped to `0–1.5`. |
106
+ | `data-time-speed` | `1` | World simulation speed; clamped to `0–8`. Set to `0` to freeze world motion. |
107
+ | `data-weather` | `default` | `default` or `snow`. Unknown values fall back to `default`. |
108
+ | `data-scene` | `countryside` | `countryside`, `japanese-street`, `apartment`, `seaside` or `mountain-cabin`. |
289
109
 
290
- ### 完整配置示例
110
+ Example:
291
111
 
292
112
  ```html
293
113
  <div
294
114
  data-ld-part="toggle-daylight"
295
- data-label="昼夜环境切换"
296
- data-speed="12"
297
- data-refraction="1.5"
298
- data-bezel="0.05"
299
115
  data-start-night="true"
300
- data-friction="0.45"
301
- data-bounce="4.2"
302
- data-dispersion="0.12"
303
- data-shine="0.6"
304
- data-time-speed="1.5"
116
+ data-scene="seaside"
305
117
  data-weather="snow"
306
- data-scene="mountain-cabin"
307
- ></div>
308
-
309
- <script
310
- defer
311
- src="https://gsvegetable.github.io/pixel-daylight-switch/app.js"
312
- ></script>
118
+ data-time-speed="1.4"
119
+ data-refraction="1.3">
120
+ </div>
313
121
  ```
314
122
 
315
- ### 参数校验规则
316
-
317
- - 数值参数为空、不是有限数字或不符合正数要求时,会回退到默认值。
318
- - `friction`、`bounce`、`dispersion`、`shine` 和 `time-speed` 会限制在表格所示范围内。
319
- - 未知的 `weather` 值回退为 `default`。
320
- - 未知的 `scene` 值回退为 `countryside`。
321
- - `data-start-night` 只有值为字符串 `true` 时才启用。
322
-
323
- ## 交互与事件
324
-
325
- ### 用户交互
326
-
327
- - **拖动**:连续控制昼夜进度。
328
- - **点击**:在白天和夜晚之间切换。
329
- - **键盘**:聚焦开关后可使用方向键操作。
330
- - **无障碍**:交互按钮使用 `role="switch"` 与 `aria-checked`;状态变化通过 live region 播报。
331
- - **减少动态效果**:尊重系统的 `prefers-reduced-motion` 设置。
332
-
333
- ### 读取昼夜进度
334
-
335
- 组件会在根元素上更新 CSS 自定义属性 `--night`:
336
-
337
- - `0`:白天。
338
- - `1`:夜晚。
339
- - `0` 到 `1` 之间:昼夜过渡。
123
+ ## Events and state
340
124
 
341
- 可以用它联动页面其他部分:
125
+ The root element exposes transition progress through the CSS custom property `--night`: `0` is day, `1` is night, and values between them represent the transition.
342
126
 
343
- ```css
344
- body {
345
- background: color-mix(
346
- in srgb,
347
- #f5ecd9,
348
- #1b2146 calc(var(--night, 0) * 100%)
349
- );
350
- }
351
- ```
352
-
353
- ### 监听状态事件
354
-
355
- 组件在昼夜状态发生变化时,会在组件根元素上派发可冒泡的 `daylight:change` 事件。事件的 `detail.night` 是布尔值。
127
+ When the committed day/night state changes, the root dispatches a bubbling, composed `daylight:change` event. `event.detail.night` is a boolean. It is not emitted on every animation frame.
356
128
 
357
129
  ```js
358
- document.addEventListener('daylight:change', (event) => {
359
- document.documentElement.classList.toggle(
360
- 'dark',
361
- event.detail.night
362
- );
363
- });
364
- ```
365
-
366
- 如果页面有多个实例,建议在单个根元素上监听,以区分具体是哪一个开关触发了事件:
130
+ const root = document.querySelector('[data-ld-part="toggle-daylight"]');
367
131
 
368
- ```js
369
132
  root.addEventListener('daylight:change', (event) => {
370
- console.log('当前是否为夜晚:', event.detail.night);
133
+ console.log(event.detail.night ? 'Night' : 'Day');
371
134
  });
372
135
  ```
373
136
 
374
- ## 自定义主题
137
+ ## Rendering, accessibility and performance
375
138
 
376
- 在组件根元素上覆盖以下 CSS 自定义属性:
139
+ - WebGL2 renders the refractive lens; Canvas 2D is used if WebGL2 is unavailable or fails to initialize.
140
+ - The component scales its drawing surface to its container.
141
+ - Rendering pauses while the document is hidden or the component is outside the viewport.
142
+ - Keyboard and switch semantics are provided, with a live status announcement.
143
+ - The runtime respects the operating system's `prefers-reduced-motion` preference.
377
144
 
378
- ```css
379
- [data-ld-part="toggle-daylight"] {
380
- --ld-bg: #f5ecd9;
381
- --ld-surface: #8c9a62;
382
- --ld-fg: #1b2146;
383
- --ld-muted: #7f97b3;
384
- --ld-accent: #efa02c;
385
- --ld-radius: 9999px;
386
- --ld-font: system-ui, sans-serif;
387
- }
388
- ```
389
-
390
- | CSS 变量 | 默认值 | 用途 |
391
- | --- | --- | --- |
392
- | `--ld-bg` | `#f5ecd9` | 背景调色输入 |
393
- | `--ld-surface` | `#8c9a62` | 表面与场景调色输入 |
394
- | `--ld-fg` | `#1b2146` | 前景颜色 |
395
- | `--ld-muted` | `#7f97b3` | 次要颜色 |
396
- | `--ld-accent` | `#efa02c` | 强调色与焦点颜色 |
397
- | `--ld-radius` | `9999px` | 交互区域圆角 |
398
- | `--ld-font` | `system-ui, sans-serif` | 组件字体 |
399
- | `--ld-space` | `8px` | 焦点轮廓偏移相关尺寸 |
400
- | `--ld-shadow` | `0 8px 24px rgb(27 33 70 / .2)` | 主题预留变量 |
401
- | `--ld-motion` | `200ms` | 焦点轮廓过渡时长 |
145
+ Performance varies with browser, graphics hardware, device pixel ratio and the number of visible instances.
402
146
 
403
- ## 动态挂载与卸载
404
-
405
- 页面加载时,脚本会自动查找已有的 `[data-ld-part="toggle-daylight"]` 元素并挂载。对于之后动态创建的元素,调用公开 API:
406
-
407
- ```js
408
- const root = document.createElement('div');
409
- root.dataset.ldPart = 'toggle-daylight';
410
- document.querySelector('#app').appendChild(root);
411
-
412
- const destroy = PixelDaylightSwitch.mount(root);
413
-
414
- // 需要移除时:
415
- PixelDaylightSwitch.unmount(root);
416
- root.remove();
417
- ```
418
-
419
- `mount(element)` 对同一元素是幂等的:重复调用不会创建重复实例。成功挂载时会返回清理函数;如果挂载失败则返回 `null`。通常使用 `unmount(element)` 即可完成清理。
420
-
421
- 卸载会停止绘制并释放组件持有的事件监听器和观察器。它不会移除根元素本身。
422
-
423
- ## 渲染与兼容性
424
-
425
- - **WebGL2**:用于绘制玻璃折射效果。
426
- - **Canvas 2D**:WebGL2 不可用时作为渲染回退。
427
- - **响应式布局**:组件保持固定宽高比,并随容器宽度缩放。
428
- - **页面可见性**:页面隐藏或组件离开视口时,会暂停不必要的绘制。
429
- - **减少动态效果**:支持系统减少动态效果偏好。
430
- - **运行时依赖**:原生脚本不需要第三方运行时依赖。
431
-
432
- 组件需要现代浏览器提供 Canvas 2D;WebGL2 用于增强玻璃渲染体验。
433
-
434
- ## 项目目录结构
435
-
436
- 仓库采用轻量的原生 JavaScript 结构,无需前端打包工具。核心组件、演示页面、NPM 入口和自动化测试分别独立维护。
437
-
438
- ```text
439
- pixel-daylight-switch/
440
- ├── assets/
441
- │ └── scene-gallery.png # 五种像素场景总览图
442
- ├── mobile/
443
- │ ├── index.html # Capacitor 原生应用入口
444
- │ └── mobile.css # 移动端安全区域与全屏布局
445
- ├── scripts/
446
- │ └── prepare-mobile.cjs # 生成 Capacitor Web 资源
447
- ├── .github/
448
- │ └── workflows/
449
- │ ├── ci.yml # Node.js 与 Chromium 持续集成
450
- │ └── mobile-build.yml # Android / iOS 构建验证
451
- │ └── npm-publish.yml # 通过 v* 标签触发 NPM 发布
452
- ├── capacitor.config.json # Capacitor 原生应用配置
453
- ├── tests/
454
- │ ├── browser/
455
- │ │ └── component.spec.cjs # Playwright 浏览器回归测试
456
- │ ├── fixture.html # 浏览器测试页面
457
- │ ├── package-smoke.cjs # 包入口与 SSR 冒烟测试
458
- │ └── server.cjs # 本地测试静态服务器
459
- ├── app.js # 核心组件:渲染、交互与生命周期
460
- ├── embed.js # 独立嵌入入口
461
- ├── index.cjs # CommonJS 包入口
462
- ├── index.mjs # ESM 包入口
463
- ├── index.html # 主演示页面
464
- ├── preview.html # 预览页面
465
- ├── playground.html # 交互式参数实验室
466
- ├── style.css # 演示页面样式
467
- ├── package.json # NPM 元数据与脚本
468
- ├── playwright.config.cjs # Playwright 配置
469
- ├── CHANGELOG.md # 版本变更记录
470
- ├── LICENSE # Unlicense 许可证
471
- ├── llms.txt # 面向 AI 工具的项目说明
472
- └── README.md # 项目文档
473
- ```
474
-
475
- ### 关键目录说明
476
-
477
- - **`app.js`**:组件主体,包含昼夜状态、Canvas/WebGL 渲染、输入交互和实例生命周期管理。
478
- - **`playground.html`**:可视化调整参数、切换场景并生成 HTML 配置。
479
- - **`embed.js`**:面向嵌入式使用场景的入口脚本。
480
- - **`tests/`**:包加载、SSR、挂载卸载、多实例、指针/触控和移动端回归测试。
481
- - **`.github/workflows/`**:自动执行持续集成,并在版本标签触发时执行发布流程。
482
-
483
- ## 本地开发与测试
484
-
485
- 克隆项目:
147
+ ## Development and tests
486
148
 
487
149
  ```bash
488
150
  git clone https://github.com/GSvegetable/pixel-daylight-switch.git
489
151
  cd pixel-daylight-switch
490
- ```
491
-
492
- 直接用浏览器打开 `index.html` 即可查看基础演示。项目无需打包构建。
493
-
494
- ### 自动化测试
495
-
496
- 运行包入口、CommonJS / ESM 和 SSR 安全检查:
497
-
498
- ```bash
499
152
  npm test
500
153
  ```
501
154
 
502
- 浏览器回归测试使用 Playwright。安装测试运行器和 Chromium 后执行:
155
+ Run the Chromium browser regression suite:
503
156
 
504
157
  ```bash
505
158
  npm install --no-save @playwright/test@1.56.1
@@ -507,237 +160,73 @@ npx playwright install chromium
507
160
  npm run test:browser
508
161
  ```
509
162
 
510
- CI 会在 Node.js 18、20、22 下执行包与 SSR 检查,并在 Chromium 中运行交互、重复挂载/卸载、多实例、事件监听器清理和移动端回归测试。
511
-
512
- ## 版本记录
513
-
514
- ### v1.3.1
515
-
516
- - 修复组件卸载时主动销毁 WebGL context,导致同一 canvas/root 后续重新挂载可能异常的问题。
517
- - 保留组件资源、事件监听器与观察器的清理,同时允许 canvas 在卸载后重新挂载。
518
- - 增加 CommonJS、ESM、SSR、Node.js 18/20/22 和 Chromium 浏览器自动化检查。
519
- - 增加重复挂载、多实例、指针交互、触控、移动端布局与监听器清理测试。
520
- - 将浏览器回归纳入 NPM 发布前置检查。
521
-
522
- ### v1.3.0
523
-
524
- - 新增日本街道、现代公寓、海边灯塔和山间木屋四种像素环境。
525
- - 保留乡村小屋作为默认场景。
526
- - Playground 支持切换场景并组合调整天气、昼夜和玻璃参数。
527
-
528
- ### v1.2.0
529
-
530
- - 新增惯性阻尼、边缘回弹、玻璃色散和表面高光参数。
531
- - 新增世界时间倍率与飘雪天气。
532
- - 新增独立 Playground,支持参数分组、场景预设和 HTML 代码复制。
533
-
534
- ### v1.1.0
535
-
536
- - 新增运动速度、镜片折射、外框厚度和初始夜晚状态配置。
537
-
538
- ### v1.0.0
539
-
540
- - 首个公开版本。
541
-
542
- 详细记录见 [CHANGELOG.md](./CHANGELOG.md)。
543
-
544
- ## 许可证
545
-
546
- 本项目使用 [Unlicense](https://unlicense.org/) 公有领域协议。允许个人和商业用途,无需署名。
547
-
548
- ---
549
-
550
- <a id="english"></a>
551
-
552
- ## English
553
-
554
- **Pixel Daylight Switch** is a draggable day/night UI component with a tiny animated pixel world inside a refractive glass lens. Drag the control to move through daylight, dusk, and night.
555
-
556
- ### Features
557
-
558
- - An animated pixel environment inside a glass switch.
559
- - WebGL2 refraction rendering with a Canvas 2D fallback.
560
- - Five scenes: countryside, Japanese street, modern apartment, seaside lighthouse, and mountain cabin.
561
- - Continuous day/night transitions, pointer dragging, click, and keyboard interaction.
562
- - Per-instance HTML data attributes for motion, optics, weather, and scene settings.
563
- - Multiple independent instances on the same page.
564
- - No runtime dependencies for the standalone script.
565
- - Accessible switch semantics and reduced-motion support.
566
-
567
- ### Quick start
568
-
569
- No package manager or build step is required:
570
-
571
- ```html
572
- <div data-ld-part="toggle-daylight"></div>
573
-
574
- <script
575
- defer
576
- src="https://gsvegetable.github.io/pixel-daylight-switch/app.js"
577
- ></script>
578
- ```
579
-
580
- The script automatically mounts matching elements. It creates the internal canvas, interaction button, and live status region. The component scales with its container and keeps an aspect ratio of approximately **1.6439:1**.
581
-
582
- ### NPM
583
-
584
- ```bash
585
- npm install pixel-daylight-switch-gsvegetable
586
- ```
587
-
588
- ESM:
589
-
590
- ```js
591
- import PixelDaylightSwitch from 'pixel-daylight-switch-gsvegetable';
592
-
593
- const root = document.querySelector('[data-ld-part="toggle-daylight"]');
594
- PixelDaylightSwitch.mount(root);
595
-
596
- // Before removing the element:
597
- PixelDaylightSwitch.unmount(root);
598
- ```
599
-
600
- CommonJS:
601
-
602
- ```js
603
- const PixelDaylightSwitch = require('pixel-daylight-switch-gsvegetable');
604
- ```
605
-
606
- The package provides ESM and CommonJS entry points and is safe to import in SSR environments. Call `mount()` only after the element exists in the browser.
607
-
608
- ### Framework integration
163
+ GitHub Actions runs package and SSR checks on Node.js 18, 20 and 22, plus Chromium interaction and mobile regression tests. See [current workflow runs](https://github.com/GSvegetable/pixel-daylight-switch/actions/workflows/ci.yml).
609
164
 
610
- For React, Vue 3, and Svelte, mount the component in the client-side lifecycle and call `unmount(element)` during cleanup. See the complete examples in the [Chinese documentation above](#框架集成).
165
+ ## Android and iOS
611
166
 
612
- ### Playground
167
+ The Capacitor app includes a native-friendly control surface for all five worlds (countryside, Japanese street, apartment, seaside and mountain cabin), plus an optional snowfall effect. Scene and weather changes reuse the shared renderer; the glass shader and day/night interaction remain the same. The app shell detects English, Simplified Chinese and Japanese from the device language. Its layout adapts to portrait and landscape: portrait keeps the switch and controls in a vertical flow, while wider landscape screens place the switch beside the scene and weather controls; compact landscape phones use a denser single-column layout. GitHub Actions produces an Android debug APK and an unsigned iOS Simulator archive. These are development artifacts, not store-ready releases: the Android APK is not a Play Store release, and the iOS Simulator build cannot be installed on a physical iPhone or iPad. See [mobile build notes](builds/README.md) and the [mobile workflow](https://github.com/GSvegetable/pixel-daylight-switch/actions/workflows/mobile-build.yml).
613
168
 
614
- Use the [Interactive Playground](https://gsvegetable.github.io/pixel-daylight-switch/playground.html) to adjust motion, glass optics, world speed, weather, and scenes. Apply settings to the preview and copy the generated HTML.
615
169
 
616
- ### Configuration
617
170
 
618
- All settings are optional and belong on the component root.
171
+ ### Embed in a web page
619
172
 
620
- | Attribute | Default | Description |
621
- | --- | ---: | --- |
622
- | `data-speed` | `9.5` | Base motion response speed; positive number. |
623
- | `data-refraction` | `1.24` | Glass lens refraction strength; positive number. |
624
- | `data-bezel` | `0.046` | Glass edge thickness ratio; positive number. |
625
- | `data-start-night` | `false` | Start at night when exactly `true`. |
626
- | `data-friction` | `0.6` | Release inertia damping, clamped to `0.1–0.9`. |
627
- | `data-bounce` | `3.2` | Edge rebound, clamped to `0–12`. |
628
- | `data-dispersion` | `0.05` | RGB edge dispersion, clamped to `0–0.25`. |
629
- | `data-shine` | `0.44` | Daytime highlight intensity, clamped to `0–1.5`. |
630
- | `data-time-speed` | `1` | World simulation speed, clamped to `0–8`; zero freezes world dynamics. |
631
- | `data-weather` | `default` | `default` or `snow`. Unknown values use `default`. |
632
- | `data-scene` | `countryside` | `countryside`, `japanese-street`, `apartment`, `seaside`, or `mountain-cabin`. |
633
-
634
- Example:
173
+ For one basic day/night switch, no package installation is required. Paste this single line into your HTML:
635
174
 
636
175
  ```html
637
- <div
638
- data-ld-part="toggle-daylight"
639
- data-label="Day and night switch"
640
- data-speed="12"
641
- data-refraction="1.5"
642
- data-bezel="0.05"
643
- data-start-night="true"
644
- data-friction="0.45"
645
- data-bounce="4.2"
646
- data-dispersion="0.12"
647
- data-shine="0.6"
648
- data-time-speed="1.5"
649
- data-weather="snow"
650
- data-scene="mountain-cabin"
651
- ></div>
652
- ```
653
-
654
- Invalid numeric values fall back to defaults. Range-limited values are clamped. Unknown weather and scene values fall back to `default` and `countryside`, respectively.
655
-
656
- ### Events and page theme
657
-
658
- The component updates `--night` on its root element: `0` means day, `1` means night, and values in between represent the transition.
659
-
660
- It also dispatches a bubbling, composed `daylight:change` event from the component root when the day/night state changes. `event.detail.night` is a boolean:
661
-
662
- ```js
663
- root.addEventListener('daylight:change', (event) => {
664
- document.documentElement.classList.toggle('dark', event.detail.night);
665
- });
666
- ```
667
-
668
- ### Theme customization
669
-
670
- Override these CSS variables on the component root:
671
-
672
- ```css
673
- [data-ld-part="toggle-daylight"] {
674
- --ld-bg: #f5ecd9;
675
- --ld-surface: #8c9a62;
676
- --ld-fg: #1b2146;
677
- --ld-muted: #7f97b3;
678
- --ld-accent: #efa02c;
679
- --ld-radius: 9999px;
680
- --ld-font: system-ui, sans-serif;
681
- }
682
- ```
683
-
684
- Supported variables: `--ld-bg`, `--ld-surface`, `--ld-fg`, `--ld-muted`, `--ld-accent`, `--ld-radius`, `--ld-font`, `--ld-space`, `--ld-shadow`, and `--ld-motion`.
685
-
686
- ### Mounting and cleanup
687
-
688
- Elements present when the script loads are mounted automatically. For elements created later:
689
-
690
- ```js
691
- const destroy = PixelDaylightSwitch.mount(element);
692
-
693
- // Before removing the root:
694
- PixelDaylightSwitch.unmount(element);
695
- element.remove();
176
+ <script src="https://gsvegetable.github.io/pixel-daylight-switch/embed.js"></script>
696
177
  ```
697
178
 
698
- Mounting the same element more than once is safe and does not create duplicate instances. A successful `mount()` returns a cleanup function; a failed mount returns `null`. `unmount()` stops rendering and removes the component's listeners and observers without removing the root element.
699
-
700
- ### Rendering and browser behavior
179
+ The loader injects its styles and one default-scene switch. For custom scenes, weather, multiple instances or full control over placement, use the npm API above and its `data-scene` / `data-weather` attributes.
701
180
 
702
- - WebGL2 is used for the refractive glass effect.
703
- - Canvas 2D is the rendering fallback when WebGL2 is unavailable.
704
- - The component respects `prefers-reduced-motion`.
705
- - Unnecessary drawing pauses when the page is hidden or the component is offscreen.
706
- - The standalone runtime has no third-party dependencies.
181
+ ### Integrate with your own Android / iOS Capacitor app
707
182
 
708
- ### Development and tests
183
+ This repository currently ships a **Capacitor app template and build scripts**, not a separately installable native UI plugin. For an existing Capacitor 8 project, integrate this repository's `mobile/`, `app.js`, `style.css` and `scripts/prepare-mobile.cjs`, then run:
709
184
 
710
185
  ```bash
711
- git clone https://github.com/GSvegetable/pixel-daylight-switch.git
712
- cd pixel-daylight-switch
713
- ```
714
-
715
- Open `index.html` in a modern browser. No build step is required.
716
-
717
- Run package and SSR checks:
718
-
719
- ```bash
720
- npm test
721
- ```
722
-
723
- Run browser regression tests after installing Playwright and Chromium:
724
-
725
- ```bash
726
- npm install --no-save @playwright/test@1.56.1
727
- npx playwright install chromium
728
- npm run test:browser
186
+ npm install
187
+ npm run mobile:prepare
188
+ npm run mobile:android:init # first-time Android project creation
189
+ npm run mobile:android # sync subsequent updates
729
190
  ```
730
191
 
731
- ### Version history
732
-
733
- - **v1.3.1** — lifecycle/remount stability fix and expanded automated regression coverage.
734
- - **v1.3.0** — four additional pixel environments and scene selection in Playground.
735
- - **v1.2.0** — motion, optics, world-speed, weather settings, and the standalone Playground.
736
- - **v1.1.0** — initial HTML configuration options.
737
- - **v1.0.0** — first public release.
192
+ For iOS, use `npm run mobile:ios:init` once and `npm run mobile:ios` for later syncs; macOS and Xcode are required. Open the native projects with `npm run mobile:open:android` or `npm run mobile:open:ios`. See [package.json](package.json) for the exact scripts.
738
193
 
739
- See [CHANGELOG.md](./CHANGELOG.md) for details.
194
+ ## Repository map
740
195
 
741
- ### License
742
-
743
- [Unlicense](https://unlicense.org/) — public-domain dedication. Free for personal and commercial use without attribution.
196
+ ```text
197
+ pixel-daylight-switch/
198
+ ├── .github/workflows/ # CI, native mobile builds, npm publishing
199
+ ├── assets/
200
+ │ └── scene-gallery.png # Five-scene overview image
201
+ ├── builds/
202
+ │ └── README.md # Mobile artifact notes; binaries are workflow outputs
203
+ ├── mobile/
204
+ │ ├── index.html # Capacitor app entry
205
+ │ └── mobile.css # Mobile-only layout
206
+ ├── scripts/
207
+ │ └── prepare-mobile.cjs # Prepare the shared web runtime for Capacitor
208
+ ├── tests/
209
+ │ ├── browser/ # Playwright interaction and mobile regressions
210
+ │ ├── fixture.html
211
+ │ ├── mobile-smoke.cjs
212
+ │ ├── package-smoke.cjs
213
+ │ └── server.cjs
214
+ ├── app.js # Component runtime, scenes, input and rendering
215
+ ├── style.css # Component styles
216
+ ├── embed.js # One-script website embed loader
217
+ ├── index.html # Live demo page
218
+ ├── preview.html # Component preview
219
+ ├── playground.html # Interactive parameter lab
220
+ ├── index.mjs / index.cjs # ESM and CommonJS package entry points
221
+ ├── package.json # Package metadata and scripts
222
+ ├── capacitor.config.json # Capacitor configuration
223
+ ├── playwright.config.cjs # Browser test configuration
224
+ ├── CHANGELOG.md
225
+ ├── llms.txt
226
+ ├── LICENSE
227
+ └── README*.md # English, Simplified Chinese and Japanese docs
228
+ ```
229
+
230
+ ## License
231
+
232
+ Released under [The Unlicense](LICENSE). You may copy, modify, publish, distribute and use the software for personal or commercial purposes without attribution. It is provided without warranty.