uni-app上传头像并裁剪压缩

2,077 阅读1分钟

头像组件封装rf-avatar

图片上传组件封装

<template name="yq-avatar">
	<view>
		<image
			:src="imgSrc.imgSrc"
			@tap="fSelect"
			:style="[imgStyle]"
			class="my-avatar"
		></image>
		<canvas
			canvas-id="avatar-canvas"
			id="avatar-canvas"
			class="my-canvas"
			:style="{ top: styleTop, height: cvsStyleHeight }"
			disable-scroll="false"
		></canvas>
		<canvas
			canvas-id="oper-canvas"
			id="oper-canvas"
			class="oper-canvas"
			:style="{ top: styleTop, height: cvsStyleHeight }"
			disable-scroll="false"
			@touchstart="fStart"
			@touchmove="fMove"
			@touchend="fEnd"
		></canvas>
		<canvas
			canvas-id="prv-canvas"
			id="prv-canvas"
			class="prv-canvas"
			disable-scroll="false"
			@touchstart="fHideImg"
			:style="{ height: cvsStyleHeight, top: prvTop }"
		></canvas>
		<view class="oper-wrapper" :style="{ display: styleDisplay }">
			<view class="oper">
				<view class="btn-wrapper" v-if="showOper">
					<view @tap="fSelect" hover-class="hover" :style="{ width: btnWidth }"
						><text>重选</text></view
					>
					<view @tap="fClose" hover-class="hover" :style="{ width: btnWidth }"
						><text>关闭</text></view
					>
					<view
						@tap="fRotate"
						hover-class="hover"
						:style="{ width: btnWidth, display: btnDsp }"
						><text>旋转</text></view
					>
					<view @tap="fPreview" hover-class="hover" :style="{ width: btnWidth }"
						><text>预览</text></view
					>
					<view @tap="fUpload" hover-class="hover" :style="{ width: btnWidth }"
						><text>上传</text></view
					>
				</view>
				<view class="clr-wrapper" v-else>
					<slider
						class="my-slider"
						@change="fColorChange"
						block-size="25"
						value="0"
						min="-100"
						max="100"
						activeColor="red"
						backgroundColor="green"
						block-color="grey"
						show-value
					></slider>
					<view
						@tap="fPrvUpload"
						hover-class="hover"
						:style="{ width: btnWidth }"
						><text>上传</text></view
					>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
/* eslint-disable */
const tabHeight = 50;
export default {
	name: 'yq-avatar',
	data() {
		return {
			cvsStyleHeight: '0px',
			styleDisplay: 'none',
			styleTop: '-10000px',
			prvTop: '-10000px',
			imgStyle: {},
			selStyle: {},
			showOper: true,
			imgSrc: {
				imgSrc: ''
			},
			btnWidth: '19%',
			btnDsp: 'flex'
		};
	},
	watch: {
		avatarSrc() {
			this.imgSrc.imgSrc = this.avatarSrc;
		}
	},
	props: {
		avatarSrc: '',
		avatarStyle: '',
		selWidth: '',
		selHeight: '',
		expWidth: '',
		expHeight: '',
		minScale: '',
		maxScale: '',
		canScale: '',
		canRotate: '',
		lockWidth: '',
		lockHeight: '',
		stretch: '',
		lock: '',
		noTab: '',
		inner: '',
		quality: '',
		index: ''
	},
	created() {
		this.ctxCanvas = uni.createCanvasContext('avatar-canvas', this);
		this.ctxCanvasOper = uni.createCanvasContext('oper-canvas', this);
		this.ctxCanvasPrv = uni.createCanvasContext('prv-canvas', this);
		this.qlty = parseInt(this.quality) || 0.9;
		this.imgSrc.imgSrc = this.avatarSrc;
		this.letRotate =
			this.canRotate === 'false' || this.inner === 'true' ? 0 : 1;
		this.letScale = this.canScale === 'false' ? 0 : 1;
		this.isin = this.inner === 'true' ? 1 : 0;
		this.indx = this.index || undefined;
		this.mnScale = this.minScale || 0.3;
		this.mxScale = this.maxScale || 4;
		this.noBar = this.noTab === 'true' ? 1 : 0;
		this.stc = this.stretch;
		this.lck = this.lock;
		if (this.isin) {
			this.btnWidth = '24%';
			this.btnDsp = 'none';
		} else {
			this.btnWidth = '19%';
			this.btnDsp = 'flex';
		}

		if (this.noBar) {
			this.moreHeight = 0;
			this.fWindowResize();
		} else {
			uni.showTabBar({
				complete: res => {
					this.moreHeight = res.errMsg === 'showTabBar:ok' ? 50 : 0;
					this.fWindowResize();
				}
			});
		}
	},
	methods: {
		fWindowResize() {
			let sysInfo = uni.getSystemInfoSync();
			this.platform = sysInfo.platform;
			this.pixelRatio = sysInfo.pixelRatio;
			this.windowWidth = sysInfo.windowWidth;
			// #ifdef H5
			this.drawTop = sysInfo.windowTop;
			this.windowHeight = sysInfo.windowHeight + sysInfo.windowBottom;
			this.cvsStyleHeight = this.windowHeight - tabHeight + 'px';
			// #endif
			// #ifdef APP-PLUS
			if (this.platform === 'android') {
				this.windowHeight = sysInfo.screenHeight + sysInfo.statusBarHeight;
				this.cvsStyleHeight = this.windowHeight - tabHeight + 'px';
			} else {
				this.windowHeight = sysInfo.windowHeight + this.moreHeight;
				this.cvsStyleHeight = this.windowHeight - tabHeight + 6 + 'px';
			}
			// #endif
			// #ifdef MP
			this.windowHeight = sysInfo.windowHeight + this.moreHeight;
			this.cvsStyleHeight = this.windowHeight - tabHeight - 2 + 'px';
			// #endif
			this.pxRatio = this.windowWidth / 750;

			let style = this.avatarStyle;
			if (style && style !== true && (style = style.trim())) {
				style = style.split(';');
				let obj = {};
				for (let v of style) {
					if (!v) continue;
					v = v.trim().split(':');
					if (v[1].indexOf('upx') >= 0) {
						let arr = v[1].trim().split(' ');
						for (let k in arr) {
							if (!arr[k]) continue;
							if (arr[k].indexOf('upx') >= 0) {
								arr[k] = parseFloat(arr[k]) * this.pxRatio + 'px';
							}
						}
						v[1] = arr.join(' ');
					}
					obj[v[0].trim()] = v[1].trim();
				}
				this.imgStyle = obj;
			}

			this.expWidth &&
				(this.exportWidth =
					this.expWidth.indexOf('upx') >= 0
						? parseInt(this.expWidth) * this.pxRatio
						: parseInt(this.expWidth));
			this.expHeight &&
				(this.exportHeight =
					this.expHeight.indexOf('upx') >= 0
						? parseInt(this.expHeight) * this.pxRatio
						: parseInt(this.expHeight));

			if (this.styleDisplay === 'flex') {
				this.fDrawInit(true);
			}
			this.fHideImg();
		},
		fSelect() {
			if (this.fSelecting) return;
			this.fSelecting = true;
			setTimeout(() => {
				this.fSelecting = false;
			}, 500);

			uni.chooseImage({
				count: 1,
				sizeType: ['original', 'compressed'],
				sourceType: ['album', 'camera'],
				success: r => {
					uni.showLoading({ mask: true });
					let path = (this.imgPath = r.tempFilePaths[0]);
					uni.getImageInfo({
						src: path,
						success: r => {
							this.imgWidth = r.width;
							this.imgHeight = r.height;
							this.path = path;
							if (!this.hasSel) {
								let style = this.selStyle || {};
								if (this.selWidth && this.selHeight) {
									let selWidth =
											this.selWidth.indexOf('upx') >= 0
												? parseInt(this.selWidth) * this.pxRatio
												: parseInt(this.selWidth),
										selHeight =
											this.selHeight.indexOf('upx') >= 0
												? parseInt(this.selHeight) * this.pxRatio
												: parseInt(this.selHeight);
									style.width = selWidth + 'px';
									style.height = selHeight + 'px';
									style.top =
										(this.windowHeight - selHeight - tabHeight) / 2 + 'px';
									style.left = (this.windowWidth - selWidth) / 2 + 'px';
								} else {
									uni.showModal({
										title: '裁剪框的宽或高没有设置',
										showCancel: false
									});
									return;
								}
								this.selStyle = style;
							}

							if (this.noBar) {
								this.fDrawInit(true);
							} else {
								uni.hideTabBar({
									complete: () => {
										this.fDrawInit(true);
									}
								});
							}
						},
						fail: () => {
							this.$mHelper.toast('error3');
						},
						complete() {
							uni.hideLoading();
						}
					});
				}
			});
		},
		fUpload() {
			if (this.fUploading) return;
			this.fUploading = true;
			setTimeout(() => {
				this.fUploading = false;
			}, 1000);

			let style = this.selStyle,
				x = parseInt(style.left),
				y = parseInt(style.top),
				width = parseInt(style.width),
				height = parseInt(style.height),
				expWidth = this.exportWidth || width,
				expHeight = this.exportHeight || height;

			// #ifdef H5
			x *= this.pixelRatio;
			y *= this.pixelRatio;
			expWidth = width;
			expHeight = height;
			// #endif

			uni.showLoading({ mask: true });
			this.styleDisplay = 'none';
			this.styleTop = '-10000px';
			this.hasSel = false;
			this.fHideImg();
			uni.canvasToTempFilePath(
				{
					x: x,
					y: y,
					width: width,
					height: height,
					destWidth: expWidth,
					destHeight: expHeight,
					canvasId: 'avatar-canvas',
					fileType: 'png',
					quality: this.qlty,
					success: r => {
						r = r.tempFilePath;
						// #ifdef H5
						this.btop(r).then(r => {
							if (this.exportWidth && this.exportHeight) {
								let ctxCanvas = this.ctxCanvas;
								(expWidth = this.exportWidth), (expHeight = this.exportHeight);

								ctxCanvas.drawImage(r, 0, 0, expWidth, expHeight);
								ctxCanvas.draw(false, () => {
									uni.canvasToTempFilePath({
										x: 0,
										y: 0,
										width: expWidth,
										height: expHeight,
										destWidth: expWidth,
										destHeight: expHeight,
										canvasId: 'avatar-canvas',
										fileType: 'png',
										quality: this.qlty,
										success: r => {
											r = r.tempFilePath;
											this.btop(r).then(r => {
												this.$emit('upload', {
													avatar: this.imgSrc,
													path: r,
													index: this.indx,
													data: this.rtn
												});
											});
										},
										fail: () => {
											this.$mHelper.toast('error0');
										}
									});
								});
							} else {
								this.$emit('upload', {
									avatar: this.imgSrc,
									path: r,
									index: this.indx,
									data: this.rtn
								});
							}
						});
						// #endif
						// #ifndef H5
						this.$emit('upload', {
							avatar: this.imgSrc,
							path: r,
							index: this.indx,
							data: this.rtn
						});
						// #endif
					},
					fail: res => {
						this.$mHelper.toast('error1');
					},
					complete: () => {
						uni.hideLoading();
						this.noBar || uni.showTabBar();
					}
				},
				this
			);
		},
		fPrvUpload() {
			if (this.fPrvUploading) return;
			this.fPrvUploading = true;
			setTimeout(() => {
				this.fPrvUploading = false;
			}, 1000);

			let style = this.selStyle,
				destWidth = parseInt(style.width),
				destHeight = parseInt(style.height),
				prvX = this.prvX,
				prvY = this.prvY,
				prvWidth = this.prvWidth,
				prvHeight = this.prvHeight,
				expWidth = this.exportWidth || prvWidth,
				expHeight = this.exportHeight || prvHeight;

			// #ifdef H5
			prvX *= this.pixelRatio;
			prvY *= this.pixelRatio;
			expWidth = prvWidth;
			expHeight = prvHeight;
			// #endif

			uni.showLoading({ mask: true });

			this.styleDisplay = 'none';
			this.styleTop = '-10000px';
			this.hasSel = false;
			this.fHideImg();
			uni.canvasToTempFilePath(
				{
					x: prvX,
					y: prvY,
					width: prvWidth,
					height: prvHeight,
					destWidth: expWidth,
					destHeight: expHeight,
					canvasId: 'prv-canvas',
					fileType: 'png',
					quality: this.qlty,
					success: r => {
						r = r.tempFilePath;
						// #ifdef H5
						this.btop(r).then(r => {
							if (this.exportWidth && this.exportHeight) {
								let ctxCanvas = this.ctxCanvas;
								(expWidth = this.exportWidth), (expHeight = this.exportHeight);

								ctxCanvas.drawImage(r, 0, 0, expWidth, expHeight);
								ctxCanvas.draw(false, () => {
									uni.canvasToTempFilePath({
										x: 0,
										y: 0,
										width: expWidth,
										height: expHeight,
										destWidth: expWidth,
										destHeight: expHeight,
										canvasId: 'avatar-canvas',
										fileType: 'png',
										quality: this.qlty,
										success: r => {
											r = r.tempFilePath;
											this.btop(r).then(r => {
												this.$emit('upload', {
													avatar: this.imgSrc,
													path: r,
													index: this.indx,
													data: this.rtn
												});
											});
										},
										fail: () => {}
									});
								});
							} else {
								this.$emit('upload', {
									avatar: this.imgSrc,
									path: r,
									index: this.indx,
									data: this.rtn
								});
							}
						});
						// #endif
						// #ifndef H5
						this.$emit('upload', {
							avatar: this.imgSrc,
							path: r,
							index: this.indx,
							data: this.rtn
						});
						// #endif
					},
					fail: () => {},
					complete: () => {
						uni.hideLoading();
						this.noBar || uni.showTabBar();
					}
				},
				this
			);
		},
		fDrawInit(ini = false) {
			let allWidth = this.windowWidth,
				allHeight = this.windowHeight,
				imgWidth = this.imgWidth,
				imgHeight = this.imgHeight,
				imgRadio = imgWidth / imgHeight,
				useWidth = allWidth - 40,
				useHeight = allHeight - tabHeight - 80,
				pixelRatio = this.pixelRatio,
				selWidth = parseInt(this.selStyle.width),
				selHeight = parseInt(this.selStyle.height);

			this.fixWidth = 0;
			this.fixHeight = 0;
			this.lckWidth = 0;
			this.lckHeight = 0;
			switch (this.stc) {
				case 'x':
					this.fixWidth = 1;
					break;
				case 'y':
					this.fixHeight = 1;
					break;
				case 'long':
					if (imgRadio > 1) this.fixWidth = 1;
					else this.fixHeight = 1;
					break;
				case 'short':
					if (imgRadio > 1) this.fixHeight = 1;
					else this.fixWidth = 1;
					break;
				case 'longSel':
					if (selWidth > selHeight) this.fixWidth = 1;
					else this.fixHeight = 1;
					break;
				case 'shortSel':
					if (selWidth > selHeight) this.fixHeight = 1;
					else this.fixWidth = 1;
					break;
			}
			switch (this.lck) {
				case 'x':
					this.lckWidth = 1;
					break;
				case 'y':
					this.lckHeight = 1;
					break;
				case 'long':
					if (imgRadio > 1) this.lckWidth = 1;
					else this.lckHeight = 1;
					break;
				case 'short':
					if (imgRadio > 1) this.lckHeight = 1;
					else this.lckWidth = 1;
					break;
				case 'longSel':
					if (selWidth > selHeight) this.lckWidth = 1;
					else this.lckHeight = 1;
					break;
				case 'shortSel':
					if (selWidth > selHeight) this.lckHeight = 1;
					else this.lckWidth = 1;
					break;
			}
			if (this.fixWidth) {
				useWidth = selWidth;
				useHeight = useWidth / imgRadio;
			} else if (this.fixHeight) {
				useHeight = selHeight;
				useWidth = useHeight * imgRadio;
			} else if (imgRadio < 1) {
				if (imgHeight < useHeight) {
					useWidth = imgWidth;
					useHeight = imgHeight;
				} else {
					useHeight = useHeight;
					useWidth = useHeight * imgRadio;
				}
			} else {
				if (imgWidth < useWidth) {
					useWidth = imgWidth;
					useHeight = imgHeight;
				} else {
					useWidth = useWidth;
					useHeight = useWidth / imgRadio;
				}
			}
			if (this.isin) {
				this.scaleWidth = 0;
				this.scaleHeight = 0;
				if (useWidth < selWidth) {
					useWidth = selWidth;
					useHeight = useWidth / imgRadio;
					this.lckHeight = 0;
				}
				if (useHeight < selHeight) {
					useHeight = selHeight;
					useWidth = useHeight * imgRadio;
					this.lckWidth = 0;
				}
			}

			this.scaleSize = 1;
			this.rotateDeg = 0;
			this.posWidth = (allWidth - useWidth) / 2;
			this.posHeight = (allHeight - useHeight - tabHeight) / 2;
			this.useWidth = useWidth;
			this.useHeight = useHeight;

			let style = this.selStyle,
				left = parseInt(style.left),
				top = parseInt(style.top),
				width = parseInt(style.width),
				height = parseInt(style.height),
				canvas = this.canvas,
				canvasOper = this.canvasOper,
				ctxCanvas = this.ctxCanvas,
				ctxCanvasOper = this.ctxCanvasOper;

			ctxCanvasOper.setLineWidth(3);
			ctxCanvasOper.setStrokeStyle('grey');
			ctxCanvasOper.setGlobalAlpha(0.4);
			ctxCanvasOper.setFillStyle('black');
			ctxCanvasOper.strokeRect(left, top, width, height);
			ctxCanvasOper.fillRect(0, 0, this.windowWidth, top);
			ctxCanvasOper.fillRect(0, top, left, height);
			ctxCanvasOper.fillRect(
				0,
				top + height,
				this.windowWidth,
				this.windowHeight - height - top - tabHeight
			);
			ctxCanvasOper.fillRect(
				left + width,
				top,
				this.windowWidth - width - left,
				height
			);
			ctxCanvasOper.setStrokeStyle('red');
			ctxCanvasOper.moveTo(left + 20, top);
			ctxCanvasOper.lineTo(left, top);
			ctxCanvasOper.lineTo(left, top + 20);
			ctxCanvasOper.moveTo(left + width - 20, top);
			ctxCanvasOper.lineTo(left + width, top);
			ctxCanvasOper.lineTo(left + width, top + 20);
			ctxCanvasOper.moveTo(left + 20, top + height);
			ctxCanvasOper.lineTo(left, top + height);
			ctxCanvasOper.lineTo(left, top + height - 20);
			ctxCanvasOper.moveTo(left + width - 20, top + height);
			ctxCanvasOper.lineTo(left + width, top + height);
			ctxCanvasOper.lineTo(left + width, top + height - 20);
			ctxCanvasOper.stroke();

			ctxCanvasOper.draw(false, () => {
				if (ini) {
					this.styleDisplay = 'flex';
					// #ifdef H5
					this.styleTop = this.drawTop + 'px';
					// #endif
					// #ifndef H5
					this.styleTop = '0';
					// #endif
					ctxCanvas.setFillStyle('black');
					this.fDrawImage();
				}
			});

			this.$emit('avtinit');
		},
		fDrawImage() {
			let tm_now = Date.now();
			if (tm_now - this.drawTm < 20) return;
			this.drawTm = tm_now;
			let ctxCanvas = this.ctxCanvas;
			ctxCanvas.fillRect(0, 0, this.windowWidth, this.windowHeight - tabHeight);
			ctxCanvas.translate(
				this.posWidth + this.useWidth / 2,
				this.posHeight + this.useHeight / 2
			);
			ctxCanvas.scale(this.scaleSize, this.scaleSize);
			ctxCanvas.rotate((this.rotateDeg * Math.PI) / 180);
			ctxCanvas.drawImage(
				this.imgPath,
				-this.useWidth / 2,
				-this.useHeight / 2,
				this.useWidth,
				this.useHeight
			);
			ctxCanvas.draw(false);
		},
		fHideImg() {
			this.prvImg = '';
			this.prvTop = '-10000px';
			this.showOper = true;
			this.prvImgData = null;
			this.target = null;
		},
		fClose() {
			this.styleDisplay = 'none';
			this.styleTop = '-10000px';
			this.hasSel = false;
			this.fHideImg();
			this.noBar || uni.showTabBar();
		},
		fPreview() {
			if (this.fPreviewing) return;
			this.fPreviewing = true;
			setTimeout(() => {
				this.fPreviewing = false;
			}, 1000);

			let style = this.selStyle,
				x = parseInt(style.left),
				y = parseInt(style.top),
				width = parseInt(style.width),
				height = parseInt(style.height);

			// #ifdef H5
			x *= this.pixelRatio;
			y *= this.pixelRatio;
			// #endif

			uni.showLoading({ mask: true });

			uni.canvasToTempFilePath(
				{
					x: x,
					y: y,
					width: width,
					height: height,
					canvasId: 'avatar-canvas',
					fileType: 'png',
					quality: this.qlty,
					success: r => {
						this.prvImgTmp = r = r.tempFilePath;

						let ctxCanvasPrv = this.ctxCanvasPrv,
							prvX = this.windowWidth,
							prvY = parseInt(this.cvsStyleHeight),
							prvWidth = parseInt(this.selStyle.width),
							prvHeight = parseInt(this.selStyle.height),
							useWidth = prvX - 40,
							useHeight = prvY - 80,
							radio = useWidth / prvWidth,
							rHeight = prvHeight * radio;
						if (rHeight < useHeight) {
							prvWidth = useWidth;
							prvHeight = rHeight;
						} else {
							radio = useHeight / prvHeight;
							prvWidth *= radio;
							prvHeight = useHeight;
						}
						ctxCanvasPrv.setFillStyle('black');
						ctxCanvasPrv.fillRect(0, 0, prvX, prvY);
						this.prvX = prvX = (prvX - prvWidth) / 2;
						this.prvY = prvY = (prvY - prvHeight) / 2;
						this.prvWidth = prvWidth;
						this.prvHeight = prvHeight;
						ctxCanvasPrv.drawImage(r, prvX, prvY, prvWidth, prvHeight);
						ctxCanvasPrv.draw(false);

						// #ifdef H5
						this.btop(r).then(r => {
							this.showOper = false;
							this.prvTop = this.drawTop + 'px';
						});
						// #endif
						// #ifndef H5
						if (this.platform != 'android') {
							this.showOper = false;
						}
						this.prvTop = '0';
						// #endif
					},
					fail: () => {},
					complete: () => {
						uni.hideLoading();
					}
				},
				this
			);
		},
		fChooseImg(index = undefined, params = undefined, data = undefined) {
			if (params) {
				let selWidth = params.selWidth,
					selHeight = params.selHeight,
					expWidth = params.expWidth,
					expHeight = params.expHeight,
					quality = params.quality,
					canRotate = params.canRotate,
					canScale = params.canScale,
					minScale = params.minScale,
					maxScale = params.maxScale,
					stretch = params.stretch,
					inner = params.inner,
					lock = params.lock;

				expWidth &&
					(this.exportWidth =
						expWidth.indexOf('upx') >= 0
							? parseInt(expWidth) * this.pxRatio
							: parseInt(expWidth));
				expHeight &&
					(this.exportHeight =
						expHeight.indexOf('upx') >= 0
							? parseInt(expHeight) * this.pxRatio
							: parseInt(expHeight));
				this.letRotate = canRotate === 'false' ? 0 : 1;
				this.letScale = canScale === 'false' ? 0 : 1;
				this.qlty = parseInt(quality) || 0.9;
				this.mnScale = minScale || 0.3;
				this.mxScale = maxScale || 4;
				this.stc = stretch;
				this.isin = inner === 'true' ? 1 : 0;
				this.lck = lock;
				if (this.isin) {
					this.btnWidth = '24%';
					this.btnDsp = 'none';
				} else {
					this.btnWidth = '19%';
					this.btnDsp = 'flex';
				}

				if (selWidth && selHeight) {
					selWidth =
						selWidth.indexOf('upx') >= 0
							? parseInt(selWidth) * this.pxRatio
							: parseInt(selWidth);
					selHeight =
						selHeight.indexOf('upx') >= 0
							? parseInt(selHeight) * this.pxRatio
							: parseInt(selHeight);
					this.selStyle.width = selWidth + 'px';
					this.selStyle.height = selHeight + 'px';
					this.selStyle.top =
						(this.windowHeight - selHeight - tabHeight) / 2 + 'px';
					this.selStyle.left = (this.windowWidth - selWidth) / 2 + 'px';
					this.hasSel = true;
				}
			}
			this.rtn = data;
			this.indx = index;
			this.fSelect();
		},
		fRotate() {
			// #ifdef APP-PLUS
			if (this.platform === 'android') {
				if (this.fRotateing) return;
				this.fRotateing = true;
				setTimeout(() => {
					this.fRotateing = false;
				}, 500);
			}
			// #endif

			// if(this.letRotate) {
			this.rotateDeg += 90 - (this.rotateDeg % 90);
			this.fDrawImage();
			// }
		},
		fStart(e) {
			let touches = e.touches,
				touch0 = touches[0],
				touch1 = touches[1];

			this.touch0 = touch0;
			this.touch1 = touch1;

			if (touch1) {
				let x = touch1.x - touch0.x,
					y = touch1.y - touch0.y;
				this.fgDistance = Math.sqrt(x * x + y * y);
			}
		},
		fMove(e) {
			let touches = e.touches,
				touch0 = touches[0],
				touch1 = touches[1];

			if (touch1) {
				let x = touch1.x - touch0.x,
					y = touch1.y - touch0.y,
					fgDistance = Math.sqrt(x * x + y * y),
					scaleSize = 0.005 * (fgDistance - this.fgDistance),
					beScaleSize = this.scaleSize + scaleSize;

				do {
					if (!this.letScale) break;
					if (beScaleSize < this.mnScale) break;
					if (beScaleSize > this.mxScale) break;
					if (this.isin) {
						let imgWidth = this.useWidth * beScaleSize,
							imgHeight = this.useHeight * beScaleSize,
							rx0 = this.posWidth + this.useWidth / 2,
							ry0 = this.posHeight + this.useHeight / 2,
							l = rx0 - imgWidth / 2,
							t = ry0 - imgHeight / 2,
							r = l + imgWidth,
							b = t + imgHeight,
							left = parseInt(this.selStyle.left),
							top = parseInt(this.selStyle.top),
							width = parseInt(this.selStyle.width),
							height = parseInt(this.selStyle.height);
						if (left < l || left + width > r || top < t || top + height > b)
							break;
						this.scaleWidth = (this.useWidth - imgWidth) / 2;
						this.scaleHeight = (this.useHeight - imgHeight) / 2;
					}

					this.scaleSize = beScaleSize;
				} while (0);
				this.fgDistance = fgDistance;

				if (touch1.x !== touch0.x && this.letRotate) {
					x = (this.touch1.y - this.touch0.y) / (this.touch1.x - this.touch0.x);
					y = (touch1.y - touch0.y) / (touch1.x - touch0.x);
					this.rotateDeg += (Math.atan((y - x) / (1 + x * y)) * 180) / Math.PI;
					this.touch0 = touch0;
					this.touch1 = touch1;
				}

				this.fDrawImage();
			} else if (this.touch0) {
				let x = touch0.x - this.touch0.x,
					y = touch0.y - this.touch0.y,
					beX = this.posWidth + x,
					beY = this.posHeight + y;
				if (this.isin) {
					let imgWidth = this.useWidth * this.scaleSize,
						imgHeight = this.useHeight * this.scaleSize,
						rx0 = beX + this.useWidth / 2,
						ry0 = beY + this.useHeight / 2,
						l = rx0 - imgWidth / 2,
						t = ry0 - imgHeight / 2,
						r = l + imgWidth,
						b = t + imgHeight,
						left = parseInt(this.selStyle.left),
						top = parseInt(this.selStyle.top),
						width = parseInt(this.selStyle.width),
						height = parseInt(this.selStyle.height);
					if (!this.lckWidth && Math.abs(x) < 100) {
						if (left >= l && left + width <= r) {
							this.posWidth = beX;
						} else if (left < l) {
							this.posWidth = left - this.scaleWidth;
						} else if (left + width > r) {
							this.posWidth = left - (imgWidth - width) - this.scaleWidth;
						}
					}
					if (!this.lckHeight && Math.abs(y) < 100) {
						if (top >= t && top + height <= b) {
							this.posHeight = beY;
						} else if (top < t) {
							this.posHeight = top - this.scaleHeight;
						} else if (top + height > b) {
							this.posHeight = top - (imgHeight - height) - this.scaleHeight;
						}
					}
				} else {
					if (Math.abs(x) < 100 && !this.lckWidth) this.posWidth = beX;
					if (Math.abs(y) < 100 && !this.lckHeight) this.posHeight = beY;
				}

				this.touch0 = touch0;
				this.fDrawImage();
			}
		},
		fEnd(e) {
			let touches = e.touches,
				touch0 = touches && touches[0],
				touch1 = touches && touches[1];
			if (touch0) {
				this.touch0 = touch0;
			} else {
				this.touch0 = null;
				this.touch1 = null;
			}
		},
		fGetImgData() {
			return new Promise((resolve, reject) => {
				let prvX = this.prvX,
					prvY = this.prvY,
					prvWidth = this.prvWidth,
					prvHeight = this.prvHeight;
				// #ifdef APP-PLUS||H5
				prvX *= this.pixelRatio;
				prvY *= this.pixelRatio;
				prvWidth *= this.pixelRatio;
				prvHeight *= this.pixelRatio;
				// #endif
				uni.canvasGetImageData(
					{
						canvasId: 'prv-canvas',
						x: prvX,
						y: prvY,
						width: prvWidth,
						height: prvHeight,
						success(res) {
							resolve(res.data);
						},
						fail(err) {
							reject(err);
						}
					},
					this
				);
			});
		},
		async fColorChange(e) {
			let tm_now = Date.now();
			if (tm_now - this.prvTm < 100) return;
			this.prvTm = tm_now;

			uni.showLoading({ mask: true });

			if (!this.prvImgData) {
				if (!(this.prvImgData = await this.fGetImgData().catch(res => {})))
					return;
				this.target = new Uint8ClampedArray(this.prvImgData.length);
			}

			let data = this.prvImgData,
				target = this.target,
				i = e.detail.value,
				r,
				g,
				b,
				a,
				h,
				s,
				l,
				d,
				p,
				q,
				t,
				min,
				max,
				hK,
				tR,
				tG,
				tB;

			if (i === 0) {
				target = data;
			} else {
				i = (i + 100) / 200;
				if (i < 0.005) i = 0;
				if (i > 0.995) i = 1;
				for (let n = data.length - 1; n >= 0; n -= 4) {
					r = data[n - 3] / 255;
					g = data[n - 2] / 255;
					b = data[n - 1] / 255;
					max = Math.max(r, g, b);
					min = Math.min(r, g, b);
					d = max - min;
					if (max === min) {
						h = 0;
					} else if (max === r && g >= b) {
						h = 60 * ((g - b) / d);
					} else if (max === r && g < b) {
						h = 60 * ((g - b) / d) + 360;
					} else if (max === g) {
						h = 60 * ((b - r) / d) + 120;
					} else if (max === b) {
						h = 60 * ((r - g) / d) + 240;
					}
					l = (max + min) / 2;
					if (l === 0 || max === min) {
						s = 0;
					} else if (0 < l && l <= 0.5) {
						s = d / (2 * l);
					} else if (l > 0.5) {
						s = d / (2 - 2 * l);
					}
					data[n] && (a = data[n]);

					if (i < 0.5) {
						s = (s * i) / 0.5;
					} else if (i > 0.5) {
						s = 2 * s + 2 * i - (s * i) / 0.5 - 1;
					}

					if (s === 0) {
						r = g = b = Math.round(l * 255);
					} else {
						if (l < 0.5) {
							q = l * (1 + s);
						} else if (l >= 0.5) {
							q = l + s - l * s;
						}
						p = 2 * l - q;
						hK = h / 360;
						tR = hK + 1 / 3;
						tG = hK;
						tB = hK - 1 / 3;
						let correctRGB = t => {
							if (t < 0) {
								return t + 1.0;
							}
							if (t > 1) {
								return t - 1.0;
							}
							return t;
						};
						let createRGB = t => {
							if (t < 1 / 6) {
								return p + (q - p) * 6 * t;
							} else if (t >= 1 / 6 && t < 1 / 2) {
								return q;
							} else if (t >= 1 / 2 && t < 2 / 3) {
								return p + (q - p) * 6 * (2 / 3 - t);
							}
							return p;
						};
						r = tR = Math.round(createRGB(correctRGB(tR)) * 255);
						g = tG = Math.round(createRGB(correctRGB(tG)) * 255);
						b = tB = Math.round(createRGB(correctRGB(tB)) * 255);
					}
					a && (target[n] = a);
					target[n - 3] = r;
					target[n - 2] = g;
					target[n - 1] = b;
				}
			}
			let prvX = this.prvX,
				prvY = this.prvY,
				prvWidth = this.prvWidth,
				prvHeight = this.prvHeight;

			this.ctxCanvasPrv.setFillStyle('black');
			this.ctxCanvasPrv.fillRect(prvX, prvY, prvWidth, prvHeight);
			this.ctxCanvasPrv.draw(true);

			// #ifdef APP-PLUS||H5
			prvX *= this.pixelRatio;
			prvY *= this.pixelRatio;
			prvWidth *= this.pixelRatio;
			prvHeight *= this.pixelRatio;
			// #endif
			uni.canvasPutImageData(
				{
					canvasId: 'prv-canvas',
					x: prvX,
					y: prvY,
					width: prvWidth,
					height: prvHeight,
					data: target,
					fail() {},
					complete() {
						uni.hideLoading();
					}
				},
				this
			);
		},
		btop(base64) {
			return new Promise(function(resolve, reject) {
				var arr = base64.split(','),
					mime = arr[0].match(/:(.*?);/)[1],
					bstr = atob(arr[1]),
					n = bstr.length,
					u8arr = new Uint8Array(n);
				while (n--) {
					u8arr[n] = bstr.charCodeAt(n);
				}
				return resolve(
					(window.URL || window.webkitURL).createObjectURL(
						new Blob([u8arr], { type: mime })
					)
				);
			});
		}
	}
};
</script>

<style>
.my-canvas {
	display: flex;
	position: fixed !important;
	background: #000000;
	left: 0;
	z-index: 100000;
	width: 100%;
}
.my-avatar {
	width: 150upx;
	height: 150upx;
	border-radius: 100%;
}
.oper-canvas {
	display: flex;
	position: fixed !important;
	left: 0;
	z-index: 100001;
	width: 100%;
}
.prv-canvas {
	display: flex;
	position: fixed !important;
	background: #000000;
	left: 0;
	z-index: 200000;
	width: 100%;
}
.oper-wrapper {
	height: 50px;
	position: fixed !important;
	box-sizing: border-box;
	border: 1px solid #f1f1f1;
	background: #ffffff;
	width: 100%;
	left: 0;
	bottom: 0;
	z-index: 100009;
	flex-direction: row;
}
.oper {
	display: flex;
	flex-direction: column;
	justify-content: center;
	padding: 10upx 20upx;
	width: 100%;
	height: 100%;
	box-sizing: border-box;
	align-self: center;
}
.btn-wrapper {
	display: flex;
	flex-direction: row;
	/* #ifndef H5 */
	flex-grow: 1;
	/* #endif */
	/* #ifdef H5 */
	height: 50px;
	/* #endif */
	justify-content: space-between;
}
.btn-wrapper view {
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 16px;
	color: #333;
	border: 1px solid #f1f1f1;
	border-radius: 6%;
}
.hover {
	background: #f1f1f1;
	border-radius: 6%;
}
.clr-wrapper {
	display: flex;
	flex-direction: row;
	flex-grow: 1;
}
.clr-wrapper view {
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 16px;
	color: #333;
	border: 1px solid #f1f1f1;
	border-radius: 6%;
}
.my-slider {
	flex-grow: 1;
}
</style>

图片压缩组件封装x-compress

<template>
	<view class="image-compress-container">
		<!-- #ifndef H5 -->
			<canvas :style="{width: W + 'px', height: H + 'px', visibility: 'hidden'}" class="canvas" canvas-id="canvas"></canvas>
		<!-- #endif -->
	</view>
</template>

<script>
	export default {
		data () {
			return {
				W: '',
				H: '',
				canvas: null,
				ctx: null,
				maxW: 1024,
				maxH: 1024,
				quality: 0.8,
				base64: false,
				showLoading: '正在压缩',
				mask: true
			}
		},
		methods: {
			async compress (args, options = {}) {
				return new Promise(async (resolve, reject) => {
					let files;
					if (arguments[0].tempFiles || arguments[0].tempFilePaths) {
						files = arguments[0].tempFilePaths || arguments[0].tempFiles;
					};
					if (arguments[0].files) {
						files = arguments[0].files;
					}
					debugger
					if(arguments[0].path){
						files = Array.of(arguments[0].path);
					}
					if (!files instanceof Array) {
						reject('数据格式错误');
					}
					if (!files.length) {
						reject('数据不能为空');
					}
					this.maxW = options.maxW || 1024;
					this.maxH = options.maxH || 1024;
					this.quality = options.quality || 0.8;
					this.base64 = options.base64 || false;
					this.showLoading = options.showLoading === true ? '正在压缩' : typeof options.showLoading === 'string' ? options.showLoading : false;
					this.mask = options.mask || true;
					if (this.showLoading) {
						uni.showLoading({
							title: this.showLoading,
							mask: this.mask
						})
					}
					try {
						const result = await this.convertImageToBase64(files);
						resolve(result);
						uni.hideLoading();
					} catch (error) {
						reject(error);
						uni.hideLoading();
					}
				})
			},
			toBase64H5 (file) {
				return new Promise((resolve, reject) => {
					let result = [];
					for (let i = 0; i < file.length; i++) {
						let reader = new FileReader();
						let base64Result;
						reader.addEventListener('load', (e) => {
							base64Result = reader.result || e.target.result;
							let filename = file[i].name.slice(0, file[i].name.lastIndexOf('.'));
							result.push({base64: base64Result, filename});
						    reader = null;
							if (result.length === file.length) {
								resolve(result);
							}
						});
						reader.readAsDataURL(file[i]);
					}
				})
			},
			compressResultH5 (base64Item) {
				return new Promise((resolve, reject) => {
					let maxW = this.maxW;
					let maxH = this.maxH;
					let ratio, needCompress = false;
					let image = new Image();
					image.src = base64Item.base64;
					image.addEventListener('load', () => {
						if (image.naturalWidth > maxW) {
							needCompress = true;
							ratio = image.naturalWidth / maxW;
							maxH = image.naturalHeight / ratio;
						}
						if (image.naturalHeight > maxH) {
							needCompress = true;
							ratio = image.naturalHeight / maxH;
							maxW = image.naturalWidth / ratio;
						}
						if (!needCompress) {
							maxW = image.naturalWidth;
							maxH = image.naturalWidth;
						}
						if (!this.canvas) {
							this.canvas = document.createElement('canvas');
						}
						this.canvas.width = maxW;
						this.canvas.height = maxH;
						const ctx = this.canvas.getContext('2d');
						ctx.clearRect(0, 0, maxW, maxH);
						ctx.drawImage(image, 0, 0, maxW, maxH);
						const compressImg = this.canvas.toDataURL('image/jpeg', this.quality);
						let file = this._dataURLtoFile(compressImg, base64Item.filename);
						if (this.base64) {
							resolve({base64: compressImg, file});
						} else {
							resolve({file});
						}
						image = null;
						// ratio: base64Item.base64.length / compressImg.length
					})
				})
			},
			compressImageH5 (base64Result) {
				let result = [];
				return new Promise(async (resolve, reject) => {
					for (let i = 0; i < base64Result.length; i++) {
						let res = await this.compressResultH5(base64Result[i]);
						result.push(res);
						if (result.length === base64Result.length) {
							resolve(result);
							this.canvas = null;
						}
					}
				})
			},
			async convertImageToBase64 (files) {
				// #ifdef H5
					if (typeof files[0] === 'object') {
						let result = await this.toBase64H5(files);
						return await this.compressImageH5(result);
					}
					if (typeof files[0] === 'string') {
						let result = files.map(item => {
							return {
								base64: item
							}
						});
						return await this.compressImageH5(result);
					}
					return [];
				// #endif
				
				// #ifndef H5
					if (typeof files[0] === 'string') {
						const result = await this.getImgInfoWX(files);
						return await this.compressImageWX(result);
					}
					if (typeof files[0] === 'object') {
						files = files.map(item => {
							return item.path
						});
						const result = await this.getImgInfoWX(files);
						return await this.compressImageWX(result);
					}
					return [];
				// #endif
				return [];
			},
			getImgInfoWX (tempFilePaths) {
				let result = [];
				return new Promise((resolve, reject) => {
					for (let i = 0; i < tempFilePaths.length; i++) {
						uni.getImageInfo({
						    src: tempFilePaths[i],
						    success: (image) => {
								result.push({tempFilePaths: tempFilePaths[i], image});
								if (result.length === tempFilePaths.length) {
									resolve(result);
								}
						    }
						});
					}
				})
			},
			compressResultWX (tempFilePaths) {
				return new Promise((resolve, reject) => {
					let maxW = this.maxW;
					let maxH = this.maxH;
					let ratio, needCompress = false;
					tempFilePaths.image.width = Number(tempFilePaths.image.width);
					tempFilePaths.image.height = Number(tempFilePaths.image.height);
					if (tempFilePaths.image.width > maxW) {
						needCompress = true;
						ratio = tempFilePaths.image.width / maxW;
						maxH = tempFilePaths.image.height / ratio;
					}
					if (tempFilePaths.image.height > maxH) {
						needCompress = true;
						ratio = tempFilePaths.image.height / maxH;
						maxW = tempFilePaths.image.width / ratio;
					}
					if (!needCompress) {
						maxW = tempFilePaths.image.width;
						maxH = tempFilePaths.image.height;
					}
					this.W = maxW;
					this.H = maxH;
					if (!this.ctx) {
						this.ctx = uni.createCanvasContext('canvas', this);
					}
					this.ctx.clearRect(0, 0, this.W, this.H);
					this.ctx.drawImage(tempFilePaths.tempFilePaths, 0, 0, maxW, maxH);
					this.ctx.draw(false, () => {
						uni.canvasToTempFilePath({
						  x: 0,
						  y: 0,
						  width: this.W,
						  height: this.H,
						  destWidth: this.W,
						  destHeight: this.H,
						  canvasId: 'canvas',
						  quality: this.quality,
						  success: (res) => {
							let file = res.tempFilePath;
							let base64 = uni.getFileSystemManager().readFileSync(file, 'base64');
							base64 = `data:image/jpeg;base64,${base64}`
							if (this.base64) {
								resolve({file, base64});
							} else {
								resolve({file});
							}
						  }
						}, this)
					});
				})
			},
			compressImageWX (tempFilePaths) {
				let result = [];
				return new Promise(async (resolve, reject) => {
					for (let i = 0; i < tempFilePaths.length; i++) {
						let res = await this.compressResultWX(tempFilePaths[i]);
						result.push(res);
						if (result.length === tempFilePaths.length) {
							resolve(result);
							this.ctx = null;
						}
					}
				})
			},
			_dataURLtoFile (dataurl, filename) {
			  let arr = dataurl.split(',');
			  let mime = arr[0].match(/:(.*?);/)[1],bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
			  while(n--){
			      u8arr[n] = bstr.charCodeAt(n);
			  }
			  return new File([u8arr], filename, {type:mime});
			} 
		}
	}
</script>

<style scoped>
	.image-compress-container {
		width: 0;
		height: 0;
		margin: 0;
		padding: 0;
		overflow: hidden;
		position: absolute;
		z-index: -100000;
	}
</style>

页面调用

  1. 上传头像
  2. 对图像进行压缩
  3. 压缩后的图片上传后台
<!--#ifdef H5-->
					<!--h5直接上传头像-->
					<view class="portrait-box" @tap="uploadImage">
						<image
							class="img portrait"
							:src="data.pictureUrl"
						></image>
					</view>
					<!-- #endif -->
					<!--#ifndef H5-->
					<!--非h5裁剪头像上传-->
					<view class="portrait-box">
						<avatar
							canRotate="false"
							selWidth="200px"
							selHeight="400upx"
							@upload="handleUploadFile"
							:avatarSrc="data.pictureUrl"
							class="img"
							avatarStyle="width: 80rpx; height: 80rpx; border-radius: 100%; background-color: #E6F9F0;"
						>
						</avatar>
					</view>
					<!-- #endif -->	
					<x-compress ref="xCompress"></x-compress>	


<script>
import avatar from '@/components/rf-avatar/rf-avatar';
import xCompress from '../../components/x-compress/x-compress.vue';
export default {
    components: { avatar ,xCompress},
    data() {
        return {
            data: {
                pictureUrl:''
            }
        };
    },
    methods: {
        // 上传头像
        uploadImage() {
            // 从相册选择图片
            const _this = this;
            uni.chooseImage({
                count: 1,
                sizeType: ['original', 'compressed'],
                sourceType: ['album'],
                success: function(res) {
                    console.log('uploadImage',res)
                    _this.handleUploadFile(res.tempFilePaths);
                }
            });
        },
        // 上传头像
        async handleUploadFile(data) {
            const _this = this;
            const filePath = data.path || data[0];
            console.log('handleUploadFile',data)
            try {
                // 调用组件的compress方法开始压缩
                let result = await this.$refs.xCompress.compress(data, {
                    base64: true, // 是否也返回base64格式,默认false,既不返回base64格式图片数据,反之亦然
                    maxW: 1024 , // 当图片宽度超过1024大小时最大为1024(高度也会按比例缩放),默认也是1024
                    maxH: 1024, //  当图片高度超过1024大小时最大为1024(宽度也会按比例缩放),默认也是1024
                    quality: 0.8, // 压缩质量(0-1),默认为0.8,值越小压缩质量越大
                    showLoading: true, // 是否显示loading提示,也可以传入一个字符串(相当于loading时的title),默认为true,
                    mask: true // 当showLoading为true时,是否显示遮罩层,默认为true
                });
                console.log('resultImg', result);
                // this.data.pictureUrl = result[0].base64
                //上传图片到后台
                this.uploadImg(result[0].base64)
                // 返回值是压缩的结果,结果为数组,每一项是一个对象:

                // 	{
                // 		file: '在H5中为File格式的图片数据,在小程序中为图片临时路径',
                // 		base64: '在H5和小程序中都是base64格式数据,只有当指定base64为true时才有'
                // 	}
            } catch (error) {
                console.log('error', error);
            }
        },
        //上传图片到后台
        uploadImg(base64Str){
            request({
                url: `/productShare/upload`,
                method: 'POST',
                data:{base64Str},
                success: (res) => {
                    console.log( '头像成功',res)
                    if(res.code === 200){
                        uni.showToast({
                                title: res.data.msg || '头像修改成功',
                                image: '/static/img/success.png'
                        })
                        //更新页面图片
                        this.$nextTick(()=>{                                
                            this.data.pictureUrl = res.data.url ||  base64Str
                            this.$set(this.data,'pictureUrl',res.data.url ||  base64Str)
                            console.log('$nextTick',this.data)
                        })
                    }
                },
                fail: (err) => {
                    console.log('uploadImg request err', err);
                }
            });
        },
        //图片加载失败显示本地默认图片
        imageError(e) {
            this.data['pictureUrl'] = '/static/img/user.png'
        },
    }
}

</script>