uniapp图片编辑器,支持自定义尺寸、缩放、拖动、裁剪

支持功能: 

1.自定义裁剪尺寸
2.等比例缩放
3.自由拖动
5.裁剪生成新图片
6.本地选择图片

实现代码:

<!-- 
	图片编辑器,支持自定义尺寸、缩放、拖动、裁剪
	@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>

使用说明:

1.自定义裁剪尺寸和默认展示图片,修改 onLoad 中得代码即可


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

 2.现已发布了图片裁剪插件2.0(建议使用),可直接使用HBuilder导入项目,代码开源注释全、裁剪功能更丰富、体验更佳、性能也大幅度提升。

uniapp微信小程序图片裁剪插件,支持自定义尺寸、定点等比例缩放、拖动、图片翻转、剪切圆形/圆角图片、定制样式_Homilier的博客-CSDN博客

  • 5
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Homilier

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值