uni-app —— 小程序自定义封装支持多图上传(支持图片压缩功能)

4 篇文章 0 订阅
2 篇文章 1 订阅

最近在使用uni-app的上传功能时,发现目前只有APP端支持多张图一起上传,而小程序不支持,于是自己动手封装一下支持一次上传多张图片。

原理:其实底层还是一张图片一张图片上传,只是获取的时候可以一次获取多张,然后写代码自动依次处理这多张图片上传,即把图片多次上传的麻烦手动过程给自动化了。

<template>
	<view class="container">
		<view class="canvas-view">
			<canvas id="firstCanvas" class="firstCanvas" canvas-id="firstCanvas" :style="{width: canvasWidth+'px', height: canvasHeight+'px'}"></canvas>
		</view>
		
		<view class="list-box">
			<view 
				class="item" 
				v-for="(item, index) in imgList" 
				:key="index">
				<image class="img" :src="item" mode="aspectFit" lazy-load="true" @tap="previewImage(index)"></image>
				<text class="del-btn" @tap="delImg(index)">-</text>
			</view>
			<view 
				class="add-btn" 
				@tap="addImg" 
				v-if="imgList.length < maxCount">
				<text class="iconfont icon">&#xe632;</text>
				<text class="txt">添加图片</text>
			</view>
		</view>
	</view>
</template>

 

<script>
	/*
		组件简介:图片压缩上传(可支持多图上传) 
	*/
	export default {
		props: {
			defaultImgList: { // 默认图片列表
				type: Array,
				default: () => []
			},
			count: { // 图片一次上传限制数量
				type: Number,
				default: 5
			}, 
			maxCount: { // 最大上传数量
				type: Number,
				default: 5
			}, 
			sizeType: { // 可以指定是原图还是压缩图,默认二者都有
				type: Array,
				default: () => ['original', 'compressed']
			}, 
			sourceType: { // album 从相册选图,camera 使用相机,默认二者都有。如需直接开相机或直接选相册,请只使用一个选项
				type: Array,
				default:  () => ['album']
			}, 
			destWidth: { // 输出图片的最大宽度
				type: Number,
				default: 500,
			},
			quality: { // 输出图片的质量,取值范围为 (0, 1],不在范围内时当作1.0处理
				type: Number,
				default: 0.6
			}
		},
		data() {
			return {
				imgList: [], // 图片列表
				uploadUrl: '', // 上传图片的服务器请求地址
				visiable: false, // 显示组件的标识
				context: undefined, // canvas 绘图上下文
				canvasWidth: 300, // 默认宽度 300px、高度 225px
				canvasHeight: 225,
			};
		},
		mounted() {
			if(this.defaultImgList.length > 0){
				this.imgList = this.defaultImgList;
			}
			if(this.$store.uploadUrl){
				this.visiable = true;
				this.uploadUrl = this.$store.uploadUrl;
			} else {
				this.getUploadFileUrl();
			}
			this.context = uni.createCanvasContext('firstCanvas', this);
		},
		methods: {
			
			/* 获取图片上传地址 */
			getUploadFileUrl() {
				this.$http.get({
					url: 'api链接',
					data: {
						uploadFileType: 'saveHeadImg'
					}
				}).then(res => {
					if(res.code === '0000'){
						this.$store.commit('setUploadUrl', res.uploadUrl);
						this.uploadUrl = res.uploadUrl;
						this.visiable = true;
					}
				})
			},
			
			/* 删除图片 */
			delImg(index) {
				uni.showModal({
					title: '',
					content: '确认删除当前图片?',
					success: (res) => {
						if (res.confirm) {
							this.imgList.splice(index, 1);
							this.$emit('imgListChange', this.imgList);
							uni.showToast({
								title: '删除成功!'
							});
						}
					}
				});			
			},
			
			/* 添加图片 */
			addImg() {
				if(!this.uploadUrl){
					return;
				}
				// 从本地相册选择图片或使用相机拍照
				uni.chooseImage({
					count: this.count, // 默认5
					sizeType: this.sizeType, //可以指定是原图还是压缩图,默认二者都有
					sourceType: this.sourceType, //从相册选择
					success: (res) => {
						console.log(JSON.stringify(res.tempFilePaths));
						const tempFilePaths = res.tempFilePaths;	
						this.compressImg(tempFilePaths, [], 0);
						// 上传服务器
						// this.uploadImg(tempFilePaths, 0);
					}
				});
			},
			
			/* 压缩图片 */
			compressImg(tempFilePaths, compressPaths, index) {
				uni.showLoading({
					title: '处理中'
				});
				let i = index;
				let arr = [...compressPaths];
				uni.getImageInfo({
					src: tempFilePaths[i],
					success: (res) => {
						
						let canvasWidth = res.width // 图片原始长宽
						let canvasHeight = res.height;
						let base = canvasWidth/canvasHeight;
						if(canvasWidth > this.destWidth){
							canvasWidth = this.destWidth;
							canvasHeight = Math.floor(canvasWidth/base);
						}
						this.canvasWidth = canvasWidth;
						this.canvasHeight = canvasHeight;
						console.log(this.quality)
						
						/* 
								canvas宽高重新设置后会有一段渲染的间隔,这个间隔可能导致画到画布的图片出现大小不一致的bug,这里做下300ms延迟处理兼容
						 */
						setTimeout(() => {
							
							this.context.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
							this.context.drawImage(res.path, 0, 0, this.canvasWidth, this.canvasHeight);
							this.context.draw(false, () => {
								// 将图片画到canvas上面,使用Canvas压缩
								uni.canvasToTempFilePath({
									x: 0,
									y: 0,
								  width: this.canvasWidth,
								  height: this.canvasHeight,
								  destWidth: this.canvasWidth,
								  destHeight: this.canvasHeight,
								  canvasId: 'firstCanvas',
									fileType: 'png',
									quality: this.quality,
								  success: (res) => {
								    // 在H5平台下,tempFilePath 为 base64
								    console.log(res.tempFilePath)
										
										i++;
										arr.push(res.tempFilePath);
										if(i < tempFilePaths.length){				
											this.compressImg(tempFilePaths, arr, i);
										} else {
											console.log('压缩后的图片:', arr);
											uni.hideLoading();
											// 上传服务器
											this.uploadImg(arr, 0);
										}
								  },
									fail: (err)=>{
										console.log(err)
									}
								}, this);
							});
						}, 300);
						
						
					},
					fail: (err) => {
						console.log(err)
					}
				});
			
			},
			/* 上传图片 */
			uploadImg(tempFilePaths, index) {
				let i = index;

				this.$http.upload({
					url: this.uploadUrl,
					filePath: tempFilePaths[i],
					name: 'file',
					success: (res) => {
						this.imgList = [...this.imgList, ...res.data];
						
						i++;
						if(i < tempFilePaths.length){
							this.uploadImg(tempFilePaths, i);
						} else {
							console.log('服务器返回的图片列表:', this.imgList)
							this.$emit('imgListChange', this.imgList);
						}
					},
					fail: (err) => {
						console.log(err)
					}
				});
			}
		}
	}
</script>

<style scoped lang="scss">
	@import 'mp-image-upload.scss';
</style>
<mp-image-upload @imgListChange="imgListChange" v-if="!isDrawbackMoney"></mp-image-upload>

/* 监听图片变化 */
imgListChange(data) {
			
    this.imgList = [...data];
		
},

css代码

.container {
	.canvas-view {
		opacity: 0;
		position: fixed;
		right: -10000px;
		bottom: -10000px;
		width: 0px;
		height: 0px;
		overflow: hidden;
	}
	.list-box {
		display: flex;
		flex-wrap: wrap;
		.item {
			position: relative;
			width: 127rpx;
			height: 127rpx;
			border-radius: 4rpx;
			margin: 0rpx 13rpx 13rpx 0rpx;
			border:1px solid #EDEDED;
			.img {
				display: block;
				width: 100%;
				height: 100%;
			}
			.del-btn {
				position: absolute;
				top: -15rpx;
				right: -15rpx;
				width: 30rpx;
				height: 30rpx;
				text-align: center;
				line-height: 30rpx;
				background-color: #BA1C22;
				color: #FFFFFF;
				font-size: 30rpx;
				font-weight: bold;
				border-radius: 50%;
			}
		}
		.add-btn {
			display: flex;
			align-items: center;
			justify-content: center;
			flex-direction: column;
			width: 127rpx;
			height: 127rpx;
			border-radius: 4rpx;
			background-color: #FAFAFA;
			border:1px solid #EDEDED;
			text-align: center;
			.icon {
				color: #CCCCCC;
				font-size: 38rpx;
				line-height: 38rpx;
			}
			.txt {
				margin-top: 12rpx;
				display: block;
				font-size: 22rpx;
				font-weight: 400;
				color: #CCCCCC;
				line-height: 32rpx;
			}
			
			
		}
	}
}

 

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值