очистка круговых областей из HTML5 Canvas

Похоже, что единственный способ очистить область от холста - использовать команду clearRect () - мне нужно очистить круг (я маскирую области на заполненном холсте, в этом конкретном случае указываю источники света) и, несмотря на все попытки это не представляется возможным.

Я пытался нарисовать круг со значением альфа 0, но просто ничего не появилось бы, если бы альфа не была выше (что противоречит точке: P) - я предполагаю, потому что contex.fill () рисует его как дополнение, а не как заменить.

Есть предложения о том, как можно (быстро) очистить круги для маскировки?

 Simon Sarris01 мая 2012 г., 17:49
Здесь есть два хороших ответа, но я хотел бы увидеть скриншот того, чего вы хотите достичь, на случай, если есть более эффективный и умный способ выполнить ту же задачу
 Simon Sarris01 мая 2012 г., 19:34
Благодарность. Я ответил с небольшим количеством информации, которая может быть полезна в зависимости от того, что вы делаете
 David Burford01 мая 2012 г., 19:00
Я принял ответ clip (), но только для того, чтобы удовлетворить ваше любопытство (и, возможно, у вас есть лучшая техника!), вот что я делаю: я рендеринг 2D точечных источников света в буфер. Я отрисовываю этот буфер поверх моей игры, используя стиль Lighten, он дает отличный эффект! Но я также хотел затемнить все, что не освещено, до амбиентного значения - это не рисует с Lighten, поэтому это должно быть отдельное рисование. Однако рендеринг отдельного окружающего буфера затемняет источники света, которые я не хотел, поэтому я хочу «замаскировать» освещенные области в моем окружающем буфере:)

Ответы на вопрос(6)

y = правая позиция, r = радиус и ctx = ваш холст:

function clearCircle( x , y , r ){
    for( var i = 0 ; i < Math.round( Math.PI * r ) ; i++ ){
        var angle = ( i / Math.round( Math.PI * r )) * 360;
        ctx.clearRect( x , y , Math.sin( angle * ( Math.PI / 180 )) * r , Math.cos( angle * ( Math.PI / 180 )) * r );
    }
}
 user15149607 мая 2015 г., 17:09
Sloooowwwwwwwwww

эти ответы в порядке. Но допустим, что вы похожи на меня и у вас есть дополнительные требования:

Вы хотите "очистить" часть фигуры, которая может частично выходить за границы очищаемой фигуры. Вы хотите видеть фон под формой, а не очищать фон.

Для первого требования, решение заключается в использованииcontext.globalCompositeOperation = 'destination-out' Синий - первая фигура, а красный - вторая фигура. Как вы видете,destination-out удаляет раздел из первой фигуры.

Вот пример кода:

  explosionCanvasCtx.fillStyle = "red"
  drawCircle(explosionCanvasCtx, projectile.radius, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()

  explosionCanvasCtx.globalCompositeOperation = 'destination-out' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
  drawCircle(explosionCanvasCtx, projectile.radius + 20, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()

Вот потенциальная проблема с этим: Второйfill() очиститвс под ним, включая фон. Иногда вам нужно очистить только первую фигуру, но вы все равно хотите видеть слои, которые находятся под ней.

Решение этой проблемы - нарисовать это на временном холсте, а затемdrawImage чтобы нарисовать временное полотно на вашем основном полотне. Код будет выглядеть так:

  diameter = projectile.radius * 2
  console.log "<canvas width='" + diameter + "' height='" + diameter + "'></canvas>"
  explosionCanvas = $("<canvas width='" + diameter + "' height='" + diameter + "'></canvas>")
  explosionCanvasCtx = explosionCanvas[0].getContext("2d")

  explosionCanvasCtx.fillStyle = "red"
  drawCircle(explosionCanvasCtx, projectile.radius, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()

  explosionCanvasCtx.globalCompositeOperation = 'destination-out' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
  durationPercent = (projectile.startDuration - projectile.duration) / projectile.startDuration
  drawCircle(explosionCanvasCtx, projectile.radius + 20, projectile.radius, projectile.radius)
  explosionCanvasCtx.fill()
  explosionCanvasCtx.globalCompositeOperation = 'source-over' #see https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html

  ctx.drawImage(explosionCanvas[0], projectile.pos.x - projectile.radius, projectile.pos.y - projectile.radius) #center
Решение Вопроса

.arc, чтобы создать круговой ход, а затем использовать.clip() чтобы сделать это текущим регионом отсечения.

Тогда ты можешь использовать.clearRect(), чтобы стереть весь холст, но изменится только область обрезки.

 David Burford01 мая 2012 г., 18:56
никогда не справился бы с этим самостоятельно - спасибо, хорошее решение! Для очистки всех видов странных фигур также!
 Viesturs24 февр. 2014 г., 11:17
Обратите внимание, что многие мобильные браузеры поддерживают только прямоугольные области отсечения.

где важна производительность, посмотрите, как я сделал этот ответ:Canvas - заполнить прямоугольник во всех областях, которые полностью прозрачны

В частности, редактирование ответа, которое приводит к этому:http: //jsfiddle.net/a2Age/2

Огромные плюсы здесь:

Не использовать пути (медленно) Не использовать клипы (медленно) Нет необходимости в сохранении / восстановлении (поскольку нет способа сбросить область отсечения без очистки всех состояний (1), это означает, что вы также должны использовать функцию сохранения / восстановления)

(1) Я на самом деле жаловался на это и resetClip () были добавлены в официальную спецификацию из-за этого, но пройдет некоторое время, прежде чем браузеры его реализуют.

Ко

var ctx          = document.getElementById('canvas1').getContext('2d'),
    ambientLight = 0.1,
    intensity    = 1,
    radius       = 100,
    amb          = 'rgba(0,0,0,' + (1 - ambientLight) + ')';

addLight(ctx, intensity, amb, 200, 200, 0, 200, 200, radius); // First circle
addLight(ctx, intensity, amb, 250, 270, 0, 250, 270, radius); // Second circle
addLight(ctx, intensity, amb, 50, 370, 0, 50, 370, radius, 50); // Third!

ctx.fillStyle = amb;
ctx.globalCompositeOperation = 'xor';
ctx.fillRect(0, 0, 500, 500);

function addLight(ctx, intsy, amb, xStart, yStart, rStart, xEnd, yEnd, rEnd, xOff, yOff) {
  xOff = xOff || 0;
  yOff = yOff || 0;

  var g = ctx.createRadialGradient(xStart, yStart, rStart, xEnd, yEnd, rEnd);
  g.addColorStop(1, 'rgba(0,0,0,' + (1 - intsy) + ')');
  g.addColorStop(0, amb);
  ctx.fillStyle = g;
  ctx.fillRect(xStart - rEnd + xOff, yStart - rEnd + yOff, xEnd + rEnd, yEnd + rEnd);
}
canvas {
  border: 1px solid black;
  background-image: url('http://placekitten.com/500/500');
}
<canvas id="canvas1" width="500" height="500"></canvas>

 Jared Forsyth10 февр. 2017 г., 18:16
Также Огромный Плюс в том, что вы можете делать радиальные градиенты!
 David Burford01 мая 2012 г., 19:48
эй, там какой-то бриллиантовый код! Я не выбираю, это мой ответ, потому что предыдущий ответ прямо отвечает на вопрос, который я задал (поэтому он более полезен для людей, которые ищут то же самое по другим причинам), но ваша ссылка может быть непосредственно применена к тому, что я делал, как, вероятно, лучше путь! Я бы проголосовал, но у меня явно недостаточно стэка, xD Так что моя сердечная благодарность должна будет сделать:)
 ellisbben03 мая 2012 г., 20:24
Хорошая работа по добавлению resetClip ()!

Во-первых, вот функция, которую мы будем использовать для заполнения круга.

var fillCircle = function(x, y, radius)
{
    context.beginPath();
    context.arc(x, y, radius, 0, 2 * Math.PI, false);
    context.fill();
};
clip()
var clearCircle = function(x, y, radius)
{
    context.beginPath();
    context.arc(x, y, radius, 0, 2 * Math.PI, false);
    context.clip();
    context.clearRect(x - radius - 1, y - radius - 1,
                      radius * 2 + 2, radius * 2 + 2);
};

Смотри это на JsFiddle.

globalCompositeOperation
var clearCircle = function(x, y, radius)
{
    context.save();
    context.globalCompositeOperation = 'destination-out';
    context.beginPath();
    context.arc(x, y, radius, 0, 2 * Math.PI, false);
    context.fill();
    context.restore();
};

Смотри это на JsFiddle.

Both дал желаемый результат на экране, однако в моем случае производительность была недостаточной, поскольку я рисовал и очищал множество кругов в каждом кадре для получения эффекта. В конце концов я нашел другой способ получить эффект, аналогичный тому, который я хотел, просто нарисовав более толстые линии на дуге, но вышеприведенное может все же быть полезным для тех, у кого другие требования к производительности.

 Rui Marques29 мая 2013 г., 12:54
Круто, интересно, какой из них быстрее.
 Daniel Kaplan25 мая 2014 г., 22:34
Проблема в том, что он также очищает фон. Смотрите мое обновление к вашему примеру: Jsfiddle.net / WWH8J / 22 Я опубликую свое решение этой проблемы

Используйтеcanvas.getContext("2d").arc(...) нарисовать круг над областью с цветом фона?

var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
context.arc(x, y, r, 0, 2*Math.PI, false);
context.fillStyle = "#FFFFFF";
context.fill();
 David Burford01 мая 2012 г., 19:07
спасибо за то, что вы нашли время ответить :) к сожалению, в этом случае фон не является непрозрачным, поэтому не даст результата, к которому я стремился.
 Simon Sarris01 мая 2012 г., 17:47
По соображениям производительности, это то, что нужно сделать вместо клипа, если фон непрозрачный, но, к сожалению, это маловероятно. Это не будет работать, если фон имеет какую-либо прозрачность.

Ваш ответ на вопрос