懒加载场景下如何在图片加载前获取图片平均RGB颜色
问题描述
我现有一段用于获取图片平均颜色的JavaScript代码,但该代码在图片懒加载场景下无法正常生效。
补充说明:我找到了相关的相似问题,但我的诉求与该问题存在差异。
我当前使用的HTML代码如下:
<div class="box"> <img class="the-img" src="http://localhost:90/image.jpg"> </div> <div class="box"> <img class="the-img" src="http://localhost:90/image.jpg"> </div> <div class="box"> <img class="the-img" src="http://localhost:90/image.jpg"> </div>
现有用于提取图片平均颜色的脚本代码如下:
function averageColor(imageElement) { // 创建canvas元素 var canvas = document.createElement('canvas'), // 获取canvas的2D上下文 context = canvas.getContext && canvas.getContext('2d'), imgData, width, height, length, // 存储红、绿、蓝三色通道值的变量 rgb = { r: 0, g: 0, b: 0 }, // 存储像素总计数的变量 count = 0; // 将canvas宽高设置为和图片一致 height = canvas.height = imageElement.naturalHeight || imageElement.offsetHeight || imageElement.height; width = canvas.width = imageElement.naturalWidth || imageElement.offsetWidth || imageElement.width; // 将图片绘制到canvas上 context.drawImage(imageElement, 0, 0); // 获取图片像素数据 imgData = context.getImageData( 0, 0, width, height); // 获取图片数据对象的长度 length = imgData.data.length; for (var i = 0; i < length; i += 4) { // 累加所有红色通道值 rgb.r += imgData.data[i]; // 累加所有绿色通道值 rgb.g += imgData.data[i + 1]; // 累加所有蓝色通道值 rgb.b += imgData.data[i + 2]; // 像素计数+1 count++; } // 计算红色通道平均值 rgb.r = Math.floor(rgb.r / count); // 计算绿色通道平均值 rgb.g = Math.floor(rgb.g / count); // 计算蓝色通道平均值 rgb.b = Math.floor(rgb.b / count); return rgb; } // 将计算出的图片平均色设置为对应box div的背景色 var rgb; var imgg = document.getElementsByClassName("the-img"); var blocks = document.getElementsByClassName("box"); var I; for (i = 0; i < imgg.length; i++) { rgb = averageColor(imgg[I]); blocks[i].style.backgroundColor = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')'; }
开启图片懒加载后上述代码失效,排查后确认原因是懒加载场景下图片初始src属性没有实际图片地址。目前查到的同类问题解决方案是监听每个图片的load事件,等图片加载完成后再计算平均色,参考代码如下:
var imgg = document.getElementsByClassName("the-img"); var blocks = document.getElementsByClassName("box"); for (var i = 0; i < imgg.length; i++) { setColor(I); } function setColor(i) { var $img = imgg[I]; // 懒加载的图片加载完成后触发 $img.addEventListener("load", e => { // 计算平均色并设置背景 var rgb = averageColor($img); blocks[i].style.backgroundColor = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')'; }); }
我的核心诉求:能不能在图片在页面上完成加载之前,就提前获取到图片的平均颜色?我知道这个实现有一定复杂度,需要相关的实现思路或者方向指引。
实现方案
完全可以在图片完整渲染到页面前拿到平均色,不需要等全图加载完成,有三个可直接落地的方向:
- 服务端预计算存元数据:这是性能最高、成本最低的方案。图片上传到存储服务时,服务端直接提前计算好每张图的平均色,和图片URL一起返回给前端。前端渲染页面时直接就能拿到平均色设置占位背景,完全不需要等图片加载,也不存在跨域问题,绝大多数内容平台、电商网站的懒加载占位色都是用这个方案实现的。
- 前端分块请求提前解析:如果改不了服务端逻辑,可以发HTTP Range请求先拉取图片的前1-3KB数据,直接解析图片文件头提取内嵌的低分辨率缩略图数据,不用等全图下载完就能算出误差极小的平均色。这个方案需要适配常见图片格式的头结构:JPEG格式本身就内嵌了缩略图数据,PNG、WEBP也能从头部拿到低分辨率版本的像素信息,计算速度比等全图加载快几十倍,注意需要图片存储服务支持Range请求、配置好跨域规则。
- 预加载极低分辨率占位图:配合懒加载逻辑,提前为每个图片生成体积只有几十字节的极低分辨率版本(比如11像素、33分辨率的模糊图),懒加载触发前先把这个极小图的地址放在
src属性上,先计算小图的平均色当占位背景,等全图加载完成后再替换即可。这个方案兼容性最好,不需要写复杂的文件解析逻辑,性能开销可以忽略。
另外提一句你现有代码里的显性bug:循环里用的计数变量是小写i,传参和取值的时候用了未定义的大写I,不管开不开懒加载这段代码都会报变量未定义错误,记得统一变量名。
内容的提问来源于stack exchange,提问作者olowu
相关产品推荐
相关产品推荐

