本文意在记录一次个人实践,背景是最近公司新起了一个 React 技术栈的项目,因此没有历史包袱,可以尝试使用框架的新特性 React hook 来实现 React 组件。

调色板Pro

在考虑做什么时候,正好看到 Ant-Design 的色彩设计,相信使用 React 的童鞋对 Ant-Design 的设计语言和风格是比较清楚的,本文实践的 Demo 是使用 React hook 新特性编写了一个帮助设计童鞋生成平滑色彩渐变的设计工具 Color-Design-Helper 。 生成规则是参考 Ant-Design色彩 ,有兴趣的童鞋可以先行阅读官方文档。

仓库:https://github.com/zerolty/color-design-helper

体验地址: http://zerolty.com/color-design-helper/

需求与实现

记得几个月前阅读过一篇介绍 Ant-Design 调色板演进的文章《 Ant Design 色板生成算法演进之路》,阅读完之后,想对现有的调色板进行增强,因此做这个工具实践方向上的需求和实现是

  1. 自定义“色级”
  2. 自定义主色
  3. 实现平均法、tint-shade 法、和 HSV 递进算法(antd现在使用的)
  4. 使用 React Hooks 抽象value-setValue的逻辑
  5. 颜色转化方法实现

自定义“色级”,更细的粒度,更多的可能

不知道这样描述是否准确,Ant Design 的基础色板共计 120 个颜色,包含 12 个主色以及衍生色。如果我使用平均法和 tint-shade 法,工具可以通过设计人员自己定制step得到更多级的过渡色,从而更好的满足视觉需求。下图是 Ant-Design 提供的色板生成工具和工具tint-shade 25色的对比。

自定义主色

这是 Ant-Design 提供的色板生成工具已经支持的了,除了官方推荐的共计 120 个颜色,你也可以自己定义主色。

三种算法

平均法、tint-shade 法、和 HSV 递进算法(antd现在使用的)

平均法

顾名思义,我可以选择两种颜色,设置好步骤数,等量平均地从第一种颜色向第二种颜色过渡。 比如我们选取的颜色分别是#ffffff#000000,我们先将HEX转化为RGB,分别是(255, 255, 255)和(0, 0, 0),在将0-255按照选择的step依次平均获得相应的色带。

tint-shade

这是第一版 Ant-Design 的实现,思路上是把纯黑和纯白和主色分别混合,生成一条常规的渐变色带。比如我们的主色是#1890ff,“色级”是10,将其转化为 RGB 后与#000000按照平均法混合,得到一条色带,取 20%、40%、60%、80%、100%五种色和#fffffff按照平均法混合,得到一条色带,取 20%、40%、60%、80%、100%五种色拼接组成我们的tint-shade色带。

import avgMix from './avg-color'; // 平均函数
function tintShade(color, granularity) {granularity = Number(granularity);const mid = Math.round(granularity/2); // 取中间的值const mixWithWhite = granularity % 2 === 0  // 奇偶情况? avgMix([255, 255, 255], color, mid).slice(1, mid+1) : avgMix([255, 255, 255], color, mid).slice(1, mid);const mixWithBlack = granularity % 2 === 0 ? avgMix(color, [0, 0, 0], mid).slice(1, mid+1): avgMix(color, [0, 0, 0], mid).slice(0, mid);return [...mixWithWhite, ...mixWithBlack];  // 合并黑白混合}复制代码

HSV 递进算法(antd现在使用的)

选定主色后,我们使用 HSV 模型,将HEX格式的主色转化成H、S、V。

const hsv = new TinyColor({ r: color[0], g: color[1], b: color[2] }).toHsv();
复制代码

第二步是生成我们色带需要数量的“生成色”数组,并将明暗线设置在60%的位置

for(let i = 1, len = granularity; i <= len; i++) {  // “生成色”数colors.push(colorPalette(color, i, Math.round((granularity)*0.6)) // 明暗线设置在60%的位置);
}// HSV的优化
const isLight = index <= num; // 判断明暗复制代码

第三步 直接跟着代码看对H、S、V的处理,也可以直接看官方实现源码

const getHue = function (hsv, i, isLight) {let hue;if (hsv.h >= 60 && hsv.h <= 240) {// 冷色调// 减淡变亮 色相顺时针旋转 更暖// 加深变暗 色相逆时针旋转 更冷hue = isLight ? hsv.h - hueStep * i : hsv.h + hueStep * i;} else {// 暖色调// 减淡变亮 色相逆时针旋转 更暖// 加深变暗 色相顺时针旋转 更冷hue = isLight ? hsv.h + hueStep * i : hsv.h - hueStep * i;}if (hue < 0) {hue += 360;} else if (hue >= 360) {hue -= 360;}return Math.round(hue);
};
// 每个生成颜色的S(饱和)计算,饱和在一定范围内折中,还原比较真实的视觉感受
const getSaturation = function (hsv, i, isLight) {let saturation;if (isLight) {// 减淡变亮 饱和度迅速降低saturation = Math.round(hsv.s * 100) - saturationStep * i;} else if (i === darkColorCount) {// 加深变暗-最暗 饱和度提高saturation = Math.round(hsv.s * 100) + saturationStep;} else {// 加深变暗 饱和度缓慢提高saturation = Math.round(hsv.s * 100) + saturationStep2 * i;}if (saturation > 100) {saturation = 100;}if (isLight && i === lightColorCount && saturation > 10) {saturation = 10;}if (saturation < 6) {saturation = 6;}return Math.round(saturation);
};
// 每个生成颜色的V计算,冷色调,变亮;暖色反之
const getValue = function (hsv, i, isLight) {if (isLight) {// 减淡变亮return Math.round(hsv.v * 100) + brightnessStep1 * i;}// 加深变暗幅度更大return Math.round(hsv.v * 100) - brightnessStep2 * i;
};复制代码

React-Hooks

在编写这个工具的时候,通过组件拆分是非常简单的三部分,Step 显示和色带是两个纯的render,因此我们很容易想到把状态都放在顶部组件。另外我们的颜色输入框、range选择器、算法选择器三个控件是有状态的,valuesetValue需要从顶部组件下发下去,而在以往不使用 React Hooks 的主组件应该是这样

<input type="color" value="state,xxx" onChange="onXXXChange" />
复制代码

这里可以用 State Hooks 抽象 Input 组件的状态和状态控制逻辑,Hooks 是React 16.7.0-alpha.0尝试使用的新特性,官方文档是最好的参考 React hook ,英文阅读不适可以看秋风翻译的React Hooks详解翻译。这个工具按照范式只使用一个函数,没有 class 和 state ,非常顺滑。

使用 react-hook ,全局有三个控制型组件使用 input ,我们也可以把 input 不要看成是原生组件而是一个render-props,useInputValue第一个参数是初始值,第二个参数setOr是供筛选使用暴露出的设置方法,可以在外层手动修改状态,如果不加筛选,setDisabled, setValue将被传到inputprops上, React 会抛出Warning提示 Input 没有定义对应的 Props 。

// define hook function
import { useState, useCallback } from "react";export default function useInputValue(initiateValue, setOr) {const [value, setValue] = useState(initiateValue);const [disabled, setDisabled] =  useState(false);let onChange = useCallback(e => setValue(e.target.value), []);if(setOr) return { value, onChange, disabled, setDisabled, setValue };return { value, onChange, disabled };}// usage
const startInputHook = useInputValue('#ffffff', true);
const endInputHook = useInputValue('#1890ff');
const stepInputHook = useInputValue(1);
const radiosHook = useInputValue('avg');
...
<input type="color" {...filterStartInputHook} disabled={startInputHook.disabled} />
<input type="text" {...filterStartInputHook} disabled={startInputHook.disabled}/>
<input type="range" min={MIN} max={MAX} {...stepInputHook} />复制代码

颜色转化

先明确我们用到的三种颜色格式

  • HEX: 我们常见的#000000这类,rgb 的16进制组合表达
  • RGB:0-255组成的三原色参数
  • HSV:HSL 和 HSV 都是一种将RGB色彩模型中的点在圆柱坐标系中的表示法。这两种表示法试图做到比基于笛卡尔坐标系的几何结构RGB更加直观。色相(H)是色彩的基本属性,就是平常所说的颜色名称,如红色、黄色等。饱和度(S)是指色彩的纯度,越高色彩越纯,低则逐渐变灰,取0-100%的数值。明度(V),亮度(L),取0-100%。

RGB 与 HEX、HSV之间的转化

RGB 与 HEX 之间转化使用toString(16)parseInt(x, 16)即可

// HEX to RGB
const hexArray = hex => /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); // 正则取出rgb的十六进制
const rgbObjectFromHex = hex => {var result = hexArray(hex)return result ? {r: parseInt(result[1], 16),g: parseInt(result[2], 16),b: parseInt(result[3], 16),} : null;
}
const rgbArrayFromHex = hex => {const rgb = rgbObjectFromHex(hex);return [rgb.r, rgb.g, rgb.b];  // rgb的数组
}// RGB to HEX
function rgbChannelToHex (channel) {const hex = channel.toString(16);return hex.length === 1 ? `0${hex}` : hex;
}function rgbToHex (r, g, b) {return `#${rgbChannelToHex(r)}${rgbChannelToHex(g)}${rgbChannelToHex(b)}`;
}function rgbArrayToHex (color) {return rgbToHex(color[0], color[1], color[2]);
}复制代码

RGB 与 HSV 的转化可以参考公式算法,源码可见:HSV-RGB

rgb to hsv

我在实践后,发现一些浮点情况边界处理的不好,之后使用这个颜色转化的库来实践,省去部分成本tinycolor

反差色文字颜色

暗底白字与明底黑字实现是通过计算 RGB 判断明暗,从而显示白色或黑色

export default function brightness(c) {const color = ((c[0] * 299 + c[1] * 587 + c[2] * 114) / 1000) < 154 ? '#ffffff' : '#000000';return color;
}
复制代码

写在最后

欢迎小伙伴们,点评和指出不足一起探讨问题,另外我和@蓝色的秋风大佬正在共建一个偏 handsome 的小工具最佳实践,如果你是热爱技术、并有想法参与技术共建欢迎和我们联系。

打赏

友情链接

无影er 秋风 阿米狗

React Hooks 起手式,实现一个「高颜值实用」的色彩设计工具相关推荐

  1. DevOps:从「蒸汽时代」到「高铁时代」,SUNMI DevOps转型之路 | 原力计划

    作者 | 文振熙.刘文沣 责编 | 徐威龙 封图| CSDN 下载于视觉中国 商米科技成立于 2013 年,总部位于上海市杨浦区创智天地,是一家具有产品创新基因和互联网基因的公司.商米在短时间内迅速成 ...

  2. 从「蒸汽时代」到「高铁时代」,SUNMI DevOps 转型之路 | 原力计划

    作者 | 文振熙.刘文沣 责编 | 徐威龙 出品 | CSDN博客 封图| CSDN 下载于视觉中国 商米科技成立于 2013 年,总部位于上海市杨浦区创智天地,是一家具有产品创新基因和互联网基因的公 ...

  3. DevOps:从「蒸汽时代」到「高铁时代」,SUNMI DevOps转型之路

    商米科技成立于 2013 年,总部位于上海市杨浦区创智天地,是一家极具产品创新基因和互联网基因的公司.商米在短时间内迅速成长为一家近1000人的企业,产品研发人数占比一度超过70%. 做为一家初创企业 ...

  4. 罗永浩与锤子手机撇清关系;微软回应「高管传奇」经历;Rust 1.38 稳定版发布 | 极客头条...

    快来收听极客头条音频版吧,智能播报由标贝科技提供技术支持. 「CSDN 极客头条」,是从 CSDN 网站延伸至官方微信公众号的特别栏目,专注于一天业界事报道.风里雨里,我们将每天为朋友们,播报最新鲜有 ...

  5. 9月30日科技资讯|罗永浩与锤子手机撇清关系;微软回应「高管传奇」经历;Rust 1.38 稳定版发布

    「CSDN 极客头条」,是从 CSDN 网站延伸至官方微信公众号的特别栏目,专注于一天业界事报道.风里雨里,我们将每天为朋友们,播报最新鲜有料的新闻资讯,让所有技术人,时刻紧跟业界潮流. 整理 | 屠 ...

  6. 「PS-CC2019新版教程」仿制图章工具,仿造一个你

    今天带着大家继续学习PS中的图章工具,一个专门"造假"的工具(指仿造一个图层,并不是去做违法的事情),它只是将图章源素材在另一个地方绘制出来!当然,用仿制图章工具可以简单的磨皮(祛 ...

  7. 渗透测试 | 几款常用的CMS识别「Web指纹识别」扫描脚本工具(含下载地址)

    在对「靶标资产」进行渗透测试的前期,通常需要对「靶标资产」进行相关的信息收集,而对「靶标资产」进行Web指纹信息扫描也是信息收集当中很关键的一部分. 能否有效识别出「靶标资产」的Web指纹信息,主要还 ...

  8. 撸了一个「合成大西瓜」

    最近「合成大西瓜」这个游戏也很火热,玩了一阵还挺有意思的.研究了一下原理,发现目前流传的版本都是魔改编译后的版本,代码经过压缩不具备可读性,因此决定自己照着实现一个.这个游戏已经开源,获取项目可以关注 ...

  9. 有哪些既实用又好看的蓝牙耳机?高颜值实用蓝牙耳机排行

    随着生活水平的不断提高,在挑选商品的时候实用性不再是唯一的考虑的因素,颜值逐渐成为首先考虑的因素,这一期给大家整理了几款高颜值的蓝牙耳机,不论是自用还是送礼都挺合适的. NO1.南卡小音舱蓝牙耳机 发 ...

  10. 5000 万用户、日订单 300 万,下沉市场又出现一个「隐形小巨头」?

    用「产业逻辑」匹配对应的市场. 近几年互联网公司有一个设定,越是关注在「下沉市场」的公司,越能受到人们的青睐,成为被研究的对象,拼多多和快手都属于这一类.现在,共享出行也有了一个案例,值得去探讨关注. ...

最新文章

  1. Coolite Toolkit学习笔记五:常用控件Menu和MenuPanel
  2. PHP 拷贝图像 imagecopy 与 imagecopyresized 函数
  3. Hadoop之道--MapReduce之Hello World实例wordcount
  4. TCP/IP的七层负载均衡
  5. director.js实现前端路由
  6. (42)Verilog HDL 打两拍设计
  7. SVN与TortoiseSVN实战:冲突详解(一)
  8. 【操作系统】—内存的基本知识
  9. .net知识和学习方法系列(三)CSharp中控件台的输入输出
  10. 华为S9303三层交换机一次配置经历和心得
  11. 个人博客搭建教程——基于WordPress
  12. Sqlmap中代理及其他常用参数
  13. 如何提高推荐量?新手必知 头条号推荐机制
  14. HEIC格式怎么转换?掌握这个方法,轻松实现!
  15. 职场中干好工作的18准则
  16. 0002 c语言 字母排序
  17. 我访问了一个「假」的 GitHub,难道是 PronHub ,还是 GayHub ?
  18. 什么是String[] argv和String args[]
  19. 外行学python有用吗_外行人应该如何学习 python?
  20. 【浏览器】1219- 换一种风格理解 Chrome 浏览器渲染全过程

热门文章

  1. LintCode 137. 克隆图
  2. serverlet生命周期
  3. PHP5.2\5.3 Xdebug 调试器配置及应用
  4. JSBinding + SharpKit / JavaScript 加载流程
  5. 全面对比T-SQL与PL/SQL
  6. JBPM开发入门指南(3)
  7. PPDuck3 for Mac(pp鸭图片批量压缩工具)最新官方版免下载
  8. Qt5 QtQuick系列----QtQuick的Secne Graph剖析(1)
  9. EH使用IPMI基础操作
  10. 2018 KDD CUP支付宝安全团队Deep X斩获两项大奖