Подтвердить что ты не робот

Рисование линии толщиной 1px в холсте создает линию толщиной 2px

В этом jsfiddle есть строка с линией шириной 1.

http://jsfiddle.net/mailrox/9bMPD/350/

например:

ctx.lineWidth = 1;

Тем не менее, линия имеет толщину 2px, когда она нарисована на холсте, как вы создаете линию толщиной 1px.

Я мог бы нарисовать прямоугольник (с высотой 1px), однако я хочу, чтобы линия также работала на диагоналях. Итак, как вы можете получить эту строку в 1px выше?

Спасибо!

4b9b3361

Ответ 1

Холст вычисляет из половины пикселя

ctx.moveTo(50,150.5);
ctx.lineTo(150,150.5);

Итак, начиная с половины, он зафиксирует его

Фиксированная версия: http://jsfiddle.net/9bMPD/357/

Этот ответ объясняет, почему он работает именно так.

Ответ 2

Вы также можете перевести половину пикселя в направлениях X и Y, а затем использовать целые значения для своих координат (в некоторых случаях вам может понадобиться округлить их):

context.translate(0.5, 0.5)

context.moveTo(5,5);
context.lineTo(55,5);

Имейте в виду, что если вы измените размер своего холста, то перевод будет reset - так что вы снова будете переводить.

Этот ответ объясняет, почему он работает именно так.

Ответ 3

Или как этот отвечает, чтобы получить ширину 1, вам нужно начинать с половины пикселя.

ctx.moveTo(50.5,150.5);
ctx.lineTo(150.5,150.5);

http://jsfiddle.net/9bMPD/355/

Ответ 4

Вы видели, как впервые попал в Google? (найдите canvas line width 1px). Хотя я должен признать, что это не совсем "чистый" или "худой". Ferry Kobus решение намного лучше. Опять же: это отстой, вам нужно использовать "половинные пиксели" в первую очередь...

Ответ 5

Холст может рисовать чистые прямые с помощью fillRect(). Прямоугольник с высотой 1px или шириной 1px выполняет задание. Не требуется полупиксельное значение:

var ctx = document.getElementById("myCanvas").getContext("2d");

ctx.drawVerticalLine = function(left, top, width, color){
    this.fillStyle=color;
    this.fillRect(left, top, 1, width);
};

ctx.drawHorizontalLine = function(left, top, width, color){
    this.fillStyle=color;
    this.fillRect(left, top, width, 1);
}

ctx.drawVerticalLine(150, 0, 300, "green");
ctx.drawHorizontalLine(0, 150, 300, "red");

https://jsfiddle.net/ynur1rab/

Ответ 6

Если ни один из этих ответов не помог вам, проверьте масштаб вашего браузера. Мой был каким-то образом на 125%, поэтому каждая четвертая линия 1px была нарисована шириной 2px.

Я часами пытался выяснить, почему каждая скрипка в интернете работает, а моя - нет (масштаб был установлен только для моей вкладки разработчика)

Ответ 7

Для меня только комбинация различных "идеальных по пикселям" техник помогла архивировать результаты:

  1. Получить и масштабировать холст с соотношением пикселей:

    pixelRatio = window.devicePixelRatio/ctx.backingStorePixelRatio

  2. Масштабируйте холст при изменении размера (избегайте растягивания холста по умолчанию).

  3. умножьте lineWidth на pixelRatio, чтобы найти правильную "реальную" толщину линии пикселя:

    context.lineWidth = толщина * pixelRatio;

  4. Проверьте, является ли толщина линии нечетной или четной. добавьте половину pixelRatio к положению линии для значений нечетной толщины.

    x = x + pixelRatio/2;

Нечетная линия будет размещена в середине пикселя. Линия выше используется, чтобы немного ее переместить.

function getPixelRatio(context) {
  dpr = window.devicePixelRatio || 1,
    bsr = context.webkitBackingStorePixelRatio ||
    context.mozBackingStorePixelRatio ||
    context.msBackingStorePixelRatio ||
    context.oBackingStorePixelRatio ||
    context.backingStorePixelRatio || 1;

  return dpr / bsr;
}


var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
var pixelRatio = getPixelRatio(context);
var initialWidth = canvas.clientWidth * pixelRatio;
var initialHeight = canvas.clientHeight * pixelRatio;


window.addEventListener('resize', function(args) {
  rescale();
  redraw();
}, false);

function rescale() {
  var width = initialWidth * pixelRatio;
  var height = initialHeight * pixelRatio;
  if (width != context.canvas.width)
    context.canvas.width = width;
  if (height != context.canvas.height)
    context.canvas.height = height;

  context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
}

function pixelPerfectLine(x) {

  context.save();
  context.beginPath();
  thickness = 1;
  // Multiple your stroke thickness  by a pixel ratio!
  context.lineWidth = thickness * pixelRatio;

  context.strokeStyle = "Black";
  context.moveTo(getSharpPixel(thickness, x), getSharpPixel(thickness, 0));
  context.lineTo(getSharpPixel(thickness, x), getSharpPixel(thickness, 200));
  context.stroke();
  context.restore();
}

function pixelPerfectRectangle(x, y, w, h, thickness, useDash) {
  context.save();
  // Pixel perfect rectange:
  context.beginPath();

  // Multiple your stroke thickness by a pixel ratio!
  context.lineWidth = thickness * pixelRatio;
  context.strokeStyle = "Red";
  if (useDash) {
    context.setLineDash([4]);
  }
  // use sharp x,y and integer w,h!
  context.strokeRect(
    getSharpPixel(thickness, x),
    getSharpPixel(thickness, y),
    Math.floor(w),
    Math.floor(h));
  context.restore();
}

function redraw() {
  context.clearRect(0, 0, canvas.width, canvas.height);
  pixelPerfectLine(50);
  pixelPerfectLine(120);
  pixelPerfectLine(122);
  pixelPerfectLine(130);
  pixelPerfectLine(132);
  pixelPerfectRectangle();
  pixelPerfectRectangle(10, 11, 200.3, 443.2, 1, false);
  pixelPerfectRectangle(41, 42, 150.3, 443.2, 1, true);
  pixelPerfectRectangle(102, 100, 150.3, 243.2, 2, true);
}

function getSharpPixel(thickness, pos) {

  if (thickness % 2 == 0) {
    return pos;
  }
  return pos + pixelRatio / 2;

}

rescale();
redraw();
canvas {
  image-rendering: -moz-crisp-edges;
  image-rendering: -webkit-crisp-edges;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  width: 100vh;
  height: 100vh;
}
<canvas id="canvas"></canvas>

Ответ 8

Метод fillRect() может использоваться для рисования тонких горизонтальных или вертикальных линий в холсте (без применения сдвига +0,5 по координатам):

this.fillRect(left, top, 1, height);
this.fillRect(left, top, width, 1);

И вы действительно можете сделать линии еще более тонкими, просто заменив этот код на что-то вроде:

this.fillRect(left, top, 0.7, height);
this.fillRect(left, top, width, 0.7);

Линии будут тоньше (имеют ширину до 1 пикселя), но их цвет немного ослаблен.

- > рабочий пример

Следует отметить, что если мы установим ctx.lineWidth = 0.7 (для классической последовательности beginPath/moveTo/lineTo/stroke), он не работает в Chrome (0,7 и 1 интерпретируются одинаково). Таким образом, интерес для этого метода fillRect().