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