2015-06-08 12 views
6

Sto tentando di utilizzare QML Canvas.requestAnimationFrame per disegnare alcune animazioni personalizzate. Mi aspettavo che il callback fornito fosse chiamato una volta per ogni frame, circa 60 volte al secondo. Il codice che ho è:QML Canvas.requestAnimationFrame esplode

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

} 

Quello che vedo è che il callback viene chiamata modo più spesso. Il contatore raggiunge 70000 in pochi secondi, dopodiché l'applicazione diventa completamente non rispondente.

Cosa sto sbagliando?

+0

Penso che sia necessario evitare la ricorsione nel codice. – folibis

+0

Correlati: http://stackoverflow.com/q/39353234/405017 – Phrogz

risposta

2

La tua funzione drawFrame() passa a se stessa come funzione di callback per il rendering e sei catturato in un ciclo. O vuoi renderizzare su richiesta solo come ad esempio dopo l'input dell'utente per mantenere le risorse al minimo, o hai una logica che cambia ogni frame o hai solo bisogno di rendering continuo.

Se il rendering basato sul tempo è ciò che si vuole, basta usare un 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() 
     } 
    } 
} 

Edit:

appena aggiornato il codice:

onPaint chiamate vengono sincronizzate con il frame rate di visualizzazione anche se l'intervallo del timer è impostato su 1 ms come si può vedere dal registro quando si esegue l'esempio sopra. Infatti, l'intero blocco assegnato al segnale onTriggered viene eseguito ogni millisecondo, ma lo standard requestPaint() assicura la sincronizzazione delle chiamate di rendering per le migliori prestazioni, proprio come fa lo requestAnimationFrame() per il canvas HTML.

A quanto pare, requestAnimationFrame() all'interno del QML.Canvas non funziona come previsto e non c'è molto la documentazione ...

Spero che questo aiuti!

+1

Grazie per la risposta - ma sembra che il tuo esempio non usi effettivamente requestAnimationFrame? Almeno in HTML Canvas, il vantaggio dell'utilizzo di requestAnimationFrame è che il tuo codice è invocato esattamente alla velocità ottimale. Stai dicendo requestAnimationFrame in Qt è rotto e dovrebbe essere evitato? –

+0

Sì hai ragione. In HTML/JS funziona, in QML no. Ho appena aggiornato la risposta per questo – qCring

1

Solo un piccolo aggiornamento su questo argomento. Ho riscontrato lo stesso problema con Qt qml Canvas e requestAnimationFrame mentre stavo lavorando al mio progetto. La soluzione che ho trovato è di passare la strategia di rendering a Threaded e utilizzare il segnale onPainted. L'esempio di codice di qCring con i miei aggiornamenti assomiglia a questo:

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

c'era un bug with requestAnimationFrame() prima di Qt 5.9. Questo errore è stato corretto.

Questo codice funziona come previsto e desiderato per mantenere la tela continuamente ridisegnata.

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