JavaScript HTML 5 Canvas wird bei Animation gelöscht

Falc410

Vice Admiral
Registriert
Juni 2006
Beiträge
6.723
Ich habe ein HTML Canvas Element welches mir die Waveform eines Audiosamples anzeigt (wurde vom Benutzer aufgenommen und in Javascript gespeichert).
Der Canvas wird so erzeugt:
Code:
<canvas id="wavedisplay" width="1024" height="200" style="display: inline-block;
                     width: 95%; height: 45%; box-shadow: 0px 0px 10px blue;"></canvas>
und anschliessend "bemalt", dafür habe ich eine etwas komplizierte Funktion gefunden die das perfekt übernimmt:
Code:
function drawBuffer( width, height, context, data ) {
    var step = Math.ceil( data.length / width );
    var amp = height / 2;
    context.fillStyle = "black";
    context.clearRect(0,0,width,height);
    for(var i=0; i < width; i++){
        var min = 1.0;
        var max = -1.0;
        for (var j=0; j<step; j++) {
            var datum = data[(i*step)+j];
            if (datum < min)
                min = datum;
            if (datum > max)
                max = datum;
        }
        context.fillRect(i,(1+min)*amp,1,Math.max(1,(max-min)*amp));
    }
}
Resultat sieht dann so aus:

Screen Shot 2016-01-07 at 13.52.00.png

Nun möchte ich, das ein Cursor über die Waveform fährt während das WAV File abgespielt wird. Den Cursor konnte ich erzeugen und der fährt auch von Links nach Rechts (die Geschwindigkeit muss ich noch an die Länge des WAV Files anpassen). Das Problem ist allerdings dass nun nur noch der Cursor angezeigt wird und die ursprüngliche Waveform verschwindet.

Kann ich das clearen des Canvas irgendwie verhindern? Wenn ich das context.clearRect rausnehme dann wird die Waveform übermalt von meinem Cursor. Ich hatte erwartet das wirklich nur das Rect bewegt wird, aber das hinterlässt dann eben eine Spur. Durch das context.clearRect wird wohl alles gelöscht und der Canvas zum Zeitpunkt x komplett neu gezeichnet. Dann müsste ich aber in jedem Frame das Waveform neu malen - was relativ aufwendig ist. Gibt es da eine andere Möglichkeit?

Hier sieht man den Cursor (bewegt sich nach rechts):

Screen Shot 2016-01-07 at 13.53.51.png

und noch einmal wenn ich das context.clearRect auskommentiere:

Screen Shot 2016-01-07 at 14.00.30.png

Hier der Code für die Animation:
Code:
 function animate(myRectangle, canvas, context, startTime) {
        // update
        var time = (new Date()).getTime() - startTime;

        var linearSpeed = 100;
        // pixels / second
        var newX = linearSpeed * time / 1000;

        if(newX < canvas.width - myRectangle.width - myRectangle.borderWidth / 2) {
            myRectangle.x = newX;
        }

        // clear
        context.clearRect(0, 0, canvas.width, canvas.height);

        drawRectangle(myRectangle, context);

        // request new frame
        requestAnimFrame(function() {
            animate(myRectangle, canvas, context, startTime);
        });
    }

    var canvas = document.getElementById('wavedisplay');
    var context = canvas.getContext('2d');

    var myRectangle = {
        x: 0,
        y: 0,
        width: 10,
        height: canvas.height,
        borderWidth: 1
    };

    window.requestAnimFrame = (function(callback) {
        return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
                function(callback) {
                    window.setTimeout(callback, 1000 / 60);
                };
    })();

    function drawRectangle(myRectangle, context) {
        context.beginPath();
        context.rect(myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height);
        context.fillStyle = '#8ED6FF';
        context.fill();
        context.lineWidth = myRectangle.borderWidth;
        context.strokeStyle = 'black';
        context.stroke();
    }

Ausgelöst wird er beim Click auf den Play Knopf:
Code:
        $('#playPauseButton').on('click', function() {
            $('#recording')[0].play();
            var startTime = (new Date()).getTime();
            animate(myRectangle, canvas, context, startTime);
        });
 
Zuletzt bearbeitet:
Du könntest doch zum einen an der gleichen stelle einen Canvas erzeugen & ihn für den cursor benutzen. Ansonsten könntest du einfach das gesamte Bild löschen & direkt wieder neu malen (also cursor und die Wave)... Zweiteres ist so wie ich es kennen gelernt habe wenn man mit einem Canvas arbeitet.
 
Ja so habe ich das nun auch gemacht. Muss mal die Performance testen. Ist halt echt blöd das ich alles neu zeichnen muss in jedem Frame. :(
 
Falls jemand anderes auch mal Probleme dieser Art haben sollte hier ein Update. Wie zu erwarten hat es unweigerlich zu Performance problemen geführt wenn man die Seite mehrfach benutzt hat. Ein Array mit ein paar Millionen Einträgen in jedem Frame auszulesen und zu zeichen ist halt doch etwas aufwendig. Glücklicherweise kann man sich einen Canvas als Bild abspeichern. Also 1x zeichen, dann als Bild speichern mit:

dataURL = canvas.toDataURL(); // Save drawn canvas as png

Und nun irgendwo das Bild erstellen:
var background = new Image();
background.src = dataURL;

Und in der Animation dann nach dem clearen erst einmal das Bild zeichen:
context.drawImage(background, 0,0);
und dann den Rest darüber. Performed nun erheblich besser. Noch besser wäre es wohl das Bild einfach so darzustellen und den Canvas darüber zu legen - da scheitere ich gerade noch an meinen CSS Künsten. Denn bei mir wird das logischerweise übereinander dargestellt (bzw. untereinander). Ich probiere mal aus ob ich beim Canvas ein style: background= angeben kann.
 

Ähnliche Themen

Zurück
Oben