uni-app APP端canvas使用总结

场景:大概21年4月份的时候,项目预规划了“自定义流程图需求”,并且能够根据后端返回数据,实现动态流程图(节点状态、流程线状态、节点数量等动态变化)。当时项目正有2-3天的空档期,于是就利用这几天,熟悉了一下uni-app中canvas api,基于设计稿,先行绘制出了静态图。并且,基于movable-area和movable-view组件,对流程图添加了手势缩放与拖拽功能。不过到目前,该需求也没有在项目中实际落实。本文将将基于该静态流程图,对uni-app中canvas基本使用做一个介绍。

官网API文档:Canvas API

Canvas组件文档:Canvas

设计图原图展示:

uni-app APP端canvas使用总结_第1张图片

开发:

        ①:首先是视图层代码,主要的是Canvas的一些配置,其中canvas-id必须配置,且要唯一;并且如果不设置宽高,默认宽高是300px与225px,我这边是根据设计图大小设置的。此外,Canvas开发最佳、最简便的是在vue页面开发,nvue页面需要兼容,并且性能可能不佳。具体见官网Canvas组件介绍。

       

         ②:注意事项:

        1. 绘制顺序要注意,比如我画一个流程节点,要先画背景,然后画字,否则字会被遮挡。

        2. Canvas的draw()方法只需在所有元素绘制完成后调用一次就行。

        3.其余还没想到。

        ③:下面介绍一下关键的绘制函:

        1. drawImage:图片绘制,入参:Canvas 上下文、x、y、宽、高;

drawImage(ctx, path, x, y, dWidth, dHeight) {
				ctx.drawImage(path, x, y, dWidth, dHeight)
			},

        2. roundNode:圆角矩形节点绘制,入参:Canvas 上下文、x、y、宽、高、弧度、填充色值;主要原理是通过arcTo方法绘制圆弧,最后填充颜色;

//圆角矩形
			roundNode(ctx, x, y, width, height, radius, color) {
				ctx.beginPath()
				if (width < 2 * radius) radius = width / 2;
				if (height < 2 * radius) radius = height / 2;
				ctx.moveTo(x + radius, y);
				ctx.arcTo(x + width, y, x + width, y + height, radius);
				ctx.arcTo(x + width, y + height, x, y + height, radius);
				ctx.arcTo(x, y + height, x, y, radius);
				ctx.arcTo(x, y, x + width, y, radius);
				ctx.setFillStyle(color)
				ctx.fill()
			},

        3. diamondNode:菱形节点绘制,入参:Canvas 上下文、x、y、宽、高、线宽、填充色值;菱形节点绘制是基于圆角节点进行的,arcTo方法相当于一阶贝塞尔曲线,通过控制控制点的位置实现圆弧位置变动。但这个方法绘制出来有个缺口,存在一点瑕疵,后续项目正式将该需求提上计划后再优化

//菱形绘制
			diamondNode(ctx, x, y, width, height, lineColor, bgColor) {
				ctx.beginPath()
				ctx.setLineDash([])
				ctx.setLineWidth(4)

				ctx.moveTo(x + 20, y + 20);
				ctx.arcTo(x + width / 2, y, x + width, y + height / 2, 5);
				ctx.arcTo(x + width, y + height / 2, x + width / 2, y + height, 8);
				ctx.arcTo(x + width / 2, y + height, x, y + height / 2, 5);
				ctx.arcTo(x, y + height / 2, x + width / 2, y, 8);

				ctx.setStrokeStyle(lineColor)
				ctx.stroke()
				ctx.setFillStyle(bgColor)
				ctx.fill()
			},

        4. drawTriangle:三角形绘制,该方法与线段绘制组合使用,入参:Canvas 上下文、x、y、填充色、箭头方向;这边为了方便起见,箭头大小是写死的,我这边的箭头绘制原理是:基于指定点,绘制线段填充实现。

//绘制三角形  type:箭头朝向:bottom、right、left
			drawTriangle(ctx, x, y, color, type) {
				ctx.beginPath()
				let height = 10 //计算等边三角形的高
				ctx.moveTo(x, y); //x y开始

				switch (type) {
					case 'bottom':
						ctx.lineTo(x - height / 2, y)
						ctx.lineTo(x, y + height)
						ctx.moveTo(x, y)
						ctx.lineTo(x + height / 2, y)
						ctx.lineTo(x, y + height)
						break;
					case 'left':
						ctx.lineTo(x, y - height / 2)
						ctx.lineTo(x - height, y)
						ctx.moveTo(x, y)
						ctx.lineTo(x, y + height / 2)
						ctx.lineTo(x - height, y)
						break;
					case 'right':
						ctx.lineTo(x, y - height / 2)
						ctx.lineTo(x + height, y)
						ctx.moveTo(x, y)
						ctx.lineTo(x, y + height / 2)
						ctx.lineTo(x + height, y)
						break;
					default:
						break;
				}
				ctx.setFillStyle(color) //以纯色绿色填充
				ctx.fill();
			}

        5. drawText:文字绘制,入参:Canvas 上下文、x、y、填充色、文字大小;这边文字绘制在水平方向,通过measureText计算文字宽度,进行了适配;而文字竖直方向适配,试了几种网上的方法,一直没有成功,暂时做了统一偏移量处理。

drawText(ctx, text, x, y, color, size) {
				//文字部分
				ctx.beginPath()
				ctx.setTextAlign('center')
				ctx.setFillStyle(color)
				ctx.setFontSize(size)
				const metrics = ctx.measureText(text)
				console.log(metrics.width)
				//文字统一偏移
				ctx.fillText(text, x + metrics.width / 2, y + 17)
			},

        6. drawLine: 绘制线段,入参:Canvas 上下文、起始点x、起始点y、目标点x、目标点y、填充色、文字大小、箭头朝向、是否带箭头、是否虚线;线段绘制方法比较简单。

drawLine(ctx, fromX, fromY, toX, toY, color, type, isArrow = true, isDash = false) {
				ctx.beginPath()
				if (isDash) {
					ctx.setLineDash([10]);
				} else {
					ctx.setLineDash([]);
				}
				ctx.moveTo(fromX, fromY)
				ctx.lineTo(toX, toY)
				ctx.setLineWidth(1)
				ctx.setStrokeStyle(color)
				ctx.stroke()
				
				//是否绘制箭头
				if (isArrow) {
					this.drawTriangle(ctx, toX, toY, color, type)
				}
			},

        7. touchstart:Canvas触摸事件监听,用于实现流程图中节点的点击。实际使用需要记录每个节点的x、y坐标点、宽、高,通过这些参数对点击区域进行计算与判断,并且还要考虑缩放而带来的坐标点变化。

touchstart(e) {
				//点击事件有点复杂,要根据点击点、绘制位置、缩放比例判断点击了哪个节点,
				let x = e.touches[0].x
				let y = e.touches[0].y
				this.node.forEach(item => {
					// console.log("item.x * this.scale:"+item.x * this.scale)
					// console.log("item.y * this.scale:"+item.y * this.scale)
					if (x > item.x * this.scale && x < (item.x + item.w) * this.scale
						&& y > item.y * this.scale && y < (item.y + item.h) * this.scale) {
						//在范围内,根据标记定义节点类型
						console.log(item.targe)
						uni.showToast({
							icon:'none',
							title:item.name
						})
					}
				}) 
				console.log("x:"+x + "    y:"+y)
			},

         ④:逻辑层代码量比较多,略微复杂,这边全部贴出来,供参考。部分静态图片资源就不提供了,运行的时候替换一下或注释掉都行。       

总结:个人观点来看,uni-app端的canvas,与Android原生相比,核心原理都是找点,找到点位后执行相关绘制方法进行绘制,只不过部分方法有所不同。作为一个Android开发者,虽然暂时没具体使用过原生canvas,但对其也有了大致的了解。此外,Canvas还能实现各种效果的动画,这边初步了解了一点,后续实现了默写效果会继续更新文章。

效果展示,将就瞅瞅:

你可能感兴趣的:(uni-app,uni-app,android,ios)