Tôi có thể tắt khử răng cưa trên phần tử <canvas> của HTML không?


85

Tôi đang chơi xung quanh <canvas>phần tử, vẽ đường và những thứ khác.

Tôi nhận thấy rằng các đường chéo của tôi được khử răng cưa. Tôi thích giao diện lởm chởm hơn cho những gì tôi đang làm - có cách nào để tắt tính năng này không?


Tôi nghĩ rằng điều đó khá liên quan đến trình duyệt. Có thể một số thông tin bổ sung về phần mềm bạn sử dụng sẽ hữu ích.
Tomalak

Tôi thích một phương pháp trên nhiều trình duyệt hơn, nhưng một phương pháp hoạt động trên bất kỳ trình duyệt đơn lẻ nào vẫn sẽ thú vị đối với tôi
Blorgbeard ra mắt

Tôi chỉ muốn xem liệu có bất kỳ thay đổi nào về chủ đề này không?
vternal3


Có bất kỳ bản cập nhật về điều này?
Roland

Câu trả lời:


60

Đối với hình ảnh hiện có .context.imageSmoothingEnabled= false

Tuy nhiên, không có gì kiểm soát rõ ràng việc vẽ đường. Bạn có thể cần phải vẽ các đường của riêng mình ( cách khó ) bằng cách sử dụng getImageDataputImageData.


1
Tôi tự hỏi về hiệu suất của một thuật toán dòng javascript .. Có thể cho phép Bresenham thử một lúc nào đó.
Blorgbeard ra mắt vào

Gần đây, các nhà cung cấp trình duyệt đang chào hàng các công cụ JS siêu nhanh mới, vì vậy cuối cùng sẽ có cách sử dụng tốt cho nó.
Kornel 14/10/08

1
Điều này có thực sự hiệu quả? Tôi đang vẽ một đường bằng cách sử dụng putImageData nhưng nó vẫn làm răng cưa của các pixel gần đó chết tiệt.
Pacerier

nếu tôi vẽ vào một canvas nhỏ hơn (bộ nhớ cache) và sau đó drawImage sang một canvas khác với cài đặt đó đang tắt, nó có hoạt động như dự định không?
SparK

69

Vẽ các 1-pixelđường của bạn trên các tọa độ như thế nào ctx.lineTo(10.5, 10.5). Vẽ một đường một pixel so với điểm (10, 10)phương tiện, rằng đây 1pixel ở đó đạt vị trí từ 9.5để 10.5mà kết quả trong hai dòng mà có được vẽ trên canvas.

Một mẹo hay để không phải lúc nào cũng cần thêm 0.5tọa độ thực tế mà bạn muốn vẽ nếu bạn có nhiều đường một pixel, đó là cho ctx.translate(0.5, 0.5)toàn bộ khung vẽ của bạn ngay từ đầu.


hmm, tôi đang gặp sự cố khi loại bỏ khử răng cưa bằng kỹ thuật này. Có lẽ, tôi không hiểu điều gì đó? Bạn có phiền đăng một số ví dụ ở đâu không?
Xavi

7
Điều này không giúp loại bỏ khử răng cưa, nhưng làm cho các đường khử răng cưa trông đẹp hơn rất nhiều --- chẳng hạn như loại bỏ những đường ngang hoặc dọc đáng xấu hổ dày hai pixel khi bạn thực sự muốn một pixel.
David Given

1
@porneL: Không, các đường được vẽ giữa các góc của pixel. Khi đường của bạn rộng 1 pixel, đường đó kéo dài nửa pixel theo một trong hai hướng
Eric

Thêm +0,5 hoạt động cho tôi, nhưng ctx.translate(0.5,0.5)không. trên FF39.0
Paulo Bueno

Cảm ơn rât nhiều! Tôi không thể tin rằng tôi có dòng 1px thực tế để thay đổi!
Chunky Chunk

24

Nó có thể được thực hiện trong Mozilla Firefox. Thêm cái này vào mã của bạn:

contextXYZ.mozImageSmoothingEnabled = false;

Trong Opera, nó hiện là một yêu cầu tính năng, nhưng hy vọng nó sẽ sớm được thêm vào.


mát mẻ. +1 cho đóng góp của bạn. tôi tự hỏi, nếu vô hiệu AA tăng tốc linedrawing
marcusklaas

7
OP muốn bỏ các dòng chống bí danh, nhưng điều này chỉ hoạt động trên hình ảnh. Theo thông số kỹ thuật , nó xác định"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

14

Nó phải đồ họa vector antialias

Khử răng cưa là bắt buộc để vẽ đúng đồ họa vectơ có liên quan đến tọa độ không phải số nguyên (0,4, 0,4), điều mà tất cả nhưng rất ít máy khách làm.

Khi được cung cấp tọa độ không phải số nguyên, canvas có hai tùy chọn:

  • Antialias - tô các pixel xung quanh tọa độ dựa trên tọa độ số nguyên cách tọa độ không phải số nguyên bao xa (tức là lỗi làm tròn).
  • Làm tròn - áp dụng một số hàm làm tròn cho tọa độ không phải là số nguyên (ví dụ: 1,4 sẽ trở thành 1).

Chiến lược sau sẽ hoạt động đối với đồ họa tĩnh, mặc dù đối với đồ họa nhỏ (hình tròn có bán kính là 2), các đường cong sẽ hiển thị các bước rõ ràng hơn là một đường cong trơn.

Vấn đề thực sự là khi đồ họa được dịch (di chuyển) - bước nhảy giữa pixel này và pixel khác (1.6 => 2, 1.4 => 1), có nghĩa là nguồn gốc của hình dạng có thể nhảy theo mối quan hệ với vùng chứa mẹ (liên tục dịch chuyển 1 pixel lên / xuống và trái / phải).

Một số lời khuyên

Mẹo số 1 : Bạn có thể làm mềm (hoặc làm cứng) khử răng cưa bằng cách chia tỷ lệ canvas (giả sử bằng x), sau đó áp dụng tỷ lệ tương hỗ (1 / x) cho các hình học (không sử dụng canvas).

So sánh (không chia tỷ lệ):

Một vài hình chữ nhật

với (tỷ lệ canvas: 0,75; tỷ lệ thủ công: 1,33):

Hình chữ nhật giống nhau với các cạnh mềm hơn

và (tỷ lệ canvas: 1,33; tỷ lệ thủ công: 0,75):

Hình chữ nhật giống nhau với các cạnh tối hơn

Mẹo số 2 : Nếu vẻ ngoài lởm chởm thực sự là điều bạn đang theo đuổi, hãy cố gắng vẽ từng hình một vài lần (không tẩy xóa). Với mỗi lần vẽ, các pixel khử răng cưa sẽ tối hơn.

So sánh. Sau khi vẽ một lần:

Một vài con đường

Sau khi vẽ ba lần:

Các đường dẫn giống nhau nhưng tối hơn và không có khả năng khử răng cưa.


@vanowm vui lòng sao chép và chơi với: github.com/Izhaki/gefri . Tất cả các hình ảnh đều là ảnh chụp màn hình từ thư mục / demo (với mã được sửa đổi một chút cho mẹo số 2). Tôi chắc rằng bạn sẽ thấy dễ dàng khi giới thiệu cách làm tròn số nguyên cho các hình đã vẽ (tôi mất 4 phút) và sau đó chỉ cần kéo để xem hiệu ứng.
Izhaki

9

Tôi sẽ vẽ mọi thứ bằng cách sử dụng thuật toán đường tùy chỉnh chẳng hạn như thuật toán đường của Bresenham. Kiểm tra triển khai javascript này: http://members.chello.at/easyfilter/canvas.html

Tôi nghĩ rằng điều này chắc chắn sẽ giải quyết vấn đề của bạn.


2
Chính xác những gì tôi cần, điều duy nhất tôi muốn thêm là bạn cần phải thực hiện setPixel(x, y); Tôi đã sử dụng câu trả lời được chấp nhận ở đây: stackoverflow.com/questions/4899799/…
Tina Vall

8

Tôi muốn nói thêm rằng tôi đã gặp sự cố khi giảm kích thước hình ảnh và vẽ trên canvas, nó vẫn đang sử dụng tính năng làm mịn, mặc dù nó không sử dụng khi nâng cấp.

Tôi đã giải quyết bằng cách sử dụng cái này:

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

Bạn có thể sử dụng chức năng này như sau:

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

Có thể điều này hữu ích cho ai đó.


tại sao không context.imageSmoothingEnabled = false?
Martijn Scheffer

Điều này không hoạt động tại thời điểm tôi viết câu trả lời của mình. Bây giờ nó có hoạt động không?
eri0o

1
nó đã làm, nó CHÍNH XÁC giống như vậy, trong javascript, việc viết obj ['name'] hoặc obj.name luôn như vậy và sẽ luôn giống nhau, một đối tượng là một tập hợp các giá trị được đặt tên (bộ giá trị), sử dụng một cái gì đó giống với bảng băm, cả hai ký hiệu sẽ được xử lý theo cùng một cách, không có lý do gì mà mã của bạn không hoạt động trước đó, tệ nhất là nó chỉ định một giá trị không có tác dụng (vì nó dành cho một trình duyệt khác. Một ví dụ đơn giản: write obj = {a: 123}; console.log (obj [ 'a'] === obj.a "có thật sự của nó": "không có nó không"?)
Martijn Scheffer

Tôi nghĩ ý của bạn là tại sao lại có tất cả những thứ khác, ý của tôi với nhận xét của mình là vào thời điểm đó các trình duyệt yêu cầu các thuộc tính khác nhau.
eri0o 19/09/18

ok vâng tất nhiên :) tôi đã nói về cú pháp, không phải là giá trị của bản thân mã (nó hoạt động)
Martijn Scheffer

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

Với combo này, tôi có thể vẽ các đường mỏng 1px đẹp.


6
Bạn không cần đặt lineWidth thành .5 ... mà sẽ (hoặc nên) chỉ làm cho nó có độ mờ một nửa.
aaaidan

4

Lưu ý một thủ thuật rất hạn chế. Nếu bạn muốn tạo hình ảnh 2 màu, bạn có thể vẽ bất kỳ hình dạng nào bạn muốn với màu # 010101 trên nền có màu # 000000. Khi điều này được thực hiện, bạn có thể kiểm tra từng pixel trong imageData.data [] và đặt thành 0xFF bất kỳ giá trị nào không phải 0x00:

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

Kết quả sẽ là một hình ảnh đen trắng không khử răng cưa. Điều này sẽ không hoàn hảo, vì một số quá trình khử răng cưa sẽ diễn ra, nhưng quá trình khử răng cưa này sẽ rất hạn chế, màu sắc của hình dạng rất giống màu của nền.


1

Dành cho những ai vẫn đang tìm kiếm câu trả lời. đây là giải pháp của tôi.

Giả sử hình ảnh có màu xám 1 kênh. Tôi vừa ngưỡng sau ctx.stroke ().

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

nếu kênh hình ảnh của bạn là 3 hoặc 4. bạn cần sửa đổi chỉ số mảng như

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

0

Chỉ có hai ghi chú về câu trả lời của StashOfCode:

  1. Nó chỉ hoạt động đối với canvas màu xám, mờ đục (fillRect với màu trắng, sau đó vẽ với màu đen hoặc ngược lại)
  2. Nó có thể không thành công khi dòng mỏng (~ 1px chiều rộng dòng)

Tốt hơn nên làm điều này thay vì:

Stroke và điền vào #FFFFFF, sau đó thực hiện điều này:

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

Điều đó giải quyết nó cho các dòng có chiều rộng 1px.

Ngoài ra, giải pháp của StashOfCode là hoàn hảo vì nó không yêu cầu viết các hàm rasterization của riêng bạn (không chỉ nghĩ đến các đường mà còn là các đường viền, cung tròn, đa giác có lỗ, v.v.)


0

Đây là cách triển khai cơ bản của thuật toán Bresenham trong JavaScript. Nó dựa trên phiên bản số nguyên-số học được mô tả trong bài viết wikipedia này: 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;

    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.