├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── docs
└── CN.md
├── package-lock.json
├── package.json
├── public
└── index.html
└── src
├── App.css
├── App.js
├── App.test.js
├── components
└── input.js
├── index.css
├── index.js
└── utils
├── antd-color-palette.js
├── avg-color.js
├── brightness.js
├── gradientor.js
├── hex-to-rgb.js
├── hsv-to-rgb.js
├── index.js
├── rgb-to-hex.js
├── rgb-to-hsv.js
└── tint-shade.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 renjie1996
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 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: help
2 | default: help
3 |
4 | .PHONY: install
5 | install:
6 | @npm install
7 |
8 | .PHONY: dev
9 | dev:
10 | @npm run start
11 |
12 | .PHONY: test
13 | test:
14 | @npm run test
15 |
16 | help:
17 | @echo " \033[35mmake\033[0m \033[1m命令使用说明\033[0m"
18 | @echo " \033[35mmake install\033[0m\t\033[0m\t--- 安装依赖"
19 | @echo " \033[35mmake dev\033[0m\t\033[0m\t--- 开发模式"
20 | @echo " \033[35mmake test\033[0m\t\033[0m\t--- 运行测试"
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
color-design-helper
2 |
3 |
4 |
5 |
6 |
7 |
8 | **Just a toy, which can help color designers to generate color plates like ant-design.**
9 |
10 | Achieved `average color`、`tint-shade color`、`@antd-v3 color` three models
11 |
12 | [Online address](http://zerolty.com/color-design-helper/)
13 |
14 | [中文文档](https://github.com/renjie1996/color-design-helper/blob/master/docs/CN.md)
15 |
16 | [Article](https://github.com/renjie1996/Maple-FrontEnd-Blog/issues/17)
17 |
18 | ### average color
19 | > only average cut two colors
20 |
21 | 
22 |
23 | ### tint-shade color
24 | > like antd v1.0. The main color is mixed with pure white (#fff), and the main color and the pure white are divided into 100 parts, and the positions of 20/40/60/80 are respectively divided to obtain the color of 4/3/2/1;
25 | > The main color is mixed with pure black (#000), and the main color and the pure black are divided into 100 parts, and the positions of 20/40/60/80 are respectively divided to obtain the color of 6/7/8/9;
26 |
27 | 
28 |
29 | ### @antd-v3 color
30 | > Like the current antd, Decrement/increment with the value of the HSV model to get a complete gradient swatch
31 |
32 | 
33 |
34 | # Usage
35 | `Just a static resource running locally`
36 |
37 | > [antd-refs: https://ant.design/docs/spec/colors-cn](https://ant.design/docs/spec/colors-cn)
38 |
39 | 
40 |
41 |
42 | > run by MakeFile
43 |
44 | - `make install`
45 | - `make dev`
46 | - `make test`
47 | > you also can run by these ways
48 |
49 | - `npm run start`
50 | - `npm run build`
51 | - `npm run test`
52 |
53 | # License
54 |
55 | MIT
56 |
57 | Copyright (c) 2019 @renjie1996 @Shadowless @无影er
58 |
--------------------------------------------------------------------------------
/docs/CN.md:
--------------------------------------------------------------------------------
1 | # color-design-helper
2 |
3 | **一个帮助设计师的调色板的小玩具**
4 |
5 | [线上地址](https://renjie1996.github.io)
6 |
7 | 完成了 `average color`、`tint-shade color`、`@antd-v3 color` 三种模式
8 |
9 | ### average color
10 | > 仅仅平均切割两种颜色
11 |
12 | 
13 |
14 | ### tint-shade color
15 | > andv第一版. 将主色与纯白色(#fff)混合,主色与纯白色之间分成 100 份, 20/40/60/80 的位置分别分割,得到 4/3/2/1 号色;
16 | > 将主色与纯黑色(#000)混合,主色与纯黑色之间分成 100 份, 20/40/60/80 的位置分别分割,得到 6/7/8/9 号色。
17 |
18 | 
19 |
20 | ### @antd-v3 color
21 | > 目前antd的色彩生成版本,用 HSV 模型的值进行递减/递增得到完整渐变色板
22 |
23 | 
24 |
25 | # Usage
26 | `仅仅是运行在本地的静态资源`
27 |
28 | > 使用MakeFile运行
29 |
30 | - `make install`
31 | - `make dev`
32 | - `make test`
33 |
34 | > 使用npm运行
35 |
36 | - `npm run start`
37 | - `npm run build`
38 | - `npm run test`
39 |
40 |
41 | > [参考antd: https://ant.design/docs/spec/colors-cn](https://ant.design/docs/spec/colors-cn)
42 |
43 | 
44 |
45 | # License
46 |
47 | MIT
48 |
49 | Copyright (c) 2019 @renjie1996 @Shadowless @无影er
50 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "color-design-helper",
3 | "version": "1.0.1",
4 | "private": true,
5 | "dependencies": {
6 | "@ctrl/tinycolor": "^2.2.1",
7 | "react": "16.7.0-alpha.2",
8 | "react-dom": "16.7.0-alpha.2",
9 | "react-scripts": "2.1.3"
10 | },
11 | "scripts": {
12 | "start": "react-scripts start",
13 | "build": "react-scripts build",
14 | "test": "react-scripts test",
15 | "eject": "react-scripts eject"
16 | },
17 | "homepage": "./",
18 | "eslintConfig": {
19 | "extends": "react-app"
20 | },
21 | "browserslist": [
22 | ">0.2%",
23 | "not dead",
24 | "not ie <= 11",
25 | "not op_mini all"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
15 |
16 |
25 | Color-Design-Helper
26 |
27 |
28 | You need to enable JavaScript to run this app.
29 |
30 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | display: flex;
3 | justify-content: space-around;
4 | align-items: center;
5 | }
6 |
7 | .controller {
8 | display: flex;
9 | flex-direction: column;
10 | align-items: center;
11 | min-height: 800px;
12 | }
13 |
14 |
15 | .color-picker {
16 | width: 300px;
17 | display: flex;
18 | background-color: var(--fg);
19 | padding: 6px;
20 | border-radius: 30px;
21 | font-weight: 300;
22 | }
23 |
24 | .range {
25 | height: 380px;
26 | position: relative;
27 | }
28 |
29 | .range p {
30 | font-size: 42px;
31 | color: var(--fg);
32 | position: absolute;
33 | right: -80px;
34 | top: 100px;
35 | }
36 |
37 | .color-picker input {
38 | display: inline-block;
39 | outline: none;
40 | border: none;
41 | box-sizing: border-box;
42 | height: 52px;
43 | width: 52px;
44 | background-color: var(--fg);
45 | border-radius: 30px;
46 | color: var(--fc);
47 | }
48 |
49 | .color-picker input[type="color"]::-webkit-color-swatch {
50 | border: none;
51 | border-radius: 50%;
52 | }
53 |
54 | .color-picker input[type="color"][disabled] {
55 | opacity: .2;
56 | cursor: not-allowed;
57 | }
58 | .color-picker input[type="text"][disabled] {
59 | opacity: .2;
60 | cursor: not-allowed;
61 | }
62 |
63 | .color-picker input[type="text"] {
64 | width: 200px;
65 | padding: 6px 16px;
66 | font-size: 32px;
67 | }
68 |
69 | .controller input[type="range"] {
70 | margin-top: 30px;
71 | width: 320px;
72 | height: 320px;
73 | transform: rotate(-90deg);
74 | }
75 |
76 | .controller input[type="range"]::-webkit-slider-thumb {
77 | appearance: none;
78 | border: none;
79 | border-radius: 30px;
80 | box-shadow: 0 0 0 .3em var(--bg);
81 | background: var(--fg);
82 | transform: scale(1.5);
83 | transition: transform .3s ease-out;
84 | }
85 |
86 | .controller input[type="range"]::-webkit-slider-thumb:focus,
87 | .controller input[type="range"]::-webkit-slider-thumb:active {
88 | appearance: none;
89 | transform: scale(.8);
90 | }
91 |
92 | .color-list {
93 | height: 800px;
94 | }
95 |
96 | .color-list .color-item{
97 | width: 200px;
98 | text-align: center;
99 | list-style: none;
100 | }
101 |
102 | .radios {
103 | display: block;
104 | position: relative;
105 | margin: 40px auto;
106 | height: auto;
107 | padding: 20px;
108 | }
109 |
110 | .radios ul{
111 | margin: 0;
112 | padding: 0;
113 | overflow: auto;
114 | display: flex;
115 | }
116 |
117 | .radios ul li{
118 | color: #AAAAAA;
119 | display: block;
120 | position: relative;
121 | float: left;
122 | width: 100%;
123 | height: 100px;
124 | border-bottom: 1px solid #333;
125 | }
126 |
127 | .radios ul li input[type=radio]{
128 | position: absolute;
129 | visibility: hidden;
130 | }
131 |
132 | .radios ul li label{
133 | display: block;
134 | position: relative;
135 | font-weight: 300;
136 | font-size: 1.35em;
137 | padding: 25px 25px 25px 80px;
138 | margin: 10px auto;
139 | height: 30px;
140 | z-index: 9;
141 | cursor: pointer;
142 | -webkit-transition: all 0.25s linear;
143 | }
144 |
145 | .radios ul li:hover label{
146 | color: var(--fg);
147 | }
148 |
149 | .radios .check{
150 | display: block;
151 | position: absolute;
152 | border: 5px solid #AAAAAA;
153 | border-radius: 100%;
154 | height: 25px;
155 | width: 25px;
156 | top: 30px;
157 | left: 20px;
158 | z-index: 5;
159 | transition: border .25s linear;
160 | -webkit-transition: border .25s linear;
161 | }
162 |
163 | .radios ul li:hover .check {
164 | border: 5px solid var(--fg);
165 | }
166 |
167 | ul li .check::before {
168 | display: block;
169 | position: absolute;
170 | content: '';
171 | border-radius: 100%;
172 | height: 15px;
173 | width: 15px;
174 | top: 5px;
175 | left: 5px;
176 | margin: auto;
177 | transition: background 0.25s linear;
178 | -webkit-transition: background 0.25s linear;
179 | }
180 |
181 | input[type=radio]:checked ~ .check {
182 | border: 5px solid var(--radc);
183 | }
184 |
185 | input[type=radio]:checked ~ .check::before{
186 | background: var(--radc);
187 | }
188 |
189 | input[type=radio]:checked ~ label{
190 | color: var(--radc);
191 | }
192 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import './App.css';
3 | import useInputValue from './components/input';
4 | import ColorHelper from './utils/index';
5 |
6 | const ViEWHEIGHT = 800;
7 | const MAX = 25;
8 | const MIN = 1;
9 |
10 | function App() {
11 | const startInputHook = useInputValue('#ffffff', true);
12 | const endInputHook = useInputValue('#1890ff');
13 | const stepInputHook = useInputValue(1);
14 | const radiosHook = useInputValue('avg');
15 | const [colors, setColors] = useState([]);
16 |
17 | const {value, onChange, disabled} = startInputHook;
18 |
19 | const filterStartInputHook = {
20 | // 过滤掉非原生的props
21 | value,
22 | onChange,
23 | disabled,
24 | };
25 |
26 | const isColor = color => {
27 | return color !== '000000'
28 | && color !== '#000000'
29 | && /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/.test(color);
30 | }
31 |
32 | useEffect(() => {
33 | if(isColor(startInputHook.value) && isColor(endInputHook.value)) {
34 | setColors(ColorHelper.gradientor(startInputHook.value, endInputHook.value, stepInputHook.value, radiosHook.value));
35 | }
36 | }, [startInputHook.value, endInputHook.value, stepInputHook.value, radiosHook.value]);
37 |
38 | useEffect(() => {
39 | if(radiosHook.value !== 'avg') {
40 | startInputHook.setValue('#ffffff');
41 | startInputHook.setDisabled(true);
42 | } else {
43 | startInputHook.setDisabled(false);
44 | }
45 | }, [radiosHook.value])
46 |
47 |
48 | const styles = c => ({
49 | backgroundColor: `rgb(${c})`,
50 | color: ColorHelper.brightness(c),
51 | height: ViEWHEIGHT/colors.length,
52 | lineHeight: `${ViEWHEIGHT/colors.length}px`
53 | })
54 |
55 | const radios = ['avg', 'tintshade', 'antd'];
56 |
57 |
58 | return (
59 |
60 |
91 |
92 | {
93 | colors.map(c => (
94 |
95 | {ColorHelper.toHex(c)}
96 |
97 | ))
98 | }
99 |
100 |
101 | );
102 |
103 | }
104 |
105 | export default App;
106 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render( , div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/components/input.js:
--------------------------------------------------------------------------------
1 | import { useState, useCallback } from "react";
2 |
3 | export default function useInputValue(initiateValue, setOr) {
4 | const [value, setValue] = useState(initiateValue);
5 | const [disabled, setDisabled] = useState(false);
6 | let onChange = useCallback(e => setValue(e.target.value), []);
7 | if(setOr) return { value, onChange, disabled, setDisabled, setValue };
8 | return { value, onChange, disabled };
9 |
10 | }
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | background-color: #000000;
5 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
6 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
7 | sans-serif;
8 | -webkit-font-smoothing: antialiased;
9 | -moz-osx-font-smoothing: grayscale;
10 | }
11 |
12 | code {
13 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
14 | monospace;
15 | }
16 |
17 | :root {
18 | --fg: #fff;
19 | --bg: #000;
20 | --fc: #0b0e0f;
21 | --radc: #0DFF92;
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render( , document.getElementById('root'));
7 |
8 |
--------------------------------------------------------------------------------
/src/utils/antd-color-palette.js:
--------------------------------------------------------------------------------
1 | import { TinyColor } from '@ctrl/tinycolor';
2 |
3 | const hueStep = 2;
4 | const saturationStep = 16;
5 | const saturationStep2 = 5;
6 | const brightnessStep1 = 5;
7 | const brightnessStep2 = 15;
8 | const lightColorCount = 5;
9 | const darkColorCount = 4;
10 |
11 | const getHue = function (hsv, i, isLight) {
12 | let hue;
13 | if (hsv.h >= 60 && hsv.h <= 240) {
14 | hue = isLight ? hsv.h - hueStep * i : hsv.h + hueStep * i;
15 | } else {
16 | hue = isLight ? hsv.h + hueStep * i : hsv.h - hueStep * i;
17 | }
18 | if (hue < 0) {
19 | hue += 360;
20 | } else if (hue >= 360) {
21 | hue -= 360;
22 | }
23 | return Math.round(hue);
24 | };
25 | const getSaturation = function (hsv, i, isLight) {
26 | let saturation;
27 | if (isLight) {
28 | saturation = Math.round(hsv.s * 100) - saturationStep * i;
29 | } else if (i === darkColorCount) {
30 | saturation = Math.round(hsv.s * 100) + saturationStep;
31 | } else {
32 | saturation = Math.round(hsv.s * 100) + saturationStep2 * i;
33 | }
34 | if (saturation > 100) {
35 | saturation = 100;
36 | }
37 | if (isLight && i === lightColorCount && saturation > 10) {
38 | saturation = 10;
39 | }
40 | if (saturation < 6) {
41 | saturation = 6;
42 | }
43 | return Math.round(saturation);
44 | };
45 | const getValue = function (hsv, i, isLight) {
46 | if (isLight) {
47 | return Math.round(hsv.v * 100) + brightnessStep1 * i;
48 | }
49 | return Math.round(hsv.v * 100) - brightnessStep2 * i;
50 | };
51 |
52 | function colorPalette(color, index, num) {
53 | const isLight = index <= num;
54 | const hsv = new TinyColor({ r: color[0], g: color[1], b: color[2] }).toHsv();
55 | const i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1;
56 | const result = {
57 | h: getHue(hsv, i, isLight),
58 | s: getSaturation(hsv, i, isLight),
59 | v: getValue(hsv, i, isLight)
60 | };
61 | const rgb = new TinyColor(result).toRgb();
62 | return [rgb.r, rgb.g, rgb.b];
63 |
64 | };
65 |
66 | function multiDimensionalUnique(arr) {
67 | const uniques = [];
68 | const itemsFound = {};
69 | for(let i = 0, l = arr.length; i < l; i++) {
70 | const stringified = JSON.stringify(arr[i]);
71 | if(itemsFound[stringified]) { continue; }
72 | uniques.push(arr[i]);
73 | itemsFound[stringified] = true;
74 | }
75 | return uniques;
76 | }
77 |
78 | function antdColorPatette(color, granularity) {
79 | let colors = [];
80 | for(let i = 1, len = granularity; i <= len; i++) {
81 | colors.push(
82 | colorPalette(color, i, Math.round((granularity)*0.6))
83 | );
84 | }
85 | return multiDimensionalUnique(colors);
86 | }
87 |
88 | export default antdColorPatette;
--------------------------------------------------------------------------------
/src/utils/avg-color.js:
--------------------------------------------------------------------------------
1 | function hexFromRgbArray(startArr, endArr, factor) {
2 | return [
3 | Math.round((startArr[0] + ((endArr[0] - startArr[0]) * factor))),
4 | Math.round((startArr[1] + ((endArr[1] - startArr[1]) * factor))),
5 | Math.round((startArr[2] + ((endArr[2] - startArr[2]) * factor)))
6 | ]
7 | }
8 | function avgMix (startRgbArr, endRgbArr, granularity) {
9 | const colors = [];
10 | const factor = 1 / (granularity + 1);
11 | for(let i = 0, len = granularity + 2;i < len; i++) {
12 | colors.push(
13 | hexFromRgbArray(startRgbArr, endRgbArr, factor * i)
14 | );
15 | }
16 | return colors;
17 | }
18 | export default avgMix;
19 |
20 |
--------------------------------------------------------------------------------
/src/utils/brightness.js:
--------------------------------------------------------------------------------
1 | export default function brightness(c) {
2 | const color = ((c[0] * 299 + c[1] * 587 + c[2] * 114) / 1000) < 154 ? '#ffffff' : '#000000';
3 | return color;
4 | }
--------------------------------------------------------------------------------
/src/utils/gradientor.js:
--------------------------------------------------------------------------------
1 | import rgbArrayFromHex from './hex-to-rgb';
2 | import avgMix from './avg-color';
3 | import tintShade from './tint-shade';
4 | import antdColorPatette from './antd-color-palette';
5 |
6 |
7 | export default function gradientor(start_color, end_color, granularity, type='avg') {
8 | const startRgbArr = rgbArrayFromHex(start_color).map(Number);
9 | const endRgbArr = rgbArrayFromHex(end_color).map(Number);
10 | granularity = Number(granularity);
11 | let res;
12 | switch(type) {
13 | case'avg':
14 | res = avgMix(startRgbArr, endRgbArr, granularity);
15 | break;
16 | case 'antd':
17 | res = antdColorPatette(endRgbArr, granularity);
18 | break;
19 | case 'tintshade':
20 | res = tintShade(endRgbArr, granularity);
21 | break;
22 | default: break;
23 | }
24 | return res;
25 | }
--------------------------------------------------------------------------------
/src/utils/hex-to-rgb.js:
--------------------------------------------------------------------------------
1 | const hexArray = hex => /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
2 | const rgbObjectFromHex = hex => {
3 | var result = hexArray(hex)
4 | return result ? {
5 | r: parseInt(result[1], 16),
6 | g: parseInt(result[2], 16),
7 | b: parseInt(result[3], 16),
8 | } : null;
9 | }
10 | const rgbArrayFromHex = hex => {
11 | const rgb = rgbObjectFromHex(hex);
12 | return [rgb.r, rgb.g, rgb.b];
13 | }
14 |
15 | export default rgbArrayFromHex;
--------------------------------------------------------------------------------
/src/utils/hsv-to-rgb.js:
--------------------------------------------------------------------------------
1 | function HSVtoRGB({h, s, v}) {
2 | let r, g, b;
3 | let i;
4 | let f, p, q, t;
5 | h = Math.max(0, Math.min(360, h));
6 | s = Math.max(0, Math.min(100, s));
7 | v = Math.max(0, Math.min(100, v));
8 |
9 | s /= 100;
10 | v /= 100;
11 |
12 | if (s === 0) {
13 | r = g = b = v;
14 | return [
15 | Math.round(r * 255),
16 | Math.round(g * 255),
17 | Math.round(b * 255)
18 | ];
19 | }
20 |
21 | h /= 60;
22 | i = Math.floor(h);
23 | f = h - i;
24 | p = v * (1 - s);
25 | q = v * (1 - s * f);
26 | t = v * (1 - s * (1 - f));
27 |
28 | switch (i) {
29 | case 0:
30 | r = v;
31 | g = t;
32 | b = p;
33 | break;
34 |
35 | case 1:
36 | r = q;
37 | g = v;
38 | b = p;
39 | break;
40 |
41 | case 2:
42 | r = p;
43 | g = v;
44 | b = t;
45 | break;
46 |
47 | case 3:
48 | r = p;
49 | g = q;
50 | b = v;
51 | break;
52 |
53 | case 4:
54 | r = t;
55 | g = p;
56 | b = v;
57 | break;
58 |
59 | default: // case 5:
60 | r = v;
61 | g = p;
62 | b = q;
63 | }
64 |
65 | return [
66 | Math.round(r * 255),
67 | Math.round(g * 255),
68 | Math.round(b * 255)
69 | ];
70 | }
71 |
72 | export default HSVtoRGB;
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | import toHex from './rgb-to-hex';
2 | import gradientor from './gradientor';
3 | import brightness from './brightness';
4 |
5 | export default {
6 | toHex,
7 | gradientor,
8 | brightness
9 | }
--------------------------------------------------------------------------------
/src/utils/rgb-to-hex.js:
--------------------------------------------------------------------------------
1 | function rgbChannelToHex (channel) {
2 | const hex = channel.toString(16);
3 | return hex.length === 1 ? `0${hex}` : hex;
4 | }
5 |
6 | function rgbToHex (r, g, b) {
7 | return `#${rgbChannelToHex(r)}${rgbChannelToHex(g)}${rgbChannelToHex(b)}`;
8 | }
9 |
10 | function rgbArrayToHex (color) {
11 | return rgbToHex(color[0], color[1], color[2]);
12 | }
13 |
14 | export default function toHex(color) {
15 | return rgbArrayToHex(color);
16 | }
--------------------------------------------------------------------------------
/src/utils/rgb-to-hsv.js:
--------------------------------------------------------------------------------
1 | function hslObjectFromhexArr(hexArr) {
2 | let r = hexArr[0]/ 255;
3 | let g = hexArr[1]/ 255;
4 | let b = hexArr[2]/ 255;
5 | let rr, gg, bb,
6 | h, s, v = Math.max(r, g, b),
7 | diff = v - Math.min(r, g, b),
8 | diffc = function(c){
9 | return (v - c) / 6 / diff + 1 / 2;
10 | };
11 |
12 | if (diff === 0) {
13 | h = s = 0;
14 | } else {
15 | s = diff / v;
16 | rr = diffc(r);
17 | gg = diffc(g);
18 | bb = diffc(b);
19 |
20 | if (r === v) {
21 | h = bb - gg;
22 | }else if (g === v) {
23 | h = (1 / 3) + rr - bb;
24 | }else if (b === v) {
25 | h = (2 / 3) + gg - rr;
26 | }
27 | if (h < 0) {
28 | h += 1;
29 | } else if (h > 1) {
30 | h -= 1;
31 | }
32 | }
33 | return {
34 | h: Math.round(h * 360),
35 | s: Math.round(s),
36 | v: Math.round(v)
37 | };
38 |
39 | }
40 |
41 | export default hslObjectFromhexArr;
--------------------------------------------------------------------------------
/src/utils/tint-shade.js:
--------------------------------------------------------------------------------
1 | import avgMix from './avg-color';
2 | function tintShade(color, granularity) {
3 | granularity = Number(granularity);
4 | const mid = Math.round(granularity/2);
5 | const mixWithWhite = granularity % 2 === 0
6 | ? avgMix([255, 255, 255], color, mid).slice(1, mid+1)
7 | : avgMix([255, 255, 255], color, mid).slice(1, mid);
8 | const mixWithBlack = granularity % 2 === 0
9 | ? avgMix(color, [0, 0, 0], mid).slice(1, mid+1)
10 | : avgMix(color, [0, 0, 0], mid).slice(0, mid);
11 |
12 | return [...mixWithWhite, ...mixWithBlack];
13 |
14 | }
15 |
16 | export default tintShade;
--------------------------------------------------------------------------------