網易雲音樂想必是你們很熟悉的一款 app 了,畢竟你們在深夜都會網抑雲
前端
開玩笑了,最近在網易雲聽歌時,發現了一個頗有意思的特效:就是切換歌曲時,會根據當前封面替換背景色。做爲資深切圖仔,我那該死的好奇心兜不住了,不行,我要去一探究竟。canvas
首先我構思了不少它可能的實現方式:api
對於第一種,他不在個人知識範圍內,這裏就不展開說明了 😂。數組
第二種的話,通常都是利用canvas
來實現。markdown
第三種相對來講,從技術層面來看,實現上是最爲簡單的。app
作了猜想分析後,我默默打開了熟悉的 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.特殊階段,帶好口罩,作好我的防禦。