HTML5 Canvas и сглаживание - PullRequest
       33

HTML5 Canvas и сглаживание

52 голосов
/ 24 ноября 2010

Как включить сглаживание на canvas .

Следующий код не рисует плавную линию:

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

   context.strokeStyle = "#df4b26";
   context.lineWidth = 3;
   context.stroke();
}

Ответы [ 7 ]

37 голосов
/ 23 августа 2013

Вы можете перевести холст на полпиксельное расстояние.

ctx.translate(0.5, 0.5);

Первоначально точка позиционирования холста между физическими пикселями.

25 голосов
/ 24 ноября 2010

Сглаживание невозможно включить или отключить, и оно управляется браузером.

Можно ли отключить сглаживание для элемента HTML ?

19 голосов
/ 05 января 2013

Мне не нужно было включать сглаживание, потому что оно включено по умолчанию, но мне нужно было его отключить. И если он может быть выключен, он также может быть включен.

ctx.imageSmoothingEnabled = true;

Обычно я отключаю его, когда работаю над своим холстом, поэтому при увеличении изображения не выглядят размытыми.

11 голосов
/ 19 марта 2018

Сейчас 2018 год, и у нас наконец-то есть дешевые способы что-то сделать ...

Действительно, поскольку 2-ый контекстный API теперь имеет свойство filter, и этоэто свойство фильтра может принимать SVGFilters , мы можем создать SVGFilter, который будет хранить только полностью непрозрачные пиксели в наших чертежах и, таким образом, исключит сглаживание по умолчанию.

, поэтому он не будет деактивированСглаживание само по себе, но обеспечивает дешевый способ как с точки зрения реализации, так и с точки зрения производительности удалить все полупрозрачные пиксели во время рисования.

Я не специалист по SVGFilters, так что может быть лучший способделая это, но для примера я буду использовать узел <feComponentTransfer>, чтобы захватывать только полностью непрозрачные пиксели.

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

А для тех, кому не нравится добавлять элемент <svg> в их DOM, вы также можете сохранить его как внешний файл SVG и установитьсвойство filter до path/to/svg_file.svg#remove-alpha.

8 голосов
/ 13 мая 2013

Вот обходной путь, который требует рисования линий пиксель за пикселем, но предотвратит сглаживание.

// 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
    }
}

По сути, вы находите длину линии и пошагово пересекаете эту линию, округляякаждая позиция и заполнение в пикселе.

Назовите его с помощью

var context = cv.getContext("2d");
drawLineNoAliasing(context, 20,30,20,50); // line from (20,30) to (20,50)
2 голосов
/ 12 сентября 2013

Если вам нужен контроль уровня пикселей для холста, вы можете использовать createImageData и putImageData .

HTML:

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

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

Рабочий образец здесь

0 голосов
/ 18 ноября 2018

, поэтому я предполагаю, что это уже не так, но на самом деле один из способов сделать это - использовать document.body.style.zoom=2.0;, но если вы сделаете это, то все ваши измерения холста придется разделить на увеличение. Кроме того, установите увеличение выше для большего наложения. Это полезно, потому что это регулируется. Также, если вы используете этот метод, я предлагаю вам сделать так, чтобы функции выполнялись так же, как ctx.fillRect() и т. Д., Но с учетом увеличения. Э.Г.

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

Надеюсь, это поможет!

Кроме того, sidenote: это может быть использовано для повышения резкости краев окружности, чтобы они не выглядели размытыми. Просто используйте зум, например 0,5!

...