运行结果如下:

通过调用resetTransform()函数,可以将变换矩阵重置为单位矩阵:

  1. ctx.resetTransform()

组合意思是,将你绘制的图形与已存在的像素做一些融合操作。canvas支持几种组合方式,使用globalCompositeOperation可以设置组合的模式。如下代码所示,我们可以看到组合的相应表现:

代码运行结果如下:canvas 组合

使用canvas,你可以将canvas内容的像素数据读取出来,并且能够针对这些数据做一些操作。

另外一个获取画布内容的方法是,将数据保存到一个图片。这可以通过Canvas的函数save(path)toDataURL(mimeType)实现,后者会返回一个图像的 URL,可以供元素加载图像。

  1. import QtQuick 2.0
  2.  
  3. Rectangle {
  4. width: 240; height: 120
  5. Canvas {
  6. id: canvas
  7. x: 10; y: 10
  8. width: 100; height: 100
  9. property real hue: 0.0
  10. onPaint: {
  11. var ctx = getContext("2d")
  12. var x = 10 + Math.random(80)*80
  13. var y = 10 + Math.random(80)*80
  14. hue += Math.random()*0.1
  15. if(hue > 1.0) { hue -= 1 }
  16. ctx.globalAlpha = 0.7
  17. ctx.fillStyle = Qt.hsla(hue, 0.5, 0.5, 1.0)
  18. ctx.beginPath()
  19. ctx.moveTo(x+5,y)
  20. ctx.arc(x,y, x/10, 0, 360)
  21. ctx.closePath()
  22. ctx.fill()
  23. }
  24. MouseArea {
  25. anchors.fill: parent
  26. onClicked: {
  27. var url = canvas.toDataURL('image/png')
  28. print('image url=', url)
  29. image.source = url
  30. }
  31. }
  32. }
  33.  
  34. Image {
  35. id: image
  36. x: 130; y: 10
  37. width: 100; height: 100
  38.  
  39. Timer {
  40. interval: 1000
  41. running: true
  42. triggeredOnStart: true
  43. repeat: true
  44. onTriggered: canvas.requestPaint()
  45. }
  46. }

在上面的例子中,我们创建了两个画布,左侧的画布每一秒产生一个圆点;鼠标点击会将画布内容保存,并且生成一个图像的 URL,右侧则会显示这个图像。

下面我们利用Canvas元素创建一个画板程序。我们程序的运行结果如下所示:窗口上方是调色板,用于设置画笔颜色。色板是一个填充了颜色的矩形,其中覆盖了一个鼠标区域,用于检测鼠标点击事件。

调色板所支持的颜色保存在一个数组中,画笔的当前颜色则保存在paintColor属性。当用户点击调色板的一个色块,该色块的颜色就会被赋值给paintColor属性。

为了监听鼠标事件,我们在画布上面覆盖了一个鼠标区域,利用鼠标按下和位置改变的信号处理函数完成绘制:

  1. Canvas {
  2. id: canvas
  3. anchors {
  4. left: parent.left
  5. right: parent.right
  6. top: colorTools.bottom
  7. bottom: parent.bottom
  8. margins: 8
  9. }
  10. property real lastX
  11. property real lastY
  12. property color color: colorTools.paintColor
  13.  
  14. onPaint: {
  15. var ctx = getContext('2d')
  16. ctx.lineWidth = 1.5
  17. ctx.strokeStyle = canvas.color
  18. ctx.beginPath()
  19. ctx.moveTo(lastX, lastY)
  20. lastX = area.mouseX
  21. lastY = area.mouseY
  22. ctx.lineTo(lastX, lastY)
  23. ctx.stroke()
  24. }
  25. MouseArea {
  26. id: area
  27. anchors.fill: parent
  28. onPressed: {
  29. canvas.lastX = mouseX
  30. canvas.lastY = mouseY
  31. }
  32. onPositionChanged: {
  33. canvas.requestPaint()
  34. }
  35. }

最后,为了绘制用户笔记,在onPaint()处理函数中,我们首先创建了一个新的路径,将其移动到最后的位置,然后我们从鼠标区域获得新的位置,在最后的位置与新的位置之间绘制直线,同时,将当前鼠标位置(也就是新的位置)设置为新的最后的位置。

由于 QML 的Canvas对象由 HTML 5 的 canvas 标签借鉴而来,将 HTML 5 的 canvas 应用移植到 QML Canvas也是相当容易。我们以 Mozilla 提供的繁华曲线页面为例,演示移植的过程。可以在看到该页面的运行结果。下面是 HTML 5 canvas 的脚本部分:

这里我们只解释如何进行移植,有关繁花曲线的算法则不在我们的阐述范围之内。幸运的是,我们需要改变的代码很少,因而这里也会很短。

HTML 按照顺序执行,draw() 会成为脚本的入口函数。但是在 QML 中,绘制必须在 onPaint 中完成,因此,我们需要将 draw() 函数的调用移至 onPaint。通常我们会在 onPaint 中获取绘制上下文,因此,我们将给 draw() 函数添加一个参数,用于接受对象。事实上,这就是我们所有的修改。移植之后的 QML 如下所示:

  1. import QtQuick 2.2
  2.  
  3. Canvas {
  4. id: root
  5. width: 300; height: 300
  6.  
  7. onPaint: {
  8. var ctx = getContext("2d");
  9. draw(ctx);
  10. }
  11.  
  12. function draw (ctx) {
  13. ctx.fillRect(0, 0, 300, 300);
  14. for (var i = 0; i < 3; i++) {
  15. for (var j = 0; j < 3; j++) {
  16. ctx.save();
  17. ctx.strokeStyle = "#9CFF00";
  18. ctx.translate(50 + j * 100, 50 + i * 100);
  19. drawSpirograph(ctx, 20 * (j + 2) / (j + 1), -8 * (i + 3) / (i + 1), 10);
  20. ctx.restore();
  21. }
  22. }
  23. }
  24.  
  25. function drawSpirograph (ctx, R, r, O) {
  26. var x1 = R - O;
  27. var y1 = 0;
  28. var i = 1;
  29. ctx.beginPath();
  30. ctx.moveTo(x1, y1);
  31. do {
  32. if (i > 20000) break;
  33. var x2 = (R + r) * Math.cos(i * Math.PI / 72) - (r + O) * Math.cos(((R + r) / r) * (i * Math.PI / 72))
  34. var y2 = (R + r) * Math.sin(i * Math.PI / 72) - (r + O) * Math.sin(((R + r) / r) * (i * Math.PI / 72))
  35. ctx.lineTo(x2, y2);
  36. x1 = x2;
  37. y1 = y2;
  38. i++;
  39. } while (x2 != R-O && y2 != 0 );
  40. ctx.stroke();
  41. }

运行一下这段代码: