231 Stimmen

Wie kann ich am besten ein einzelnes Pixel in einem HTML5-Canvas setzen?

Der HTML5-Canvas hat keine Methode zum expliziten Setzen eines einzelnen Pixels.

Es könnte möglich sein, ein Pixel mit einer sehr kurzen Zeile zu setzen, aber dann könnten Antialisierung und Zeilenbegrenzung stören.

Eine andere Möglichkeit wäre die Erstellung eines kleinen ImageData Objekt und verwenden:

context.putImageData(data, x, y)

um sie einzurichten.

Kann jemand einen effizienten und zuverlässigen Weg beschreiben, dies zu tun?

-1voto

Boing Punkte 236

Um Phrogz sehr ausführliche Antwort zu vervollständigen, gibt es einen entscheidenden Unterschied zwischen fillRect() y putImageData() .
Die erste nutzt den Kontext, um die über von Hinzufügen von ein Rechteck (NICHT ein Pixel), unter Verwendung der fillStyle Alphawert UND den Kontext globalAlpha und die Transformationsmatrix , Leitungskappen usw..
Die zweite ersetzt eine ganze Pixelmenge (vielleicht eine, aber warum?)
Das Ergebnis ist unterschiedlich, wie Sie auf jsperf .

Niemand möchte ein Pixel nach dem anderen setzen (d. h. auf dem Bildschirm zeichnen). Deshalb gibt es keine spezielle API, um das zu tun (und das zu Recht).
Wenn das Ziel darin besteht, ein Bild zu erzeugen (z. B. mit einer Raytracing-Software), ist die Leistung immer ein Array verwenden möchten, das durch getImageData() was ein optimiertes Uint8Array ist. Dann rufen Sie putImageData() EINMAL oder ein paar Mal pro Sekunde mit setTimeout/seTInterval .

-1voto

Martin Ždila Punkte 2734

Wenn Sie sich über die Geschwindigkeit Gedanken machen, können Sie auch WebGL in Betracht ziehen.

-1voto

Kamil Kiełczewski Punkte 69048

Schnell und praktisch

Die folgende Klasse implementiert die schnelle Methode, die in dieser Artikel und enthält alles, was Sie brauchen: readPixel , putPixel erhalten width/height . Klasse aktualisieren Leinwand nach dem Aufruf refresh() Methode. Beispiel lösen einfachen Fall von 2d Wellengleichung

class Screen{
  constructor(canvasSelector) {
    this.canvas = document.querySelector(canvasSelector);
    this.width  = this.canvas.width;
    this.height = this.canvas.height;
    this.ctx = this.canvas.getContext('2d');
    this.imageData = this.ctx.getImageData(0, 0, this.width, this.height);
    this.buf = new ArrayBuffer(this.imageData.data.length);
    this.buf8 = new Uint8ClampedArray(this.buf);
    this.data = new Uint32Array(this.buf);  
  }

  // r,g,b,a - red, gren, blue, alpha components in range 0-255
  putPixel(x,y,r,g,b,a=255) {
    this.data[y * this.width + x] = (a<<24) | (b<<16) | (g<<8) | r;
  }

  readPixel(x,y) {
    let p= this.data[y * this.width + x]
    return [p&0xff, p>>8&0xff, p>>16&0xff, p>>>24];
  }

  refresh() {
    this.imageData.data.set(this.buf8);
    this.ctx.putImageData(this.imageData, 0, 0);
  }
}

// --------
// TEST
// --------

let s= new Screen('#canvas');                // initialise

function draw() {

  for (var y = 1; y < s.height-1; ++y) {
    for (var x = 1; x < s.width-1; ++x) {      
      let a = [[1,0],[-1,0],[0,1],[0,-1]].reduce((a,[xp,yp])=> 
        a+= s.readPixel(x+xp,y+yp)[0]        // read pixel
      ,0);
      let v= a/1.99446-tmp[x][y];
      tmp[x][y]=v<0 ? 0:v;
    }
  }

  for (var y = 1; y < s.height-1; ++y) {
    for (var x = 1; x < s.width-1; ++x) {  
      let v=tmp[x][y];
      tmp[x][y]= s.readPixel(x,y)[0];        // read pixel
      s.putPixel(x,y, v,0,0);                // put pixel
    }
  }

  s.refresh(); 
  window.requestAnimationFrame(draw)
}

// temporary 2d buffer ()for solving wave equation)
let tmp = [...Array(s.width)].map(x => Array(s.height).fill(0));

function move(e) { s.putPixel(e.x-10, e.y-10, 255,255,255);}

draw();

<canvas id="canvas" height="150" width="512" onmousemove="move(event)"></canvas>
<div>Move mouse on black square</div>

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X