uniapp实现背景颜色跟随图片主题色变化(多端兼容)
最近做uniapp项目时遇到一个需求,要求模仿腾讯视频app首页的背景颜色跟随banner图片的主题颜色变化,并且还要兼容H5、APP、微信小程序三端。
由于项目的技术栈为uniapp,所以以下使用uni-ui的组件库作为栗子。
需求分析
腾讯视频app效果如下:
从上图看出,大致分为两步:
1.获取图片主题色
2.设置从上到下的
主题色
to白色
的渐变:
background: linear-gradient(to bottom, 主题色, 白色)
获取主题色主要采用canvas
绘图,绘制完成后获取r、g、b
三个通道的颜色像素累加值,最后再分别除以画布大小,得到每个颜色通道的平均值即可。
搭建页面结构
page.vue
<script>
import {
getImageThemeColor
} from '@/utils/index'
export default {
data() {
return {
// 图片列表
list: [],
// 当前轮播图索引
current: 0,
// 缓存banner图片主题色
colors: [],
// 记录当前提取到第几张banner图片
count: 0
}
},
computed: {
// 动态设置banner主题颜色背景
getStyle() {
const color = this.colors[this.current]
return {
background: color ? `linear-gradient(to bottom, rgb(${color}), #fff)` : '#fff'
}
}
},
methods: {
// banner改变
onChange(e) {
this.current = e.target.current
},
getList() {
this.list = [
'https://img.zcool.cn/community/0121e65c3d83bda8012090dbb6566c.jpg@3000w_1l_0o_100sh.jpg',
'https://img.zcool.cn/community/010ff956cc53d86ac7252ce64c31ff.jpg@900w_1l_2o_100sh.jpg',
'https://img.zcool.cn/community/017fc25ee25221a801215aa050fab5.jpg@1280w_1l_2o_100sh.jpg',
]
},
// 获取主题颜色
getThemColor() {
getImageThemeColor(this, this.list[this.count], 'canvas', (color) => {
const colors = [...this.colors]
colors[this.count] = color
this.colors = colors
this.count++
if (this.count < this.list.length) {
this.getThemColor()
}
})
}
},
onLoad() {
this.getList()
// banner图片请求完成后,获取主题色
this.getThemColor()
}
}
script>
<style>
.box {
display: flex;
flex-direction: column;
background-color: deeppink;
padding: 10px;
}
.tabs {
height: 100px;
color: #fff;
}
.swiper {
width: 95%;
height: 200px;
margin: auto;
border-radius: 10px;
overflow: hidden;
}
image {
width: 100%;
height: 100%;
}
style>
封装获取图片主题颜色函数
先简单讲下思路 (想直接看源码可直接跳到下面) 。先通过request请求图片地址,获取图片的二进制数据,再将图片资源其转换成base64,调用drawImage
进行绘图,最后调用draw
方法绘制到画布上。
CanvasContext.draw介绍
更多api使用方法可参考:uniapp官方文档
getImageThemeColor.js
/**
* 获取图片主题颜色
* @param path 图片的路径
* @param canvasId 画布id
* @param success 获取图片颜色成功回调,主题色的RGB颜色值
* @param fail 获取图片颜色失败回调
*/
export const getImageThemeColor = (that, path, canvasId, success = () => {}, fail = () => {}) => {
// 获取图片后缀名
const suffix = path.split('.').slice(-1)[0]
// uni.getImageInfo({
// src: path,
// success: (e) => {
// console.log(e.path) // 在安卓app端,不管src路径怎样变化,path路径始终为第一次调用的图片路径
// }
// })
// 由于getImageInfo存在问题,所以改用base64
uni.request({
url: path,
responseType: 'arraybuffer',
success: (res) => {
let base64 = uni.arrayBufferToBase64(res.data);
const img = {
path: `data:image/${suffix};base64,${base64}`
}
// 创建canvas对象
const ctx = uni.createCanvasContext(canvasId, that);
// 图片绘制尺寸
const imgWidth = 300;
const imgHeight = 150;
ctx.drawImage(img.path, 0, 0, imgWidth, imgHeight);
ctx.save();
ctx.draw(true, () => {
uni.canvasGetImageData({
canvasId: canvasId,
x: 0,
y: 0,
width: imgWidth,
height: imgHeight,
fail: fail,
success(res) {
let data = res.data;
let r = 1,
g = 1,
b = 1;
// 获取所有像素的累加值
for (let row = 0; row < imgHeight; row++) {
for (let col = 0; col < imgWidth; col++) {
if (row == 0) {
r += data[imgWidth * row + col];
g += data[imgWidth * row + col + 1];
b += data[imgWidth * row + col + 2];
} else {
r += data[(imgWidth * row + col) * 4];
g += data[(imgWidth * row + col) * 4 + 1];
b += data[(imgWidth * row + col) * 4 + 2];
}
}
}
// 求rgb平均值
r /= imgWidth * imgHeight;
g /= imgWidth * imgHeight;
b /= imgWidth * imgHeight;
// 四舍五入
r = Math.round(r);
g = Math.round(g);
b = Math.round(b);
success([r, g, b].join(','));
},
}, that);
});
}
});
}
主题色计算公式
计算图片主题色的公式主要有两种常见的方法:平均法和主成分分析法。
平均法:
平均法是最简单的一种方法,它通过对图片中所有像素点的颜色进行平均来计算主题色。具体步骤如下:
- 遍历图片的每个像素点,获取其RGB颜色值。
- 将所有像素点的R、G、B分量分别求和,并除以像素点的总数,得到平均的R、G、B值。
- 最终的主题色即为平均的R、G、B值。
主成分分析法
主成分分析法是一种更复杂但更准确的方法,它通过对图片中的颜色数据进行降维处理,提取出最能代表整个图片颜色分布的主要特征。具体步骤如下:
- 将图片的所有像素点的颜色值转换为Lab颜色空间(Lab颜色空间是一种与人眼感知相关的颜色空间)。
- 对转换后的颜色数据进行主成分分析,找出相应的主成分。
- 根据主成分的权重,计算得到最能代表整个图片颜色分布的主题色。
需要注意的是,计算图片主题色的方法可以根据具体需求和算法的实现方式有所不同,上述方法只是其中的两种常见做法。
结语
大家有更好的实现方式,欢迎评论区留言哦!
作者:vilan_微澜
来源:juejin.cn/post/7313979304513044531
来源:juejin.cn/post/7313979304513044531