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 đ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?
Câu trả lời:
Đố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 getImageData
và putImageData
.
putImageData
nhưng nó vẫn làm răng cưa của các pixel gần đó chết tiệt.
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 1
pixel ở đó đạt vị trí từ 9.5
để 10.5
mà 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.5
tọ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.
ctx.translate(0.5,0.5)
không. trên FF39.0
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.
"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"
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:
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ẹ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ệ):
với (tỷ lệ canvas: 0,75; tỷ lệ thủ công: 1,33):
và (tỷ lệ canvas: 1,33; tỷ lệ thủ công: 0,75):
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:
Sau khi vẽ ba lần:
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.
setPixel(x, y)
; Tôi đã sử dụng câu trả lời được chấp nhận ở đây: stackoverflow.com/questions/4899799/…
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 đó.
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.
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.
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
Chỉ có hai ghi chú về câu trả lời của StashOfCode:
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.)
Đâ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;
}