Qt6 QML Book/画布/从HTML5画布移植

Porting from HTML5 Canvas

从HTML5画布移植

Porting from an HTML5 canvas to a QML canvas is fairly easy. In this chapter we will look at the example below and do the conversion.

从HTML5画布移植到QML画布相当容易。在本章中,我们将查看下面的示例并进行转换。

Spirograph

呼吸运动记录器

We use a spirograph example from the Mozilla project as our foundation. The original HTML5 was posted as part of the canvas tutorial.

我们使用Mozilla项目的一个例子来作为我们的基础。最初的HTML5是作为画布教程的一部分发布的。​

There were a few lines we needed to change:

有几行我们需要更改:

  • Qt Quick requires you to declare a variable, so we needed to add some var declarations

  • Qt Quick要求您声明一个变量,因此我们需要添加一些var声明

    for (var i=0;i<3;i++) {
        ...
    }
    
  • We adapted the draw method to receive the Context2D object

  • 我们调整了draw方法以接收Context2D对象

    function draw(ctx) {
        ...
    }
    
  • We needed to adapt the translation for each spiro due to different sizes

  • 由于尺寸不同,我们需要调整每个spiro的实现

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

Finally, we completed our onPaint handler. Inside we acquire a context and call our draw function.

最后,我们完成了onPaint处理器。在内部,我们获取一个上下文并调用draw函数。

onPaint: {
    var ctx = getContext("2d");
    draw(ctx);
}

The result is a ported spiro graph graphics running using the QML canvas.

结果是一个使用QML画布运行的移植spiro graph图形。

As you can see, with no changes to the actual logic, and relatively few changes to the code itself, a port from HTML5 to QML is possible.

正如您所看到的,没有对实际逻辑的更改,代码本身的更改相对较少,从HTML5到QML的移植是可能的。

Glowing Lines

荧光线条

Here is another more complicated port from the W3C organization. The original pretty glowing lines has some pretty nice aspects, which makes the porting more challenging.

这里是来自W3C组织的另一个更复杂的移植。最初的漂亮发光线条有一些漂亮的方面,这使得移植更具挑战性。​

<!DOCTYPE HTML>
<html lang="en">
<head>
    <title>Pretty Glowing Lines</title>
</head>
<body>

<canvas width="800" height="450"></canvas>
<script>
var context = document.getElementsByTagName('canvas')[0].getContext('2d');

// initial start position
var lastX = context.canvas.width * Math.random();
var lastY = context.canvas.height * Math.random();
var hue = 0;

// closure function to draw
// a random bezier curve with random color with a glow effect
function line() {

    context.save();

    // scale with factor 0.9 around the center of canvas
    context.translate(context.canvas.width/2, context.canvas.height/2);
    context.scale(0.9, 0.9);
    context.translate(-context.canvas.width/2, -context.canvas.height/2);

    context.beginPath();
    context.lineWidth = 5 + Math.random() * 10;

    // our start position
    context.moveTo(lastX, lastY);

    // our new end position
    lastX = context.canvas.width * Math.random();
    lastY = context.canvas.height * Math.random();

    // random bezier curve, which ends on lastX, lastY
    context.bezierCurveTo(context.canvas.width * Math.random(),
    context.canvas.height * Math.random(),
    context.canvas.width * Math.random(),
    context.canvas.height * Math.random(),
    lastX, lastY);

    // glow effect
    hue = hue + 10 * Math.random();
    context.strokeStyle = 'hsl(' + hue + ', 50%, 50%)';
    context.shadowColor = 'white';
    context.shadowBlur = 10;
    // stroke the curve
    context.stroke();
    context.restore();
}

// call line function every 50msecs
setInterval(line, 50);

function blank() {
    // makes the background 10% darker on each call
    context.fillStyle = 'rgba(0,0,0,0.1)';
    context.fillRect(0, 0, context.canvas.width, context.canvas.height);
}

// call blank function every 50msecs
setInterval(blank, 40);

</script>
</body>
</html>

In HTML5 the Context2D object can paint at any time on the canvas. In QML it can only point inside the onPaint handler. The timer in usage with setInterval triggers in HTML5 the stroke of the line or to blank the screen. Due to the different handling in QML, it’s not possible to just call these functions, because we need to go through the onPaint handler. Also, the color presentations need to be adapted. Let’s go through the changes on by one.

在HTML5中,Context2D对象可以随时在画布上绘制。在QML中,它只能在onPaint处理器内部执行。在HTML5中,与setInterval一起使用的计时器会触发线条的绘制或空白屏幕。由于QML中的处理方式不同,不可能只调用这些函数,因为我们需要通过onPaint处理器实现。此外,还需要调整颜色表示。让我们逐一看一下这些变化。

Everything starts with the canvas element. For simplicity, we just use the Canvas element as the root element of our QML file.

一切都从canvas元素类型开始。为简单起见,我们只使用Canvas元素类型作为QML文件的根元素类型。

import QtQuick

Canvas {
   id: canvas
   width: 800; height: 450

   ...
}

To untangle the direct call of the functions through the setInterval, we replace the setInterval calls with two timers which will request a repaint. A Timer is triggered after a short interval and allows us to execute some code. As we can’t tell the paint function which operation we would like to trigger we define for each operation a bool flag request an operation and trigger then a repaint request.

为了通过setInterval解开函数的直接调用,我们将setInterval调用替换为两个请求重新绘制的计时器。一个定时器在短时间间隔后被触发,允许我们执行一些代码。由于我们无法告诉paint函数要触发哪个操作,因此我们为每个操作定义了一个bool标志请求一个操作,然后触发一个重绘请求。

Here is the code for the line operation. The blank operation is similar.

这是行操作的代码。空白操作类似。

...
property bool requestLine: false

Timer {
    id: lineTimer
    interval: 40
    repeat: true
    triggeredOnStart: true
    onTriggered: {
        canvas.requestLine = true
        canvas.requestPaint()
    }
}

Component.onCompleted: {
    lineTimer.start()
}
...

Now we have an indication which (line or blank or even both) operation we need to perform during the onPaint operation. As we enter the onPaint handler for each paint request we need to extract the initialization of the variable into the canvas element.

现在我们有了一个指示,在onPaint操作期间需要执行哪个操作(线条或空白,甚至两者都有)。在为每个绘制请求输入onPaint处理器时,我们需要将变量的初始化提取到canvas元素类型中。

Canvas {
    ...
    property real hue: 0
    property real lastX: width * Math.random();
    property real lastY: height * Math.random();
    ...
}

Now our paint function should look like this:

现在,我们的绘制功能应该如下所示:

onPaint: {
    var context = getContext('2d')
    if(requestLine) {
        line(context)
        requestLine = false
    }
    if(requestBlank) {
        blank(context)
        requestBlank = false
    }
}

The line function was extracted for a canvas as an argument.

line函数是作为参数为画布提取的。

function line(context) {
    context.save();
    context.translate(canvas.width/2, canvas.height/2);
    context.scale(0.9, 0.9);
    context.translate(-canvas.width/2, -canvas.height/2);
    context.beginPath();
    context.lineWidth = 5 + Math.random() * 10;
    context.moveTo(lastX, lastY);
    lastX = canvas.width * Math.random();
    lastY = canvas.height * Math.random();
    context.bezierCurveTo(canvas.width * Math.random(),
        canvas.height * Math.random(),
        canvas.width * Math.random(),
        canvas.height * Math.random(),
        lastX, lastY);

    hue += Math.random()*0.1
    if(hue > 1.0) {
        hue -= 1
    }
    context.strokeStyle = Qt.hsla(hue, 0.5, 0.5, 1.0);
    // context.shadowColor = 'white';
    // context.shadowBlur = 10;
    context.stroke();
    context.restore();
}

The biggest change was the use of the QML Qt.rgba() and Qt.hsla() functions, which required to adopt the values to the used 0.0 … 1.0 range in QML.

最大的变化是使用Qt.rgba()Qt.hsla()函数,该函数需要调整到QML中使用的0.0…1.0范围的值。

Same applies to the blank function.

这同样的调整,适用于空白函数。

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

The final result will look similar to this.

最终结果与此类似。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值