Bir HTML <canvas> öğesinde kenar yumuşatmayı kapatabilir miyim?


88

<canvas>Öğe, çizgi çizme ve benzeri şeylerle oynuyorum .

Çapraz çizgilerimin yumuşatıldığını fark ettim. Yaptığım şey için pürüzlü görünümü tercih ederim - bu özelliği kapatmanın bir yolu var mı?


Sanırım bu daha çok tarayıcıyla ilgili. Belki hangi yazılımı kullandığınızla ilgili bazı ek bilgiler yardımcı olabilir.
Tomalak

Çapraz tarayıcı yöntemini tercih ederim, ancak herhangi bir tarayıcıda çalışan bir yöntem benim için hala ilginç olacaktır
Blorgbeard

Bu konuda henüz bir değişiklik olup olmadığını görmek istedim.
vternal3


bununla ilgili herhangi bir güncelleme var mı?
Roland

Yanıtlar:


64

Görüntüler için şimdi var .context.imageSmoothingEnabled= false

Ancak, çizgi çizmeyi açıkça kontrol eden hiçbir şey yoktur. Ve kullanarak kendi çizgilerinizi ( zor yoldan ) çizmeniz gerekebilir .getImageDataputImageData


1
Bir javascript çizgi algoritmasının performansını merak ediyorum .. Bir noktada Bresenham'a bir şans verebilir.
Blorgbeard

Tarayıcı satıcıları son zamanlarda yeni süper hızlı JS motorlarını piyasaya sürüyor, bu yüzden sonunda bunun iyi bir kullanımı olacaktır.
Kornel

1
Bu gerçekten çalışıyor mu? Kullanarak bir çizgi çiziyorum putImageData ama yine de yakındaki piksellerin örtüşmesini yapıyor lanet
Pacerier

Daha küçük bir tuvale (önbellek) çizer ve ardından bu ayar kapalıyken Görüntüyü başka bir tuvale çizersem, amaçlandığı gibi çalışır mı?
SparK

69

1-pixelÇizgilerinizi koordinatlara çizin ctx.lineTo(10.5, 10.5). Noktası üzerinde bir piksel çizgisinin (10, 10), bu demektir ki, 1o pozisyon ulaşır piksel 9.5için 10.5olan tuval üzerinde de çizilebilir iki hat ile sonuçlanır.

0.5Çok sayıda tek piksel çizginiz varsa, çizmek istediğiniz gerçek koordinata her zaman eklemeniz gerekmeyen güzel bir numara ctx.translate(0.5, 0.5), başlangıçta tüm tuvalinize.


hmm, bu tekniği kullanarak kenar yumuşatmadan kurtulmakta sorun yaşıyorum. Belki bir şeyi anlamayı özlüyorum? Örnek vermeyi bir sakıncası var mı?
Xavi

7
Bu, kenar yumuşatmadan kurtulmaz, ancak kenar yumuşatılmış çizgilerin çok daha iyi görünmesini sağlar - aslında bir piksel istediğinizde iki piksel kalınlığındaki utanç verici yatay veya dikey çizgilerden kurtulmak gibi.
David

1
@porneL: Hayır, piksellerin köşeleri arasında çizgiler çizilir. Çizginiz 1 piksel genişliğinde olduğunda, bu her iki yönde de yarım piksel genişler
Eric

+0.5 eklemek işime ctx.translate(0.5,0.5)yaradı ama yaramadı. FF39.0
Paulo Bueno

Çok teşekkür ederim! Bir değişiklik için gerçek 1px satırlarım olduğuna inanamıyorum!
Chunky Chunk

24

Mozilla Firefox'ta yapılabilir. Bunu kodunuza ekleyin:

contextXYZ.mozImageSmoothingEnabled = false;

Opera'da şu anda bir özellik isteği, ancak umarım yakında eklenecektir.


güzel. Katkınız için +1. AA'nın devre dışı bırakılmasının çizgi çizmeyi hızlandırıp hızlandırmadığını merak ediyorum
marcusklaas

7
OP, kenarları yumuşatmak ister, ancak bu yalnızca görüntülerde işe yarar. Başına spec , bu belirler"whether pattern fills and the drawImage() method will attempt to smooth images if their pixels don't line up exactly with the display, when scaling images up"
rvighne

13

Vektör grafiklerini yumuşatmalıdır

Tamsayı olmayan koordinatları (0.4, 0.4) içeren vektör grafiklerinin doğru şekilde çizilmesi için, çok az sayıda istemcinin yaptığı gibi kenar yumuşatma gereklidir .

Tamsayı olmayan koordinatlar verildiğinde, tuvalin iki seçeneği vardır:

  • Kenar Yumuşatma - tamsayı koordinatının tamsayı olmayan bir koordinattan ne kadar uzakta olduğuna bağlı olarak koordinatın etrafındaki pikselleri boyayın (yani, yuvarlama hatası).
  • Yuvarlama - tamsayı olmayan koordinata bir miktar yuvarlama işlevi uygulayın (örneğin, 1.4, 1 olacaktır).

Daha sonraki strateji statik grafikler için işe yarayacaktır, ancak küçük grafikler için (2 yarıçaplı bir daire) eğriler düzgün bir eğri yerine net adımlar gösterecektir.

Asıl sorun, grafikler çevrildiğinde (taşındığında) - bir piksel ile diğeri arasındaki atlamalar (1.6 => 2, 1.4 => 1), şeklin başlangıç ​​noktasının üst kapsayıcıya göre atlayabileceği anlamına gelir (sürekli değişen 1 piksel yukarı / aşağı ve sol / sağ).

Bazı ipuçları

İpucu 1 : Tuvali ölçeklendirerek (örneğin x ile) kenar yumuşatmayı yumuşatabilir (veya sertleştirebilirsiniz) ve ardından karşılıklı ölçeği (1 / x) geometrilere kendiniz uygulayabilirsiniz (tuvali kullanmadan).

Karşılaştır (ölçeklendirme yok):

Birkaç dikdörtgen

(tuval ölçeği: 0.75; manuel ölçek: 1.33):

Daha yumuşak kenarlı aynı dikdörtgenler

ve (tuval ölçeği: 1.33; manuel ölçek: 0.75):

Daha koyu kenarlı aynı dikdörtgenler

İpucu # 2 : Bir jaggy göz ne sonra konum gerçekten ise (silmeden) her şeklin bir kaç kez çıkartmayı dene. Her çizimde, kenar yumuşatma pikselleri koyulaşır.

Karşılaştırmak. Bir kez çizdikten sonra:

Birkaç yol

Üç kez çizdikten sonra:

Aynı yollar ancak daha koyu ve görünür kenar yumuşatma yok.


@vanowm github.com/Izhaki/gefri ile klonlamaktan ve oynamaktan çekinmeyin . Tüm resimler / demo klasöründen alınan ekran görüntüleridir (kod 2 numaralı ipucu için biraz değiştirilmiştir). Eminim çizilen rakamlara tamsayı yuvarlamayı kolay bulacaksınız (4 dakika sürdü) ve ardından efekti görmek için sürükleyin.
Izhaki

Bu inanılmaz görünebilir ama orada olan sen antialias kesilmesini istiyorum nadir durumlar. Nitekim insanların tuval üzerine alanları boyaması gereken bir oyun programladım ve sadece 4 renk olması gerekiyor ve kenar yumuşatma yüzünden sıkıştım. Deseni üç kez tekrarlamak sorunu çözmedi (200'e çıktım) ve hala yanlış renklerin pikselleri var.
Arnaud


8

Bir görüntünün boyutunu küçültürken ve tuval üzerine çizim yaparken sorun yaşadığımı eklemek istiyorum, ölçek büyütme sırasında kullanılmamasına rağmen hala düzgünleştirmeyi kullanıyordu.

Bunu kullanarak çözdüm:

function setpixelated(context){
    context['imageSmoothingEnabled'] = false;       /* standard */
    context['mozImageSmoothingEnabled'] = false;    /* Firefox */
    context['oImageSmoothingEnabled'] = false;      /* Opera */
    context['webkitImageSmoothingEnabled'] = false; /* Safari */
    context['msImageSmoothingEnabled'] = false;     /* IE */
}

Bu işlevi şu şekilde kullanabilirsiniz:

var canvas = document.getElementById('mycanvas')
setpixelated(canvas.getContext('2d'))

Belki bu birisi için yararlıdır.


neden context.imageSmoothingEnabled = false değil?
Martijn Scheffer

Cevabımı yazdığım sırada bu işe yaramadı. Şu anda çalışıyor mu?
eri0o

1
yaptı, tam olarak aynı şey, javascript'te obj ['ad'] veya obj.name yazımı her zaman olmuştur ve her zaman aynı kalacaktır, bir nesne adlandırılmış değerlerin (demetler) bir koleksiyonudur. karma tablo, her iki gösterim de aynı şekilde ele alınacaktır, kodunuzun daha önce çalışmamış olması için hiçbir neden yoktur, en kötü ihtimalle etkisi olmayan bir değer atar (çünkü başka bir tarayıcı için tasarlanmıştır. basit bir örnek: yazma obj = {a: 123}; console.log (obj ['a'] === obj.a? "evet doğru": "hayır değil")
Martijn Scheffer

Neden diğer şeylere sahip olduğunuzu kastettiğinizi sanıyordum, yorumumla kastettiğim, o zamanlar tarayıcıların farklı özellikler gerektirmesiydi.
eri0o

tamam evet elbette :) kodun kendisinin geçerliliğinden değil sözdiziminden bahsediyordum (işe yarıyor)
Martijn Scheffer

6
ctx.translate(0.5, 0.5);
ctx.lineWidth = .5;

Bu combo ile güzel 1px ince çizgiler çizebilirim.


6
LineWidth'i 0,5'e ayarlamanıza gerek yok ... bu onu yarı opaklık yapacak (veya yapmalı).
aaaidan

4

Çok sınırlı bir numaraya dikkat edin. 2 renkli bir resim oluşturmak istiyorsanız, # 000000 rengi olan bir arka plan üzerine # 010101 rengiyle istediğiniz şekli çizebilirsiniz. Bu yapıldıktan sonra, imageData.data [] içindeki her pikseli test edebilir ve 0x00 değilse 0xFF olarak ayarlayabilirsiniz:

imageData = context2d.getImageData (0, 0, g.width, g.height);
for (i = 0; i != imageData.data.length; i ++) {
    if (imageData.data[i] != 0x00)
        imageData.data[i] = 0xFF;
}
context2d.putImageData (imageData, 0, 0);

Sonuç, kenar yumuşatılmamış siyah beyaz bir resim olacaktır. Bu mükemmel olmayacak, çünkü bazı kenar yumuşatma gerçekleşecek, ancak bu kenar yumuşatma çok sınırlı olacak, şeklin rengi arka plan rengine çok benziyor.


1

Hala cevap arayanlar için. İşte benim çözümüm.

Görüntünün 1 kanal gri olduğu varsayılır. Ctx.stroke () 'dan sonra eşik belirledim.

ctx.beginPath();
ctx.moveTo(some_x, some_y);
ctx.lineTo(some_x, some_y);
...
ctx.closePath();
ctx.fill();
ctx.stroke();

let image = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height)
for(let x=0; x < ctx.canvas.width; x++) {
  for(let y=0; y < ctx.canvas.height; y++) {
    if(image.data[x*image.height + y] < 128) {
      image.data[x*image.height + y] = 0;
    } else {
      image.data[x*image.height + y] = 255;
    }
  }
}

görüntü kanalınız 3 veya 4 ise dizi indeksini aşağıdaki gibi değiştirmeniz gerekir

x*image.height*number_channel + y*number_channel + channel

0

StashOfCode'un cevabına sadece iki not:

  1. Yalnızca gri tonlamalı, opak bir tuval için çalışır (fillRect ile beyaz, ardından siyahla çizin veya tam tersi)
  2. Çizgiler ince olduğunda başarısız olabilir (~ 1 piksel çizgi genişliği)

Bunun yerine bunu yapmak daha iyidir:

Vurun ve doldurun #FFFFFF, sonra şunu yapın:

imageData.data[i] = (imageData.data[i] >> 7) * 0xFF

Bu, 1px genişliğindeki çizgiler için çözer.

Bunun dışında StashOfCode'un çözümü mükemmeldir çünkü kendi rasterleştirme işlevlerinizi yazmanıza gerek yoktur (sadece çizgileri değil beziers, dairesel yaylar, delikli dolu çokgenler vb.)


0

İşte Bresenham algoritmasının JavaScript'te temel bir uygulaması. Bu wikipedia makalesinde açıklanan tamsayı-aritmetik versiyona dayanmaktadır: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm

    function range(f=0, l) {
        var list = [];
        const lower = Math.min(f, l);
        const higher = Math.max(f, l);
        for (var i = lower; i <= higher; i++) {
            list.push(i);
        }
        return list;
    }

    //Don't ask me.
    //https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
    function bresenhamLinePoints(start, end) {

        let points = [];

        if(start.x === end.x) {
            return range(f=start.y, l=end.y)
                        .map(yIdx => {
                            return {x: start.x, y: yIdx};
                        });
        } else if (start.y === end.y) {
            return range(f=start.x, l=end.x)
                        .map(xIdx => {
                            return {x: xIdx, y: start.y};
                        });
        }

        let dx = Math.abs(end.x - start.x);
        let sx = start.x < end.x ? 1 : -1;
        let dy = -1*Math.abs(end.y - start.y);
        let sy = start.y < end.y ? 1 : - 1;
        let err = dx + dy;

        let currX = start.x;
        let currY = start.y;

        while(true) {
            points.push({x: currX, y: currY});
            if(currX === end.x && currY === end.y) break;
            let e2 = 2*err;
            if (e2 >= dy) {
                err += dy;
                currX += sx;
            }
            if(e2 <= dx) {
                err += dx;
                currY += sy;
            }
        }

        return points;

    }

0

Gibi bir şey dene canvas { image-rendering: pixelated; }.

Yalnızca bir satırı kenar yumuşatmasız yapmaya çalışıyorsanız, bu işe yaramayabilir.

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

ctx.fillRect(4, 4, 2, 2);
canvas {
  image-rendering: pixelated;
  width: 100px;
  height: 100px; /* Scale 10x */
}
<html>
  <head></head>
  <body>
    <canvas width="10" height="10">Canvas unsupported</canvas>
  </body>
</html>

Yine de bunu birçok tarayıcıda test etmedim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.