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

JavaScript实现将颜色数组按指定数量合并(取相近色平均值)

Color Reduction by Merging Closest Colors in JavaScript

Got it, let's tackle this color reduction problem step by step. The goal is to take your list of named hex colors and iteratively merge the two most similar ones (by averaging their RGB values) until we hit the target count (15 or 10). Here's a complete, practical JavaScript solution tailored to your needs:

Step-by-Step Approach

  • Parse Input: Convert your raw color strings into structured objects with hex, name, and RGB values for easier manipulation.
  • Measure Similarity: Use Euclidean distance between RGB values to gauge how close two colors are—smaller distance means more similar colors.
  • Find Closest Pair: Iterate through all color pairs to locate the pair with the smallest similarity distance.
  • Merge Colors: Replace the two closest colors with their average (round RGB values to integers, convert back to hex, and combine their names for traceability).
  • Repeat: Keep merging pairs until your array reaches the target number of colors.

Complete Implementation

// Helper: Convert hex color to RGB object
function hexToRgb(hex) {
    const r = parseInt(hex.slice(0, 2), 16);
    const g = parseInt(hex.slice(2, 4), 16);
    const b = parseInt(hex.slice(4, 6), 16);
    return { r, g, b };
}

// Helper: Convert RGB object back to hex string (with leading zeros if needed)
function rgbToHex(rgb) {
    const pad = val => val.toString(16).padStart(2, '0');
    return `${pad(rgb.r)}${pad(rgb.g)}${pad(rgb.b)}`;
}

// Calculate Euclidean distance between two RGB colors (smaller = more similar)
function getColorDistance(colorA, colorB) {
    const dr = colorA.r - colorB.r;
    const dg = colorA.g - colorB.g;
    const db = colorA.b - colorB.b;
    return Math.sqrt(dr*dr + dg*dg + db*db);
}

// Find the indices of the most similar color pair in the array
function findClosestPair(colors) {
    let minDistance = Infinity;
    let closestPair = [0, 1];

    for (let i = 0; i < colors.length; i++) {
        for (let j = i + 1; j < colors.length; j++) {
            const distance = getColorDistance(colors[i].rgb, colors[j].rgb);
            if (distance < minDistance) {
                minDistance = distance;
                closestPair = [i, j];
            }
        }
    }

    return closestPair;
}

// Merge two colors into their RGB average, with combined name
function mergeColors(colorA, colorB) {
    const avgR = Math.round((colorA.rgb.r + colorB.rgb.r) / 2);
    const avgG = Math.round((colorA.rgb.g + colorB.rgb.g) / 2);
    const avgB = Math.round((colorA.rgb.b + colorB.rgb.b) / 2);
    const avgRgb = { r: avgR, g: avgG, b: avgB };
    
    return {
        hex: rgbToHex(avgRgb),
        name: `${colorA.name}+${colorB.name}`,
        rgb: avgRgb
    };
}

// Main function: Reduce color array to target count
function reduceColors(rawColors, targetCount) {
    // Convert raw input to structured color objects
    const colors = rawColors.map(str => {
        const [hex, name] = str.split(',');
        return { hex, name, rgb: hexToRgb(hex) };
    });

    // Keep merging until we hit the target
    while (colors.length > targetCount) {
        const [idx1, idx2] = findClosestPair(colors);
        const merged = mergeColors(colors[idx1], colors[idx2]);
        
        // Remove original colors and add merged one
        colors.splice(Math.max(idx1, idx2), 1);
        colors.splice(Math.min(idx1, idx2), 1);
        colors.push(merged);
    }

    // Format back to your original string format
    return colors.map(color => `${color.hex},${color.name}`);
}

// Your input color array
const inputColors = [
    "000000,Color01",
    "8d6137,Color02",
    "ebc197,Color03",
    "ae733f,Color04",
    "935524,Color05",
    "ebc193,Color06",
    "320d06,Color07",
    "3f0f00,Color08",
    "724b35,Color09",
    "6d402c,Color10",
    "1c0800,Color11",
    "925829,Color12",
    "522b13,Color13",
    "7f4a2d,Color14",
    "603719,Color15",
    "e56a17,Color16",
    "8c331e,Color17",
    "110501,Color18",
    "674713,Color19"
];

// Get reduced color sets
const reducedTo15 = reduceColors(inputColors, 15);
console.log("Reduced to 15 colors:", reducedTo15);

const reducedTo10 = reduceColors(inputColors, 10);
console.log("Reduced to 10 colors:", reducedTo10);

Key Details to Note

  • Similarity Accuracy: Euclidean distance works great for your small color set. If you need more perceptually accurate results (matching human eye better), you could swap in CIELAB color space distance, but that adds complexity unnecessary here.
  • Naming Logic: Merged colors get combined names (e.g., Color03+Color06) so you can track which originals were merged. Feel free to tweak this to use a different naming convention if needed.
  • Efficiency: The nested loop to find closest pairs is totally efficient for your 19-color array. For much larger arrays, you might want to optimize with a k-d tree, but that's overkill here.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:58:07