QML Canvas.requestAnimationFrame взрывается

Я пытаюсь использовать 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 в течение нескольких секунд, после чего приложение перестает отвечать на запросы.

Что я делаю неправильно?

3 ответа

Решение

Была ошибка сrequestAnimationFrame() до Qt 5.9. Эта ошибка была исправлена.

Этот код работает так, как ожидалось и желательно, чтобы холст постоянно перерисовывался.

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);
    }
}

Ваш 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.

По-видимому, requestAnimationFrame() внутри QML.Canvas не работает должным образом, и документации не так много...

Надеюсь это поможет!

Просто небольшое обновление по этой теме. Я столкнулся с той же проблемой с 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();
    }
}
Другие вопросы по тегам