Як очистити полотно для перемальовування


1012

Після експериментів із композиційними операціями та малюванням зображень на полотні я зараз намагаюся видалити зображення та композицію. Як це зробити?

Мені потрібно очистити полотно для перемальовування інших зображень; це може тривати деякий час, тому я не думаю, що малювання нового прямокутника кожного разу буде найбільш ефективним варіантом.

Відповіді:


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

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

42
Зауважте, що для clearRectвас потрібно або мати непереформований контекст, або слідкувати за своїми фактичними межами.
Фрогз

7
Далі зауважте, що для встановлення ширини полотна a) потрібно встановити його на інше значення, а потім знову повернутись для сумісності Webkit , і b) це скине ваше перетворення контексту .
Фрогз

45
Не використовуйте фокус із шириною. Це брудний злом. На мові високого рівня, такому як JavaScript, ви хочете найкраще викласти свої наміри, а потім дозволити коду нижчого рівня їх виконувати. Встановлення ширини під себе не означає вашого справжнього наміру, а саме - очистити полотно.
andrewrk

22
Зовсім другорядне, але я б припустив context.clearRect(0, 0, context.canvas.width, context.canvas.height). Це фактично те саме, але одна менша залежність (1 змінна замість 2)
gman

6
Більш пізні читачі цієї відповіді: пам’ятайте, що ця відповідь була змінена і що деякі з вищезазначених коментарів більше не застосовуються .
daveslab

719

Використання: context.clearRect(0, 0, canvas.width, canvas.height);

Це найшвидший і описовий спосіб очистити все полотно.

Не використовувати: canvas.width = canvas.width;

Скидання canvas.widthскидає всі стани полотна (наприклад, перетворення, ширину рядка, strokeStyle тощо), воно дуже повільне (порівняно з clearRect), воно працює не у всіх браузерах, і не описує те, що ви насправді намагаєтеся зробити.

Робота з трансформованими координатами

Якщо ви змінили матрицю перетворення (наприклад, використовуючи scale, rotateабо translate), то context.clearRect(0,0,canvas.width,canvas.height), ймовірно, не буде очищена вся видима частина полотна.

Рішення? Скиньте матрицю перетворення до очищення полотна:

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

Редагувати: Я тільки що проробив профілювання, і (в Chrome) на 10% швидше очистити полотно розміром 300x150 (розмір за замовчуванням), не скидаючи перетворення. Зі збільшенням розміру полотна ця різниця зменшується.

Це вже відносно незначно, але в більшості випадків ви будете малювати значно більше, ніж очищаєте, і я вважаю, що ця різниця у виконанні не має значення.

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

4
@YumYumYum: Чи зрозуміла () стандартна функція? Здається, це не так.
nobar

7
Зауважте, що ви можете усунути необхідність локальної canvasзмінної, скориставшись ctx.canvasнатомість.
Дрю Ноакс

1
@DrewNoakes, хороший момент, але я майже завжди вибираю окремі змінні для швидкості. Це вкрай незначно, але я намагаюсь уникати часу перенаправлення шляхом вилучення часто використовуваних властивостей (особливо всередині циклу анімації).
Prestaul

Демонстрація AlexanderN більше не працює через пошкоджене посилання на зображення, виправлене: jsfiddle.net/Ktqfs/50
Domino

Ще один спосіб очистити все полотно у випадку модифікації матриці перетворень - це просто слідкувати за перетвореннями та застосовувати їх самостійно до canvas.widthта canvas.heightпри очищенні. Я не робив жодного чисельного аналізу на різницю часу виконання в порівнянні зі скиданням перетворення, але я підозрюю, що це призведе до трохи кращої продуктивності.
NanoWizard

226

Якщо ви малюєте лінії, не забудьте:

context.beginPath();

Інакше лінії не будуть очищені.


10
Я знаю, що це вже деякий час, і мені, мабуть, нерозумно коментувати цей час, але це мене турбує вже рік ... Телефонуйте, beginPathколи ви починаєте новий шлях , не вважайте, що тільки тому, що ви очищаєте полотно, на якому хочете очистити і свій існуючий шлях! Це було б жахливою практикою і є зворотним способом погляду на малювання.
Prestaul

Що робити, якщо ви просто хочете очистити полотно від усього. Давайте скажемо, що ви створюєте гру і що вам потрібно перемальовувати екран кожні стільки сотих секунди.

1
@JoseQuinones, beginPathнічого не очищає від вашого полотна, він скидає шлях, щоб попередні записи шляху були видалені перед тим, як намалювати. Вам, напевно, це знадобилося, але як операція малювання, а не операція очищення. ясно, потім почнемо Шлях і малюємо, не ясно і почнемо Шлях, потім намалюй. Чи має значення різниця? Подивіться тут для прикладу: w3schools.com/tags/canvas_beginpath.asp
Prestaul

1
Це вирішило уповільнення моєї анімації, яку я відчував з часом. Я перемальовував лінії сітки на кожному кроці, але не очищаючи лінії від попереднього кроку.
Георг Патчайдер

118

Інші вже виконали чудову роботу, відповідаючи на запитання, але якщо простий clear()метод на контекстному об'єкті був би корисним для вас (це було для мене), це реалізація, яку я використовую на основі відповідей тут:

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

Використання:

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
Це чудова реалізація. Я повністю підтримую вдосконалення власних прототипів, але ви, можливо, захочете переконатися, що "clear" не визначено, перш ніж призначити його - я все ще сподіваюся на натисну реалізацію якогось дня. :) Чи знаєте ви, як широкоформатні браузери підтримують CanvasRenderingContext2D і залишають його "для запису"?
Prestaul

Дякуємо за відгук @Prestaul - також жоден браузер не повинен заважати вам розширювати об’єкти javascript таким чином.
JonathanK

@JonathanK, я хотів би ознайомитися з різницею різниці в продуктивності між очищенням та без скидання перетворення. Я здогадуюсь, що різниця буде очевидною, якщо ти робиш маленький малюнок, але якщо те, що ти малюєш, не є тривіальним, то чіткий крок незначний ... Можливо, мені доведеться перевірити це пізніше, коли у мене буде більше часу :)
Prestaul

Гаразд, я зробив профілювання, і я збираюся додати деталі до своєї публікації.
Prestaul

35
  • Chrome добре реагує на: context.clearRect ( x , y , w , h );як це запропонував @ Pentium10, але IE9, здається, повністю ігнорує цю інструкцію.
  • IE9, схоже, відповідає: canvas.width = canvas.width;але він не чіткі лінії, а лише фігури, зображення та інші об'єкти, якщо ви також не використовуєте рішення @John Allsopp про першу зміну ширини.

Тож якщо у вас є полотно та контекст, створені так:

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

Ви можете використовувати такий метод:

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

13
Зауважте, що метод можна спростити, передаючи лише контекст і використовуючи context.clearRect(0,0,context.canvas.width,context.canvas.height).
Фрогз

5
IE 9 повинен абсолютно відповідати на виклик clearRect ... (Див.: Msdn.microsoft.com/en-us/library/ff975407(v=vs.85).aspx ) Так само повільно, як змінюється canvas.width - єдиний спосіб ви можете повільніше, змінивши його двічі і зателефонувавши clearRect.
Prestaul

1
Вибачте, мені повинно бути більш зрозуміло ... Цей метод буде працювати (доки ви не застосували трансформацію), але є [повільною] технікою грубої сили там, де це не потрібно. Просто використовуйте clearRect, оскільки він найшвидший і повинен працювати в кожному браузері з використанням полотна.
Prestaul

1
Я вважаю, що IE перемальовує лінії завдяки тому, що вони все ще знаходяться в буфері шляху. Я використовую цей формат, і він працює ідеально. function clearCanvas(ctx) { ctx.beginPath(); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); }
DarrenMB

Я випробував кожен метод перед цим ... і це був єдиний, який працював. Я використовую Chrome з лініями, прямокутниками та текстом ... не подумав би, що буде так важко зробити щось, що має бути вбудовано! Дякую!
Ентоні Гріггс

26

Це 2018 рік, і досі немає рідного методу, щоб повністю очистити полотно для перемальовування. clearRect() не очищає полотно повністю. Малюнки типу незаливки не очищаються (напр. rect())

1.Дль повністю очистити полотно незалежно від того , як ви малюєте:

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

Плюси: зберігає StroStyle, fillStyle тощо; Немає відставання;

Мінуси: непотрібно, якщо ви вже використовуєте startPath, перш ніж щось малювати

2. Використання ширини / висоти злому:

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

АБО

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

Плюси: працює з мінусами IE: скидає StroStyle, fillStyle на чорний; Лаггі;

Мені було цікаво, чому рідного рішення не існує. Насправді, clearRect()це розглядається як єдине рішення, тому що більшість користувачів роблять beginPath()перед тим, як намалювати будь-який новий шлях. Хоча BeginPath слід використовувати лише під час малювання ліній, а не на закритому шляхуrect().

Це причина, чому прийнята відповідь не вирішила моєї проблеми, і я закінчив витрачати години на спроби різних хак. Проклинаю мозилу


Я думаю, що це правильна відповідь, особливо для малювання полотна. Я постраждав від залишився контексту. Удар, як би я не називав clearRect, в той час як beginPath допоміг!
Шао-Куй

20

Використовуйте метод clearRect, передаючи координати x, y та висоту та ширину полотна. ClearRect очистить цілі полотна як:

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

Ви можете просто вкласти це в метод і викликати його кожен раз, коли ви хочете оновити екран, виправити.

@ XXX.xxx plz перевірити ідентифікатор полотна у вашому html та використати той самий для першого рядка (щоб отримати елемент за ідентифікатором)
Vishwas

14

тут є багато хороших відповідей. Ще одна зауваження полягає в тому, що іноді цікаво лише частково очистити полотно. тобто "згасає" попереднє зображення, а не видаляти його повністю. це може дати приємні сліди.

це легко. припустимо, що ваш колір тла білий:

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

Я знаю, що це можливо, і я не маю проблем з вашою відповіддю, я просто цікавлюсь, якщо у вас є два об'єкти, які рухаються, і ви хочете простежити лише один з них, як би ви це зробили?
супер

це набагато складніше. у такому випадку слід створити позаекранне полотно, і кожен кадр намалювати об'єкти, які ви хочете зафіксувати на цьому полотні, використовуючи описаний тут метод часткового стирання, а також кожен кадр очистити основне полотно на 100%, намалюйте непричесане об'єктів, а потім складіть полотно поза екраном на основне, використовуючи drawImage (). Вам також потрібно буде встановити globalCompositeOperation на щось відповідне для зображень. наприклад, "розмножувати" було б добре для темних предметів на світлому фоні.
orion elenzil

12

Швидкий шлях - це зробити

canvas.width = canvas.width

Айк, як це працює, але це робить!


Ого. Це справді працює, як ти це коли-небудь знайшов?
zipzit

@zipit Швидкий трюк, який я виявив у середовищі medium.com після нормального очищення, не виводився :)
Jacob Morris

1
Я витягаю волосся, намагаючись зрозуміти, чому це працює! Дякую, що поділились!
aabbccsmith

Чи може хто-небудь пояснити, чому це працює, і навіть canvas.width + = 0 виконує також роботу, що за це чудова наука?
PYK

8

Це те, що я використовую, незалежно від меж та перетворень матриці:

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

В основному, він зберігає поточний стан контексту і малює прозорий піксель з copyа globalCompositeOperation. Потім відновлює попередній стан контексту.


це працює для мене ...! Дякую.
NomanJaved

6

Це працювало для моєї pieChart в 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

Я виявив, що в усіх браузерах, які я тестую, найшвидший спосіб - це фактично заповнитиRect білим або будь-яким кольором, який ви хочете. У мене дуже великий монітор, і в повноекранному режимі clearRect агонізує повільно, але fillRect є розумним.

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

Недолік - те, що полотно більше не прозоре.


4

у webkit потрібно встановити ширину на інше значення, тоді ви можете встановити її до початкового значення


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

Це повільніше, ніж clearRect (0,0, canvas.width, canvas.height)
sEver

4

Простий, але не дуже читаний спосіб - це написати:

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

// after doing some rendering

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

2

Я завжди користуюся

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

це найпростіший спосіб! Ну, принаймні, для мене.
Жак Олів'є

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

заповніть заданий прямокутник значеннями RGBA:
0 0 0 0: за допомогою Chrome
0 0 0 255: FF і Safari

Але

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

нехай прямокутник заповнюється
0 0 0 255
незалежно від браузера!


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

Приклад: context.clearRect(0, 0, canvas.width, canvas.height);



0

найшвидший спосіб:

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
ух ... У якому браузері? У моєму (Chrome 22 та Firefox 14 на OS X) ваш метод, безумовно, найповільніший варіант. jsperf.com/canvas-clear-speed/9
Prestaul

1
> 5 років у майбутньому це все ще найповільніший метод дуже великою кількістю, ~ 700 разів повільніше, ніж clearRect.
mgthomas99

0

Якщо ви використовуєте тільки clearRect, якщо у вас є форма для подання малюнка, ви отримаєте подання замість поля очищення, або, можливо, він може бути очищений спочатку, а потім завантажити недійсний малюнок, тому вам потрібно буде додати prevenDefault під час початку функції:

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

Сподіваюся, це комусь допоможе.


0

Я завжди цим користуюся

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

ПРИМІТКА

поєднання clearRect і requestAnimationFrame дозволяє отримати більш флюїдну анімацію, якщо саме так ви збираєтеся


-2

Це все чудові приклади того, як ви очищаєте стандартне полотно, але якщо ви використовуєте paperjs, це спрацює:

Визначте глобальну змінну в JavaScript:

var clearCanvas = false;

З вашого PaperScript визначте:

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

Тепер, де ви встановите clearCanvas на істинне, воно очистить усі елементи з екрана.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.