• uni-app(微信小程序)图片旋转放缩,文字绘制、海报绘制


    总结一下:

    要进行海报绘制离不开canvas,我们是先进行图片,文字的拖拽、旋转等操作
    最后再对canvas进行绘制,完成海报绘制。

    1. 背景区域设置为 position: relative,方便图片在当前区域中拖动等处理。
    2. 添加图片,监听图片在背景区域下的 touchstart touchmove touchend 事件
    3. 拖动图片,在touchmove中,对图片进行位置(后续坐标-初始坐标)、角度(勾股定理计算点到圆心距离,利用角度计算公式计算)、放缩比例(勾股定理计算点到圆心的半径距离,拖动停止的半径除以初始的半径,获得放缩比例scale)的计算
    4. 最终canvas绘制,利用dom中的空canvas,将图片依次绘制到canvas中,并获取链接

    部分主要代码如下:

    const ctx = uni.createCanvasContext("myCanvas", this);
    ctx.drawImage(this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H);
    
    const ctx = uni.createCanvasContext("myCanvas", this);
    ctx.drawImage(this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H);
    ctx.save();
    ctx.beginPath();
    
    // 画背景色(白色)
    // ctx.setFillStyle('#fff');
    // ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
    for (let i=0; i<items.length; i++) {
    	const cur = items[i]
    	ctx.save();
    	ctx.translate(0, 0);
    	ctx.beginPath();
    	if(cur.image) {
    		ctx.translate(cur.x, cur.y); // 圆心坐标
    		ctx.rotate(cur.angle * Math.PI / 180); // 图片旋转的角度
    		ctx.translate(-(cur.width * cur.scale / 2), -(cur.height * cur.scale / 2)) // 图片的缩放
    		ctx.drawImage(cur.image, 0, 0, cur.width * cur.scale, cur.height * cur.scale); // 图片绘制
    	}
    	if (cur.text) {
    		ctx.font = `${cur.font}px arial`;
    		ctx.fillStyle = cur.fillStyle;
    		ctx.fillText(cur.text, cur.left, cur.top + Number(cur.font));
    		console.log(cur.left, cur.top + Number(cur.font))
    	}
    	ctx.restore();
    }
    ctx.draw(true, () => {
    	// 获取画布要裁剪的位置和宽度   均为百分比 * 画布中图片的宽度    保证了在微信小程序中裁剪的图片模糊  位置不对的问题 canvasT = (this.cutT / this.cropperH) * (this.imageH / pixelRatio)
    	var canvasW = ((this.cropperW - this.cutL - this.cutR) / this.cropperW) * IMG_REAL_W;
    	var canvasH = ((this.cropperH - this.cutT - this.cutB) / this.cropperH) * IMG_REAL_H;
    	var canvasL = (this.cutL / this.cropperW) * IMG_REAL_W;
    	var canvasT = (this.cutT / this.cropperH) * IMG_REAL_H;
    	uni.canvasToTempFilePath({
    			x: canvasL,
    			y: canvasT,
    			width: canvasW,
    			height: canvasH,
    			// destWidth: canvasW,
    			// destHeight: canvasH,
    			quality: +this.quality,
    			fileType: this.fileType,
    			canvasId: "myCanvas",
    			success: (res) => {
    				uni.hideLoading();
    				// 成功获得地址的地方
    				// this.$emit("getImg", res.tempFilePath);
    				this.saveImg(res.tempFilePath)
    				this.isShow = false;
    			},
    			fail: (err) => {
    				uni.hideLoading();
    				uni.showToast({
    					title: "图片截取失败!",
    					icon: "none",
    				});
    			},
    		},
    		this
    	);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    
    			<view class="uni-corpper"
    				:style="'width:' + cropperInitW + 'px;height:' + cropperInitH + 'px;background:#000'">
    				
    				<view class="uni-corpper-content" :style="
    						'width:' +
    							cropperW +
    							'px;height:' +
    							cropperH +
    							'px;left:' +
    							cropperL +
    							'px;top:' +
    							cropperT +
    							'px'
    					">
    					
    					<image :src="imageSrc" :style="'width:' + cropperW + 'px;height:' + cropperH + 'px;' + 'border: 3px solid #ff0000;'">image>
    					
    					<block v-for="item in itemList" :key="item.id">
    					
    						<view class='touchWrap' :style="{transform: 'scale(' + item.scale + ')', top: item.top + 'px', left: item.left + 'px', 'z-index':item.active ? 100 : 1}">
    							<view class='imgWrap' :class="item.active ? 'touchActive' : ''" :style="{transform: 'rotate(' + item.angle + 'deg)', border: item.active ? 4 * item.oScale : 0 + 'rpx #fff dashed'}">
    								<image 
    									v-if="item.image"
    									:src='item.image' 
    									:style="{width: item.width + 'px', height: item.height + 'px'}" 
    									
    									@touchmove="(e) => WraptouchMove(e, item)"
    									
    									@touchend="(e) => WraptouchEnd(e, item)"
    									mode="widthFix"
    								>
    								image>
    								
    								<image 
    									class='x' 
    									src='/static/close.png' 
    									:style="{transform: 'scale(' + item.oScale + ')', 'transform-origin': center}"
    									@click="(e) => deleteItem(e, item)"
    								>
    								image>
    								
    								<image 
    									v-if="item.image"
    									class='o' 
    									src='/static/scale.png' 
    									:style="{transform: 'scale(' + item.oScale + ')', 'transform-origin': center}"
    									
    									@touchmove="(e) => oTouchMove(e, item)"
    									@touchend="(e) => WraptouchEnd(e, item)"
    								>
    								image>
    							view>
    						view>
    					block>
    				view>
    							<canvas canvas-id="myCanvas" :style="
    				'position:absolute;border: 2px solid red; width:' +
    					imageW +
    					'px;height:' +
    					imageH +
    					'px;top:-9999px;left:-9999px;'
    			">
    			canvas>
    			view>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    // 点击图片或文字
    			WraptouchStart(e, it) {
    				currentChoose = it
    				// 循环图片数组获取点击的图片信息
    				for (let i = 0; i < items.length; i++) {
    					items[i].active = false;
    					if (it.id == items[i].id) {
    						index = i;
    						items[index].active = true;
    					}
    				}
    				// this.setData({
    				//   itemList: items
    				// })
    				this.setList(items, 'itemList')
    				// 获取点击的坐标值 lx ly是图片点击时的位置值
    				items[index].lx = e.touches[0].clientX;
    				items[index].ly = e.touches[0].clientY;
    			},
    			// 拖动图片
    			WraptouchMove(e) {
    				// 获取点击的坐标值 _lx _ly 是图片移动的位置值
    				items[index]._lx = e.touches[0].clientX;
    				items[index]._ly = e.touches[0].clientY;
    				// left 是_lx 减去 lx,_ly 减去 ly,也就是现在位置,减去原来的位置。
    				items[index].left += items[index]._lx - items[index].lx;
    				items[index].top += items[index]._ly - items[index].ly;
    				// 同理更新图片中心坐标点,用于旋转
    				items[index].x += items[index]._lx - items[index].lx;
    				items[index].y += items[index]._ly - items[index].ly;
    				// 停止了以后,把lx的值赋值为现在的位置
    				items[index].lx = e.touches[0].clientX;
    				items[index].ly = e.touches[0].clientY;
    				
    				// this.setData({
    				//   itemList: items
    				// })
    				this.setList(items, 'itemList')
    			},
    			// 放开图片
    			WraptouchEnd(e, it) {
    				touchNum ++
    				clearTimeout(timer)
    				timer = null
    				timer = setTimeout(this.timeSta, 250)
    			},
    			// 计算坐标点到圆心的距离
    			getDistancs(cx, cy, pointer_x, pointer_y) {
    				var ox = pointer_x - cx;
    				var oy = pointer_y - cy;
    				return Math.sqrt(
    					ox * ox + oy * oy
    				);
    			},
    			/*
    			*参数cx和cy为图片圆心坐标
    			*参数pointer_x和pointer_y为手点击的坐标
    			*返回值为手点击的坐标到圆心的角度
    			*/
    			countDeg(cx, cy, pointer_x, pointer_y) {
    				var ox = pointer_x - cx;
    				var oy = pointer_y - cy;
    				var to = Math.abs(ox / oy); // 勾股定理,计算当前点距离中心点的距离。
    				var angle = Math.atan(to) / (2 * Math.PI) * 360; // 计算当前角度
    				if (ox < 0 && oy < 0) //相对在左上角,第四象限,js中坐标系是从左上角开始的,这里的象限是正常坐标系  
    				{
    					angle = -angle;
    				} else if (ox <= 0 && oy >= 0) //左下角,3象限  
    				{
    					angle = -(180 - angle)
    				} else if (ox > 0 && oy < 0) //右上角,1象限  
    				{
    					angle = angle;
    				} else if (ox > 0 && oy > 0) //右下角,2象限  
    				{
    					angle = 180 - angle;
    				}
    				return angle; // 返回角度
    			},
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    体验:
    自定义画报

  • 相关阅读:
    每日一练 | 网络工程师软考真题Day42
    实现Ubuntu与Nvida Nano远程连接
    Ajax入门-Express框架介绍和基本使用
    vue3 tsx语法
    《Hello Algo》:动画图解引领的数据结构与算法学习之旅
    读书笔记-《ON JAVA 中文版》-摘要24[第二十一章 数组]
    信息学奥赛一本通:1137:加密的病历单
    Redis核心原理与应用实操
    C++:vector容器是否包含给定元素
    缓冲区与C库函数的实现
  • 原文地址:https://blog.csdn.net/Li_Ning21/article/details/132813633