
📖 项目简介
基于 React + Tailwind CSS 构建的专业颜色转换工具,支持多种颜色格式的实时转换。无论是设计师、开发者,都能在这个工具中找到所需的颜色转换功能。
✨ 核心功能
🎯 多格式颜色转换
- HEX 格式: 支持 3 位缩写 (
#000
,#fff
) 和 6 位完整格式 (#ff6b6b
) - RGB/RGBA: 红绿蓝颜色空间,支持透明度
- HSL/HSLA: 色相饱和度亮度,更直观的颜色表示
- HSV: 色相饱和度明度,常用于图像处理
- CMYK: 青品黄黑,印刷行业标准
🌟 亮点
1. 全面的颜色空间支持
// 支持的颜色格式示例
#ff6b6b // HEX 6位
#f66 // HEX 3位缩写
rgb(255, 107, 107) // RGB
rgba(255, 107, 107, 0.8) // RGBA
hsl(0, 100%, 71%) // HSL
hsla(0, 100%, 71%, 0.8) // HSLA
hsv(0, 58%, 100%) // HSV
cmyk(0%, 58%, 58%, 0%) // CMYK
🎯 使用场景
- CSS 开发: 快速获取不同格式的颜色值用于样式开发
- 主题系统: 构建多主题应用时的颜色格式转换
- 调试工具: 快速验证和转换颜色值
💻 核心代码实现
HEX 到 RGB 转换(支持缩写)
const hexToRgb = (hex) => {
// 支持 3 位 HEX 缩写,如 #000, #fff
if (/^#?([a-f\d])([a-f\d])([a-f\d])$/i.test(hex)) {
const result = /^#?([a-f\d])([a-f\d])([a-f\d])$/i.exec(hex);
return {
r: parseInt(result[1] + result[1], 16),
g: parseInt(result[2] + result[2], 16),
b: parseInt(result[3] + result[3], 16),
};
}
// 支持 6 位 HEX,如 #000000, #ffffff
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: null;
};
2. RGB 到 HEX 转换
const rgbToHex = (r, g, b) => {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
};
3. RGB 到 HSL 转换
const rgbToHsl = (r, g, b) => {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h,
s,
l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
l: Math.round(l * 100),
};
};
4. HSL 到 RGB 转换
const hslToRgb = (h, s, l) => {
h /= 360;
s /= 100;
l /= 100;
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255),
};
};
5. RGB 到 HSV 转换
const rgbToHsv = (r, g, b) => {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const d = max - min;
let h,
s,
v = max;
s = max === 0 ? 0 : d / max;
if (max === min) {
h = 0;
} else {
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
v: Math.round(v * 100),
};
};
6. RGB 到 CMYK 转换
const rgbToCmyk = (r, g, b) => {
r /= 255;
g /= 255;
b /= 255;
const k = 1 - Math.max(r, g, b);
const c = (1 - r - k) / (1 - k);
const m = (1 - g - k) / (1 - k);
const y = (1 - b - k) / (1 - k);
return {
c: Math.round(c * 100),
m: Math.round(m * 100),
y: Math.round(y * 100),
k: Math.round(k * 100),
};
};
完整源码
import { useState, useEffect } from "react";
export default function ColorConversion() {
const [colorInput, setColorInput] = useState("#ff6b6b");
const [colorFormats, setColorFormats] = useState({
hex: "#ff6b6b",
rgb: { r: 255, g: 107, b: 107 },
hsl: { h: 0, s: 100, l: 71 },
hsv: { h: 0, s: 58, v: 100 },
cmyk: { c: 0, m: 58, y: 58, k: 0 },
rgba: { r: 255, g: 107, b: 107, a: 1 },
hsla: { h: 0, s: 100, l: 71, a: 1 },
});
// 颜色转换函数
const hexToRgb = (hex) => {
// 支持 3 位 HEX 缩写,如 #000, #fff
if (/^#?([a-f\d])([a-f\d])([a-f\d])$/i.test(hex)) {
const result = /^#?([a-f\d])([a-f\d])([a-f\d])$/i.exec(hex);
return {
r: parseInt(result[1] + result[1], 16),
g: parseInt(result[2] + result[2], 16),
b: parseInt(result[3] + result[3], 16),
};
}
// 支持 6 位 HEX,如 #000000, #ffffff
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: null;
};
const rgbToHex = (r, g, b) => {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
};
const rgbToHsl = (r, g, b) => {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h,
s,
l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
l: Math.round(l * 100),
};
};
const hslToRgb = (h, s, l) => {
h /= 360;
s /= 100;
l /= 100;
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255),
};
};
const rgbToHsv = (r, g, b) => {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const d = max - min;
let h,
s,
v = max;
s = max === 0 ? 0 : d / max;
if (max === min) {
h = 0;
} else {
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
v: Math.round(v * 100),
};
};
const rgbToCmyk = (r, g, b) => {
r /= 255;
g /= 255;
b /= 255;
const k = 1 - Math.max(r, g, b);
const c = (1 - r - k) / (1 - k);
const m = (1 - g - k) / (1 - k);
const y = (1 - b - k) / (1 - k);
return {
c: Math.round(c * 100),
m: Math.round(m * 100),
y: Math.round(y * 100),
k: Math.round(k * 100),
};
};
// 更新所有颜色格式
const updateColorFormats = (input) => {
let rgb;
// 检测输入格式并转换为RGB
if (input.startsWith("#")) {
rgb = hexToRgb(input);
} else if (input.startsWith("rgb")) {
const match = input.match(
/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/
);
if (match) {
rgb = {
r: parseInt(match[1]),
g: parseInt(match[2]),
b: parseInt(match[3]),
};
}
} else if (input.startsWith("hsl")) {
const match = input.match(
/hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*([\d.]+))?\)/
);
if (match) {
rgb = hslToRgb(
parseInt(match[1]),
parseInt(match[2]),
parseInt(match[3])
);
}
}
if (rgb) {
const hex = rgbToHex(rgb.r, rgb.g, rgb.b);
const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
const cmyk = rgbToCmyk(rgb.r, rgb.g, rgb.b);
setColorFormats({
hex,
rgb,
hsl,
hsv,
cmyk,
rgba: { ...rgb, a: 1 },
hsla: { ...hsl, a: 1 },
});
}
};
useEffect(() => {
updateColorFormats(colorInput);
}, [colorInput]);
const copyToClipboard = (text) => {
navigator.clipboard.writeText(text);
};
const ColorFormatCard = ({ title, format, copyText }) => (
<div className="bg-white rounded-lg shadow-md p-3 sm:p-4 border border-gray-200">
<h3 className="text-base sm:text-lg font-semibold text-gray-800 mb-2">
{title}
</h3>
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-0">
<code className="text-xs sm:text-sm bg-gray-100 px-2 py-1 rounded flex-1 font-mono break-all">
{format}
</code>
<button
onClick={() => copyToClipboard(copyText)}
className="bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded text-xs sm:text-sm transition-colors whitespace-nowrap"
>
复制
</button>
</div>
</div>
);
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 py-8">
<div className="container mx-auto px-4 max-w-6xl">
<div className="text-center mb-8">
<h1 className="text-4xl font-bold text-gray-800 mb-4">
颜色转换工具
</h1>
<p className="text-gray-600 text-lg">支持多种颜色格式的相互转换</p>
</div>
{/* 颜色输入区域 */}
<div className="bg-white rounded-xl shadow-lg p-6 mb-8">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
输入颜色值
</label>
<div className="flex items-center gap-3">
<input
type="text"
value={colorInput}
onChange={(e) => setColorInput(e.target.value)}
placeholder="输入 HEX、RGB、HSL 等颜色值"
className="flex-1 px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
<div
className="w-12 h-12 rounded-lg border-2 border-gray-300 shadow-sm flex-shrink-0"
style={{ backgroundColor: colorFormats.hex }}
></div>
</div>
</div>
</div>
{/* 颜色格式转换结果 */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
<ColorFormatCard
title="HEX 颜色"
format={colorFormats.hex}
copyText={colorFormats.hex}
/>
<ColorFormatCard
title="RGB 颜色"
format={`rgb(${colorFormats.rgb.r}, ${colorFormats.rgb.g}, ${colorFormats.rgb.b})`}
copyText={`rgb(${colorFormats.rgb.r}, ${colorFormats.rgb.g}, ${colorFormats.rgb.b})`}
/>
<ColorFormatCard
title="RGBA 颜色"
format={`rgba(${colorFormats.rgba.r}, ${colorFormats.rgba.g}, ${colorFormats.rgba.b}, ${colorFormats.rgba.a})`}
copyText={`rgba(${colorFormats.rgba.r}, ${colorFormats.rgba.g}, ${colorFormats.rgba.b}, ${colorFormats.rgba.a})`}
/>
<ColorFormatCard
title="HSL 颜色"
format={`hsl(${colorFormats.hsl.h}, ${colorFormats.hsl.s}%, ${colorFormats.hsl.l}%)`}
copyText={`hsl(${colorFormats.hsl.h}, ${colorFormats.hsl.s}%, ${colorFormats.hsl.l}%)`}
/>
<ColorFormatCard
title="HSLA 颜色"
format={`hsla(${colorFormats.hsla.h}, ${colorFormats.hsla.s}%, ${colorFormats.hsla.l}%, ${colorFormats.hsla.a})`}
copyText={`hsla(${colorFormats.hsla.h}, ${colorFormats.hsla.s}%, ${colorFormats.hsla.l}%, ${colorFormats.hsla.a})`}
/>
<ColorFormatCard
title="HSV 颜色"
format={`hsv(${colorFormats.hsv.h}, ${colorFormats.hsv.s}%, ${colorFormats.hsv.v}%)`}
copyText={`hsv(${colorFormats.hsv.h}, ${colorFormats.hsv.s}%, ${colorFormats.hsv.v}%)`}
/>
<ColorFormatCard
title="CMYK 颜色"
format={`cmyk(${colorFormats.cmyk.c}%, ${colorFormats.cmyk.m}%, ${colorFormats.cmyk.y}%, ${colorFormats.cmyk.k}%)`}
copyText={`cmyk(${colorFormats.cmyk.c}%, ${colorFormats.cmyk.m}%, ${colorFormats.cmyk.y}%, ${colorFormats.cmyk.k}%)`}
/>
</div>
{/* 使用说明 */}
<div className="mt-8 sm:mt-12 bg-white rounded-xl shadow-lg p-4 sm:p-6">
<h2 className="text-xl sm:text-2xl font-bold text-gray-800 mb-4">
使用说明
</h2>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6">
<div>
<h3 className="text-lg font-semibold text-gray-700 mb-2">
支持的输入格式:
</h3>
<ul className="space-y-2 text-gray-600">
<li>• HEX: #ff6b6b 或 #f66</li>
<li>• HEX 缩写: #000, #fff, #abc</li>
<li>• RGB: rgb(255, 107, 107)</li>
<li>• RGBA: rgba(255, 107, 107, 0.8)</li>
<li>• HSL: hsl(0, 100%, 71%)</li>
<li>• HSLA: hsla(0, 100%, 71%, 0.8)</li>
</ul>
</div>
<div>
<h3 className="text-lg font-semibold text-gray-700 mb-2">
输出格式:
</h3>
<ul className="space-y-2 text-gray-600">
<li>• HEX 十六进制</li>
<li>• RGB/RGBA 红绿蓝</li>
<li>• HSL/HSLA 色相饱和度亮度</li>
<li>• HSV 色相饱和度明度</li>
<li>• CMYK 青品黄黑</li>
</ul>
</div>
</div>
</div>
</div>
</div>
);
}
原文链接:https://code.ifrontend.net/archives/723,转载请注明出处。
评论0