QML Canvas.requestAnimationFrame 爆炸

Posted

技术标签:

【中文标题】QML Canvas.requestAnimationFrame 爆炸【英文标题】:QML Canvas.requestAnimationFrame explodes 【发布时间】:2015-06-08 12:30:05 【问题描述】:

我正在尝试使用 QML Canvas.requestAnimationFrame 来绘制一些自定义动画。我希望每帧调用一次提供的回调,大约每秒 60 次。我的代码是:

Canvas 
    id: canvas

    width: 600
    height: 600

    function draw() 
    

    Component.onCompleted: 
        var i = 1;

        function drawFrame() 
            requestAnimationFrame(drawFrame)
            console.log("Frame callback: " + i++)
            draw()
        

        drawFrame()
    

    onPaint: 
        draw()
    


我看到的是回调被更频繁地调用。计数器在几秒钟内达到 70000,之后应用程序完全没有响应。

我做错了什么?

【问题讨论】:

我认为你必须避免代码中的递归。 相关:***.com/q/39353234/405017 【参考方案1】:

您的drawFrame() 函数将自己作为回调函数传递给渲染,而您陷入了循环。您要么只想按需渲染,例如在用户输入后将资源保持在最低限度,要么您有一些逻辑会改变每一帧,或者您只需要连续渲染。

如果您想要基于时间的渲染,只需使用Timer

import QtQuick 2.4

Canvas  
    id: cvs
    width: 600; height: 600
    contextType: "2d"
    property real i : 0

    onPaint: 
        console.timeEnd("t")
        if (context) 
            context.clearRect (0, 0, width, height)
            context.fillRect(i, 50, 50, 50 + i)
        
        console.time("t")
    

    Timer 
        interval: 1
        repeat: true
        running: true

        onTriggered: 
            cvs.i = (cvs.i + 0.1) % cvs.width
            cvs.requestPaint()
        
    

编辑:

刚刚更新了代码:

onPaint 调用会同步到显示帧速率,即使计时器间隔设置为 1 毫秒,从运行上述示例时的日志中可以看出。 事实上,分配给onTriggered 信号的整个块每毫秒执行一次,但requestPaint() 确保同步渲染调用以获得最佳性能,就像requestAnimationFrame()html 画布所做的那样。

显然,QML.Canvas 中的requestAnimationFrame() 无法按预期工作,并且没有太多文档...

希望这会有所帮助!

【讨论】:

感谢您的回复-但您的示例似乎实际上并未使用 requestAnimationFrame?至少在 HTML Canvas 中,使用 requestAnimationFrame 的优势在于您的代码以最佳速率被调用。你是说 Qt 中的 requestAnimationFrame 坏了,应该避免吗? 是的,你是对的。在 HTML/JS 中这有效,在 QML 中则无效。我刚刚更新了答案【参考方案2】:

只是关于这个主题的一个小更新。我在处理项目时遇到了与 Qt qml Canvas 和 requestAnimationFrame 相同的问题。我找到的解决方案是将渲染策略切换到Threaded 并使用onPainted 信号。我更新的 qCring 代码示例如下所示:

import QtQuick 2.4

Canvas  
    id: cvs
    width: 600; height: 600

    //renderStrategy: Canvas.Cooperative // Will work as well but animation chops on my computer from time to time
    renderStrategy: Canvas.Threaded

    contextType: "2d"
    property real i : 0

    function animate() 
        cvs.i = (cvs.i + 0.1) % cvs.width;
    

    onPaint: 
        console.timeEnd( "t" )
        if ( context ) 
            context.clearRect( 0, 0, width, height )
            context.fillRect( i, 50, 50, 50 + i )
        
        console.time("t")

        cvs.requestAnimationFrame(animate);
    

    onPainted: 
        cvs.requestPaint();
    

【讨论】:

【参考方案3】:

在 Qt 5.9 之前有一个 bug with requestAnimationFrame()。此错误已修复。

此代码按预期工作并保持画布不断重绘。

Canvas 
    width:100; height:100;
    property var ctx
    onAvailableChanged: if (available) ctx = getContext('2d');
    onPaint: 
        if (!ctx) return;
        ctx.clearRect(0, 0, width, height);
        // draw here
        requestAnimationFrame(paint);
    

【讨论】:

以上是关于QML Canvas.requestAnimationFrame 爆炸的主要内容,如果未能解决你的问题,请参考以下文章

Qt/QML - 在 C++ 中注册 QML 类型会使 QML 代码不起作用

QML , 如何从另一个 qml 访问元素

2 个不同 QML 之间的 QML 信号和时隙

Qml 应用程序编译 *Form.ui.qml 文件但忽略关联的 .qml 文件

QML 导入模块

C++ 和 QML 中的 QT QML 项