uniapp image editor, supports custom size, scaling, dragging, cropping

Support functions: 

1. Custom cropping size
2. Proportional zooming
3. Free dragging
5. Cropping to generate new pictures
6. Local selection of pictures

Implementation code:

<!-- 
	图片编辑器,支持自定义尺寸、缩放、拖动、裁剪
	@version 	1.0.2
	@updatedAt 	2021/01/06
	-->
<template>
	<view class="upload-image">
		<view class="upload-image-body">
			<canvas canvas-id="picCanvas" :style="'position: absolute; width: ' + picSizeW + 'px; height: ' + picSizeH + 'px; left: -' + picSizeW + 'px;'"></canvas>
			<view class="pic-preview" @touchstart="touchstart" @touchmove="touchmove">
				<scroll-view class='pic-area' @scroll='scroll' scroll-x scroll-y>
					<view :style="{ height: `calc(50% - ${areaH * 0.5}vw)` }"></view>
					<image :src="picSrc || picUrl" :style="styleImg"></image>
					<view :style="{ height: `calc(50% - ${areaH * 0.5}vw)` }"></view>
				</scroll-view>
				<view class="outside-mask-block" :style="styleV"></view>
				<view class="outside-mask-block" :style="styleV" style="bottom: 0;"></view>
				<view class="outside-mask-block" :style="styleH" style="left: 0;"></view>
				<view class="outside-mask-block" :style="styleH" style="right: 0;"></view>
			</view>
			<view class="bottom-bar safe-area-inset-bottom">
				<block v-if="picSrc != ''">
					<view class="rechoose" @click="chooseImage">重选</view>
					<button class="button" size="mini" @click="uploadClick">确定</button>
				</block>
				<view v-else class="choose-btn" @click="chooseImage">选择图片</view>
			</view>
		</view>
	</view>
</template>

<script>
	const AREA_SIZE = 75; // 裁剪框占屏幕尺寸百分比
	const IMG_SIZE = 300; // 裁剪图片默认尺寸

	export default {
		data() {
			return {
				// bobyHeight: this.getBobyHeight(),
				picSrc: '',
				picUrl: '',
				dataKey: '',
				areaW: AREA_SIZE,
				areaH: AREA_SIZE,
				width: this.areaW,
				height: this.areaH,
				old_width: 0,
				old_height: 0,
				picSizeW: IMG_SIZE,
				picSizeH: IMG_SIZE,
				x: 0,
				y: 0,
				distance: 0,
				scale: 1,
				disable: false
			}
		},
		onLoad(options) {
			this.picSizeW = Number(options.w) || IMG_SIZE;
			this.picSizeH = Number(options.h) || IMG_SIZE;
			this.picUrl = ''; // 初始化图片
			this.initAreaSize();
		},
		computed: {
			styleImg() {
				return `padding: 0 ${50 - this.areaW * 0.5}%;width: ${this.width}%;height: ${this.height}vw;`;
			},
			styleV() {
				// (屏幕高度 - 图片高度) / 2
				return `height: calc(50% - ${this.areaH * 0.5}vw);left: ${50 - this.areaW * 0.5}%;right: ${50 - this.areaW * 0.5}%;`;
			},
			styleH() {
				// (屏幕宽度 - 图片宽度) / 2
				return `top: 0;bottom: 0;width: ${50 - this.areaW * 0.5}%;`;
			},
		},
		methods: {
			initAreaSize() {
				if (this.picSizeW > this.picSizeH) {
					this.areaH = AREA_SIZE * this.picSizeH / this.picSizeW;
				} else if (this.picSizeW < this.picSizeH) {
					this.areaW = AREA_SIZE * this.picSizeW / this.picSizeH;
				}
				this.width = this.areaW;
				this.height = this.areaH;
			},
			chooseImage() {
				uni.chooseImage({
					count: 1,
					success: (res) => {
						this.resetData();
						this.initImage(res.tempFiles[0].path);
					}
				});
			},

			resetData() {
				this.picSrc = '';
				this.distance = 0;
				this.old_width = 0;
				this.old_height = 0;
				this.x = 0;
				this.y = 0;
				this.scale = 1;
				this.disable = false;
				this.initAreaSize();
			},

			initImage(url) {
				uni.getImageInfo({
					src: url,
					success: (res) => {
						// #ifdef APP-PLUS || MP
						if (['png', 'jpeg', 'jpg'].indexOf(res.type) == -1) {
							uni.showModal({
								title: '',
								content: '仅支持上传png和jpg格式图片',
								showCancel: true,
								cancelText: '取消',
								confirmText: '重选',
								success: (res) => {
									if (res.confirm) {
										this.chooseImage();
									}
								},
							});
							return;
						}
						// #endif
						let scale = res.width / res.height;
						let areaScale = this.areaW / this.areaH;
						this.picSrc = url;
						this.scale = scale;
						if (scale > 1) { // 横向图片
							if (scale >= areaScale) { // 图片宽不小于目标宽,则高固定,宽自适应
								this.width = (this.height / res.height) * this.width * (res.width / this.width);
							} else { // 否则宽固定、高自适应
								this.height = res.height * this.width / res.width;
							}
						} else { // 纵向图片
							if (scale <= areaScale) { // 图片高不小于目标高,宽固定,高自适应
								this.height = (this.width / res.width) * this.height / (this.height / res.height);
							} else { // 否则高固定,宽自适应
								this.width = res.width * this.height / res.height;
							}
						}
						// 记录原始宽高,为缩放比列做限制
						this.old_width = this.width;
						this.old_height = this.height;
					},
				});

			},

			touchstart(e) {
				if (this.picSrc && e.touches.length == 2) {
					let _x = e.touches[1].pageX - e.touches[0].pageX,
						_y = e.touches[1].pageY - e.touches[0].pageY,
						distance = Math.sqrt(Math.pow(_x, 2) + Math.pow(_y, 2));
					this.distance = distance;
				}
			},

			touchmove(e) {
				if (this.picSrc && e.touches.length == 2) {
					let _x = e.touches[1].pageX - e.touches[0].pageX,
						_y = e.touches[1].pageY - e.touches[0].pageY,
						old_width = this.old_width,
						old_height = this.old_height,
						newdistance = Math.sqrt(Math.pow(_x, 2) + Math.pow(_y, 2)),
						distance = this.distance,
						end_distance = newdistance - distance,
						pic_scale = 1 + end_distance * 0.001,
						width = this.width * pic_scale,
						height = this.height * pic_scale;
					let max = width / old_width;
					if (max > 2) {
						width = old_width * 2;
						height = old_height * 2;
					} else if (max < 1) {
						width = old_width;
						height = old_height;
					}
					this.width = width;
					this.height = height;
				}
			},

			scroll(e) {
				if(this.picSrc) {
					let x = e.detail.scrollLeft,
						y = e.detail.scrollTop;
					this.x = x;
					this.y = y;
				}
			},

			uploadClick(e) {
				uni.showModal({
					content: '确定要截取当前可视区域图片并上传吗?',
					success: modalRes => {
						if (modalRes.confirm) {
							uni.showLoading({
								title: '上传中...',
								mask: true
							});
							const systemInfo = uni.getSystemInfoSync();
							let whScale = systemInfo.screenWidth * 0.01, // 图片宽高vw与px比
								// 生成图片的实际尺寸与截取区域比
								xScale = this.picSizeW / (systemInfo.screenWidth * this.areaW * 0.01),
								yScale = this.picSizeH / (systemInfo.screenWidth * this.areaH * 0.01);
							const canvas = uni.createCanvasContext('picCanvas');
							// 注意:无法直接绘制网络图片,需要先下载到本地
							canvas.drawImage(this.picSrc, -this.x * xScale, -this.y * yScale, this.width * whScale * xScale, this.height *
								whScale * xScale);
							canvas.draw(setTimeout(() => {
								uni.canvasToTempFilePath({
									x: 0,
									y: 0,
									width: this.picSizeW,
									height: this.picSizeH,
									destWidth: this.picSizeW, // 必要,保证生成图片宽度不受设备分辨率影响
									destHeight: this.picSizeH, // 必要,保证生成图片高度不受设备分辨率影响
									canvasId: 'picCanvas',
									success: (fileRes) => {
										console.log(fileRes)
										this.uploadImage(fileRes.tempFilePath);
									},
									fail: function(err) {
										console.log(err);
										uni.showToast({
											title: '上传失败:图片生成过程中遇到错误',
											icon: 'none'
										});
									}
								}, this);
							}, 1000));
						}
					}
				});
			},
			uploadImage(tempFilePath){
				// 在H5平台下,tempFilePath 为 base64
			}
		}
	}
</script>

<style lang="scss">
	.upload-image {
		position: fixed;
		left: 0;
		right: 0;
		top: 0;
		bottom: 0;

		.upload-image-body {
			width: 100%;
			height: 100%;
			overflow: hidden;
			position: relative;
			display: flex;
			flex-direction: column;

			.pic-preview {
				width: 100%;
				flex: 1;
				position: relative;

				.pic-area {
					left: 0;
					right: 0;
					top: 0;
					bottom: 0;
					font-size: 0;
					z-index: 1;
					background-color: $uni-bg-color-grey;
					position: absolute;
					display: flex;
					flex-direction: column;
				}

				.outside-mask-block {
					background-color: rgba(51, 51, 51, 0.9);
					z-index: 2;
					position: absolute;
				}
			}

			.bottom-bar {
				display: flex;
				flex-direction: row;
				position: relative;
				background-color: $uni-bg-color-grey;

				.rechoose {
					color: $uni-color-primary;
					padding: 0 $uni-spacing-row-lg;
					line-height: 100rpx;
				}

				.choose-btn {
					color: $uni-color-primary;
					text-align: center;
					line-height: 100rpx;
					flex: 1;
				}

				.button {
					margin: auto $uni-spacing-row-lg auto auto;
				}
			}

            .safe-area-inset-bottom {
                padding-bottom: 0;  
                padding-bottom: constant(safe-area-inset-bottom); // 兼容 IOS<11.2
                padding-bottom: env(safe-area-inset-bottom); // 兼容 IOS>=11.2
            }

		}
	}
</style>

Instructions for use:

1. Customize the cropping size and the default display picture, just modify the code in onLoad


onLoad(options) {
	this.picSizeW = Number(options.w) || IMG_SIZE;
	this.picSizeH = Number(options.h) || IMG_SIZE;
	this.picUrl = ''; // 初始化图片
	this.initAreaSize();
},

2. The image cropping plug-in 2.0 (recommended)  has been released . You can directly use HBuilder to import projects. The code is open source and fully commented. The cropping function is richer, the experience is better, and the performance is also greatly improved.

uniapp WeChat applet picture cropping plug-in, supports custom size, fixed-point scaling, dragging, picture flipping, cutting round/rounded pictures, custom styles_Homilier's Blog-CSDN Blog

Guess you like

Origin blog.csdn.net/Honiler/article/details/114311627