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


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.