Làm thế nào để xóa vải để vẽ lại


1012

Sau khi thử nghiệm các thao tác tổng hợp và vẽ hình ảnh trên khung vẽ, bây giờ tôi đang cố gắng xóa hình ảnh và kết hợp. Làm thế nào để tôi làm điều này?

Tôi cần phải xóa khung vẽ để vẽ lại các hình ảnh khác; việc này có thể diễn ra trong một thời gian vì vậy tôi không nghĩ rằng việc vẽ một hình chữ nhật mới mỗi lần sẽ là lựa chọn hiệu quả nhất.

Câu trả lời:


1292
const context = canvas.getContext('2d');

context.clearRect(0, 0, canvas.width, canvas.height);

42
Lưu ý rằng đối với clearRectbạn cần phải có một bối cảnh chưa được xử lý hoặc theo dõi các ranh giới thực tế của bạn.
Phrogz

7
Lưu ý thêm rằng việc đặt độ rộng của khung vẽ a) cần đặt nó thành một giá trị khác và sau đó quay lại để tương thích với Webkit và b) điều này sẽ đặt lại chuyển đổi ngữ cảnh của bạn .
Phrogz

45
Đừng sử dụng thủ thuật chiều rộng. Đó là một hack bẩn. Trong một ngôn ngữ cấp cao như JavaScript, điều bạn muốn là nói rõ nhất ý định của bạn và sau đó để mã cấp thấp hơn thực hiện chúng. Đặt chiều rộng cho chính nó không nêu rõ ý định thực sự của bạn, đó là xóa khung vẽ.
andrewrk

22
Một gợi ý hoàn toàn nhỏ nhưng tôi đề nghị context.clearRect(0, 0, context.canvas.width, context.canvas.height). Đó thực sự là điều tương tự nhưng một sự phụ thuộc ít hơn (1 biến thay vì 2)
gman

6
Đối với những độc giả gần đây hơn của câu trả lời này: hãy lưu ý rằng câu trả lời này đã được sửa đổi và một số ý kiến ​​trên không còn được áp dụng .
daveslab

719

Sử dụng: context.clearRect(0, 0, canvas.width, canvas.height);

Đây là cách nhanh nhất và mô tả nhất để xóa toàn bộ khung vẽ.

Không được dùng: canvas.width = canvas.width;

Đặt canvas.widthlại tất cả các trạng thái canvas (ví dụ: biến đổi, lineWidth, StroStyle, v.v.), nó rất chậm (so với ClearRect), nó không hoạt động trong tất cả các trình duyệt và nó không mô tả những gì bạn đang thực sự làm.

Xử lý tọa độ biến đổi

Nếu bạn đã sửa đổi ma trận chuyển đổi (ví dụ sử dụng scale, rotatehoặc translate) sau đó context.clearRect(0,0,canvas.width,canvas.height)có thể sẽ không xóa toàn bộ phần hữu hình của canvas.

Giải pháp? Đặt lại ma trận chuyển đổi trước khi xóa khung vẽ:

// Store the current transformation matrix
context.save();

// Use the identity matrix while clearing the canvas
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);

// Restore the transform
context.restore();

Chỉnh sửa: Tôi vừa thực hiện một số lược tả và (trong Chrome), nhanh hơn khoảng 10% để xóa khung vẽ 300x150 (kích thước mặc định) mà không cần đặt lại biến đổi. Khi kích thước của khung vẽ của bạn tăng sự khác biệt này giảm xuống.

Điều đó đã tương đối không đáng kể, nhưng trong hầu hết các trường hợp, bạn sẽ vẽ nhiều hơn đáng kể so với bạn đang xóa và tôi tin rằng sự khác biệt hiệu suất này là không liên quan.

100000 iterations averaged 10 times:
1885ms to clear
2112ms to reset and clear

4
@YumYumYum: Clear () có phải là hàm chuẩn không? Có vẻ như không phải vậy.
tộc

7
Lưu ý rằng bạn có thể loại bỏ sự cần thiết của một canvasbiến cục bộ bằng cách sử dụng ctx.canvasthay thế.
Drew Noakes

1
@DrewNoakes, điểm tốt, nhưng tôi hầu như luôn chọn các biến riêng biệt cho mục đích tốc độ. Nó là cực kỳ nhỏ, nhưng tôi cố gắng tránh thời gian hội thảo bằng cách đặt bí danh các thuộc tính được sử dụng thường xuyên (đặc biệt là bên trong một vòng lặp hoạt hình).
Prestaul

Bản demo của AlexanderN không hoạt động nữa do liên kết hình ảnh bị hỏng, đã được sửa: jsfiddle.net/Ktqfs/50
Domino

Một cách khác để xóa toàn bộ khung trong trường hợp sửa đổi ma trận biến đổi là chỉ cần theo dõi các biến đổi và tự áp dụng chúng cho canvas.widthcanvas.heightkhi xóa. Tôi chưa thực hiện bất kỳ phân tích số nào về chênh lệch thời gian chạy so với việc đặt lại biến đổi, nhưng tôi nghi ngờ nó sẽ mang lại hiệu suất tốt hơn một chút.
NanoWizard

226

Nếu bạn đang vẽ đường, hãy chắc chắn rằng bạn không quên:

context.beginPath();

Nếu không, các dòng sẽ không được xóa.


10
Tôi biết điều này đã ở đây được một thời gian và có lẽ thật ngớ ngẩn khi tôi bình luận sau tất cả thời gian này, nhưng điều này đã làm phiền tôi trong một năm ... Gọi beginPathkhi bạn bắt đầu một con đường mới , đừng cho rằng chỉ vì bạn đang xóa khung vẽ bạn muốn xóa đường dẫn hiện tại của bạn! Đây sẽ là một thực tế khủng khiếp và là một cách nhìn ngược lại về vẽ.
Prestaul

Điều gì sẽ xảy ra nếu bạn chỉ muốn xóa khung vẽ của mọi thứ. Hãy nói rằng bạn đang tạo một trò chơi và bạn cần vẽ lại màn hình cứ sau một phần trăm giây.

1
@JoseQuinones, beginPathkhông xóa bất cứ thứ gì trên khung vẽ của bạn, nó đặt lại đường dẫn để các mục nhập đường dẫn trước đó bị xóa trước khi bạn vẽ. Bạn có thể đã cần nó, nhưng là một hoạt động vẽ, không phải là một hoạt động xóa. rõ ràng, sau đó bắt đầu và vẽ, không rõ ràng và bắt đầu, sau đó vẽ. Liệu sự khác biệt có ý nghĩa? Nhìn vào đây để xem ví dụ: w3schools.com/tags/canvas_beginpath.asp
Prestaul

1
Điều này đã giải quyết sự chậm lại của hoạt hình tôi đã trải qua theo thời gian. Tôi đã vẽ lại các đường lưới trên mỗi bước, nhưng không xóa các dòng từ bước trước.
Georg Patscheider

118

Những người khác đã hoàn thành công việc tuyệt vời khi trả lời câu hỏi nhưng nếu một clear()phương pháp đơn giản trên đối tượng ngữ cảnh sẽ hữu ích cho bạn (thì đó là với tôi), đây là cách triển khai tôi sử dụng dựa trên câu trả lời ở đây:

CanvasRenderingContext2D.prototype.clear = 
  CanvasRenderingContext2D.prototype.clear || function (preserveTransform) {
    if (preserveTransform) {
      this.save();
      this.setTransform(1, 0, 0, 1, 0, 0);
    }

    this.clearRect(0, 0, this.canvas.width, this.canvas.height);

    if (preserveTransform) {
      this.restore();
    }           
};

Sử dụng:

window.onload = function () {
  var canvas = document.getElementById('canvasId');
  var context = canvas.getContext('2d');

  // do some drawing
  context.clear();

  // do some more drawing
  context.setTransform(-1, 0, 0, 1, 200, 200);
  // do some drawing with the new transform
  context.clear(true);
  // draw more, still using the preserved transform
};

6
Đây là một thực hiện tuyệt vời. Tôi hoàn toàn ủng hộ việc tăng cường các nguyên mẫu bản địa, nhưng bạn có thể muốn đảm bảo rằng "rõ ràng" không được xác định trước khi gán nó - tôi vẫn hy vọng một triển khai gốc một ngày nào đó. :) Bạn có biết các trình duyệt hỗ trợ CanvasRenderingContext2D rộng rãi như thế nào và để nó "có thể ghi" không?
Prestaul

Cảm ơn phản hồi @Prestaul - đồng thời, không trình duyệt nào ngăn bạn mở rộng các đối tượng javascript theo cách này.
JonathanK

@JonathanK, tôi rất muốn thấy một số hồ sơ về sự khác biệt hiệu suất giữa xóa và không đặt lại chuyển đổi. Tôi đoán rằng sự khác biệt sẽ rõ ràng nếu bạn đang vẽ ít nhưng nếu những gì bạn đang vẽ không tầm thường thì bước rõ ràng là không đáng kể ... Tôi có thể phải kiểm tra điều đó sau khi tôi có nhiều thời gian hơn :)
Prestaul

Ok, tôi đã làm hồ sơ và tôi sẽ thêm các chi tiết vào bài viết của mình.
Prestaul

35
  • Chrome đáp ứng tốt với: context.clearRect ( x , y , w , h );như được đề xuất bởi @ Pentium10 nhưng IE9 dường như hoàn toàn bỏ qua hướng dẫn này.
  • IE9 dường như đáp ứng: canvas.width = canvas.width;nhưng nó không xóa các đường kẻ, chỉ hình dạng, hình ảnh và các đối tượng khác trừ khi bạn cũng sử dụng giải pháp thay đổi độ rộng đầu tiên của @John Allsopp.

Vì vậy, nếu bạn có một khung vẽ và bối cảnh được tạo như thế này:

var canvas = document.getElementById('my-canvas');
var context = canvas.getContext('2d');

Bạn có thể sử dụng một phương pháp như thế này:

function clearCanvas(context, canvas) {
  context.clearRect(0, 0, canvas.width, canvas.height);
  var w = canvas.width;
  canvas.width = 1;
  canvas.width = w;
}

13
Lưu ý rằng phương pháp có thể được đơn giản hóa bằng cách chỉ chuyển vào ngữ cảnh và sử dụng context.clearRect(0,0,context.canvas.width,context.canvas.height).
Phrogz

5
IE 9 hoàn toàn nên đáp ứng với một cuộc gọi ClearRect ... (Xem: msdn.microsoft.com/en-us/l Library / ff975407 (v = vs85 ) .aspx ) Làm chậm như thay đổi canvas. Băng thông bạn có thể trở nên chậm hơn bằng cách thay đổi nó hai lần và gọi ClearRect.
Prestaul

1
Xin lỗi, tôi nên nói rõ hơn ... Phương pháp này sẽ hoạt động (miễn là bạn chưa áp dụng biến đổi), nhưng là một kỹ thuật vũ phu [chậm] trong trường hợp không cần thiết. Chỉ cần sử dụng ClearRect vì nó nhanh nhất và sẽ hoạt động trên mọi trình duyệt với triển khai canvas.
Prestaul

1
Tôi tin rằng IE đang vẽ lại các dòng do chúng vẫn nằm trong bộ đệm đường dẫn. Tôi sử dụng định dạng này và nó hoạt động hoàn hảo. function clearCanvas(ctx) { ctx.beginPath(); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); }
DarrenMB

Tôi đã thử từng phương pháp trước đây ... và đây là phương pháp duy nhất có hiệu quả. Tôi đang sử dụng Chrome với các dòng, hình chữ nhật và văn bản ... sẽ không nghĩ rằng sẽ rất khó để làm một cái gì đó nên được xây dựng! Cảm ơn!
Anthony Griggs

26

Đây là năm 2018 và vẫn chưa có phương pháp gốc để xóa hoàn toàn canvas để vẽ lại. clearRect() không xóa hoàn toàn vải. Bản vẽ loại không điền không được xóa (ví dụ. rect())

1. Để hoàn toàn rõ ràng canvas bất kể cách bạn vẽ:

context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.beginPath();

Ưu điểm: Bảo toàn đột quỵStyle, fillStyle, v.v.; Không bị giật, lag;

Nhược điểm: Không cần thiết nếu bạn đã sử dụng BeginPath trước khi vẽ bất cứ thứ gì

2.Sử dụng hack chiều rộng / chiều cao:

context.canvas.width = context.canvas.width;

HOẶC LÀ

context.canvas.height = context.canvas.height;

Ưu điểm: Hoạt động với IE Nhược điểm: Đặt lại StroStyle, fillStyle thành màu đen; Chậm trễ;

Tôi đã tự hỏi tại sao một giải pháp bản địa không tồn tại. Trên thực tế, clearRect()được coi là giải pháp dòng đơn vì hầu hết người dùng thực hiện beginPath()trước khi vẽ bất kỳ đường dẫn mới nào. Mặc dù BeginPath chỉ được sử dụng trong khi vẽ các đường và không đóng đường dẫn nhưrect().

Đây là lý do tại sao câu trả lời được chấp nhận không giải quyết được vấn đề của tôi và cuối cùng tôi đã lãng phí hàng giờ để thử các bản hack khác nhau. Nguyền rủa bạn


Tôi nghĩ rằng đây là câu trả lời đúng, đặc biệt là để vẽ canvas. Tôi đã chịu đựng bối cảnh còn lại. Không có vấn đề làm thế nào tôi gọi ClearRect, trong khi BeginPath đã giúp!
Shao-Kui

20

Sử dụng phương thức clearRect bằng cách chuyển x, y tọa độ và chiều cao và chiều rộng của khung vẽ. ClearRect sẽ xóa toàn bộ khung như:

canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);

Bạn có thể chỉ cần đặt nó trong một phương thức và gọi nó mỗi lần bạn muốn làm mới màn hình, chính xác.

@ XXX.xxx plz kiểm tra id của canvas trong html của bạn và sử dụng tương tự cho dòng đầu tiên (để lấy phần tử theo id)
Vishwas

14

Có rất nhiều câu trả lời hay ở đây. một lưu ý nữa là đôi khi thật thú vị khi chỉ xóa một phần khung vẽ. nghĩa là "làm mờ" hình ảnh trước đó thay vì xóa nó hoàn toàn. điều này có thể cho hiệu ứng đường mòn tốt đẹp.

dễ thôi. giả sử màu nền của bạn là màu trắng:

// assuming background color = white and "eraseAlpha" is a value from 0 to 1.
myContext.fillStyle = "rgba(255, 255, 255, " + eraseAlpha + ")";
myContext.fillRect(0, 0, w, h);

Tôi biết điều đó là có thể và tôi không có vấn đề gì với câu trả lời của bạn, tôi chỉ tự hỏi, nếu bạn có 2 đối tượng đang di chuyển và bạn chỉ muốn theo dõi một trong số chúng, bạn sẽ làm thế nào?
siêu

đó là một thỏa thuận tốt phức tạp hơn. trong trường hợp đó, bạn nên tạo một khung vẽ ngoài màn hình và mỗi khung hình sẽ vẽ các đối tượng bạn muốn theo dõi trên khung vẽ đó bằng phương pháp xóa một phần được mô tả ở đây và mỗi khung hình sẽ xóa khung hình chính 100%, vẽ đường kẻ không theo dõi các đối tượng và sau đó kết hợp khung vẽ ngoài màn hình lên khung chính bằng drawImage (). Bạn cũng sẽ cần đặt globalCompổngOperation thành một cái gì đó phù hợp cho hình ảnh. ví dụ: "nhân" sẽ hoạt động tốt đối với các vật thể tối trên nền sáng.
orion elenzil

12

Một cách nhanh chóng là làm

canvas.width = canvas.width

Idk làm thế nào nó hoạt động nhưng nó làm!


Ồ Điều này thực sự có tác dụng, Làm thế nào bạn tìm thấy điều này?
zipzit

@zipit Thủ thuật nhanh Tôi đã tìm thấy trên trang trung bình sau khi xóa thông thường không hiển thị :)
Jacob Morris

1
Tôi đang kéo tóc ra để cố gắng tìm ra lý do tại sao điều này hoạt động! Cám ơn vì đã chia sẻ!
aabbccsmith

Bất cứ ai cũng có thể giải thích lý do tại sao nó hoạt động và thậm chí canvas. Thong + = 0 cũng làm công việc đó, khoa học kỳ dị đằng sau này là gì?
PYK

8

Đây là những gì tôi sử dụng, bất kể ranh giới và biến đổi ma trận:

function clearCanvas(canvas) {
  const ctx = canvas.getContext('2d');
  ctx.save();
  ctx.globalCompositeOperation = 'copy';
  ctx.strokeStyle = 'transparent';
  ctx.beginPath();
  ctx.lineTo(0, 0);
  ctx.stroke();
  ctx.restore();
}

Về cơ bản, nó tiết kiệm tình trạng hiện tại của bối cảnh, và rút ra một điểm ảnh trong suốt với copynhư globalCompositeOperation. Sau đó, khôi phục trạng thái bối cảnh trước đó.


nó làm việc cho tôi ...! Cảm ơn.
NomanJaving

6

Điều này làm việc cho pieChart của tôi trong chart.js

<div class="pie_nut" id="pieChartContainer">
    <canvas id="pieChart" height="5" width="6"></canvas> 
</div>

$('#pieChartContainer').html(''); //remove canvas from container
$('#pieChartContainer').html('<canvas id="pieChart" height="5" width="6"></canvas>'); //add it back to the container

5

Tôi đã thấy rằng trong tất cả các trình duyệt mà tôi kiểm tra, cách nhanh nhất là thực sự fillRect với màu trắng hoặc màu trắng mà bạn muốn. Tôi có một màn hình rất lớn và ở chế độ toàn màn hình, ClearRect rất chậm, nhưng fillRect là hợp lý.

context.fillStyle = "#ffffff";
context.fillRect(0,0,canvas.width, canvas.height);

Hạn chế là vải không còn trong suốt.


4

trong webkit, bạn cần đặt chiều rộng thành một giá trị khác, sau đó bạn có thể đặt lại về giá trị ban đầu


4
function clear(context, color)
{
    var tmp = context.fillStyle;
    context.fillStyle = color;
    context.fillRect(0, 0, context.canvas.width, context.canvas.height);
    context.fillStyle = tmp;
}

Tốc độ này chậm hơn ClearRect (0,0, canvas. Thong, canvas.height)
sEver

4

Một cách đơn giản, nhưng không dễ đọc là viết cái này:

var canvas = document.getElementId('canvas');

// after doing some rendering

canvas.width = canvas.width;  // clear the whole canvas

2

Tôi luôn luôn sử dụng

cxt.fillStyle = "rgb(255, 255, 255)";
cxt.fillRect(0, 0, canvas.width, canvas.height);

Đây là cách dễ dàng nhất! Vâng, cho tôi ít nhất.
Jacques Olivier

1
context.clearRect(0,0,w,h)   

điền vào hình chữ nhật đã cho bằng các giá trị RGBA:
0 0 0 0: với Chrome
0 0 0 255: với FF & Safari

Nhưng

context.clearRect(0,0,w,h);    
context.fillStyle = 'rgba(0,0,0,1)';  
context.fillRect(0,0,w,h);  

hãy để hình chữ nhật đầy
0 0 0 255
bất kể trình duyệt!


1
Context.clearRect(starting width, starting height, ending width, ending height);

Thí dụ: context.clearRect(0, 0, canvas.width, canvas.height);



0

cách nhanh nhất:

canvas = document.getElementById("canvas");
c = canvas.getContext("2d");

//... some drawing here

i = c.createImageData(canvas.width, canvas.height);
c.putImageData(i, 0, 0); // clear context by putting empty image data

12
wow ... Trong trình duyệt nào? Trong tôi (Chrome 22 và Firefox 14 trên OS X), cho đến nay, phương pháp của bạn là tùy chọn chậm nhất. jsperf.com/canvas-clear-speed/9
Prestaul

1
> 5 năm trong tương lai, đây vẫn là phương pháp chậm nhất với số lượng rất lớn, chậm hơn ~ 700 lần clearRect.
mgthomas99

0

Nếu bạn chỉ sử dụng ClearRect, nếu bạn có nó trong một hình thức để gửi bản vẽ của mình, bạn sẽ nhận được một bản gửi thay vì xóa, hoặc có thể xóa nó trước và sau đó tải lên một bản vẽ trống, vì vậy bạn cần thêm một bản vẽ Ngăn chặn Lỗi tại chức năng cầu xin:

   function clearCanvas(canvas,ctx) {
        event.preventDefault();
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    }


<input type="button" value="Clear Sketchpad" id="clearbutton" onclick="clearCanvas(canvas,ctx);">

Hy vọng nó sẽ giúp được ai đó.


0

Tôi luôn dùng cái này

ctx.clearRect(0, 0, canvas.width, canvas.height)
window.requestAnimationFrame(functionToBeCalled)

GHI CHÚ

kết hợp ClearRect và requestAnimationFrame cho phép hoạt ảnh linh hoạt hơn nếu đó là những gì bạn sẽ làm


-2

Đây là tất cả các ví dụ tuyệt vời về cách bạn xóa một khung vẽ tiêu chuẩn, nhưng nếu bạn đang sử dụng paperjs, thì điều này sẽ hoạt động:

Xác định một biến toàn cục trong JavaScript:

var clearCanvas = false;

Từ PaperScript của bạn xác định:

function onFrame(event){
    if(clearCanvas && project.activeLayer.hasChildren()){
        project.activeLayer.removeChildren();
        clearCanvas = false;
    }
}

Bây giờ, bất cứ nơi nào bạn đặt ClearCanvas thành true, nó sẽ xóa tất cả các mục khỏi màn hình.

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.