├── example ├── v3-color-picker.png └── index.html ├── .editorconfig ├── index.d.ts ├── LICENSE ├── src ├── draggable.js ├── component │ ├── ColorList.vue │ ├── ColorStraw.vue │ ├── ColorPreview.vue │ ├── ColorHue.vue │ ├── ColorPanel.vue │ ├── ColorAlpha.vue │ ├── ColorPicker.vue │ └── ColorValue.vue ├── index.js ├── V3ColorPicker.vue └── color.js ├── package.json ├── rollup.config.js ├── .gitignore └── README.md /example/v3-color-picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xfy520/v3-color-picker/HEAD/example/v3-color-picker.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # 根目录的配置文件,编辑器会由当前目录向上查找,如果找到 `roor = true` 的文件,则不再查找 2 | root = true 3 | # 匹配全部文件 4 | [*] 5 | # 设置字符集 6 | charset = utf-8 7 | # 结尾换行符,可选"lf"、"cr"、"crlf" 8 | end_of_line = lf 9 | # 在文件结尾插入新行 10 | insert_final_newline = true 11 | # 删除一行中的前后空格 12 | trim_trailing_whitespace = true 13 | # 缩进风格,可选"space"、"tab" 14 | indent_style = space 15 | # 缩进的空格数 16 | indent_size = 2 17 | # 单行最大长度 18 | max_line_length = 100 19 | # 大括号不另起一行 20 | curly_bracket_next_line = false 21 | # 运算符两遍都有空格 22 | spaces_around_operators = true 23 | # 条件语句格式是 1tbs 24 | indent_brace_style = 1tbs 25 | 26 | # 以下匹配,类同 27 | [Makefile] 28 | indent_style = tab 29 | [*.{js,ts}] 30 | # 字符串使用单引号 31 | quote_type = single 32 | [*.{json,vue}] 33 | # 字符串使用双引号 34 | quote_type = double 35 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | type colorType = { 2 | value?: string | null; 3 | zIndex?: number; 4 | theme?: "dark" | "light"; 5 | height?: number; 6 | width?: number; 7 | colors?: Array; 8 | btn?: boolean; 9 | } 10 | 11 | declare module 'v3-color-picker' { 12 | export const V3ColorPicker: import('vue').DefineComponent; 15 | 16 | export const colorEvent: (event: MouseEvent, options: colorType & { 17 | change?: (v: string) => void; 18 | }) => { 19 | close: () => void 20 | }; 21 | 22 | export const directive: import('vue').Directive; 23 | 24 | const install: (app: import('vue').App, options: { 25 | name: string 26 | }) => unknown; 27 | export default install; 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 xfy520 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/draggable.js: -------------------------------------------------------------------------------- 1 | let isDragging = false; 2 | 3 | function on(element, event, handler) { 4 | if (element && event && handler) { 5 | element?.addEventListener(event, handler, false) 6 | } 7 | } 8 | 9 | function off(element, event, handler) { 10 | if (element && event && handler) { 11 | element?.removeEventListener(event, handler, false) 12 | } 13 | } 14 | 15 | export default function (element, options) { 16 | const moveFn = function (event) { 17 | options.drag?.(event) 18 | } 19 | 20 | const upFn = function (event) { 21 | off(globalThis.document, "mousemove", moveFn); 22 | off(globalThis.document, "mouseup", upFn); 23 | globalThis.document.onselectstart = null; 24 | globalThis.document.ondragstart = null; 25 | isDragging = false; 26 | options.end?.(event); 27 | } 28 | 29 | on(element, "mousedown", function (event) { 30 | if (isDragging) { 31 | return; 32 | } 33 | globalThis.document.onselectstart = () => false; 34 | globalThis.document.ondragstart = () => false; 35 | on(globalThis.document, "mousemove", moveFn); 36 | on(globalThis.document, "mouseup", upFn); 37 | isDragging = true; 38 | options.start?.(event); 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v3-color-picker", 3 | "version": "2.0.0", 4 | "author": "xufangyi", 5 | "private": false, 6 | "description": "Vue3.0 颜色选择器", 7 | "keywords": [ 8 | "vue", 9 | "vue3", 10 | "color", 11 | "vue3-color", 12 | "color-picker", 13 | "color picker", 14 | "vue3-color-picker", 15 | "vue3 color picker" 16 | ], 17 | "main": "dist/v3-color-picker.js", 18 | "module": "dist/v3-color-picker.es.js", 19 | "types": "./index.d.ts", 20 | "files": [ 21 | "package.json", 22 | "README.md", 23 | ".gitignore", 24 | "LICENSE", 25 | "dist/v3-color-picker.es.js", 26 | "dist/v3-color-picker.es.min.js", 27 | "dist/v3-color-picker.js", 28 | "dist/v3-color-picker.min.js", 29 | "src", 30 | "index.d.ts" 31 | ], 32 | "license": "MIT", 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/xfy520/v3-color-picker.git" 36 | }, 37 | "scripts": { 38 | "build": "rollup -c -w" 39 | }, 40 | "dependencies": {}, 41 | "devDependencies": { 42 | "@babel/core": "^7.15.5", 43 | "@vue/compiler-sfc": "3.0.0", 44 | "rollup": "^2.57.0", 45 | "rollup-plugin-babel": "^4.4.0", 46 | "rollup-plugin-clear": "^2.0.7", 47 | "rollup-plugin-commonjs": "^10.1.0", 48 | "rollup-plugin-json": "^4.0.0", 49 | "rollup-plugin-node-resolve": "^5.2.0", 50 | "rollup-plugin-postcss": "3.1.8", 51 | "rollup-plugin-terser": "^7.0.2", 52 | "rollup-plugin-uglify": "^6.0.4", 53 | "rollup-plugin-vue": "^6.0.0", 54 | "vue": "3.0.0" 55 | } 56 | } -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import resolve from 'rollup-plugin-node-resolve' 3 | import commonjs from 'rollup-plugin-commonjs' 4 | import pluginVue from 'rollup-plugin-vue' 5 | import postcss from 'rollup-plugin-postcss' 6 | import { terser } from 'rollup-plugin-terser' 7 | import { uglify } from 'rollup-plugin-uglify' 8 | import clear from 'rollup-plugin-clear' 9 | 10 | module.exports = { 11 | input: path.resolve(__dirname, './src/index.js'), 12 | output: [ 13 | { 14 | file: path.resolve(__dirname, './dist/v3-color-picker.js'), 15 | format: 'umd', 16 | name: 'V3ColorPicker', 17 | globals: { 18 | vue: 'Vue' 19 | } 20 | }, 21 | { 22 | file: path.resolve(__dirname, './dist/v3-color-picker.min.js'), 23 | format: 'umd', 24 | name: 'V3ColorPicker', 25 | globals: { 26 | vue: 'Vue' 27 | }, 28 | plugins: [ 29 | uglify(), 30 | terser() 31 | ] 32 | }, 33 | { 34 | file: path.resolve(__dirname, './dist/v3-color-picker.es.js'), 35 | format: 'es', 36 | globals: { 37 | vue: 'Vue' 38 | } 39 | }, 40 | { 41 | file: path.resolve(__dirname, './dist/v3-color-picker.es.min.js'), 42 | format: 'es', 43 | globals: { 44 | vue: 'Vue' 45 | }, 46 | plugins: [ 47 | uglify(), 48 | terser() 49 | ] 50 | } 51 | ], 52 | plugins: [ 53 | resolve(), 54 | pluginVue(), 55 | commonjs(), 56 | postcss(), 57 | clear({ 58 | targets: ['./dist'], 59 | watch: true, 60 | }) 61 | ], 62 | external: [ 63 | 'vue' 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /src/component/ColorList.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 54 | 55 | 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | yarn.lock 19 | dist 20 | 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | *.lcov 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (https://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # TypeScript v1 declaration files 48 | typings/ 49 | 50 | # TypeScript cache 51 | *.tsbuildinfo 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Microbundle cache 60 | .rpt2_cache/ 61 | .rts2_cache_cjs/ 62 | .rts2_cache_es/ 63 | .rts2_cache_umd/ 64 | 65 | # Optional REPL history 66 | .node_repl_history 67 | 68 | # Output of 'npm pack' 69 | *.tgz 70 | 71 | # Yarn Integrity file 72 | .yarn-integrity 73 | 74 | # dotenv environment variables file 75 | .env 76 | .env.test 77 | 78 | # parcel-bundler cache (https://parceljs.org/) 79 | .cache 80 | 81 | # Next.js build output 82 | .next 83 | 84 | # Nuxt.js build / generate output 85 | .nuxt 86 | dist 87 | 88 | # Gatsby files 89 | .cache/ 90 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 91 | # https://nextjs.org/blog/next-9-1#public-directory-support 92 | # public 93 | 94 | # vuepress build output 95 | .vuepress/dist 96 | 97 | # Serverless directories 98 | .serverless/ 99 | 100 | # FuseBox cache 101 | .fusebox/ 102 | 103 | # DynamoDB Local files 104 | .dynamodb/ 105 | 106 | # TernJS port file 107 | .tern-port 108 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { createVNode, render, watch, createApp } from 'vue'; 2 | 3 | import V3ColorPicker from "./V3ColorPicker.vue"; 4 | import ColorPicker from "./component/ColorPicker.vue"; 5 | 6 | function _colorEvent(options, event) { 7 | const temp = options || {}; 8 | const change = temp.change && typeof temp.change === "function" ? temp.change : (_v) => { }; 9 | const props = { 10 | position: { 11 | x: event.clientX, 12 | y: event.clientY 13 | }, 14 | ...temp, 15 | value: temp.value || "#fff", 16 | confirm: (v) => { 17 | change(v); 18 | options.color = v; 19 | close(); 20 | }, 21 | clear: () => { 22 | change(null); 23 | options.color = null; 24 | close(); 25 | }, 26 | } 27 | const container = document.createElement("div"); 28 | const vNode = createVNode(ColorPicker, props); 29 | render(vNode, container); 30 | if (container.firstElementChild) { 31 | document.body.appendChild(container.firstElementChild); 32 | } 33 | watch(options, (opts) => { 34 | if (opts.value) { 35 | vNode.component.props.value = opts.value; 36 | } 37 | }) 38 | if (!temp.change) { 39 | watch(vNode.component.ctx.color, (color) => { 40 | options.value = color.v; 41 | }) 42 | } 43 | function close() { 44 | vNode && vNode.el && document.body.removeChild(vNode.el); 45 | globalThis.document.removeEventListener("click", close); 46 | globalThis.document.removeEventListener("contextmenu", close); 47 | } 48 | setTimeout(() => { 49 | globalThis.document.addEventListener("click", close); 50 | globalThis.document.addEventListener("contextmenu", close); 51 | }, 0); 52 | return { 53 | close 54 | } 55 | } 56 | 57 | const directive = { 58 | mounted(el, { value, instance }) { 59 | el.addEventListener("click", _colorEvent.bind(instance, value)); 60 | }, 61 | unmounted(el) { 62 | el.removeEventListener("click", _colorEvent); 63 | } 64 | } 65 | 66 | const colorEvent = (event, options) => _colorEvent(options, event); 67 | 68 | const install = function (app) { 69 | app.component(V3ColorPicker.name, V3ColorPicker); 70 | app.directive("color", directive); 71 | app.config.globalProperties.colorEvent = (event, options) => _colorEvent(options, event); 72 | } 73 | 74 | 75 | export default function (app) { 76 | app.use(install) 77 | } 78 | 79 | export { 80 | V3ColorPicker, 81 | directive, 82 | colorEvent 83 | } 84 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Vue3 颜色选择器例子 9 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 51 |
52 | 53 |
54 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/component/ColorStraw.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 36 | 37 | 59 | -------------------------------------------------------------------------------- /src/component/ColorPreview.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 72 | 73 | 103 | -------------------------------------------------------------------------------- /src/component/ColorHue.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 100 | 101 | 137 | -------------------------------------------------------------------------------- /src/component/ColorPanel.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 101 | 102 | 139 | 140 | -------------------------------------------------------------------------------- /src/component/ColorAlpha.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 116 | 117 | 144 | 145 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # v3-color-picker 2 | 3 | Vue3.0 原生颜色选择器,与浏览器自带几乎一样,支持 Vite2.0,[npm地址](https://www.npmjs.com/package/v3-color-picker) 4 | 5 | ![演示](./example/v3-color-picker.png) 6 | 7 | ## [在线文档](https://doc.wssio.com/opensource/v3-color-picker/) 8 | 9 | ## 安装 10 | 11 | ### NPM 12 | 13 | ```shell 14 | npm install v3-color-picker 15 | ``` 16 | 17 | 或 18 | 19 | ```shell 20 | yarn add v3-color-picker 21 | ``` 22 | 23 | ### CDN 24 | 25 | ```html 26 | 74 | 75 | 81 | ``` 82 | 83 | ```css 84 | 85 | ``` 86 | 87 | ### 指令方式使用 88 | 89 | ```html 90 | 93 | 111 | 117 | ``` 118 | 119 | ### 方法方式使用 120 | 121 | ```html 122 | 125 | 145 | ``` 146 | 147 | ### 组件方式使用 148 | 149 | ```html 150 | 156 | 171 | ``` 172 | 173 | ## 参数说明 174 | 175 | ### 方法使用参数 176 | 177 | colorEvent(event, options),`event`:事件对象,`options`:公共参数与方法参数 178 | 179 | ### 公共参数 180 | 181 | | 属性 | 描述 | 类型 | 是否必填 | 默认值 | 182 | | :----: | :----------------: | :------------------------: | :------: | :-----: | 183 | | value | 初始颜色值 | `string` \| `rbg` \| `hsl` | `false` | `#fff` | 184 | | theme | 主题 | `dark` \| `light` | `false` | `dark` | 185 | | height | 颜色选择器区域高度 | `number` | `false` | `150` | 186 | | width | 颜色选择器区域宽度 | `number` | `false` | `233` | 187 | | colors | 预选颜色列表 | `[string]` | `false` | `[]` | 188 | | btn | 是否显示按钮按钮 | `boolean` | `false` | `false` | 189 | | zIndex | 菜单层级 | `number` | `false` | `2` | 190 | 191 | ### 组件参数 192 | 193 | | 属性 | 描述 | 类型 | 是否必填 | 默认值 | 194 | | :---: | :------: | :------------------------------------------: | :------: | :---------: | 195 | | size | 组件大小 | `undefined` \| `medium` \| `small` \| `mini` | `false` | `undefined` | 196 | 197 | ### 指令、方法参数 198 | 199 | | 属性 | 描述 | 类型 | 是否必填 | 默认值 | 200 | | :----: | :----------------------: | :--------: | :------: | :----: | 201 | | change | 颜色值发生改变时触发事件 | `Function` | `false` | `none` | 202 | -------------------------------------------------------------------------------- /src/component/ColorPicker.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 169 | 170 | 211 | -------------------------------------------------------------------------------- /src/V3ColorPicker.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 165 | 166 | 267 | -------------------------------------------------------------------------------- /src/component/ColorValue.vue: -------------------------------------------------------------------------------- 1 | 81 | 82 | 183 | 184 | 233 | -------------------------------------------------------------------------------- /src/color.js: -------------------------------------------------------------------------------- 1 | export default class Color { 2 | _h = 0; 3 | _s = 100; 4 | _v = 100; 5 | _a = 100; 6 | _f = "hex"; 7 | v = "#fff"; 8 | s = false; 9 | 10 | constructor (color) { 11 | if (color) { 12 | this._f = checkIsColor(wordToRgb(color)); 13 | this.format(color); 14 | } 15 | } 16 | 17 | format(color, _f) { 18 | if (!color) { 19 | this._h = 0 20 | this._s = 100 21 | this._v = 100 22 | this.update(); 23 | } else { 24 | const fromHsv = (h, s, v) => { 25 | this._h = Math.max(0, Math.min(360, h)); 26 | this._s = Math.max(0, Math.min(100, s)); 27 | this._v = Math.max(0, Math.min(100, v)); 28 | this.update(); 29 | } 30 | switch (checkIsColor(wordToRgb(color))) { 31 | case "hex": 32 | this._hex(color, fromHsv); 33 | break; 34 | case "rgb": 35 | this._rgba(color, fromHsv); 36 | break; 37 | case "hsl": 38 | this._hsla(color, fromHsv); 39 | break; 40 | default: 41 | throw Error(`非法颜色值--->${color}`); 42 | } 43 | } 44 | } 45 | 46 | _hsla(hsla, fromHsv) { 47 | hsla = strToArry(hsla, /hsla|hsl|\(|\)/gm); 48 | if (hsla.length === 4) { 49 | this._a = Math.floor(parseFloat(hsla[3]) * 100); 50 | } else if (hsla.length === 3) { 51 | this._a = 100; 52 | } 53 | if (hsla.length >= 3) { 54 | const { h, s, v } = hslToHsv(hsla[0], hsla[1], hsla[2]); 55 | fromHsv(h, s, v); 56 | } 57 | } 58 | 59 | _rgba(rgba, fromHsv) { 60 | rgba = strToArry(rgba, /rgba|rgb|\(|\)/gm); 61 | if (rgba.length === 4) { 62 | this._a = Math.floor(parseFloat(rgba[3]) * 100); 63 | } else if (rgba.length === 3) { 64 | this._a = 100; 65 | } 66 | if (rgba.length >= 3) { 67 | const { h, s, v } = rgbToHsv(rgba[0], rgba[1], rgba[2]); 68 | fromHsv(h, s, v); 69 | } 70 | } 71 | 72 | _hex(hex, fromHsv) { 73 | hex = hex.replace("#", "").trim(); 74 | let { r, g, b } = hexToRgb(hex); 75 | if (hex.length === 8) { 76 | this._a = Math.floor(parseInt(hex.substring(6), 16) / 255 * 100); 77 | } else if (hex.length === 3 || hex.length === 6) { 78 | this._a = 100; 79 | } 80 | const { h, s, v } = rgbToHsv(r, g, b); 81 | fromHsv(h, s, v); 82 | } 83 | 84 | set(k, v) { 85 | this[`_${k}`] = v; 86 | this.update(); 87 | } 88 | 89 | get(k) { 90 | return this[`_${k}`]; 91 | } 92 | 93 | update() { 94 | const { _h, _s, _v, _a, _f } = this; 95 | switch (_f) { 96 | case "rgb": { 97 | const { r, g, b } = hsvToRgb(_h, _s, _v); 98 | this.v = _a === 100 ? `rgb(${r}, ${g}, ${b})` : `rgba(${r}, ${g}, ${b}, ${_a / 100})`; 99 | break; 100 | } 101 | case "hsl": { 102 | const hsl = hsvToHsl(_h, _s / 100, _v / 100); 103 | this.v = _a === 100 ? `hsl(${Math.round(_h)}, ${Math.round(hsl[0] * 100)}%, ${Math.round(hsl[1] * 100)}%)` : 104 | `hsla(${Math.round(_h)}, ${Math.round(hsl[0] * 100)}%, ${Math.round(hsl[1] * 100)}%, ${_a / 100})`; 105 | break; 106 | } 107 | default: { 108 | const hex = rgbToHex(hsvToRgb(_h, _s, _v)); 109 | const alpha = Math.round(this._a / 100 * 255); 110 | this.v = _a === 100 ? hex : `${hex}${alpha <= 16 ? "0" : ""}${alpha.toString(16)}`; 111 | break; 112 | } 113 | } 114 | } 115 | 116 | get hsla() { 117 | const { _h, _s, _v, _a } = this; 118 | const hsl = hsvToHsl(_h, _s / 100, _v / 100); 119 | return { 120 | h: Math.round(_h), s: Math.round(hsl[0] * 100), l: Math.round(hsl[1] * 100), a: _a / 100 121 | } 122 | } 123 | 124 | get rgba() { 125 | const { _h, _s, _v, _a } = this; 126 | const { r, g, b } = hsvToRgb(_h, _s, _v); 127 | return { 128 | r, g, b, a: _a / 100 129 | }; 130 | } 131 | 132 | get hex() { 133 | const { _h, _s, _v, _a } = this; 134 | const hex = rgbToHex(hsvToRgb(_h, _s, _v)); 135 | const alpha = Math.round(this._a / 100 * 255); 136 | return _a === 100 ? hex : `${hex}${alpha <= 16 ? "0" : ""}${alpha.toString(16)}`; 137 | } 138 | } 139 | 140 | function strToArry(color, reg) { 141 | return color 142 | .replace(reg, "") 143 | .split(/\s|,/g) 144 | .filter((v) => v !== "") 145 | .map((v, i) => (i > 2 ? parseFloat(v) : parseInt(v, 10))) 146 | } 147 | 148 | const hsvToHsl = function (h, s, v) { 149 | return [(s * v) / ((h = (2 - s) * v) < 1 ? h : 2 - h) || 0, h / 2]; 150 | } 151 | 152 | function hslToHsv(h, s, l) { 153 | s = s / 100; 154 | l = l / 100; 155 | let smin = s; 156 | const lmin = Math.max(l, 0.01); 157 | l *= 2; 158 | s *= l <= 1 ? l : 2 - l 159 | smin *= lmin <= 1 ? lmin : 2 - lmin; 160 | const v = (l + s) / 2; 161 | const sv = s === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); 162 | return { 163 | h, 164 | s: sv * 100, 165 | v: v * 100, 166 | } 167 | } 168 | 169 | function hsvToRgb(h, s, v) { 170 | h = limit(h, 360) * 6; 171 | s = limit(s, 100); 172 | v = limit(v, 100); 173 | const i = Math.floor(h); 174 | const f = h - i; 175 | const p = v * (1 - s); 176 | const q = v * (1 - f * s); 177 | const t = v * (1 - (1 - f) * s); 178 | const mod = i % 6; 179 | const r = [v, q, p, p, t, v][mod]; 180 | const g = [t, v, v, q, p, p][mod]; 181 | const b = [p, p, t, v, v, q][mod]; 182 | return { 183 | r: Math.round(r * 255), 184 | g: Math.round(g * 255), 185 | b: Math.round(b * 255), 186 | } 187 | } 188 | 189 | function rgbToHsv(r, g, b) { 190 | r = limit(r, 255); 191 | g = limit(g, 255); 192 | b = limit(b, 255); 193 | const v = Math.max(r, g, b); 194 | let h, s; 195 | const diff = v - Math.min(r, g, b); 196 | if (diff === 0) { 197 | h = s = 0; 198 | } else { 199 | s = diff / v; 200 | const diffc = function (c) { 201 | return (v - c) / 6 / diff + 1 / 2; 202 | }; 203 | switch (v) { 204 | case r: { 205 | h = diffc(b) - diffc(g); 206 | break 207 | } 208 | case g: { 209 | h = (1 / 3) + diffc(r) - diffc(b); 210 | break 211 | } 212 | case b: { 213 | h = (2 / 3) + diffc(g) - diffc(r); 214 | break 215 | } 216 | } 217 | h < 0 ? h += 1 : h > 1 ? h -= 1 : h; 218 | } 219 | return { h: h * 360, s: s * 100, v: v * 100 } 220 | } 221 | 222 | function limit(v, max) { 223 | v = typeof v === "string" && v.indexOf('.') !== -1 && parseFloat(v) === 1 ? "100%" : v; 224 | v = Math.min(max, Math.max(0, parseFloat(`${v}`))) 225 | v = typeof n === "string" && n.indexOf('%') !== -1 ? parseInt(`${v * (max)}`, 10) / 100 : v; 226 | return Math.abs(v - (max)) < 0.000001 ? 1 : (v % (max)) / parseFloat(max); 227 | } 228 | 229 | function hexToRgb(hex) { 230 | if (!/^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{6}$|^[0-9a-fA-F]{8}$/.test(hex)) { 231 | throw Error(`非法颜色值--->${hex}`); 232 | } 233 | let r, g, b; 234 | if (hex.length === 3) { 235 | r = parseInt(hex[0] + hex[0], 16); 236 | g = parseInt(hex[1] + hex[1], 16); 237 | b = parseInt(hex[2] + hex[2], 16); 238 | } else if (hex.length === 6 || hex.length === 8) { 239 | r = parseInt(hex.substring(0, 2), 16); 240 | g = parseInt(hex.substring(2, 4), 16); 241 | b = parseInt(hex.substring(4, 6), 16); 242 | } 243 | return { 244 | r, g, b 245 | }; 246 | } 247 | 248 | function rgbToHex({ r, g, b }) { 249 | function hex(x) { 250 | return ("0" + parseInt(x).toString(16)).slice(-2); 251 | } 252 | return (isNaN(r) || isNaN(g) || isNaN(b)) ? "" : `#${hex(r)}${hex(g)}${hex(b)}`.toUpperCase(); 253 | } 254 | 255 | function checkIsColor(color) { 256 | if (/^#/.test(color)) { 257 | return "hex"; 258 | } else if (/^rgb\(/.test(color) || /^rgba\(/.test(color)) { 259 | return "rgb"; 260 | } else if (/^hsl\(/.test(color) || /^hsla\(/.test(color)) { 261 | return "hsl"; 262 | } 263 | throw Error(`错误的颜色参数--->${color}`); 264 | } 265 | 266 | function wordToRgb(color) { 267 | const colors = { 268 | red: "rgba(255, 0, 0, 1)", 269 | green: "rgba(0, 128, 0, 1)", 270 | blue: "rgba(0, 0, 255, 1)", 271 | magenta: "rgba(255, 0, 255, 1)", 272 | yellow: "rgba(255, 255, 0, 1)", 273 | chocolate: "rgba(210, 105, 30, 1)", 274 | black: "rgba(0, 0, 0, 1)", 275 | aquamarine: "rgba(127, 255, 212, 1)", 276 | lime: "rgba(0, 255, 0, 1)", 277 | fuchsia: "rgba(255, 0, 255, 1)", 278 | brass: "rgba(176, 160, 0, 1)", 279 | azure: "rgba(240, 255, 255, 1)", 280 | brown: "rgba(255, 127, 80, 1)", 281 | bronze: "rgba(254, 208, 160, 1)", 282 | deeppink: "rgba(255, 20, 147, 1)", 283 | aliceblue: "rgba(240, 248, 255, 1)", 284 | gray: "rgba(128, 128, 128, 1)", 285 | copper: "rgba(192, 0, 224, 1)", 286 | coral: "rgba(255, 127, 80, 1)", 287 | feldspar: "rgba(254, 208, 160, 1)", 288 | orange: "rgba(255, 165, 0, 1)", 289 | orchid: "rgba(255, 165, 0, 1)", 290 | pink: "rgba(255, 165, 0, 1)", 291 | plum: "rgba(221, 160, 221, 1)", 292 | quartz: "rgba(0, 160, 0, 1)", 293 | purple: "rgba(128, 0, 128, 1)", 294 | aliceblue: "rgba(240, 248, 255, 1)", 295 | antiquewith: "rgba(160, 0, 0, 1)", 296 | blanchedalmond: "rgba(255, 235, 205, 1)", 297 | blueviolet: "rgba(138, 43, 226, 1)", 298 | beige: "rgba(245, 245, 220, 1)", 299 | burlywood: "rgba(222, 184, 135, 1)", 300 | bisque: "rgba(255, 228, 196, 1)", 301 | cadetblue: "rgba(95, 158, 160, 1)", 302 | pink: "rgba(255, 192, 203, 1)", 303 | saddlebrown: "rgba(139, 69, 19, 1)", 304 | royalblue: "rgba(65, 105, 225, 1)", 305 | rosybrown: "rgba(188, 143, 143, 1)", 306 | purple: "rgba(128, 0, 128, 1)", 307 | orengered: "rgba(14, 14, 237, 1)", 308 | olivedrab: "rgba(107, 142, 35, 1)", 309 | powderblue: "rgba(176, 224, 230, 1)", 310 | peachpuff: "rgba(255, 218, 185, 1)", 311 | papayawhip: "rgba(255, 239, 213, 1)", 312 | paleturquoise: "rgba(175, 238, 238, 1)", 313 | palevioletred: "rgba(219, 112, 147, 1)", 314 | palegreen: "rgba(152, 251, 152, 1)", 315 | navyblue: "rgba(160, 176, 224, 1)", 316 | navajowhite: "rgba(255, 222, 173, 1)", 317 | palegodenrod: "rgba(160, 13, 0, 1)", 318 | violetred: "rgba(0, 224, 237, 1)", 319 | yellowgreen: "rgba(154, 205, 50, 1)", 320 | tomato: "rgba(255, 99, 71, 1)", 321 | turquoise: "rgba(64, 224, 208, 1)", 322 | thistle: "rgba(216, 191, 216, 1)", 323 | springgreen: "rgba(0, 255, 127, 1)", 324 | steelblue: "rgba(70, 130, 180, 1)", 325 | salmon: "rgba(250, 128, 114, 1)", 326 | scarlet: "rgba(202, 14, 0, 1)", 327 | sienna: "rgba(160, 82, 45, 1)", 328 | silver: "rgba(192, 192, 192, 1)", 329 | tan: "rgba(210, 180, 140, 1)", 330 | thistle: "rgba(216, 191, 216, 1)", 331 | turquoise: "rgba(64, 224, 208, 1)", 332 | violet: "rgba(238, 130, 238, 1)", 333 | snow: "rgba(255, 250, 250, 1)", 334 | salmon: "rgba(250, 128, 114, 1)", 335 | scarlet: "rgba(202, 14, 0, 1)", 336 | sienna: "rgba(160, 82, 45, 1)", 337 | silver: "rgba(192, 192, 192, 1)", 338 | thistle: "rgba(216, 191, 216, 1)", 339 | turquoise: "rgba(64, 224, 208, 1)", 340 | violet: "rgba(238, 130, 238, 1)", 341 | chartreuse: "rgba(127, 255, 0, 1)", 342 | firebrick: "rgba(178, 34, 34, 1)", 343 | gold: "rgba(255, 215, 0, 1)", 344 | khaki: "rgba(240, 230, 140, 1)", 345 | mediumslateblue: "rgba(123, 104, 238, 1)", 346 | mediumvioletred: "rgba(199, 21, 133, 1)", 347 | oldlace: "rgba(253, 245, 230, 1)", 348 | maroom: "rgba(10, 0, 0, 1)", 349 | goldenrod: "rgba(218, 165, 32, 1)", 350 | wheat: "rgba(245, 222, 179, 1)", 351 | whitesmoke: "rgba(245, 245, 245, 1)", 352 | orange: "rgba(255, 165, 0, 1)", 353 | moccasin: "rgba(255, 228, 181, 1)", 354 | mistyrose: "rgba(255, 228, 225, 1)", 355 | mintcream: "rgba(245, 255, 250, 1)", 356 | midnightblue: "rgba(25, 25, 112, 1)", 357 | dimgray: "rgba(105, 105, 105, 1)", 358 | darksalmon: "rgba(233, 150, 122, 1)", 359 | slategray: "rgba(112, 128, 144, 1)", 360 | skyblue: "rgba(135, 206, 235, 1)", 361 | sienna: "rgba(160, 82, 45, 1)", 362 | seashell: "rgba(255, 245, 238, 1)", 363 | salmon: "rgba(250, 128, 114, 1)", 364 | seagreen: "rgba(46, 139, 87, 1)", 365 | sandybrown: "rgba(244, 164, 96, 1)", 366 | firebrick: "rgba(178, 34, 34, 1)", 367 | gold: "rgba(255, 215, 0, 1)", 368 | khaki: "rgba(240, 230, 140, 1)", 369 | maroom: "rgba(10, 0, 0, 1)", 370 | goldenrod: "rgba(218, 165, 32, 1)", 371 | wheat: "rgba(245, 222, 179, 1)", 372 | whitesmoke: "rgba(245, 245, 245, 1)", 373 | mediumturquoise: "rgba(72, 209, 204, 1)", 374 | navy: "rgba(0, 0, 128, 1)", 375 | mediumspringgreen: "rgba(0, 250, 154, 1)", 376 | mediumseagreen: "rgba(60, 179, 113, 1)", 377 | mediumpurpul: "rgba(237, 0, 0, 1)", 378 | peru: "rgba(205, 133, 63, 1)", 379 | mediumorchid: "rgba(186, 85, 211, 1)", 380 | mediumblue: "rgba(0, 0, 205, 1)", 381 | mediumaquamarine: "rgba(102, 205, 170, 1)", 382 | maroon: "rgba(128, 0, 0, 1)", 383 | limegreen: "rgba(50, 205, 50, 1)", 384 | lightyellow: "rgba(255, 255, 224, 1)", 385 | lightsteelblue: "rgba(176, 196, 222, 1)", 386 | magenta: "rgba(255, 0, 255, 1)", 387 | lightslateblue: "rgba(0, 0, 176, 1)", 388 | lightslategray: "rgba(119, 136, 153, 1)", 389 | lightskyblue: "rgba(135, 206, 250, 1)", 390 | inen: "rgba(0, 224, 0, 1)", 391 | lightseagreen: "rgba(32, 178, 170, 1)", 392 | lightsalmon: "rgba(255, 160, 122, 1)", 393 | lightpink: "rgba(255, 182, 193, 1)", 394 | plum: "rgba(221, 160, 221, 1)", 395 | lightgray: "rgba(0, 0, 160, 1)", 396 | lightgreen: "rgba(144, 238, 144, 1)", 397 | lightgodenrodyellow: "rgba(0, 222, 224, 1)", 398 | indianred: "rgba(205, 92, 92, 1)", 399 | lavender: "rgba(230, 230, 250, 1)", 400 | lightblue: "rgba(173, 216, 230, 1)", 401 | lavenderblush: "rgba(255, 240, 245, 1)", 402 | lightcoral: "rgba(240, 128, 128, 1)", 403 | lightcyan: "rgba(224, 255, 255, 1)", 404 | lightgodenrod: "rgba(0, 222, 208, 1)", 405 | hotpink: "rgba(255, 105, 180, 1)", 406 | greenyellow: "rgba(173, 255, 47, 1)", 407 | lemonchiffon: "rgba(255, 250, 205, 1)", 408 | lawngreen: "rgba(124, 252, 0, 1)", 409 | khaki: "rgba(240, 230, 140, 1)", 410 | deepskyblue: "rgba(0, 191, 255, 1)", 411 | honeydew: "rgba(240, 255, 240, 1)", 412 | golenrod: "rgba(0, 224, 13, 1)", 413 | forestgreen: "rgba(34, 139, 34, 1)", 414 | gostwhite: "rgba(0, 0, 14, 1)", 415 | greenyellow: "rgba(173, 255, 47, 1)", 416 | gainsboro: "rgba(220, 220, 220, 1)", 417 | firebrick: "rgba(178, 34, 34, 1)", 418 | dodgerblue: "rgba(30, 144, 255, 1)", 419 | darkturquoise: "rgba(0, 206, 209, 1)", 420 | darkslateblue: "rgba(72, 61, 139, 1)", 421 | darkslategray: "rgba(47, 79, 79, 1)", 422 | darkseagreen: "rgba(143, 188, 143, 1)", 423 | darkred: "rgba(139, 0, 0, 1)", 424 | darkorchid: "rgba(153, 50, 204, 1)", 425 | darkorenge: "rgba(218, 0, 14, 1)", 426 | darkslateblue: "rgba(72, 61, 139, 1)", 427 | darkviolet: "rgba(148, 0, 211, 1)", 428 | floralwhite: "rgba(255, 250, 240, 1)", 429 | cyan: "rgba(0, 255, 255, 1)", 430 | 'bisque darkgray': "rgba(255, 228, 196, 1)", 431 | cornsilk: "rgba(255, 248, 220, 1)", 432 | darkolivegreen: "rgba(255, 248, 220, 1)", 433 | darkgoldenrod: "rgba(184, 134, 11, 1)", 434 | darkblue: "rgba(0, 0, 139, 1)", 435 | darkcyan: "rgba(0, 139, 139, 1)", 436 | darkgreen: "rgba(0, 100, 0, 1)", 437 | darkhaki: "rgba(218, 0, 0, 1)", 438 | ivory: "rgba(255, 255, 240, 1)", 439 | darkmagenta: "rgba(139, 0, 139, 1)", 440 | darkgray: "rgba(169, 169, 169, 1)", 441 | cornfloewrblue: "rgba(192, 0, 176, 1)", 442 | cornfloewrblue: "rgba(192, 0, 176, 1)", 443 | darkviolet: "rgba(148, 0, 211, 1)", 444 | floralwhite: "rgba(255, 250, 240, 1)", 445 | darkslategray: "rgba(47, 79, 79, 1)", 446 | darkseagreen: "rgba(143, 188, 143, 1)", 447 | darkred: "rgba(139, 0, 0, 1)", 448 | darkorchid: "rgba(153, 50, 204, 1)", 449 | darkorenge: "rgba(218, 0, 14, 1)", 450 | darkslateblue: "rgba(72, 61, 139, 1)", 451 | } 452 | if (colors[color]) { 453 | color = colors[color]; 454 | } 455 | return color; 456 | } 457 | --------------------------------------------------------------------------------