内容简介:本文意在记录一次个人实践,背景是最近公司新起了一个 React 技术栈的项目,因此没有历史包袱,可以尝试使用框架的新特性React hook 来实现 React 组件。在考虑做什么时候,正好看到 Ant-Design 的色彩设计,相信使用 React 的童鞋对Ant-Design 的设计语言和风格是比较清楚的,本文实践的 Demo 是使用React hook 新特性编写了一个帮助设计童鞋生成平滑色彩渐变的设计工具Color-Design-Helper 。 生成规则是参考Ant-Design色彩 ,有兴趣的童
本文意在记录一次个人实践,背景是最近公司新起了一个 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 色板生成算法演进之路 》,阅读完之后,想对现有的调色板进行增强,因此做这个 工具 实践方向上的需求和实现是
- 自定义“色级”
- 自定义主色
- 实现平均法、tint-shade 法、和 HSV 递进算法(antd现在使用的)
- 使用 React Hooks 抽象value-setValue的逻辑
- 颜色转化方法实现
自定义“色级”,更细的粒度,更多的可能
不知道这样描述是否准确,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选择器、算法选择器
三个控件是有状态的, value
和 setValue
需要从顶部组件下发下去,而在以往不使用 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
将被传到 input
的 props
上, 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; } 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 图像编辑新作:连续色彩迁移
- Flutter | ShaderMask - 给你的Widget加上色彩
- Android系统色彩模式RGB_565的问题
- 一起来学opencv(二):图片通道及色彩空间转化
- Taro iconfont v1.2.0 发布,Taro 的多色彩图标方案
- Taro iconfont v0.3.1 发布,Taro 的多色彩图标方案
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
趣学Python编程
Jason Briggs / 尹哲 / 人民邮电出版社 / 2014-3 / 45.00元
python是一款解释型、面向对象、动态数据类型的高级程序设计语言。python语法简捷而清晰,具有丰富和强大的类库,因而在各种行业中得到广泛的应用。对于初学者来讲,python是一款既容易学又相当有用的编程语言,国内外很多大学开设这款语言课程,将python作为一门编程语言学习。 《趣学python编程》是一本轻松、快速掌握python编程的入门读物。全书分为3部分,共18章。第1部分是第......一起来看看 《趣学Python编程》 这本书的介绍吧!