uni-app开发小程序,根据图片提取主题色值

需求,在页面根据传入的图片提取图片主色值并用来设置区块背景色

<template>
	<view class="icon-container">
		<view class="sport-icon" :style="{ backgroundColor: mainColor }">
			<image :src="'/static/images/sport/'+item.image" @load="handleImageLoad" class="li-img" mode="widthFix" />
		</view>
		<view class="sport-right">
			<view>
				<text class="sport-name">{{ item.name }}</text>
			</view>
			<view class="sport-text">{{ item.calorie }}千卡/{{ item.unit }}分钟</view>
		</view>
		<view class="align-self-end">
			<product-change :selected.sync="item.selected" @onChange="onChange" />
		</view>
		<!-- Canvas 2D画布(隐藏) -->
		<canvas type="2d" id="colorCanvas"
			style="position: absolute; left: -1000px; top: -1000px; width: 100px; height: 100px;"></canvas>
	</view>
</template>


<script>
	import productChange from './product-change.vue'
	export default {
		name: 'productItem',
		components: {
			productChange
		},
		props: {
			name: {
				type: String,
				default: ''
			},
			item: {
				type: Object,
				default: () => {}
			}
		},
		data() {
			return {
				imgUrl: '@/static/images/sport/icon-sport-default.png', // 示例图片
				mainColor: '#ffffff', // 初始背景色
				textColor: '#000000', // 文字颜色,根据背景色自动调整
				canvas: null, // Canvas实例
				ctx: null // Canvas 2D上下文
			};
		},
		mounted() {
			console.log(this.item)
			// 初始化Canvas 2D上下文(在组件挂载后获取)
			this.initCanvas();
		},
		methods: {
			onChange() {
				this.$emit('onChange', {
					name: this.name,
					item: this.item
				})
			},
			// 初始化Canvas 2D上下文
			initCanvas() {
				// 通过ID获取Canvas实例(兼容uni-app的获取方式)
				const query = uni.createSelectorQuery().in(this);
				query.select('#colorCanvas')
					.fields({
						node: true,
						size: true
					})
					.exec(res => {
						if (!res[0]) {
							console.error('未找到Canvas元素');
							return;
						}
						this.canvas = res[0].node;
						this.ctx = this.canvas.getContext('2d'); // 获取2D上下文
						// 设置Canvas尺寸(与样式尺寸一致)
						this.canvas.width = 100;
						this.canvas.height = 100;
					});
			},
			// 图片加载完成后触发
			handleImageLoad(e) {
				if (!this.canvas || !this.ctx) {
					console.error('Canvas未初始化完成');
					return;
				}
				const imgSrc = "/static/images/sport/" + this.item.image;
				// 获取图片信息(转为本地路径)
				uni.getImageInfo({
					src: imgSrc,
					success: (res) => this.drawToCanvas(res.path), // 绘制本地图片
					fail: (err) => {
						console.error('获取图片信息失败:', err);
						this.useDefaultColor();
					}
				});
			},
			// 绘制图片到Canvas(Canvas 2D方式)
			drawToCanvas(imagePath) {
				// 创建Image对象(Canvas 2D需要通过Image加载图片)
				const img = this.canvas.createImage();
				if (!imagePath.startsWith('/')) {
					imagePath = '/' + imagePath;
				}
				img.src = imagePath;
				// 图片加载完成后绘制到Canvas
				img.onload = () => {
					// 清空画布(避免残留旧内容)
					this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
					// 绘制图片(缩放到100x100,覆盖整个画布)
					this.ctx.drawImage(img, 0, 0, this.canvas.width, this.canvas.height);
					// 延迟100ms后获取数据(确保绘制完成)
					setTimeout(() => this.extractMainColor(), 100);
				};

				// 图片加载失败处理
				img.onerror = (err) => {
					console.error('图片绘制失败:', err);
					this.useDefaultColor();
				};
			},
			// 提取主色
			extractMainColor() {
				try {
					// 读取Canvas像素数据(Canvas 2D的getImageData)
					const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
					this.processImageData(imageData.data); // 处理像素数据
				} catch (err) {
					console.error('色值提取失败:{}', err);
				}
			},
			// 处理像素数据,计算主色
			processImageData(pixelData) {
				const colorFreq = {}; // 颜色出现频率
				let maxFreq = 0;
				let mainR = 255,
					mainG = 255,
					mainB = 255;

				// 遍历像素(每4个值为一组:R, G, B, A)
				for (let i = 0; i < pixelData.length; i += 4) {
					const r = pixelData[i];
					const g = pixelData[i + 1];
					const b = pixelData[i + 2];
					const a = pixelData[i + 3];

					// 忽略透明像素(透明度>50%的不统计)
					if (a < 128) continue;

					// 颜色量化(减少颜色种类,如每20阶合并一次)
					const key = `${Math.floor(r / 20)}-${Math.floor(g / 20)}-${Math.floor(b / 20)}`;
					colorFreq[key] = (colorFreq[key] || 0) + 1;

					// 记录出现频率最高的颜色
					if (colorFreq[key] > maxFreq) {
						maxFreq = colorFreq[key];
						mainR = r;
						mainG = g;
						mainB = b;
					}
				}

				// 设置主色和文字对比色
				this.mainColor = `rgb(${mainR}, ${mainG}, ${mainB},0.2)`;
				// 计算亮度(决定文字颜色)
				const luminance = (mainR * 299 + mainG * 587 + mainB * 114) / 1000;
				this.textColor = luminance > 130 ? '#000000' : '#ffffff';
			},
			// 使用默认颜色(失败时)
			useDefaultColor() {
				this.mainColor = '#f0f0f0';
				this.textColor = '#000000';
			}
		}
	}
</script>

<style lang="scss" scoped>
	.icon-container {
		border-bottom: 1px solid #F2F6FC;
		padding: 20rpx 40rpx;
		display: flex;
	}

	.li-img {
		// width: 55rpx;
		// height: 55rpx;
	}

	.sport-icon {
		width: 85rpx;
		height: 85rpx;
		padding: 15rpx;
		border-radius: 20rpx;
	}

	.sport-right {
		flex: 1;
		margin-left: 25rpx;
		width: 100%;
	}

	.sport-name {
		font-size: 32rpx;
	}

	.sport-text {
		color: #999;
		font-size: 26rpx;
	}

	.align-self-end {
		align-self: flex-end
	}

	.flex-end {
		display: flex;
		flex-direction: column;
		justify-content: flex-end;
	}
</style>

Color颜小程序Color颜是一个基于轮配原理的小程序。通过这一小程序,可以搭配出协调的彩,令你的配工作更加轻松。一、配广场,提供了中国和日本传统颜色,Adobe Color CC上最受欢迎的配方案以及自己收藏的配方案。二、颜色转换,提供了颜色在RGB、CMYK、LAB、HSB四个彩空间以及HEX的换算操作,方便在查询彩分量的同时,参考其他彩空间的。三、配工具,提供了基于六种颜色关系的配算法。 1、类比、相似:相邻的颜色称为类比。类比都拥有相近的颜色,该配原理根据给定的基准和角度,计算两个与基准夹角递增的类比以及两个与基准夹角递减的类比。1、类比、相似:相邻的颜色称为类比。类比都拥有相近的颜色,该配原理根据给定的基准和角度,计算两个与基准夹角递增的类比以及两个与基准夹角递减的类比。2、互补:在轮上直线相对的两种颜色称为互补。补形成强列的对比效果,传达出活力、能量、兴奋等意义。3、分裂补:如果同时用补及类比的方法来确定的颜色关系,就称为分裂补。这种颜色搭配既具有类比的低对比度的美感,又具有补的力量感。形成了一种既和谐又有重点的颜色关系。4、单:一种相由暗、中、明三种调组成。该算法根据基准相,按比例调整配的亮度和饱和度。5、渐变:渐变由指定的基准通过变化一定比例的亮度或饱和度来得到。6、二次配比:二次之间都拥有一种共同的颜色,其中两种拥有相同的蓝分量,两种拥有相同的绿分量,两种拥有相同的红分量。它们轻易能够形成协调的搭配。如果三种二次同时使用,则显得很舒适、吸引,并具有丰富的调。它们同时具有的颜色深度及广度。7、I am Feeling Lucky是在使用者无需考虑配原理的前提下,随机搭配一种配算法给出的配方案。四、Web颜色,列举了Web标准规范中HTML和CSS定义的标准和安全。扫码体验小程序截图        
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值