HTML5 Canvas e Anti-aliasing

Come accendere la anti-aliasing su un tela.

Il codice riportato di seguito non disegna una linea liscia:

var context = mainCanv.getContext("2d");
if (context) {
   context.moveTo(0,0);
   context.lineTo(100,75);

   context.strokeStyle = "#df4b26";
   context.lineWidth = 3;
   context.stroke();
}
  • Secondo questo stackoverflow domanda sembrerebbe che la tela è anti-aliasing per impostazione predefinita. Quale sistema operativo/browser/versione stai usando?
  • Quando si dice che non è liscia, cosa vuoi dire? Stai vedendo i bordi frastagliati / pixel, o è solo sfocata?
  • Succede su Firefox Mobile su Android, c’è un brutto bordo grigio attorno alla linea rossa.
  • phoboslab.org/log/2012/09/drawing-pixels-is-hard
InformationsquelleAutor KRouane | 2010-11-23

 

7 Replies
  1. 38

    Si possono tradurre in tela da mezza distanza in pixel.

    ctx.translate(0.5, 0.5);

    Inizialmente tela punto di posizionamento tra i pixel fisici.

    • Si noti che se si cancella la tela con il metodo di impostazione della larghezza di sé: in tela.larghezza = canvas.la larghezza di questo resetta il transzlation matrice, e avrete bisogno di tradurre da mezzo pixel di nuovo. È possibile evitare questo utilizzando clearRect (), invece.
    • Questa è l’unica risposta che effettivamente risolve il problema OPs
    • Chiaramente aiuta con linee dritte, ma le linee diagonali ancora guardare piuttosto terribile.
    • Si muove appena, dove l’aliasing si verifica.
    • Nota che in virgola mobile i valori dei pixel di ridurre notevolmente le prestazioni, quindi se si sta disegnando spesso questo non può essere una buona idea
  2. 22

    Non ho bisogno di attivare l’anti-alias, perché è il default, ma ho bisogno di spegnerlo. E se può essere spento può anche essere acceso.

    ctx.imageSmoothingEnabled = true;

    Io di solito spento quando sto lavorando sulla mia tela rpg così quando faccio lo zoom le immagini non sembrano sfocate.

  3. 12

    Ora 2018, e infine ci sono modi economici per fare qualcosa intorno…

    Infatti, poiché il contesto 2d ora di API ha un filtro proprietà, e che questo filtro di proprietà può accettare SVGFilters, siamo in grado di costruire un SVGFilter che si terrà il solo completamente i pixel opachi dai disegni, ed eliminare così il default di anti-aliasing.

    In modo da non disattivare l’antialias di per sé, ma fornisce un modo economico, sia in termini di attuazione e di prestazioni per rimuovere tutti i semi-trasparente pixel, mentre il disegno.

    Io non sono davvero un esperto di SVGFilters, quindi ci potrebbe essere un modo migliore di farlo, ma per esempio, io uso un <feComponentTransfer> nodo di afferrare completamente i pixel opachi.

    JS:

    var ctx = canvas.getContext('2d');
    ctx.fillStyle = '#ABEDBE';
    ctx.fillRect(0,0,canvas.width,canvas.height);
    ctx.fillStyle = 'black';
    ctx.font = '14px sans-serif';
    ctx.textAlign = 'center';
    
    //first without filter
    ctx.fillText('no filter', 60, 20);
    drawArc();
    drawTriangle();
    //then with filter
    ctx.setTransform(1, 0, 0, 1, 120, 0);
    ctx.filter = 'url(#remove-alpha)';
    //and do the same ops
    ctx.fillText('no alpha', 60, 20);
    drawArc();
    drawTriangle();
    
    //to remove the filter
    ctx.filter = 'none';
    
    
    function drawArc() {
      ctx.beginPath();
      ctx.arc(60, 80, 50, 0, Math.PI * 2);
      ctx.stroke();
    }
    
    function drawTriangle() {
      ctx.beginPath();
      ctx.moveTo(60, 150);
      ctx.lineTo(110, 230);
      ctx.lineTo(10, 230);
      ctx.closePath();
      ctx.stroke();
    }
    //unrelated
    //simply to show a zoomed-in version
    var zCtx = zoomed.getContext('2d');
    zCtx.imageSmoothingEnabled = false;
    canvas.onmousemove = function drawToZoommed(e) {
      var x = e.pageX - this.offsetLeft,
        y = e.pageY - this.offsetTop,
        w = this.width,
        h = this.height;
        
      zCtx.clearRect(0,0,w,h);
      zCtx.drawImage(this, x-w/6,y-h/6,w, h, 0,0,w*3, h*3);
    }

    HTML:

    <svg width="0" height="0" style="position:absolute;z-index:-1;">
      <defs>
        <filter id="remove-alpha" x="0" y="0" width="100%" height="100%">
          <feComponentTransfer>
            <feFuncA type="discrete" tableValues="0 1"></feFuncA>
          </feComponentTransfer>
          </filter>
      </defs>
    </svg>
    
    <canvas id="canvas" width="250" height="250" ></canvas>
    <canvas id="zoomed" width="250" height="250" ></canvas>

    E per quelli che non amano aggiungere un <svg> elemento nella loro DOM, puoi anche salvarlo come esterno di file in formato svg e impostare il filter proprietà path/to/svg_file.svg#remove-alpha.

  4. 8

    Ecco una soluzione che richiede di disegnare linee di pixel per pixel, ma impedire l’antialiasing.

    //some helper functions
    //finds the distance between points
    function DBP(x1,y1,x2,y2) {
        return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
    }
    //finds the angle of (x,y) on a plane from the origin
    function getAngle(x,y) { return Math.atan(y/(x==0?0.01:x))+(x<0?Math.PI:0); }
    //the function
    function drawLineNoAliasing(ctx, sx, sy, tx, ty) {
        var dist = DBP(sx,sy,tx,ty); //length of line
        var ang = getAngle(tx-sx,ty-sy); //angle of line
        for(var i=0;i<dist;i++) {
            //for each point along the line
            ctx.fillRect(Math.round(sx + Math.cos(ang)*i), //round for perfect pixels
                         Math.round(sy + Math.sin(ang)*i), //thus no aliasing
                         1,1); //fill in one pixel, 1x1
        }
    }

    Fondamentalmente, trovare la lunghezza della linea, e passo passo attraversare quella linea, arrotondando ogni posizione, e la compilazione di un pixel.

    Chiamare con

    var context = cv.getContext("2d");
    drawLineNoAliasing(context, 20,30,20,50); //line from (20,30) to (20,50)
    • Non credo getAngle è necessario per il disegno di linee. Tutto quello che dovete fare è dividere la differenza tra i due punti ‘dist’ e moltiplicare che da ‘i’. Sono io che sbaglio?
    • sì, hai corretto. Ho appena usato angoli per chiarezza..? Credo
    • santo toledo appena mi ha salvato un sacco di tempo! circa un anno fa ho iniziato a lavorare su un pixel di verniciatura web app, e ha frustrato con l’impossibilità di disattivare l’anti-aliasing. penso che l’unica soluzione id trovato il tempo è andato così lento che era disperato, ma la tua è eseguito in modo incredibilmente veloce e fa esattamente quello che ho bisogno di! grazie!!!
    • Disegno di linee di pixel per pixel non dovrebbe aver bisogno di chiamate verso sin e cos. Utilizzare l’algoritmo di Bresenham, è un pezzo di grafica storia: en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
  5. 2

    Se avete bisogno di pixel del livello di controllo sulla tela si può fare utilizzando createImageData e putImageData.

    HTML:

    <canvas id="qrCode" width="200", height="200">
      QR Code
    </canvas>

    E JavaScript:

    function setPixel(imageData, pixelData) {
      var index = (pixelData.x + pixelData.y * imageData.width) * 4;
        imageData.data[index+0] = pixelData.r;
        imageData.data[index+1] = pixelData.g;
        imageData.data[index+2] = pixelData.b;
        imageData.data[index+3] = pixelData.a;
    }
    
    element = document.getElementById("qrCode");
    c = element.getContext("2d");
    
    pixcelSize = 4;
    width = element.width;
    height = element.height;
    
    
    imageData = c.createImageData(width, height);
    
    for (i = 0; i < 1000; i++) {
      x = Math.random() * width / pixcelSize | 0; //|0 to Int32
      y = Math.random() * height / pixcelSize| 0;
    
      for(j=0;j < pixcelSize; j++){
        for(k=0;k < pixcelSize; k++){
         setPixel( imageData, {
             x: x * pixcelSize + j,  
             y: y * pixcelSize + k,
             r: 0 | 0,
             g: 0 | 0,
             b: 0 * 256 | 0,
             a: 255 //255 opaque
           });
          }
      }
    }
    
    c.putImageData(imageData, 0, 0);

    Campione di lavoro qui

  6. 0

    quindi io parto dal presupposto che questo è un pò fuori uso ora, ma un modo per farlo è in realtà utilizzando document.body.style.zoom=2.0; ma se si fa questo, allora tutti i tuoi tela misure dovrà essere diviso per lo zoom. Inoltre, impostare lo zoom maggiore aliasing. Questo è utile perché è regolabile. Anche se si utilizza questo metodo, vi suggerisco di funzioni per fare la stessa ctx.fillRect() etc. ma con lo zoom presi in considerazione. E. g.

    function fillRect(x, y, width, height) {
        var zoom = document.body.style.zoom;
        ctx.fillRect(x/zoom, y/zoom, width/zoom, height/zoom);
    }

    Spero che questo aiuta!

    Inoltre, una nota a margine: questo può essere utilizzato per affinare il cerchio bordi in modo che essi non sembrano così sfocato. Basta usare uno zoom, ad esempio 0,5!

Lascia un commento