
返回笔记
QUICK NOTE
提取图片的近似平均色
提取图片的近似平均色 把图片缩成 1×1 得到的是浏览器缩放后的代表像素,不能直接称为“主色”。这里使用小尺寸采样并按透明度加权,得到近似平均 RGB;它适合做占位背景,不适合替代色彩聚类或无障碍对比度检查。 getImageData() 的 alpha 是 0—255,拼成传统 rgba() 前应除以 255。全透明
提取图片的近似平均色
把图片缩成 1×1 得到的是浏览器缩放后的代表像素,不能直接称为“主色”。这里使用小尺寸采样并按透明度加权,得到近似平均 RGB;它适合做占位背景,不适合替代色彩聚类或无障碍对比度检查。
async function approximateAverageColor(src) {
const image = new Image();
image.crossOrigin = 'anonymous'; // 必须在设置 src 前;服务器也要允许跨域
image.src = src;
await image.decode();
const canvas = document.createElement('canvas');
const scale = Math.min(1, 64 / Math.max(image.naturalWidth, image.naturalHeight));
canvas.width = Math.max(1, Math.round(image.naturalWidth * scale));
canvas.height = Math.max(1, Math.round(image.naturalHeight * scale));
const context = canvas.getContext('2d', { willReadFrequently: true });
if (!context) throw new Error('当前环境无法使用 Canvas 2D');
context.drawImage(image, 0, 0, canvas.width, canvas.height);
// 跨域资源未授权时,此处会抛 SecurityError,交由调用方处理。
const { data } = context.getImageData(0, 0, canvas.width, canvas.height);
let red = 0, green = 0, blue = 0, weight = 0;
for (let i = 0; i < data.length; i += 4) {
const alpha = data[i + 3] / 255;
red += data[i] * alpha;
green += data[i + 1] * alpha;
blue += data[i + 2] * alpha;
weight += alpha;
}
if (weight === 0) return null; // 全透明图没有可见平均色
return {
r: Math.round(red / weight),
g: Math.round(green / weight),
b: Math.round(blue / weight),
alpha: weight / (data.length / 4),
};
}
async function example(src) {
try {
const color = await approximateAverageColor(src);
return color ? `rgba(${color.r}, ${color.g}, ${color.b}, ${color.alpha})` : 'transparent';
} catch (error) {
console.error('读取图片失败', error);
return '#eeeeee';
}
}
getImageData() 的 alpha 是 0—255,拼成传统 rgba() 前应除以 255。全透明像素不参与 RGB 权重,避免透明区域里的隐藏颜色污染结果。这个平均值仍受采样尺寸和浏览器插值影响;代码没有做线性光空间换算。
红蓝各半的图可能得到紫色平均值,而紫色并不存在于原图中。若需求是“出现最多的颜色”,应对可见像素量化并统计直方图,或做聚类后按簇权重选色,并明确透明度、色彩空间与采样策略。
参考:MDN getImageData、跨域图片与 Canvas。2026-11-11 修订。