Как нарисовать сегмент пончика с холстом HTML5? - PullRequest
19 голосов
/ 07 ноября 2011

Как гласит заголовок. Возможно ли это?

Редактировать: Когда я говорю пончик, я имею в виду топ, 2D вид

Является ли единственной возможностью нарисовать сегмент круга, а затем нарисовать сегмент меньшего круга с тем же началом и меньшим радиусом сверху, с цветом фона? Это было бы дерьмо, если так: (

Ответы [ 6 ]

29 голосов
/ 07 ноября 2011

Вы делаете это путем создания одного пути с двумя дугами.

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

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

Вот код для пончика:

var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');

// Pay attention to my last argument!
//ctx.arc(x,y,radius,startAngle,endAngle, anticlockwise);  

ctx.beginPath()
ctx.arc(100,100,100,0,Math.PI*2, false); // outer (filled)
ctx.arc(100,100,55,0,Math.PI*2, true); // inner (unfills it)
ctx.fill();

Пример:

http://jsfiddle.net/Hnw6a/

Рисование только его «сегмента» может быть выполнено путем уменьшения пути (вам может потребоваться использовать Безье вместо дуги) или путем отсеченияобласть, край.Это действительно зависит от того, как именно вы хотите «сегмент»

Вот один пример: http://jsfiddle.net/Hnw6a/8/

// half doughnut
ctx.beginPath()
ctx.arc(100,100,100,0,Math.PI, false); // outer (filled)
ctx.arc(100,100,55,Math.PI,Math.PI*2, true); // outer (unfills it)
ctx.fill();
3 голосов
/ 08 ноября 2011

Вы можете сделать «пончик сверху» (круг с полым центром), поглаживая дугу.Вы можете увидеть пример этого здесь: http://phrogz.net/tmp/connections.html

enter image description here

Круги (с кончиком) нарисованы линиями 239-245:

ctx.lineWidth = half*0.2;          // set a nice fat line width
var r = half*0.65;                 // calculate the radius
ctx.arc(0,0,r,0,Math.PI*2,false);  // create the circle part of the path
// ... some commands for the nib
ctx.stroke();                      // actually draw the path
2 голосов
/ 28 октября 2016

Да, я понимаю, сколько лет этому вопросу:)

Вот мои два цента:

(function(){
 var annulus = function(centerX, centerY,
                       innerRadius, outerRadius,
                       startAngle, endAngle,
                       anticlockwise) {
  var th1 = startAngle*Math.PI/180;
  var th2 = endAngle*Math.PI/180;
  var startOfOuterArcX = outerRadius*Math.cos(th2) + centerX;
  var startOfOuterArcY = outerRadius*Math.sin(th2) + centerY;

  this.beginPath();
  this.arc(centerX, centerY, innerRadius, th1, th2, anticlockwise);
  this.lineTo(startOfOuterArcX, startOfOuterArcY);
  this.arc(centerX, centerY, outerRadius, th2, th1, !anticlockwise);
  this.closePath();
 }
 CanvasRenderingContext2D.prototype.annulus = annulus;
})();

Который добавит функцию "annulus ()", аналогичную "arc ()" в прототипе CanvasRenderingContext2D. Создание замкнутого пути пригодится, если вы хотите проверить наличие точек.

С помощью этой функции вы можете сделать что-то вроде:

var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");
ctx.annulus(0, 0, 100, 200, 15, 45);
ctx.fill();

Или проверьте это: https://jsfiddle.net/rj2r0k1z/10/

Спасибо!

1 голос
/ 26 мая 2014

Учитывая требования, то, что говорит @SimonSarris, решает проблему.Но допустим, что вы похожи на меня, и вместо этого вы хотите «очистить» часть фигуры, которая может частично выходить за границы очищаемой фигуры.Если у вас есть это требование, его решение не даст вам того, чего вы хотите.Это будет выглядеть как «xor» на изображении ниже.

enter image description here

Решение состоит в том, чтобы использовать 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
1 голос
/ 07 ноября 2011

С WebGL (один из контекстов холста HTML5) это возможно. Есть даже некоторые библиотеки JS для браузеров, которые еще не поддерживают / не реализуют его - посмотрите эти ссылки:

0 голосов
/ 19 декабря 2016

Адаптация / упрощение ответа @Simon Sarris для простой работы с любым углом дает следующее:

Чтобы создать сегмент дуги, вы рисуете внешнюю дугу (из n радианов) в одном направлении, а затем противоположную дугу (одинакового числа радиан) на меньшем радиусе и заполните полученную площадь.

var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
 
var angle = (Math.PI*2)/8;

var outer_arc_radius = 100;
var inner_arc_radius = 50.;

ctx.beginPath()

//ctx.arc(x,y,radius,startAngle,endAngle, anticlockwise); 
ctx.arc(100,100,outer_arc_radius,0,angle, false); // outer (filled)
// the tip of the "pen is now at 0,100
ctx.arc(100,100,inner_arc_radius,angle,0, true); // outer (unfills it)
ctx.fill();
<canvas id="canvas1" width="200" height="200"></canvas>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...