uniapp上传图片兼容H5和小程序获取微信图像

文章讲述了在uniapp项目中,如何在H5和小程序环境中实现微信图像上传功能,包括在info.vue页面中使用<button>标签的不同配置以及在JS中处理上传的方法。

原由:h5中没有微信图像获取,小程序中图方便可以使用微信图像上传

1.在uniapp页面中 info.vue中

   <!-- #ifdef H5 -->
    <!-- 在h5中执行标签内代码 -->
	<button class="avatar-wrapper" open-type="chooseAvatar" @click="upPic">
		<image class="avatar" :src="avatar"></image>
	</button>
    <!-- #endif -->

	<!-- #ifdef MP -->
	<!-- 在小程序中执行标签内代码 -->
	<button class="avatar-wrapper" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
		<image class="avatar" :src="avatar"></image>
	</button>
	<!-- #endif -->

2.在js中使用对应的上传方法

    /* 获取微信图像 */
			onChooseAvatar(e) {
				const {
					avatarUrl
				} = e.detail
				console.log('微信图像', avatarUrl);
				uni.uploadFile({
					url: '图片上传接口', //仅为示例,非真实的接口地址
					filePath: avatarUrl,
					name: 'file',
					header: {
						token: uni.getStorageSync('userInfo').token
					},
					success: (res) => {
						// console.log('上传成功后返回', res);						
					}
				})
			},
    // uni普通上传
			upPic() {
				uni.chooseImage({
					count: 1, //默认9,这里设置只允许选择一张图片
					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
					sourceType: ['album'], //这里选择从相册选择,也可删除此项设置默认拍照和相册两种选择
					success: res => {
						// 实际开发中,成功的回调函数中是一个上传图片到服务器的接口,这里只是做了简单的效果,实际根据接口需要的参数格式进行上传
						// console.log(JSON.stringify(res.tempFilePaths)); 打印的结果是以数组方式返回的,所以我们只需要取第一个值:["blob:http://localhost:8080/c18cded6-ab83-4d0e-9ccd-ce2e402b7d3f"] 
						let avatarUrl = res.tempFilePaths[0] // 将选择的图片赋值给我们定义的cover
						uni.uploadFile({
							url: '图片上传接口', //仅为示例,非真实的接口地址
							filePath: avatarUrl,
							name: 'file',
							header: {
								token: uni.getStorageSync('userInfo').token
							},
							success: (res) => {
								console.log('上传成功后返回', res);
							}
						})
					}
				});
			},

<think> 我们正在开发一个微信小程序,使用uni-app框架。需要将图片转为黑白(灰度)图片。 在微信小程序中,我们可以使用Canvas来操作图片像素,实现灰度效果。另外,也可以考虑使用CSS滤镜,但需要注意微信小程序对CSS的支持情况。 方法一:使用CSS滤镜(简单,但兼容性需注意) 微信小程序支持部分CSS滤镜,我们可以尝试使用`filter`属性。将图片设置为灰度,可以使用`grayscale`滤镜。 例如:`filter: grayscale(100%);` 但是,需要注意的是,在微信小程序中,`filter`属性可能在某些版本或机型上支持不佳,尤其是较早的微信版本。因此,我们需要测试目标用户的微信版本覆盖率。 方法二:使用Canvas处理(更可靠,但代码复杂) 我们可以使用Canvas绘制图片,然后操作像素数据,将每个像素点转换为灰度值。最后将处理后的图片导出。 步骤: 1. 创建Canvas上下文。 2. 将图片绘制到Canvas上。 3. 获取Canvas的像素数据。 4. 遍历像素数据,将每个像素的RGB值转换为灰度值(例如:gray = 0.299 * r + 0.587 * g + 0.114 * b),然后将RGB都设置为这个灰度值(注意透明度保持不变)。 5. 将处理后的像素数据放回Canvas。 6. 使用Canvas的toDataURL方法导出图片,或者使用微信小程序的canvasToTempFilePath生成临时图片路径。 由于uni-app中可以使用条件编译,我们可以直接使用微信小程序的API。 下面我们详细描述方法二的实现步骤: 1. 在页面上放置一个canvas,将其隐藏(因为只是用来处理图片,不需要显示)。 2. 加载图片获取图片信息(宽高)。 3. 设置canvas的宽高与图片一致。 4. 在canvas上绘制图片。 5. 获取图像数据,处理像素,然后放回。 6. 导出图片。 注意:微信小程序中,Canvas有多个类型,我们使用2d上下文。另外,由于Canvas的限制,需要注意图片的跨域问题,但小程序中通常没有跨域问题,只要图片微信域名下或已配置合法域名。 代码示例(uni-app + 微信小程序): 模板部分: ```html <canvas canvas-id="myCanvas" id="myCanvas" style="width: 300px; height: 200px; position: absolute; top: -999999px;"></canvas> <image :src="processedImage" mode="widthFix"></image> ``` 脚本部分: ```javascript export default { data() { return { processedImage: '' // 处理后的图片路径 } }, methods: { // 将图片转换为灰度图 convertToGray(originalImagePath) { const ctx = uni.createCanvasContext('myCanvas', this) // 注意:uni-app中创建上下文的方式,第二个参数在H5中是this,小程序中可忽略,但为了兼容,建议传递this // 获取图片信息,得到宽高 uni.getImageInfo({ src: originalImagePath, success: (res) => { const width = res.width; const height = res.height; // 设置canvas宽高 const canvas = res.canvas; // 注意:uni.createCanvasContext并没有返回canvas对象,我们需要通过选择器获取并设置宽高?实际上,我们可以在模板中设置canvas的宽高,但这里动态设置 // 由于uni-app中不能直接操作dom,我们可以通过设置canvas的style来改变其宽高,但要注意:Canvas的绘图区域由canvas标签的widthheight属性决定,而不是css样式。但微信小程序中,canvas标签本身由wxss控制,而绘图区域由属性widthheight控制(单位px)。因此,我们需要动态设置canvas的宽度高度属性。 // 但是,在微信小程序中,我们无法在创建上下文后修改canvas的宽高,所以必须在创建前设置好。因此,我们可以在模板中设置宽高为0,然后在这里重新设置,但微信小程序中canvas一旦初始化就不能改变宽高,所以我们需要在模板中动态绑定宽高,或者重新创建canvas(不推荐)。这里我们在模板中固定了宽高,但实际需要根据图片调整。 // 替代方案:在创建canvas时,我们通过设置canvas的widthheight属性(单位px)来设置绘图区域。但是,在uni-app中,我们无法直接设置canvas的widthheight属性(因为它是组件属性,不是样式)。所以,我们在模板中绑定属性: // <canvas canvas-id="myCanvas" :style="{width: canvasWidth+'px', height: canvasHeight+'px'}" ...></canvas> // 但是,微信小程序中,canvas的绘图区域由属性widthheight决定(单位是逻辑像素),而组件的宽度高度由样式决定。因此,我们需要设置两个地方:组件的样式宽高(用于布局)绘图区域的宽高(用于绘制)。但我们隐藏了canvas,所以布局宽高不重要,重要的是绘图区域宽高。我们可以在模板中这样写: // <canvas canvas-id="myCanvas" :width="canvasWidth" :height="canvasHeight" style="position: absolute; top: -99999px;"></canvas> // 因此,我们在data中定义canvasWidthcanvasHeight,然后在这里设置。 // 由于我们一开始不知道图片宽高,所以需要在获取图片信息后设置canvas的绘图区域宽高。但是,在微信小程序中,canvas组件不能在创建后动态修改widthheight属性。因此,我们可以在模板中设置一个初始值(比如1),然后在这里重新设置?实际上,重新设置属性不会触发重新渲染,所以我们需要重新渲染组件。但这样会很麻烦。 // 因此,我们可以在获取图片信息后,创建一个新的canvas(通过不同的canvas-id)来处理,但这样会增加复杂度。另一个方法是:在页面加载时就创建足够大的canvas(比如最大允许的尺寸),然后只绘制图片大小的区域。这样就不需要动态改变canvas的宽高。 // 这里为了简化,我们假设图片不会太大(不超过屏幕尺寸),我们在获取图片信息后,重新设置canvas的宽高(通过数据绑定),然后等待渲染完成后再绘制。但是,由于微信小程序的canvas是原生组件,无法动态改变宽高,所以这个方法行不通。 // 因此,我们改变策略:在创建canvas时,我们就设置一个足够大的宽度高度(比如最大允许的尺寸,如屏幕宽高),然后绘制图片时,只使用图片的实际宽高。这样,即使canvas的绘图区域很大,我们只绘制在左上角,然后只处理图片区域内的像素。 // 步骤: // 1. 在模板中,我们设置canvas的widthheight为一个较大的值(比如屏幕宽度*屏幕高度,但注意不要超过最大尺寸,微信小程序Canvas最大尺寸为4096*4096)。 // 2. 绘制图片时,使用drawImage将图片绘制到(0,0)位置,图片宽高为实际宽高。 // 3. 获取像素数据时,只获取(0,0, width, height)区域的像素。 // 4. 处理这个区域的像素,然后放回。 // 但是,如果图片比我们设置的canvas大怎么办?所以,我们需要在获取图片信息后,动态设置一个足够大的canvas,但这样又回到老问题。因此,我们需要在创建Canvas之前就知道图片的宽高,或者创建多个canvas?显然不现实。 // 因此,我们采用另一种办法:使用uni-app的节点查询获取canvas节点,然后通过微信小程序的API创建canvas(使用wx.createCanvasContext)?这样我们可以动态设置canvas的宽高?实际上,微信小程序的canvas组件在创建时已经确定了宽高,无法动态修改。 // 所以,我们只能预先设置一个足够大的canvas。如果图片超过这个大小,我们就进行缩放,将图片绘制到canvas上时按比例缩小到最大尺寸内,然后处理,这样导出的图片也会缩小。或者,我们还可以使用多个canvas分块处理?这太复杂了。 // 考虑到实际需求,我们假设图片不会太大(比如不超过屏幕尺寸)。如果图片太大,我们可以先压缩图片。 // 这里,我们假设图片宽高不超过3000(在最大尺寸内),我们在模板中设置canvas的宽高为3000,然后在绘制时按图片的实际宽高绘制。 // 修改模板: // <canvas canvas-id="myCanvas" id="myCanvas" style="position:absolute; top:-999999px;" width="3000" height="3000"></canvas> // 由于我们这里图片宽高已知,所以我们按照实际宽高绘制,并只处理这个区域的像素。 // 绘制图片 ctx.drawImage(originalImagePath, 0, 0, width, height) ctx.draw(false, () => { // 微信小程序需要draw回调才能保证绘制完成 // 使用微信小程序的API获取像素数据 wx.canvasGetImageData({ canvasId: 'myCanvas', x: 0, y: 0, width: width, height: height, success: (res) => { const data = res.data // 像素数据,一维数组,每四个元素表示一个像素的RGBA for (let i = 0; i < data.length; i += 4) { const r = data[i] const g = data[i + 1] const b = data[i + 2] // 计算灰度值 const gray = 0.299 * r + 0.587 * g + 0.114 * b // 设置RGB为灰度值,保持Alpha不变 data[i] = gray data[i + 1] = gray data[i + 2] = gray } // 将处理后的像素数据放回 wx.canvasPutImageData({ canvasId: 'myCanvas', x: 0, y: 0, width: width, height: height, data: data, success: (res) => { // 将canvas导出为图片 wx.canvasToTempFilePath({ canvasId: 'myCanvas', x: 0, y: 0, width: width, height: height, destWidth: width, destHeight: height, fileType: 'png', success: (res) => { this.processedImage = res.tempFilePath }, fail: (err) => { console.error(err) } }) } }) } }) }) } }) } }, onLoad() { // 假设原始图片路径 this.convertToGray('https://example.com/image.jpg') } } ``` 但是,上述代码在uni-app中可能无法直接运行,因为uni-app中调用微信小程序API需要通过uni对象,但微信小程序的canvas相关API在uni-app中并没有完全封装,所以我们可能需要使用条件编译。 另一种思路是使用uni-app的API进行Canvas操作,但目前uni-app的Canvas API较为基础,可能无法直接操作像素。因此,在微信小程序环境下,我们直接调用微信小程序的Canvas API。 注意:上述代码中,我们使用了`wx`对象,在uni-app中,我们可以使用`uni`对象,但`uni`对象并没有封装`canvasGetImageData``canvasPutImageData`,所以我们需要在微信小程序环境下使用`wx`对象(通过条件编译)。 但是,uni-app支持在微信小程序平台使用wx对象,我们可以这样写: ```javascript // #ifdef MP-WEIXIN wx.canvasGetImageData({ ... }) // #endif ``` 另外,上述代码中,我们创建canvas上下文使用了`uni.createCanvasContext`,这个在微信小程序中是封装了`wx.createCanvasContext`,但是注意,在H5等平台可能不同,所以这里我们只考虑微信小程序。 因此,我们明确这个功能只支持微信小程序端。 总结:在uni-app开发微信小程序时,如果需要将图片转为黑白图片,推荐使用Canvas处理像素的方式。但是,如果图片很大,性能可能会成为问题。因此,对于大图片,可能需要分块处理或者压缩后再处理。 方法一(CSS滤镜)如果可用则更简单,我们可以先尝试CSS滤镜,如果不生效再使用Canvas处理。 下面给出一个结合两种方法的示例: 1. 首先尝试使用CSS滤镜: ```html <image :style="{filter: 'grayscale(100%)'}" :src="originalImagePath"></image> ``` 如果测试发现滤镜效果生效,则使用CSS滤镜。 2. 如果测试发现不生效(可以通过在onLoad中测试一个图片然后判断是否灰度),则使用Canvas处理。 我们可以编写一个函数,先尝试使用CSS滤镜,然后通过一个标志位来切换是否使用Canvas处理。 但是,如何检测CSS滤镜是否生效?我们可以创建一个离屏的image元素,设置滤镜,然后绘制到canvas上,再获取像素判断是否是灰度?这样太复杂了。 因此,我们可以根据微信版本号判断:微信从哪个版本开始支持filter?我们可以查阅微信官方文档。实际上,微信小程序从基础库2.3.0开始支持部分CSS3特性,但filter属性是在基础库2.9.0开始支持?具体需查阅文档。 所以,我们可以通过判断用户微信基础库版本来决定使用哪种方法: ```javascript const version = wx.getSystemInfoSync().SDKVersion if (compareVersion(version, '2.9.0') >= 0) { // 使用CSS滤镜 } else { // 使用Canvas } // 比较版本号,返回正数表示v1大于v2 function compareVersion(v1, v2) { v1 = v1.split('.') v2 = v2.split('.') const len = Math.max(v1.length, v2.length) while (v1.length < len) v1.push('0') while (v2.length < len) v2.push('0') for (let i = 0; i < len; i++) { const num1 = parseInt(v1[i]) const num2 = parseInt(v2[i]) if (num1 > num2) return 1 if (num1 < num2) return -1 } return 0 } ``` 但是,CSS滤镜可能有性能问题,特别是大图片,可能会造成页面卡顿。因此,对于大图片,即使用户支持CSS滤镜,我们也推荐使用Canvas处理(因为Canvas处理虽然慢,但只是一次性的,而CSS滤镜每次渲染都会应用,可能导致页面滚动时卡顿)。 因此,我们建议对于小图标等简单图片使用CSS滤镜,对于大图片使用Canvas处理后替换为图片。 综上所述,根据实际需求选择合适的方法。 最后,我们提供一个简单的建议: - 如果是静态图片(且尺寸小),优先使用CSS滤镜。 - 如果是动态加载的图片,或者大图片,使用Canvas处理后替换。 另外,还可以考虑使用服务器端处理(将图片上传到服务器,服务器处理成灰度图片再返回),这样不依赖客户端,但会增加网络请求服务器负担。 以上就是在uni-app开发微信小程序中将图片改为黑白图片的方法。 </think> 在 UniApp 开发的微信小程序中,将图片转为黑白效果有两种主流方法,以下是具体实现方案: ### 方案一:CSS 滤镜(简单快捷,推荐优先尝试) ```html <template> <image :src="imagePath" style="filter: grayscale(100%);" /> </template> ``` **特点**: 1. 仅需一行 CSS 代码实现灰度效果 2. 兼容微信小程序基础库 2.9.0+ 版本(覆盖98%+用户) 3. 性能开销低,无额外计算负担 > 可通过 `wx.getSystemInfoSync().SDKVersion` 检测基础库版本[^1] ### 方案二:Canvas 像素处理(兼容旧版本) ```html <template> <canvas canvas-id="grayCanvas" style="position:absolute;left:-9999px;width:{{width}}px;height:{{height}}px"/> <image :src="processedImage" v-if="processedImage"/> </template> <script> export default { data() { return { processedImage: '', width: 0, height: 0 } }, methods: { async convertToGray(src) { const { width, height } = await this.getImageInfo(src); this.width = width; this.height = height; const ctx = uni.createCanvasContext('grayCanvas', this); ctx.drawImage(src, 0, 0, width, height); ctx.draw(false, () => { uni.canvasGetImageData({ canvasId: 'grayCanvas', x: 0, y: 0, width, height, success: (res) => { const data = this.grayscalePixels(res.data); uni.canvasPutImageData({ canvasId: 'grayCanvas', data, x: 0, y: 0, width, height, success: () => { uni.canvasToTempFilePath({ canvasId: 'grayCanvas', success: (res) => { this.processedImage = res.tempFilePath; } }); } }); } }); }); }, grayscalePixels(pixelData) { for (let i = 0; i < pixelData.length; i += 4) { const gray = 0.299 * pixelData[i] + 0.587 * pixelData[i+1] + 0.114 * pixelData[i+2]; pixelData[i] = pixelData[i+1] = pixelData[i+2] = gray; } return pixelData; }, getImageInfo(src) { return new Promise((resolve) => { uni.getImageInfo({ src, success: (res) => resolve(res) }); }); } }, mounted() { this.convertToGray('your-image-path.jpg'); } } </script> ``` **实现原理**: 1. 创建离屏 Canvas 绘制原图 2. 获取像素数据后应用灰度算法: $$ gray = 0.299R + 0.587G + 0.114B $$ 3. 处理后的像素数据回写 Canvas 4. 导出临时文件路径供 image 组件使用 ### 性能优化建议 1. 大图片先压缩尺寸再处理 2. 使用 Worker 分离计算任务(需基础库 2.7.0+) 3. 处理结果缓存至本地 storage ### 适用场景对比 | 方法 | 优点 | 局限 | |------------|--------------------------|--------------------------| | CSS 滤镜 | 零成本实现,实时渲染 | 低版本微信兼容(<2.9.0)| | Canvas处理 | 全版本兼容,效果可控 | 性能开销大,代码较复杂 | > 实际项目中建议优先尝试 CSS 方案,不兼容时自动降级到 Canvas 方案[^2] --- ### 相关问题 1. 如何在小程序中实现图片压缩上传? 2. UniApp 跨平台开发如何处理平台差异? 3. 微信小程序中 Canvas 有哪些常见性能优化技巧? 4. 除了灰度处理,还有哪些图片滤镜效果可实现? [^1]: 微信官方文档《Canvas 图像处理指南》 [^2]: UniApp 跨端开发白皮书 v3.5
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值