canvas-color-disc 1.2.1 → 1.3.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.cn.md CHANGED
@@ -121,7 +121,22 @@ export default {
121
121
  </script>
122
122
  ```
123
123
 
124
- `v-model` 绑定的是 RGB 对象,父组件修改该对象时色盘选择器会同步移动。
124
+ `v-model` 绑定的形状由 `format` 决定,父组件修改绑定值时色盘选择器会同步移动。
125
+
126
+ ### 颜色格式
127
+
128
+ `format` 决定绑定值的形状(默认 `rgb`):
129
+
130
+ | format | 绑定值 | 示例 |
131
+ | --- | --- | --- |
132
+ | `rgb` | `{ r, g, b }`,通道 0~255 | `{ r: 255, g: 0, b: 0 }` |
133
+ | `hex` | 字符串,支持 3/4/6/8 位 | `'#ff0000'` |
134
+ | `hsv` | `{ h, s, v }`,h 0~360、s/v 0~100 | `{ h: 0, s: 100, v: 100 }` |
135
+ | `hsl` | `{ h, s, l }`,h 0~360、s/l 0~100 | `{ h: 0, s: 100, l: 50 }` |
136
+
137
+ 字符串一律按 hex 解析;对象会先按 `format` 解析,未指定 `format` 时按字段推断。
138
+
139
+ `alpha` 为 `true` 时额外读写 `a` 分量(0~1),hex 格式下输出 8 位(如 `#ff000080`)。色盘是二维的,本身不调节透明度与明度,这两项需要由外部控件或绑定值给出。
125
140
 
126
141
  ## API
127
142
 
@@ -129,30 +144,75 @@ export default {
129
144
 
130
145
  | 参数 | 说明 | 类型 | 默认值 | 适用版本 |
131
146
  | --- | --- | --- | --- | --- |
132
- | value | 当前颜色,配合 `v-model` 使用 | Object `{ r, g, b }` | `{ r: 255, g: 255, b: 255 }` | Vue 2 |
133
- | modelValue | 当前颜色,配合 `v-model` 使用 | Object `{ r, g, b }` | `undefined`(回退到 `value`) | Vue 3 |
134
- | width | 画布宽度(CSS 像素) | Number | 300 | 通用 |
135
- | height | 画布高度(CSS 像素) | Number | 300 | 通用 |
147
+ | value | 当前颜色,配合 `v-model` 使用 | Object / String | `{ r: 255, g: 255, b: 255 }` | Vue 2 |
148
+ | modelValue | 当前颜色,配合 `v-model` 使用 | Object / String | `undefined`(回退到 `value`) | Vue 3 |
149
+ | width | 画布宽度(CSS 像素),`'auto'` 表示跟随容器 | Number / String | 300 | 通用 |
150
+ | height | 画布高度(CSS 像素),`'auto'` 表示跟随容器 | Number / String | 300 | 通用 |
151
+ | format | 绑定值格式:`rgb` / `hex` / `hsv` / `hsl` | String | `rgb` | 通用 |
152
+ | alpha | 是否保留并输出 alpha 分量 | Boolean | `false` | 通用 |
153
+ | disabled | 禁用指针与键盘交互 | Boolean | `false` | 通用 |
154
+ | keyboardStep | 方向键步进(饱和度百分点 / 色相度数),按住 Shift 时 ×10 | Number | 1 | 通用 |
136
155
 
137
156
  ### Events
138
157
 
139
158
  | 事件名 | 说明 | 回调参数 | 适用版本 |
140
159
  | --- | --- | --- | --- |
141
- | input | 颜色变化时实时触发(按帧节流,配合 `v-model`) | `{ r, g, b }` | Vue 2 |
142
- | update:modelValue | 同 `input`,配合 `v-model` 使用 | `{ r, g, b }` | Vue 3 |
143
- | change | 一次选择(按下后抬起)结束时触发 | `{ r, g, b }` | 通用 |
160
+ | input | 颜色变化时实时触发(按帧节流,配合 `v-model`) | 与 `format` 一致 | Vue 2 |
161
+ | update:modelValue | 同 `input`,配合 `v-model` 使用 | 与 `format` 一致 | Vue 3 |
162
+ | change | 一次选择(按下后抬起,或一次键盘调整)结束时触发 | 与 `format` 一致 | 通用 |
144
163
 
145
164
  `input` 与 `update:modelValue` 会同时派发,未被监听的一方会被忽略。
146
165
 
166
+ ### 插槽
167
+
168
+ | 名称 | 作用域参数 | 说明 |
169
+ | --- | --- | --- |
170
+ | palette | `color`(当前颜色)、`setColor(value)` | 渲染在色盘下方的附加区域,常用于预设色板 |
171
+
172
+ ```vue
173
+ <canvas-color-disc v-model="rgb">
174
+ <template #palette="{ setColor }">
175
+ <button @click="setColor({ r: 255, g: 0, b: 0 })">红</button>
176
+ </template>
177
+ </canvas-color-disc>
178
+ ```
179
+
180
+ ### 实例方法
181
+
182
+ 通过 `ref` 调用(Vue 3 下经由 `expose` 暴露,Vue 2 下直接从 `$refs` 取到):
183
+
184
+ | 方法 | 说明 |
185
+ | --- | --- |
186
+ | `getColor()` | 取当前颜色,对象格式为副本 |
187
+ | `setColor(value)` | 设置颜色,值需符合当前 `format`,非法值被忽略;会同时派发 `change` |
188
+ | `reset()` | 重置为白色 |
189
+
190
+ ### 键盘操作
191
+
192
+ 色盘的交互元素可被聚焦(`tabindex="0"`,带 `role="slider"` 与 `aria-valuetext`),聚焦后:
193
+
194
+ | 按键 | 行为 |
195
+ | --- | --- |
196
+ | ← / → | 色相 ∓/± `keyboardStep` 度 |
197
+ | ↑ / ↓ | 饱和度 ±/∓ `keyboardStep` |
198
+ | Home / End | 饱和度归零 / 拉满 |
199
+ | Shift + 方向键 | 步进 ×10 |
200
+
147
201
  ### 说明
148
202
 
149
- - 色盘为 HSV 模型:色相取角度、饱和度取离圆心的半径比例,明度固定为最高,圆心为白色
150
- - `width`/`height` 为画布逻辑尺寸(CSS 像素),色盘半径自动按 `min(width, height) / 2 - 18` 适配,圆心居中;两者可以不相等
203
+ - 色盘为 HSV 模型:色相取角度、饱和度取离圆心的半径比例;明度(V)取自绑定值并原样保留,因此传入灰色不会被拉成纯色
204
+ - `width`/`height` 为画布逻辑尺寸(CSS 像素),色盘半径自动按「短边半径减去比例留白(收敛在 8~18px)」适配,圆心居中;两者可以不相等。选择器圆环与圆心白色死区同样随半径缩放,因此极小与极大的色盘都能保持一致的观感
151
205
  - 画布按 `devicePixelRatio` 放大绘制,高清屏下不会模糊
152
206
  - 取色坐标会自动换算回画布逻辑坐标:即使画布被外部 CSS 缩放(如全局 `canvas { max-width: 100% }`、父容器压缩、transform 缩放),取色位置仍与视觉位置一致
153
207
  - 点击或拖动超出色盘范围时,选择器会钉在圆周上(取圆周对应位置的颜色)
154
- - 基于 Pointer Events,鼠标与触摸的操作方式一致;拖动通过 `setPointerCapture` 捕获,指针移出画布也不中断
208
+ - 基于 Pointer Events,鼠标与触摸的操作方式一致;拖动通过 `setPointerCapture` 捕获,指针移出画布也不中断;若捕获失败(部分浏览器/合成事件),window 上的兜底监听仍会正常结束一次选择
209
+ - 只响应主键与主指针:右键/中键不会取色,多指触摸时后续手指不会打断正在进行的拖动
210
+ - 拖动过程中页面滚动会立即使缓存的画布位置失效,避免出现取色偏移
211
+ - `width`/`height` 传 `'auto'` 时画布尺寸跟随容器,容器尺寸变化会自动重绘;该方向需要父容器有确定尺寸
212
+ - `devicePixelRatio` 变化(跨屏拖动窗口、修改系统缩放)时会自动按新比例重建画布
213
+ - 色盘像素按几何参数缓存,尺寸与 dpr 未变时不会重算;圆周做 1 物理像素的透明度渐变,避免锯齿
155
214
  - 样式为普通 CSS,通过 `.canvas-color-disc` 后代选择器限定作用域,每个应用引入一次对应版本的 `style.css` 即可
215
+ - 包内附带 `src/index.d.ts`,TS 项目可直接获得 props 与实例方法的类型
156
216
 
157
217
  ## 本地开发
158
218
 
@@ -163,12 +223,18 @@ npm install
163
223
  # 启动 Vue 2 demo(localhost:5173)
164
224
  npm run dev
165
225
 
226
+ # 启动 Vue 3 demo(localhost:5174)
227
+ npm run dev:vue3
228
+
166
229
  # 同时构建 Vue 2(dist/)与 Vue 3(dist/vue3/)产物
167
230
  npm run build
168
231
 
169
232
  # 单独构建某一版本
170
233
  npm run build:vue2
171
234
  npm run build:vue3
235
+
236
+ # 运行单元测试
237
+ npm test
172
238
  ```
173
239
 
174
240
  ## 许可证
package/README.md CHANGED
@@ -121,7 +121,22 @@ export default {
121
121
  </script>
122
122
  ```
123
123
 
124
- `v-model` binds an RGB object. Updating that object from the parent moves the selector accordingly.
124
+ The shape of the bound value is decided by `format`. Updating it from the parent moves the selector accordingly.
125
+
126
+ ### Color formats
127
+
128
+ `format` (default `rgb`) decides the shape of the bound value:
129
+
130
+ | format | Value | Example |
131
+ | --- | --- | --- |
132
+ | `rgb` | `{ r, g, b }`, channels 0~255 | `{ r: 255, g: 0, b: 0 }` |
133
+ | `hex` | string, 3/4/6/8 digits | `'#ff0000'` |
134
+ | `hsv` | `{ h, s, v }`, h 0~360, s/v 0~100 | `{ h: 0, s: 100, v: 100 }` |
135
+ | `hsl` | `{ h, s, l }`, h 0~360, s/l 0~100 | `{ h: 0, s: 100, l: 50 }` |
136
+
137
+ Strings are always parsed as hex. Objects are parsed with `format` first, and by field names when `format` is not given.
138
+
139
+ With `alpha` enabled an extra `a` channel (0~1) is read and written, and hex output becomes 8 digits (e.g. `#ff000080`). The disc is two-dimensional, so it does not adjust alpha or value itself — provide them through the bound value or your own controls.
125
140
 
126
141
  ## API
127
142
 
@@ -129,30 +144,75 @@ export default {
129
144
 
130
145
  | Prop | Description | Type | Default | Vue version |
131
146
  | --- | --- | --- | --- | --- |
132
- | value | Current color, used by `v-model` | Object `{ r, g, b }` | `{ r: 255, g: 255, b: 255 }` | Vue 2 |
133
- | modelValue | Current color, used by `v-model` | Object `{ r, g, b }` | `undefined` (falls back to `value`) | Vue 3 |
134
- | width | Canvas width (CSS pixels) | Number | 300 | both |
135
- | height | Canvas height (CSS pixels) | Number | 300 | both |
147
+ | value | Current color, used by `v-model` | Object / String | `{ r: 255, g: 255, b: 255 }` | Vue 2 |
148
+ | modelValue | Current color, used by `v-model` | Object / String | `undefined` (falls back to `value`) | Vue 3 |
149
+ | width | Canvas width in CSS pixels, `'auto'` follows the container | Number / String | 300 | both |
150
+ | height | Canvas height in CSS pixels, `'auto'` follows the container | Number / String | 300 | both |
151
+ | format | Bound value format: `rgb` / `hex` / `hsv` / `hsl` | String | `rgb` | both |
152
+ | alpha | Keep and emit the alpha channel | Boolean | `false` | both |
153
+ | disabled | Disable pointer and keyboard interaction | Boolean | `false` | both |
154
+ | keyboardStep | Arrow key step (saturation percentage / hue degrees), ×10 with Shift | Number | 1 | both |
136
155
 
137
156
  ### Events
138
157
 
139
158
  | Event | Description | Payload | Vue version |
140
159
  | --- | --- | --- | --- |
141
- | input | Fired in real time while the color changes (throttled per frame, used by `v-model`) | `{ r, g, b }` | Vue 2 |
142
- | update:modelValue | Same as `input`, used by `v-model` | `{ r, g, b }` | Vue 3 |
143
- | change | Fired when a pick gesture (press then release) ends | `{ r, g, b }` | both |
160
+ | input | Fired in real time while the color changes (throttled per frame, used by `v-model`) | matches `format` | Vue 2 |
161
+ | update:modelValue | Same as `input`, used by `v-model` | matches `format` | Vue 3 |
162
+ | change | Fired when a pick gesture (press then release) or a key adjustment ends | matches `format` | both |
144
163
 
145
164
  Both `input` and `update:modelValue` are always emitted, so the unused one is simply ignored.
146
165
 
166
+ ### Slot
167
+
168
+ | Name | Scope | Description |
169
+ | --- | --- | --- |
170
+ | palette | `color` (current color), `setColor(value)` | Extra area rendered below the disc, typically used for preset swatches |
171
+
172
+ ```vue
173
+ <canvas-color-disc v-model="rgb">
174
+ <template #palette="{ setColor }">
175
+ <button @click="setColor({ r: 255, g: 0, b: 0 })">Red</button>
176
+ </template>
177
+ </canvas-color-disc>
178
+ ```
179
+
180
+ ### Instance methods
181
+
182
+ Available through a `ref` (exposed via `expose` on Vue 3, directly on `$refs` on Vue 2):
183
+
184
+ | Method | Description |
185
+ | --- | --- |
186
+ | `getColor()` | Returns the current color; objects are returned as copies |
187
+ | `setColor(value)` | Sets the color; the value must match `format`, invalid values are ignored. Also emits `change` |
188
+ | `reset()` | Resets to white |
189
+
190
+ ### Keyboard
191
+
192
+ The interactive element is focusable (`tabindex="0"`, `role="slider"` with `aria-valuetext`). Once focused:
193
+
194
+ | Key | Action |
195
+ | --- | --- |
196
+ | ← / → | Hue ∓/± `keyboardStep` degrees |
197
+ | ↑ / ↓ | Saturation ±/∓ `keyboardStep` |
198
+ | Home / End | Saturation to 0 / 100 |
199
+ | Shift + arrows | Step ×10 |
200
+
147
201
  ### Notes
148
202
 
149
- - The disc uses the HSV model: hue comes from the angle, saturation from the distance to the center ratio, value is fixed to maximum, and the center is white
150
- - `width` / `height` are logical canvas sizes in CSS pixels. The disc radius is `min(width, height) / 2 - 18` and the origin is centered; they do not have to be equal
203
+ - The disc uses the HSV model: hue comes from the angle, saturation from the distance to the center ratio. Value is taken from the bound value and preserved, so passing a gray does not turn it into a fully saturated color
204
+ - `width` / `height` are logical canvas sizes in CSS pixels. The disc radius is the shorter half side minus a proportional padding (clamped to 8~18px) and the origin is centered; they do not have to be equal. The selector ring and the white dead zone at the center scale with the radius too, so tiny and large discs keep the same proportions
151
205
  - The canvas is rendered with `devicePixelRatio` scaling, so it stays sharp on high-DPI screens
152
206
  - Pointer coordinates are converted back to logical canvas coordinates: even if the canvas is scaled by external CSS (e.g. a global `canvas { max-width: 100% }`, a squeezed parent, or a `transform` scale), the picked position still matches the visual position
153
207
  - Clicking or dragging outside the disc pins the selector to the circumference and takes the color at that point
154
- - Built on Pointer Events, so mouse and touch behave the same; dragging uses `setPointerCapture`, so it continues even when the pointer leaves the canvas
208
+ - Built on Pointer Events, so mouse and touch behave the same; dragging uses `setPointerCapture`, so it continues even when the pointer leaves the canvas, and a window-level fallback ends the gesture if the capture could not be taken
209
+ - Only the primary button / primary pointer starts a pick: right and middle clicks are ignored, and extra fingers in a multi-touch gesture do not take over an ongoing drag
210
+ - The cached canvas rect is invalidated on scroll, so picking stays accurate while the page is scrolled during a drag
211
+ - Passing `'auto'` for `width` / `height` makes the canvas follow its container and redraw on container resizes; the parent needs a definite size on that axis
212
+ - When `devicePixelRatio` changes (dragging the window to another screen, changing the OS scaling), the canvas is rebuilt at the new ratio
213
+ - Disc pixels are cached by geometry, so nothing is recomputed while size and dpr stay the same; the circumference fades over one physical pixel to avoid aliasing
155
214
  - Styles are shipped as plain CSS scoped by the `.canvas-color-disc` ancestor selector; import the matching `style.css` once per app
215
+ - `src/index.d.ts` ships with the package, so TS projects get prop and instance method types out of the box
156
216
 
157
217
  ## Local development
158
218
 
@@ -163,12 +223,18 @@ npm install
163
223
  # serve the Vue 2 demo at localhost:5173
164
224
  npm run dev
165
225
 
226
+ # serve the Vue 3 demo at localhost:5174
227
+ npm run dev:vue3
228
+
166
229
  # build both Vue 2 (dist/) and Vue 3 (dist/vue3/) bundles
167
230
  npm run build
168
231
 
169
232
  # build a single target
170
233
  npm run build:vue2
171
234
  npm run build:vue3
235
+
236
+ # run the unit tests
237
+ npm test
172
238
  ```
173
239
 
174
240
  ## License