You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现固定亮度的两指定颜色间颜色生成函数?

实现固定亮度的两色间颜色生成函数

核心思路

要实现固定亮度下在两个指定颜色间生成过渡色,关键是利用HSL色彩空间的特性——亮度(L)是独立分量,可直接固定。步骤如下:

  1. 将输入的十六进制颜色转换为HSL格式,分离色相(H)、饱和度(S)、亮度(L)。
  2. 把两个输入颜色的亮度统一替换为指定的目标亮度,保持色相和饱和度不变,得到同一亮度平面上的两个基准点。
  3. 在固定亮度的HSL平面内,对两个基准点的色相和饱和度进行线性插值,生成过渡色。
  4. 将插值后的HSL值转回十六进制格式输出。

关键细节处理

  • 色相的环形插值:色相H的取值范围是0-360°,属于环形维度。插值时需判断两个色相的最短路径(比如H=350°和H=10°的中间值是0°,而非180°)。
  • 亮度参数兼容:支持传入0-1的小数或0%-100%的百分比字符串,统一转换为0-1的小数进行计算。
  • 边界情况:若两个输入颜色调整到目标亮度后完全一致,直接返回该颜色。

代码实现(JavaScript)

// 辅助函数:十六进制转RGB
function hexToRgb(hex) {
  // 处理简写格式(如#F00)
  const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
  hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);
  
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result ? {
    r: parseInt(result[1], 16) / 255,
    g: parseInt(result[2], 16) / 255,
    b: parseInt(result[3], 16) / 255
  } : null;
}

// 辅助函数:RGB转HSL
function rgbToHsl(r, g, b) {
  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: h * 360, s, l };
}

// 辅助函数:HSL转RGB
function hslToRgb(h, s, l) {
  h /= 360;
  let r, g, b;

  if (s === 0) {
    r = g = b = l; // 灰度
  } else {
    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;
    };

    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) };
}

// 辅助函数:RGB转十六进制
function rgbToHex(r, g, b) {
  return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`;
}

// 主函数:生成固定亮度的过渡色
function generateFixedBrightnessColor(color1, color2, brightness) {
  // 处理亮度参数:转换为0-1的小数
  let targetL;
  if (typeof brightness === 'string' && brightness.includes('%')) {
    targetL = parseFloat(brightness) / 100;
  } else {
    targetL = Number(brightness);
    // 处理0-100的数字输入
    if (targetL > 1) targetL /= 100;
  }
  targetL = Math.max(0, Math.min(1, targetL)); // 限制在0-1范围

  // 转换输入颜色为HSL,并调整到目标亮度
  const rgb1 = hexToRgb(color1);
  const rgb2 = hexToRgb(color2);
  if (!rgb1 || !rgb2) throw new Error('无效的颜色格式');

  const hsl1 = rgbToHsl(rgb1.r, rgb1.g, rgb1.b);
  const hsl2 = rgbToHsl(rgb2.r, rgb2.g, rgb2.b);

  // 固定亮度,得到基准HSL点
  const baseHsl1 = { h: hsl1.h, s: hsl1.s, l: targetL };
  const baseHsl2 = { h: hsl2.h, s: hsl2.s, l: targetL };

  // 处理色相的环形插值:取最短路径
  let deltaH = baseHsl2.h - baseHsl1.h;
  if (Math.abs(deltaH) > 180) {
    deltaH = deltaH > 0 ? deltaH - 360 : deltaH + 360;
  }

  // 随机取0-1之间的插值比例(也可改为接收比例参数)
  const ratio = Math.random();
  const interpolatedH = (baseHsl1.h + deltaH * ratio + 360) % 360;
  const interpolatedS = baseHsl1.s + (baseHsl2.s - baseHsl1.s) * ratio;
  const interpolatedL = targetL;

  // 转回RGB和十六进制
  const rgb = hslToRgb(interpolatedH, interpolatedS, interpolatedL);
  return rgbToHex(rgb.r, rgb.g, rgb.b);
}

// 示例用法
console.log(generateFixedBrightnessColor('#FF0000', '#00FF00', 30)); // 30%亮度的红绿过渡色
console.log(generateFixedBrightnessColor('#FF0000', '#00FF00', '100%')); // 100%亮度的过渡色

说明

  • 函数默认生成随机过渡色,若需要指定过渡比例(比如生成两色中间50%的颜色),可新增一个ratio参数替代Math.random()。
  • 所有颜色转换函数均处理了边界情况,确保输入输出格式的兼容性。
  • 完全符合需求:生成的颜色亮度严格等于指定值,仅在固定亮度的水平范围内过渡。

内容的提问来源于stack exchange,提问作者Jacob T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 15:00:49