在QML中使用Canvas

【写在前面】

        在qml中,要进行一般的绘图,如果不想使用 C++ API,那么就只能使用 Canvas 了。

        qml Canvas 提供类似 HTML5 Canvas 的 API,需要修改 HTML5 Canvas 应用程序以在 Canvas 项中运行。

        使用 qml 属性绑定或 Canvas 项方法替换所有 DOM API 调用。

        使用 MouseArea 项替换所有 HTML 事件处理程序。

        使用 Timer 项或使用 requestAnimationFrame() 更改 setInterval / setTimeout 函数调用。

        将绘制代码放入 onPaint 处理程序并通过调用 markDirty() 或 requestPaint() 方法触发绘制。

        要绘制图像,请通过调用 Canvas 的 loadImage() 方法加载它们,然后请求在 onImageLoaded 处理程序中绘制它们。

        从Qt 5.4开始,Canvas 是一个纹理提供者,可以直接在 ShaderEffects 和其他使用纹理提供者的类中使用。

        注意:一般情况下,Canvas.Image 渲染目标应避免使用大型画布,频繁更新和动画。这是因为使用加速图形 API,每次更新都会导致纹理上传。

        此外,如果可能的话,更应该使用 QQuickPaintedItem 并通过 QPainter 在C++中实现绘图,而不是更昂贵且可能性能更差的 JavaScript 和 Context2D 方法。


【正文开始】

        首先,先上效果图:

        其中,扫描部分的动画就是用 Canvas 做的,当然实际上要快很多。

        接下来上代码:

import QtQuick 2.12

Item
{
    id: root
    clip: true

    Canvas
    {
        id: canvas
        anchors.fill: parent
        antialiasing: true
        property bool drawable: false;
        property bool first: true;
        property real r: Math.sqrt(Math.pow(width / 2, 2) + Math.pow(height / 2, 2));
        property real r1: 0;
        property real r2: r / 2;

        function clear()
        {
            first = true;
            r1 = 0;
            r2 = r / 2;
            var ctx = getContext("2d");
            ctx.clearRect(0, 0, width, height);
        }

        function drawLine(ctx, color, width, startX, startY, endX, endY)
        {
            ctx.strokeStyle = color;
            ctx.lineWidth = width;
            ctx.beginPath();
            ctx.moveTo(startX, startY);
            ctx.lineTo(endX, endY);
            ctx.closePath();
            ctx.stroke();
        }

        Timer
        {
            id: timer
            interval: 16
            running: false
            repeat: true
            onTriggered: canvas.requestPaint();
        }

        onPaint:
        {
            var ctx = getContext("2d");
            ctx.fillStyle = "#10FFFFFF";
            ctx.fillRect(0, 0, width, height);

            for(var i = 0; i < width; i += 20)
                drawLine(ctx, "#7266fc", 0.5,i + 0.5, 0, i + 0.5, height);
            for(var j = 0; j < height; j += 20)
                drawLine(ctx, "#7266fc", 0.5, 0, j + 0.5, width, j + 0.5);

            if (drawable)
            {
                var halfW = width / 2;
                var halfH = height / 2;

                ctx.lineWidth = 2;
                ctx.strokeStyle = "#72d6fc";
                for(var k = 0; k < 5; k += 0.5)
                {
                    ctx.beginPath();
                    ctx.arc(halfW, halfH, r1 + k, 0, Math.PI * 2);
                    ctx.closePath();
                    ctx.stroke();

                    ctx.beginPath();
                    if(!first) ctx.arc(halfW, halfH, r2 + k, 0, Math.PI * 2);
                    ctx.closePath();
                    ctx.stroke();
                }
                if(r1 > r) r1 = 0;
                if(r2 > r)
                {
                    r2 = 0;
                    if(first) first = false;
                }
                r1 += 3;
                r2 += 3;
            }
        }
    }

    MyButton
    {
        text: "扫描"
        widthMargin: 12
        heightMargin: 8
        anchors.centerIn: parent
        onClicked:
        {
            canvas.clear();
            canvas.drawable = true;
            timer.restart();
            discoverCon.discover();
        }
    }
}

        onPaint 是实际绘图的地方,但开始只会调用一次,因此需要使用 Timer 进行定时请求绘制,这里 interva 设置为16ms,即60fps,并且 repeat 为 true (重复)。

        Canvas 中绘图步骤如下:

        1、使用 getContext() 获取一个2D绘图上下文。

        2、使用绘图上下文提供的API进行绘图。

        步骤很简单,不过比较有意思的是其中的水波的半透明阴影效果的实现。

        一般情况下,我们每一帧都需要清空上一帧绘制的内容,然鹅在这里,我们不清空,而是选择用一个接近透明的颜色来覆盖在上一帧绘制的内容之上,这里我用的颜色为 #10FFFFFF(ARGB),这样,上一帧的内容就会成为半透明的效果,然后接着绘制本次帧(非透明),这样在视觉上,就会形成类似于半透明阴影的效果。


【结语】

        emmm,其实本篇并没有多少值得写的东西,不过这个效果我从别的地方看到的,觉得有点意思,因此才想着写一篇来分析一下。

        真正要写的是扫描和文件传输部分,这些后面我都会写的。

        最后,项目地址:https://github.com/mengps/FileTransfer

  • 10
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦起丶

您的鼓励和支持是我创作最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值