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

栏目: 服务器 · 发布时间: 7年前

内容简介:本文意在记录一次个人实践,背景是最近公司新起了一个 React 技术栈的项目,因此没有历史包袱,可以尝试使用框架的新特性React hook 来实现 React 组件。在考虑做什么时候,正好看到 Ant-Design 的色彩设计,相信使用 React 的童鞋对Ant-Design 的设计语言和风格是比较清楚的,本文实践的 Demo 是使用React hook 新特性编写了一个帮助设计童鞋生成平滑色彩渐变的设计工具Color-Design-Helper 。 生成规则是参考Ant-Design色彩 ,有兴趣的童

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

本文意在记录一次个人实践,背景是最近公司新起了一个 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色的对比。

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

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

自定义主色

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

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

三种算法

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

平均法

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

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

tint-shade

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

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

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现在使用的)

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

选定主色后,我们使用 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

React Hooks 起手式,实现一个「高颜值实用」的色彩设计工具 rgb to hsv React Hooks 起手式,实现一个「高颜值实用」的色彩设计工具

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

反差色文字颜色

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

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

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Hacking Growth

Hacking Growth

Sean Ellis、Morgan Brown / Crown Business / 2017-4-25 / USD 29.00

The definitive playbook by the pioneers of Growth Hacking, one of the hottest business methodologies in Silicon Valley and beyond. It seems hard to believe today, but there was a time when Airbnb w......一起来看看 《Hacking Growth》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具