"Как очистить холст для перерисовки"
Тема: Как удалить изображения и композиции с холста в JavaScript?
Я экспериментировал с композиционными операциями и рисованием изображений на холсте, и теперь мне нужно убрать изображения и композиции. Как мне это сделать?
Мне нужно очистить холст для перерисовки других изображений; это может продолжаться длительное время, поэтому я не думаю, что рисовать новый прямоугольник каждый раз будет самым эффективным вариантом. Каковы возможные подходы для очистки холста, чтобы потом можно было заново рисовать без потерь в производительности?
5 ответ(ов)
Используйте: context.clearRect(0, 0, canvas.width, canvas.height);
Это самый быстрый и описательный способ очистить весь холст.
Не используйте: canvas.width = canvas.width;
Сброс размера canvas.width
приводит к сбросу всех свойств холста (например, трансформаций, lineWidth, strokeStyle и т.д.), это довольно медленно (по сравнению с clearRect
), может работать не во всех браузерах и не описывает вашу истинную цель.
Работа с трансформированными координатами
Если вы изменяли матрицу трансформации (например, с помощью scale
, rotate
или translate
), то context.clearRect(0,0,canvas.width,canvas.height)
скорее всего не очистит всю видимую часть холста.
Решение? Сбросьте матрицу трансформации перед очисткой холста:
// Сохраняем текущую матрицу трансформации
context.save();
// Используем единичную матрицу при очистке холста
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);
// Восстанавливаем трансформацию
context.restore();
Правка: Я только что провёл некоторые замеры производительности и (в Chrome) очистка холста размером 300x150 (размер по умолчанию) без сброса трансформации оказывается примерно на 10% быстрее. С увеличением размера холста это различие уменьшается.
Хотя это уже относительно незначительно, в большинстве случаев вы будете рисовать гораздо больше, чем очищать, и я считаю, что это различие в производительности не имеет значения.
100000 итераций усреднено за 10 раз:
1885ms на очистку
2112ms на сброс и очистку
Если вы рисуете линии, не забудьте вызвать:
context.beginPath();
В противном случае линии не будут очищены.
Другие уже отлично ответили на этот вопрос, но если вам будет полезен простой метод 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');
// делаем некоторые рисунки
context.clear();
// продолжаем рисовать
context.setTransform(-1, 0, 0, 1, 200, 200);
// делаем рисунок с новым преобразованием
context.clear(true);
// продолжаем рисовать, сохраняя преобразование
};
Если у вас есть дополнительные вопросы или нужна помощь, не стесняйтесь спрашивать!
В 2018 году все еще нет нативного метода для полного очищения.canvas перед перерисовкой. clearRect()
не полностью очищает холст — не залитые элементы (например, rect()
) не удаляются.
- Чтобы полностью очистить холст, независимо от способа рисования:
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.beginPath();
Плюсы: Сохраняет настройки strokeStyle
, fillStyle
и т.д.; Не вызывает задержек.
Минусы: Не требуется, если вы уже используете beginPath()
перед рисованием чего-либо.
- Использование хакa с шириной/высотой:
context.canvas.width = context.canvas.width;
ИЛИ
context.canvas.height = context.canvas.height;
Плюсы: Работает с IE.
Минусы: Сбрасывает strokeStyle
, fillStyle
на черный; Вызывает задержки.
Я задавался вопросом, почему не существует нативного решения. На самом деле, clearRect()
считается простым решением, поскольку большинство пользователей вызывают beginPath()
перед рисованием нового пути. Однако beginPath()
предназначен только для рисования линий, а не закрытых фигур, таких как rect()
.
Вот почему принятый ответ не решил моей проблемы, и я потратил часы, пробуя различные хаки. Черт тебя, Mozilla!
Проблема, с которой вы столкнулись, связана с различиями в поведении графических контекстов в разных браузерах. В Chrome команда context.clearRect(x, y, w, h);
работает корректно для очистки области на холсте. Однако в IE9 вы можете заметить, что данная команда может не работать должным образом.
Для IE9 можно использовать подход, при котором вы сбрасываете ширину холста. Это немного нестандартный метод, но он действительно помогает. Вы правы, что изменение ширины холста (canvas.width = canvas.width;
) очищает только определенные элементы и может не удалять линии, если изменять ширину холста не в паре с clearRect
.
Вот пример функции, которая должна работать на всех браузерах, включая IE9:
function clearCanvas(context, canvas) {
context.clearRect(0, 0, canvas.width, canvas.height); // Сначала очищаем холст
var w = canvas.width; // Сохраняем текущую ширину
canvas.width = 1; // Меняем ширину на 1
canvas.width = w; // Сбрасываем обратно на оригинальную ширину
}
Эта функция сначала очищает холст с помощью clearRect
, а затем меняет ширину, что позволяет IE9 удалить все объекты, включая линии. Вызывая clearCanvas(context, canvas)
, вы можете уверенно очищать холст во всех основных браузерах.
Как определить, виден ли элемент DOM в текущей области просмотра?
Как выбрать элемент по имени с помощью jQuery?
jQuery: Получение Выбранного Значения Из Выпадающего Списка
Прокрутка до низа div?
Как получить фактическую ширину и высоту HTML-элемента?