对比与qt的图形视图框架的介绍(可以查看我的其他博文https://www.cnblogs.com/laiyingpeng/p/12294990.html),本章介绍QML 2D绘图相关知识,本文提到的相关操作均指Qt Quick中,与HTML5以及JavaScript可能存在部分差异,均以本文为准。
一、Canvas 介绍
Qt Quick Canvas与HTML 5的canvas标签类似,基本使用方法包括绘制API、渐变、阴影、图像使用等,Canvas提供了一个空白绘图区域,可以利用api在上面绘制图形。
Qt5引入了Canvas类型,该类型继承自Item,所以Canvas对象;也可以称为Canvas项目,Canvas提供了一个依赖分辨率的位图画布,能够使用JavaScript绘制直线和曲线、简单和复杂的图形、图像等等,还可以添加文本、颜色、渐变和图案以及像素的操作。
Canvas项目API基于HTML5的canvas元素,其基本思想是提供一个用于渲染路径的Context2D对象,这个对象就是在Canvas上进行绘制的画笔,提供必要的绘图函数,包括画线、填充、渐变、文字、路径的创建等,更多细节类容可在帮助文档中搜索Canvas关键字了解:
1.1 Canvas如何使用
使用canvs对象创建一个宽100高200的绘制区域,代码如下:
Canvas{ id :canvas 100 height: 200 }
1.2 基本属性介绍
available :该属性用于设置Canvs是否可用,只有为true时后续的操作才有效;
canvasSize:设置canvas的逻辑大小,逻辑大小也是也是可以进行绘制的区域大小,默认情况下与当前画布中已有项目大小一致,虽然可以设置,但是只有出现在视口的元素时才会被Canvas渲染引擎绘制;
renderStrategy:用于设置渲染策略
renderTarget :用于设置canvas的渲染目标,目前支持以下两种
二、绘制操作
在QML中,Canvas扮演了绘制容器的角色,它本身不提供任何有关绘制的函数,所有的绘制操作都是通过getContext()函数获取Context2D上下文类型来完成,实际的绘制都是在Canvas的onPaint()事件处理器进行的。
Context2D提供了一个经典的二维笛卡尔坐标,默认情况下是与窗口坐标系统相同,原点(0,0)位于左上角,x轴正方向向右,y轴正方向向下,坐标如下:
然而Canvas的坐标系并不是固定的,我们可以对坐标系统进行平移、缩放及旋转等,在后边的坐标转换一节我们会专门讲解。
2.1 绘制参数设置
Context2D类型提供了两种绘制方式:填充或描边:
- 填充会将一个区域的内部使用某种方式进行覆盖,使用fillStyle()函数
- 描边则使用线条将一个区域的边框勾画出来,使用strokeStyle()函数
2.2 绘制矩形
Canvas没有提供过多的基本图元的绘制API仅有矩形的绘制,这是因为矩形相比其他图元更为常用,并且矩形的填充可以直接作为Canvas的背景填充,同时在实现动画等效果时,由于效率问题,通常还要清除某一矩形区域。
针对矩形的绘制,Canvas提供了三种方法:
- fillRect函数以填充方式绘制矩形;
- strokeRect函数以描边方式绘制矩形
- clearRect函数清除矩形区域
示例如下:
import QtQuick 2.12 Canvas { 200; height: 200 onPaint: { var ctx = getContext("2d"); ctx.fillStyle = "lightgrey" // 设置填充颜色为浅灰色 ctx.strokeStyle = "blue" // 设置边线颜色为蓝色 ctx.lineWidth = 4 // 设置边线宽度为4px ctx.lineJoin = "round" ctx.fillRect(20, 20, 160, 160) ctx.clearRect(30, 30, 140, 140) ctx.strokeRect(20, 20, 80, 80) } }
运行效果如下:
2.3 状态的保存与恢复
Context2D是一个状态机,有很多状态,当前状态会一直保留,直到该状态被赋予新的值,但是如果我们绘制矩形a和b,绘制a时不指定指定颜色(默认黑色),绘制b时指定颜色绿色,程序运行后会出现黑色a和绿色b,但是当我们调整窗口大小后,会看到a和b都是绿色,这是因为绘制时上下文被填充成了绿色,当窗口大小改变后,画布内容需要重新绘制,由于Context2D是一个状态机,绘制第一个矩形时并没有指定填充颜色,,因而使用第二个矩形设置的绿色作为了填充色,这样两个矩形在绘制时都被绿色填充,那么如何修改呢,我们可以在每一个要绘制的前面都设置填充色,当然这可以解决,不过当绘制过程非常复杂需要设置很多属性时,一个个单独恢复之前的状态是不现实的,这里我们就要用到保存和恢复函数函数,如下介绍:
- save()函数将当前属性值,压入状态栈;
- restore()函数将save()函数压入状态栈的栈顶状态弹出恢复为上下文;
下面看一个示例:
import QtQuick 2.12 Canvas { 200; height: 100 onPaint: { var ctx = getContext("2d") ctx.fillStyle = "black" ctx.fillRect(10, 10, 50, 50) // 第一个矩形 ctx.save() ctx.fillStyle = ctx.createPattern("lightgrey", Qt.Dense1Pattern) ctx.fillRect(70, 10, 50, 50) // 第二个矩形 ctx.restore() ctx.fillRect(130, 10, 50, 50) // 第三个矩形 } }
运行结果如下:
我们使用restore恢复到save之前的状态。
2.4 绘制文本
与矩形类似,Canvas也提供了两种绘制文本的方法:
- 填充:fillText(text,x,y)以填充方式绘制文本text,其中文本的左上角位于(x,y);
- 描边:strokeText(text,x,y)以描边方式绘制文本text,其中文本的左上角位于(x,y);
下面看一个示例:
import QtQuick 2.12 Canvas { 210; height: 200 onPaint: { var ctx = getContext("2d"); ctx.fillStyle = "green" ctx.strokeStyle = "blue" ctx.lineWidth = 2 ctx.font = "bold 50px Arial" var text = "qter.org"; context.fillText(text, 10, 80) context.strokeText(text, 10, 150) } }
运行结果如下:
2.5 绘制路径
Canvas提供了简单的矩形绘制API,但是实际应用中还有很多复杂图形,在Canvas中所有的图形都以路径为基础,我们以beginPath()和closePath()一组函数去通知Context2D开始绘制路径和结束绘制路径,一边形成一个环路,closePath也可有系统自动调用。
- lineTo(x,y):该函数将提供当前坐标到x,y之间的一条直线;
- object arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise):添加一段圆弧
下面是一段示例:
import QtQuick 2.12 Canvas { 240; height: 160 onPaint: { var ctx = getContext("2d"); ctx.lineWidth = 2 ctx.beginPath() ctx.moveTo(0, 60) ctx.lineTo(240, 60) ctx.stroke() ctx.beginPath() ctx.moveTo(30, 60) ctx.arc(30, 60, 20, 0, -Math.PI / 2, true) ctx.stroke() ctx.beginPath() ctx.moveTo(90, 60) ctx.arc(90, 60, 20, 0, Math.PI, true) ctx.stroke() ctx.beginPath() ctx.moveTo(150, 60) ctx.arc(150, 60, 20, 0, -3 * Math.PI / 2, true) ctx.stroke() ctx.beginPath() ctx.moveTo(210, 60) ctx.arc(210, 60, 20, 0, Math.PI * 2, true) ctx.stroke() } }
运行效果如下: