HTML5画布移植(Porting from HTML5 Canvas)

移植一个HTML5画布图像到QML画布非常简单。在成百上千的例子中,我们选择了一个来移植。

螺旋图形(Spiro Graph)

我们使用一个来自Mozila项目的螺旋图形例子来作为我们的基础示例。原始的HTML5代码被作为画布教程发布。

下面是我们需要修改的代码:

  • Qt Quick要求定义变量使用,所以我们需要添加var的定义:

    1. for (var i=0;i<3;i++) {
    2. ...
    3. }
  • 修改绘制方法接收Context2D对象:

    1. function draw(ctx) {
    2. ...
    3. }
  • 由于不同的大小,我们需要对每个螺旋适配转换:

    1. ctx.translate(20+j*50,20+i*50);

最后我们实现onPaint操作。在onPaint中我们请求一个context,并且调用我们的绘制方法。

  1. onPaint: {
  2. var ctx = getContext("2d");
  3. draw(ctx);
  4. }

下面这个结果就是我们使用QML画布移植的螺旋图形。

HTML5画布移植(Porting from HTML5 Canvas) - 图1

发光线(Glowing Lines)

下面有一个更加复杂的移植来自W3C组织。原始的发光线有些很不错的地方,这使得移植更加具有挑战性。

HTML5画布移植(Porting from HTML5 Canvas) - 图2

  1. <!DOCTYPE HTML>
  2. <html lang="en">
  3. <head>
  4. <title>Pretty Glowing Lines</title>
  5. </head>
  6. <body>
  7. <canvas width="800" height="450"></canvas>
  8. <script>
  9. var context = document.getElementsByTagName('canvas')[0].getContext('2d');
  10. // initial start position
  11. var lastX = context.canvas.width * Math.random();
  12. var lastY = context.canvas.height * Math.random();
  13. var hue = 0;
  14. // closure function to draw
  15. // a random bezier curve with random color with a glow effect
  16. function line() {
  17. context.save();
  18. // scale with factor 0.9 around the center of canvas
  19. context.translate(context.canvas.width/2, context.canvas.height/2);
  20. context.scale(0.9, 0.9);
  21. context.translate(-context.canvas.width/2, -context.canvas.height/2);
  22. context.beginPath();
  23. context.lineWidth = 5 + Math.random() * 10;
  24. // our start position
  25. context.moveTo(lastX, lastY);
  26. // our new end position
  27. lastX = context.canvas.width * Math.random();
  28. lastY = context.canvas.height * Math.random();
  29. // random bezier curve, which ends on lastX, lastY
  30. context.bezierCurveTo(context.canvas.width * Math.random(),
  31. context.canvas.height * Math.random(),
  32. context.canvas.width * Math.random(),
  33. context.canvas.height * Math.random(),
  34. lastX, lastY);
  35. // glow effect
  36. hue = hue + 10 * Math.random();
  37. context.strokeStyle = 'hsl(' + hue + ', 50%, 50%)';
  38. context.shadowColor = 'white';
  39. context.shadowBlur = 10;
  40. // stroke the curve
  41. context.stroke();
  42. context.restore();
  43. }
  44. // call line function every 50msecs
  45. setInterval(line, 50);
  46. function blank() {
  47. // makes the background 10% darker on each call
  48. context.fillStyle = 'rgba(0,0,0,0.1)';
  49. context.fillRect(0, 0, context.canvas.width, context.canvas.height);
  50. }
  51. // call blank function every 50msecs
  52. setInterval(blank, 40);
  53. </script>
  54. </body>
  55. </html>

在HTML5中,context2D对象可以随意在画布上绘制。在QML中,只能在onPaint操作中绘制。在HTML5中,通常调用setInterval使用计时器触发线段的绘制或者清屏。由于QML中不同的操作方法,仅仅只是调用这些函数不能实现我们想要的结果,因为我们需要通过onPaint操作来实现。我们也需要修改颜色的格式。让我们看看需要改变哪些东西。

修改从画布元素开始。为了简单,我们使用画布元素(Canvas)作为我们QML文件的根元素。

  1. import QtQuick 2.0
  2. Canvas {
  3. id: canvas
  4. width: 800; height: 450
  5. ...
  6. }

代替直接调用的setInterval函数,我们使用两个计时器来请求重新绘制。一个计时器触发间隔较短,允许我们可以执行一些代码。我们无法告诉绘制函数哪个操作是我想触发的,我们为每个操作定义一个布尔标识,当重新绘制请求时,我们请求一个操作并且触发它。

下面是线段绘制的代码,清屏操作类似。

  1. ...
  2. property bool requestLine: false
  3. Timer {
  4. id: lineTimer
  5. interval: 40
  6. repeat: true
  7. triggeredOnStart: true
  8. onTriggered: {
  9. canvas.requestLine = true
  10. canvas.requestPaint()
  11. }
  12. }
  13. Component.onCompleted: {
  14. lineTimer.start()
  15. }
  16. ...

现在我们已经有了告诉onPaint操作中我们需要执行哪个操作的指示。当我们进入onPaint处理每个绘制请求时,我们需要提取画布元素中的初始化变量。

  1. Canvas {
  2. ...
  3. property real hue: 0
  4. property real lastX: width * Math.random();
  5. property real lastY: height * Math.random();
  6. ...
  7. }

现在我们的绘制函数应该像这样:

  1. onPaint: {
  2. var context = getContext('2d')
  3. if(requestLine) {
  4. line(context)
  5. requestLine = false
  6. }
  7. if(requestBlank) {
  8. blank(context)
  9. requestBlank = false
  10. }
  11. }

线段绘制函数提取画布作为一个参数。

  1. function line(context) {
  2. context.save();
  3. context.translate(canvas.width/2, canvas.height/2);
  4. context.scale(0.9, 0.9);
  5. context.translate(-canvas.width/2, -canvas.height/2);
  6. context.beginPath();
  7. context.lineWidth = 5 + Math.random() * 10;
  8. context.moveTo(lastX, lastY);
  9. lastX = canvas.width * Math.random();
  10. lastY = canvas.height * Math.random();
  11. context.bezierCurveTo(canvas.width * Math.random(),
  12. canvas.height * Math.random(),
  13. canvas.width * Math.random(),
  14. canvas.height * Math.random(),
  15. lastX, lastY);
  16. hue += Math.random()*0.1
  17. if(hue > 1.0) {
  18. hue -= 1
  19. }
  20. context.strokeStyle = Qt.hsla(hue, 0.5, 0.5, 1.0);
  21. // context.shadowColor = 'white';
  22. // context.shadowBlur = 10;
  23. context.stroke();
  24. context.restore();
  25. }

最大的变化是使用QML的Qt.rgba()和Qt.hsla()。在QML中需要把变量值适配在0.0到1.0之间。

同样应用在清屏函数中。

  1. function blank(context) {
  2. context.fillStyle = Qt.rgba(0,0,0,0.1)
  3. context.fillRect(0, 0, canvas.width, canvas.height);
  4. }

下面是最终结果(目前没有阴影)类似下面这样。

HTML5画布移植(Porting from HTML5 Canvas) - 图3

查看下面的链接获得更多的信息: