naive-ui ColorPicker 颜色选择器组件完整使用指南:模式、色板、表单与源码剖析
【免费下载链接】naive-uiA Vue 3 Component Library. Fairly Complete. Theme Customizable. Uses TypeScript. Fast.项目地址: https://gitcode.com/gh_mirrors/na/naive-ui
naive-ui 的NColorPicker是一个基于 Vue 3 + TypeScript 的高可定制颜色选择器组件,支持 RGB、HEX、HSL、HSV 四种颜色格式,内置取色面板、透明度调节、色板预设、撤销/重做与表单集成能力。本文以 src/color-picker/demos/zhCN/index.demo-entry.md 为核心骨架,结合组件源码与测试用例,系统讲解其全部 Props、Slots、常用实战写法与底层实现原理,读完即可在真实项目中熟练落地。
组件概览:不连续的颜色空间
文档用一句话概括了组件的本质:"和真实世界比起来,它的空间是不连续的。" 现实中的颜色是连续的光谱,而NColorPicker只能表示特定格式下的离散取值——例如 HEX 模式只输出#RRGGBB形式、RGB 模式只输出rgb(...)/rgba(...)字符串。组件所有交互都围绕"把用户操作翻译成某种格式的颜色字符串"展开,理解这一点有助于理解后续的模式切换与默认值行为。
组件入口与完整 Props 定义位于 src/color-picker/src/ColorPicker.tsx,对外通过 src/color-picker/index.ts 导出NColorPicker与类型ColorPickerProps、ColorPickerSlots。
快速上手:基础用法与默认值
最简单的用法是直接渲染组件(见 basic.demo.vue):
<template> <n-color-picker /> </template>此时组件默认展示一个触发器,点击后弹出取色面板。面板自上而下依次为:色彩平面(饱和度/明度取色区)、色相滑块、透明度滑块、输入区与可选色板。
组件在未传入value或default-value时会自动推导一个默认颜色。从 src/color-picker/src/utils.ts 的deriveDefaultValue实现可以看到,默认值由modes[0]与showAlpha共同决定:
| 首个 mode | showAlpha=true | showAlpha=false |
|---|---|---|
hex | #000000FF | #000000 |
rgb | rgba(0, 0, 0, 1) | rgb(0, 0, 0) |
hsl | hsla(0, 0%, 0%, 1) | hsl(0, 0%, 0%) |
hsv | hsva(0, 0%, 0%, 1) | hsv(0, 0%, 0%) |
也就是说,默认颜色始终是"与第一个 mode 对应的黑色值"。
颜色模式:modes 与值格式跟随
modes用于声明颜色选择器支持的颜色格式,类型为Array<'rgb' | 'hex' | 'hsl' | 'hsv'>,默认值为['rgb', 'hex', 'hsl']。注意源码注释特别说明:默认不包含hsv,因为浏览器本身不支持 hsv 表示法。
面板输入区右侧有一个模式切换按钮,点击后会在modes声明的模式间循环切换。关键行为是:一旦你在某个模式下选择了值,组件对外输出的值格式将跟随该模式。例如只声明hex(见 modes.demo.vue):
<template> <n-color-picker :modes="['hex']" /> </template>此时组件只能输出#RRGGBB(开启 alpha 后为#RRGGBBAA)形式的字符串。
模式的识别逻辑位于 src/color-picker/src/utils.ts 的getModeFromValue:以#开头判定为hex,字符串包含rgb/hsl/hsv则分别判定为对应模式。当用户切换模式时,组件通过同一文件中的convertColor借助seemly库在四种模式间做无损转换(保持 alpha 通道)。
这一行为已被测试用例覆盖,见 src/color-picker/tests/ColorPicker.spec.tsx:声明modes: ['hex', 'hsl']时,输入区模式标签在HEXA与HSLA之间循环切换;声明单一模式['hsl']时标签恒为HSLA。
尺寸、禁用与透明度调节
组件提供三种尺寸(见 size.demo.vue),尺寸类型ColorPickerSize = 'small' | 'medium' | 'large'定义在 src/color-picker/src/public-types.ts:
<template> <n-space vertical> <n-color-picker size="small" /> <n-color-picker /> <n-color-picker size="large" /> </n-space> </template>disabled可直接禁用组件(见 disabled.demo.vue):
<template> <n-color-picker disabled /> </template>从 ColorPicker.tsx 的实现看,禁用状态下点击触发器会被拦截(handleTriggerClick直接 return),面板不会弹出。
show-alpha控制是否显示透明度滑块、以及输出字符串是否携带 alpha 通道,默认true。设为false后(见 alpha.demo.vue),面板隐藏透明度滑块,输出变为rgb(...)/hsl(...)/#RRGGBB等不含透明度的形式:
<template> <n-color-picker :show-alpha="false" :actions="['confirm']" @confirm="handleConfirm" /> </template>面板弹出行为:show、placement 与 to
组件受控/非受控地管理弹出层可见性:
show:受控的可见状态(boolean,默认undefined);default-show:非受控模式下的初始可见状态;on-update:show/onUpdateShow:可见状态改变回调。
从源码看,组件通过useMergedState合并show与内部uncontrolledShowRef,并依赖vueuc的VBinder/VTarget/VFollower实现触发器与弹出层的绑定跟随(见 ColorPicker.tsx)。弹出层还通过vdirs的clickoutside指令实现点击外部自动关闭。
placement:面板弹出位置,默认'bottom-start',可选值覆盖top/right/bottom/left及其-start/-end变体,完整类型即FollowerPlacement;to:面板卸载位置,默认'body',传false则保留在原位。
动作按钮:actions、确认与清除
默认情况下面板底部不显示任何按钮。通过actions属性可声明'confirm'与'clear'按钮(见 actions.demo.vue):
<template> <n-color-picker :actions="['clear']" /> </template>两个按钮的渲染逻辑在 ColorPicker.tsx:
- confirm:点击后触发
on-confirm回调(2.29.0+),并关闭面板; - clear:点击后把值清空为
null,触发on-clear回调(2.39.0+),并关闭面板;当当前值为空时按钮自动禁用。
此外,组件内部还维护了一个仅存在于面板生命周期内的撤销/重做栈(undoStackRef/valueIndexRef,见 ColorPicker.tsx 与undo/redo实现),每次"完成"一次颜色修改都会入栈,面板关闭时重置栈。这个内部能力通过internalActions属性('redo' | 'undo')暴露,虽然未出现在公开文档表中,但从源码结构看它是组件内部用于承载撤销/重做 UI 的机制。
色板:swatches 预设
通过swatches属性可以预设一组颜色供用户一键选取(见 swatches.demo.vue):
<template> <n-color-picker :swatches="[ '#FFFFFF', '#18A058', '#2080F0', '#F0A020', 'rgba(208, 48, 80, 1)', ]" /> </template>注意swatches数组内的元素并不要求与当前模式一致——上例中'rgba(208, 48, 80, 1)'就是 rgb 格式。从渲染逻辑(ColorPicker.tsx)看,面板仅在props.swatches?.length为真时渲染ColorPickerSwatches,选取后会把该色值按当前模式转换后写回组件值。色板区渲染组件为 ColorPickerSwatches.tsx。
插槽:label、trigger 与 action
组件提供三个插槽(见 ColorPicker.tsx):
trigger 插槽(2.44.0+)—— 自定义整个触发器,参数为{ value, onClick, ref }。文档明确要求:只允许一个元素,不可以是纯文本。ref必须绑定到根元素上,否则点击外部关闭的判定会失效(源码中handleClickOutside依赖triggerRef判断点击是否落在触发器内)。trigger.demo.vue 给出了三种典型用法:
<template> <n-color-picker v-model:value="color1"> <template #trigger="{ value, onClick, ref: triggerRef }"> <n-button :ref="triggerRef" circle quaternary @click="onClick"> <template #icon> <n-icon :color="value || '#000'"> <PaletteIcon /> </n-icon> </template> </n-button> </template> </n-color-picker> </template>上例用带当前颜色的图标按钮作为触发器;另两种写法分别是用圆点色块、以及用n-text直接显示当前颜色字符串的文本触发器。自定义触发器时务必把ref、onClick正确地挂到唯一根元素上。
label 插槽(2.24.0+)—— 自定义默认触发器的显示内容,参数为当前颜色值color: string | null,对应 Props 中的render-label。
action 插槽(2.24.0+)—— 渲染在面板底部的自定义操作区,无参数。从渲染顺序看,它的优先级高于internalActions(见 ColorPicker.tsx)。
与表单一起使用
ColorPicker 是一个标准的数据录入组件,可直接配合n-form使用(见 form.demo.vue):
<script lang="ts"> import { defineComponent, reactive } from 'vue' export default defineComponent({ setup() { const model = reactive({ color: '#18A058' }) return { model, colorRule: { trigger: 'change', validator(_: unknown, value: string) { if (value !== '#18A058') return new Error('不许改颜色') } } } } }) </script> <template> <n-form :model="model"> <n-form-item label="颜色(#18A058)" path="color" :rule="colorRule"> <n-color-picker v-model:value="model.color" :show-alpha="false" /> </n-form-item> </n-form> </template>从源码看,组件内部通过useFormItem(见 ColorPicker.tsx)接入表单上下文:尺寸会自动继承n-form-item的尺寸,禁用状态同步n-form的禁用,并在每次颜色改变时调用nTriggerFormChange/nTriggerFormInput触发校验。doUpdateValue中同时触发 change 与 input 两类校验时机,因此校验规则建议使用trigger: 'change'。
原生颜色选择器:show-preview
show-preview开启后,面板色相滑块下方会渲染一个颜色预览块(见 native.demo.vue):
<template> <n-color-picker :show-preview="true" /> </template>点击该预览块会触发浏览器原生颜色选择器(<input type="color">一类能力)。文档说明这是有意的设计——浏览器厂商在原生的颜色选择器上实现了一些很棒的功能(如取色器、更精细的选色体验),如果你需要这些能力可以开启它。预览块由 ColorPreview.tsx 渲染,其渲染逻辑在 ColorPicker.tsx:点击后把原生选择器的返回值写回组件(doUpdateValue(color, 'input'))。
受控与非受控:value 与 default-value
与 naive-ui 其他组件一致,ColorPicker 同时支持受控与非受控两种模式:
default-value(默认值:与第一个 mode 对应的黑色值):非受控模式的初始颜色;value:受控模式的颜色值,类型为string | null,null表示清空状态;on-update:value/onUpdateValue:值改变回调,对应v-model:value;on-complete:一次取色"完成"后的回调(鼠标拖拽过程中不会触发,只有松开或确认输入时才触发)。
从源码看,值在内部始终以字符串形式保存,任何取色操作最终都收敛到doUpdateValue(value, 'cursor' | 'input')(见 ColorPicker.tsx):拖拽取色标为'cursor'(频繁触发、不立即 complete),输入框编辑标为'input'(nextTick后触发handleComplete)。
Q&A:如何从颜色名称转化为色值
官方文档明确:naive-ui 不内置"颜色名称 → 色值"的转换功能。如果你需要支持类似red、blue这样的命名色,有两种推荐做法:
- 借助成熟颜色库的映射表,例如 TinyColor 项目中的 颜色名 → 色值映射表 中的
rgba/hsla/hsva等函数); - 自己写一个利用浏览器能力的小函数:
export function getRgb(colorName) { const el = document.createElement('div') el.style.color = colorName document.body.appendChild(el) const rgbColor = getComputedStyle(el).color document.body.removeChild(el) return rgbColor }原理是利用getComputedStyle让浏览器把任意合法颜色名称解析为标准rgb(r, g, b)字符串,从而完成转换。注意该函数依赖 DOM 环境,服务端渲染(SSR)下不可用。
完整 API 速查
ColorPicker Props
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|---|---|---|---|---|
| default-show | boolean | undefined | 默认是否展示弹出层 | |
| default-value | string \| null | 和第一个 mode 对应的黑色值 | 默认的颜色值 | |
| modes | Array<'rgb' \| 'hex' \| 'hsl' \| 'hsv'> | ['rgb', 'hex', 'hsl'] | 支持的颜色格式,选定某模式后值格式跟随该模式 | |
| placement | 'top-start' \| 'top' \| 'top-end' \| 'right-start' \| 'right' \| 'right-end' \| 'bottom-start' \| 'bottom' \| 'bottom-end' \| 'left-start' \| 'left' \| 'left-end' | 'bottom-start' | 面板的弹出位置 | 2.25.0 |
| render-label | (color: string \| null) => VNodeChild | undefined | 触发器的内容 | 2.24.0 |
| show | boolean | undefined | 是否展示面板 | |
| show-alpha | boolean | true | 是否可调节 alpha 通道 | |
| show-preview | boolean | false | 是否展示颜色预览块 | |
| size | 'small' \| 'medium' \| 'large' | 'medium' | 颜色选择器的尺寸 | |
| disabled | boolean | false | 是否禁用 | 2.24.5 |
| swatches | string[] | undefined | 色板的值 | |
| to | string \| HTMLElement \| false | 'body' | 面板的卸载位置,false会待在原地 | |
| value | string \| null | undefined | 颜色选择器的值 | |
| on-complete | (value: string) => void | undefined | 颜色完成改变后的回调(鼠标移动时不会调用) | |
| on-confirm | (value: string) => void | undefined | 点击确定按钮的回调 | 2.29.0 |
| on-clear | () => void | undefined | 点击清除按钮的回调 | 2.39.0 |
| on-update:show | (value: boolean) => void | undefined | 面板可见状态改变的回调 | |
| on-update:value | (value: string) => void | undefined | 颜色改变时的回调 | |
| actions | Array<'confirm' \| 'clear'> \| null | null | 显示按钮 |
ColorPicker Slots
| 名称 | 参数 | 说明 | 版本 |
|---|---|---|---|
| action | () | 菜单操作区的 slot | 2.24.0 |
| label | (color: string \| null) | 触发器的内容 | 2.24.0 |
| trigger | (props: { value: string \| null, onClick: (() => void) \| undefined, ref: (ref: Element \| ComponentPublicInstance \| null) => void }) | 自定义触发器,只允许一个元素,不可以是纯文本 | 2.44.0 |
延伸阅读
- 组件源码:src/color-picker/src/ColorPicker.tsx
- 模式识别、默认值推导与颜色转换工具:src/color-picker/src/utils.ts
- 公开类型与导出:src/color-picker/index.ts、src/color-picker/src/public-types.ts
- 组件测试:src/color-picker/tests/ColorPicker.spec.tsx(覆盖 modes 切换、默认值推导等行为)、src/color-picker/tests/server.spec.tsx
- 完整演示示例:src/color-picker/demos/zhCN/(basic、alpha、size、disabled、modes、actions、form、swatches、trigger、native、close-debug 共 11 个示例)
- 主题变量:src/color-picker/styles/index.ts
【免费下载链接】naive-uiA Vue 3 Component Library. Fairly Complete. Theme Customizable. Uses TypeScript. Fast.项目地址: https://gitcode.com/gh_mirrors/na/naive-ui
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考