Canvas drawImage - видимые края плиток в firefox/opera/ie (не хром)

Я рисую игровую карту на холсте. Земля состоит из плиток - простых изображений размером 64x64 png.

Когда я рисую его в Chrome, он выглядит хорошо (слева), но когда я рисую его в Firefox/Opera/IE (справа), я получаю видимые края:

Проблема исчезает, когда я использую округленные числа:

ctx.drawImage(img, parseInt(x), parseInt(y), w, h);

Но это не помогает, когда я использую масштабирование:

ctx.scale(scale); // anything from 0.1 to 2.0

Я тоже попробовал это, но без изменений:

  1. ctx.drawImage(img, 5, 5, 50, 50, x, y, w, h); // so not an issue of clamping
  2. ctx.imageSmoothingEnabled = false;
  3. image-rendering: -moz-crisp-edges; (css)

Есть ли способ заставить его работать в ff/op/ie?


Изменить: Частичное решение найдено

Добавление 1 пикселя к ширине / высоте и компенсация его масштабом (ширина +1/ масштаб), кажется, помогает:

ctx.drawImage(props.img, 0, 0, width + 1/scale, height + 1/scale);

Это делает некоторые артефакты, но я думаю, что это приемлемо. На этом изображении вы видите зеленые плитки без краев и синие окна, которые не компенсируются, но с видимыми краями:

фиксированный

4 ответа

Решение

Самое простое решение (и я бы сказал, что наиболее эффективное) - это использовать плитки, которые перекрываются на 1 пиксель (имеют размер 1x1 или 2x2) при рисовании фоновых плиток в вашей игре.

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

Например:

var img = new Image();
img.onload = function () {
    for (var x = 0.3; x < 200; x += 15) {
        for (var y = 0.3; y < 200; y += 15) {
            ctx.drawImage(img, 0, 0, 15, 15, x, y, 15, 15);
            // If we merely use 16x16 tiles instead,
            // this will never happen:
            //ctx.drawImage(img, 0, 0, 16, 16, x, y, 16, 16);
        }
    }
}
img.src = "http://upload.wikimedia.org/wikipedia/en/thumb/0/06/Neptune.jpg/100px-Neptune.jpg";

До: http://jsfiddle.net/d9MSV

И после: http://jsfiddle.net/d9MSV/1/


Обратите внимание, как отметил автор, дополнительный масштаб должен учитывать масштабирование, поэтому более правильным решением является его модификация: http://jsfiddle.net/d9MSV/3/

В каждом розыгрыше тайла используйте Math.floor, когда есть деление, например:

    ctx.drawImage(image,Math.floor(xpos/3),ypos+1)

Также, если у вас есть цикл для рисования, который вызывает сам себя, всегда используйте requestAnimationFrame. Я не знаю почему, но так как я перешел с тайм-аута таймера на requestAnimationFrame, у меня больше нет артефактов.

причина

Это вызвано сглаживанием.

Canvas все еще находится в стадии разработки, и браузер имеет различные реализации для обработки сглаживания.

Возможные решения

1

Вы можете попробовать отключить сглаживание для изображений в Firefox следующим образом:

context.mozImageSmoothingEnabled = false;

В Chrome:

context.webkitImageSmoothingEnabled = false;

и добавьте класс к элементу следующим образом (должен работать с Opera):

canvas {
    image-rendering: optimizeSpeed;             // Older versions of FF
    image-rendering: -moz-crisp-edges;          // FF 6.0+
    image-rendering: -webkit-optimize-contrast; // Webkit
    image-rendering: -o-crisp-edges;            // OS X & Windows Opera (12.02+)
    image-rendering: optimize-contrast;         // Possible future browsers.
    -ms-interpolation-mode: nearest-neighbor;   // IE
}

Вот тест браузера, который я сделал, чтобы увидеть эффект отключения сглаживания:

АНТИАЛИЙСКИЙ БРАУЗЕР ТЕСТ

2

Перевести весь холст на 0,5 балла.

ctx.translate(0.5, 0.5);

Это не всегда работает и может вступать в конфликт с другими переводами. Однако вы можете добавлять фиксированное смещение каждый раз:

ctx.translate(scrollX + 0.5, scrollY + 0.5);

3

Другой вариант - пойти на компромисс, чтобы вы либо добавили плитки одним дополнительным пикселем, который я не рекомендую, поскольку вам придется поддерживать эту дополнительную работу.

4

Этот метод рисует плитки немного масштабированными, чтобы они перекрывались:

ctx.drawImage(tile, x, y, 65, 65); //source tile = 64x64

Этого может быть достаточно, чтобы покрыть глюк. В сочетании с отключением сглаживания (или использованием ближайшего соседа) это не повлияет на большую часть графического элемента мозаичного изображения, но может немного снизить производительность из-за масштабирования.

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

5

Просто нарисуйте все со смещением на -1 позицию (т. Е. Grid = 63x63). Конечно, это все испортит в отношении проверок, так что...

Я рисую все свои плитки в буфер идеального размера, а затем рисую этот буфер на холсте дисплея с помощью drawImage, которое заботится о масштабировании. Если у вас есть плитки 16x16, сделайте ваш буфер кратным 16, например, 256x128 или 64x96 или что-то вроде этого. Это устраняет пробелы между плитками, которые возникают из-за рисования с масштабированными размерами. Единственным недостатком является то, что вы должны нарисовать полный фон дважды: один раз, чтобы нарисовать фон в идеальном для пикселя пространстве, и один раз, чтобы нарисовать масштабированное изображение на конечном холсте дисплея. Не забудьте сохранить соотношение сторон между буфером и холстом дисплея, чтобы избежать искажения конечного изображения.

Другие вопросы по тегам