一起网易云
网易云音乐想必是大家很相熟的一款 app 了,毕竟大家在深夜都会网抑云
开玩笑了,最近在网易云听歌时,发现了一个很有意思的特效:
就是切换歌曲时,会依据以后封面替换背景色。作为资深切图仔,我那该死的好奇心兜不住了,不行,我要去一探到底。
首先我构思了很多它可能的实现形式:
- 机器学习对图片进行色调剖析
- 前端提取图片主色调,做突变解决
- 封面背景图做高斯含糊
对于第一种,他不在我的常识范畴内,这里就不开展阐明了 。
第二种的话,个别都是利用canvas
来实现。
第三种相对来说,从技术层面来看,实现上是最为简略的。
做了猜想剖析后,我默默关上了相熟的 Chrome 控制台,关上了网易云音乐的源代码:
好家伙,果然是第三种实现形式。
原本到这里,本文就该完结了。但之前也有敌人问过我如何对前端图片主题色进行提取
的问题,正好之前也做过相似的需要,这里就开展做个阐明吧。
咱们这里以一个图片网站为例,来展现理论业务中利用较广的场景:
在弱网下,图片加载速度较慢,此时在图片齐全加载之前,提取图片的主色调,而后填充为背景色。这样用户体验能有较大的晋升。
那具体是怎么实现的呢?
咱们这里采纳canvas
来实现,具体分为三步:
- 获取图片数据
- 对图片数据进行解决
- 对色彩列表排序
这里咱们应用的测试图片为:
相对来说,主色调较为显著,也便于测试~
获取图片数据
咱们晓得图片是由一个个像素点组成的。通过 canvas 的getImageData()
办法恰好能够获取图片的像素数据:
let imgObj = document.getElementById('yourId');// 创立画布let canvas = document.createElement('canvas');canvas.setAttribute('width', imgObj.width);canvas.setAttribute('height', imgObj.height);let context = canvas.getContext('2d');// 将图片画在画布上context.drawImage(imgObj, 0, 0);// 获取像素数据let imgData = context.getImageData(0, 0, imgObj.width, imgObj.height);let pixelData = imgData.data;
但这时你去打印pixelData
,你会发现后果为:
好家伙,全是 0,,,
我一时想不到是什么起因:难道是 canvas 的 api 应用不纯熟?
在stackoverflow
上找到了下面的答复:
然而我批改后还是不行。
这时,我想到图片加载是异步的。可能图片还没加载结束就开始从画布读取图片数据了,显然这是不对的。于是我对原有代码做了一番调整:
getMainColor("./test.jpeg");function getMainColor(image) { return new Promise((resolve, reject) => { try { const canvas = document.createElement("canvas"); const img = new Image(); // 创立img元素 img.src = image; // 设置图片源地址 img.onload = () => { let color = getImageColor(canvas, img); resolve(color); }; } catch (e) { reject(e); } });}function getImageColor(canvas, img) { const context = canvas.getContext("2d"); context.drawImage(img, 0, 0); // 获取像素数据 let pixelData = context.getImageData( 0, 0, canvas.width, canvas.height ).data; console.log("pixelData", pixelData); return pixelData;}
事实证明:it's true
获取了图片数据,下一步就要对其进行相应的解决。
对图片数据进行解决
开展上一步失去的数据:
这里的数据是什么意思呢?其实就是rgba
,散布代表红色(Red)
,绿色(Green)
,蓝色(Blue)
和透明度(Alpha)
。 rgba
的图片每个像素点是由下面四个数值示意的。也就是说每四个为一组。
晓得了法则,那让咱们来对数据做一下荡涤:次要就是对色彩进行分组,并统计每种色彩别离呈现的次数:
function getImageColor(canvas, img) { const context = canvas.getContext("2d"); context.drawImage(img, 0, 0); // 获取像素数据 let pixelData = context.getImageData( 0, 0, canvas.width, canvas.height ).data; console.log("pixelData", pixelData); return getCountsArr(pixelData);}function getCountsArr(pixelData) { let colorList = []; let rgba = []; let rgbaStr = ""; // 分组循环 for (let i = 0; i < pixelData.length; i += 4) { rgba[0] = pixelData[i]; rgba[1] = pixelData[i + 1]; rgba[2] = pixelData[i + 2]; rgba[3] = pixelData[i + 3]; if (rgba.indexOf(undefined) !== -1 || pixelData[i + 3] === 0) { continue; } // console.log("rgba", rgba); rgbaStr = rgba.join(","); if (rgbaStr in colorList) { ++colorList[rgbaStr]; } else { colorList[rgbaStr] = 1; } } console.log("colorList", colorList); return colorList;}
打印colorList
后果为:
到这里,咱们就失去了每种数据别离呈现的次数。
对色彩列表排序
最初一步,对下面失去的色值对象做一个排序:
for (let prop in colorList) { arr.push({ // 如果只获取rgb,则为`rgb(${prop})` color: `rgba(${prop})`, count: colorList[prop], });}// 数组排序arr.sort((a, b) => { return b.count - a.count;});console.log("arr", arr);
排序后失去如下后果:
到这里咱们就失去了图片色值呈现次数从大到小的排序数组,咱们来看排在第一位的rgba(206,205,201,255)
:
再把测试图片贴一下:
肉眼可见的主题色曾经被提取进去了!
反思
最初还是回到文章最开始提到的网易云音乐的播放器特效。不论它的实现形式是怎么样的,它的这种产品创意是值得咱们学习的。
咱们平时在浏览国内外的一些网站或者应用一些 app 时,总能遇到一些让你拍手称誉的成果。而这些特效往往又与咱们前端分不开。
俗话说:前端是离产品最近的开发工程师
,那最近你有没有遇到一些让你感觉很惊艳或者很有想法的成果呢,欢送在评论区留言
❤️爱心三连
1.如果感觉这篇文章还不错,来个分享、点赞、在看三连吧,让更多的人也看到~
2.关注公众号前端森林,定期为你推送陈腐干货好文。
3.非凡阶段,带好口罩,做好集体防护。