Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

275
Visualizações
Use canvas to draw a pill shaped segmented line, draw a thick line with rounded corners

I'm working on a snake game in JavaScript and the Phaser3 framework, and I want to draw a pill shaped line to use for the snake. So I want to draw rounded line segments filled with one color (green) and outlined with another color (black). It is not one straight line, but rather several connected line segments. They are all connected at right angles, so 0, 90, 180 or 270 degrees.

See the mock up image below.

enter image description here

Here is my code to draw a line, there is an array with the grid locations of line segments positions:

    var TILE_SIZE = 32;
    var c = document.getElementById("myCanvas");
    var ctx = c.getContext("2d");
    
    // points of each snake segment
    snake_segs = [ [9, 11], [10, 11], [10, 12], [11, 12], [11, 13], [12, 13], [12, 12] ];
    
    // set line color
    ctx.strokeStyle = "#FFFF00";
    ctx.beginPath();

    // draw all line segments
    for (var i = 0; i < snake_segs.length-1; i++) {
        var xpos1 = snake_segs[i][0] * TILE_SIZE;
        var ypos1 = snake_segs[i][1] * TILE_SIZE;
        
        var xpos2 = snake_segs[i+1][0] * TILE_SIZE;
        var ypos2 = snake_segs[i+1][1] * TILE_SIZE;
        
        ctx.moveTo(xpos1, ypos1);
        ctx.lineTo(xpos2, ypos2);
    }
    
    ctx.stroke();
    

How to turn this into a rounded pill shaped line?

What is the best approach to do this? This is harder than I initially thought. Is there an algorithm to draw a pill shaped outline? Or is there a clever way to using rounded rectangles that overlap? Or is it best to draw the outline in a clock-wise fashion? But then how do you find the shape of that outline?

about 4 years ago · Juan Pablo Isaza
2 Respostas
Responde à pergunta

0

Well just using phaser, I would use the phaser graphics object, for a quick and easy solution. It's abit cumbersome, but works.

Demo:
(to optimize the whole drawing[reduce one for - loop], one could add an extra graphics object, and like that one would be able to draw everthing in one for - loop, but I didn't want to make demo too complex)

document.body.style = 'margin:0;';

var config = {
    type: Phaser.AUTO,
    width: 536,
    height: 183,
    scene: {
        create,
        update
    },
    banner: false
}; 

let foregroundGraphics;
let backgroundGraphics;

let snake_segs = [ [1,1], [2,1], [3,1], [3,2], [3,3], [4,3], [5,3]];
let length_seg = 20;
    
function create () {
    backgroundGraphics = this.add.graphics();
    foregroundGraphics = this.add.graphics();
}

function update(){
   drawSnake(0x00a000, 0xffffff, 3);
}

function drawSnake(fgColor, bgColor, padding){
    foregroundGraphics.clear();
    backgroundGraphics.clear();
    
    drawCorners(fgColor, bgColor, padding);
    drawSegments(fgColor, bgColor, padding); 
}

function drawCorners(fgColor, bgColor, padding){
    foregroundGraphics.lineStyle(length_seg/4, fgColor);
    backgroundGraphics.lineStyle((length_seg + padding * 2)/4, bgColor);
    snake_segs.forEach( point => {
      backgroundGraphics.strokeCircle(point[0]  * length_seg, point[1]  * length_seg, (length_seg + padding * 2)/8);
      foregroundGraphics.strokeCircle(point[0]  * length_seg, point[1]  * length_seg, length_seg/8);
    });
}

function drawSegments(fgColor, bgColor, padding){
    backgroundGraphics.beginPath();
    foregroundGraphics.beginPath();
    backgroundGraphics.lineStyle((length_seg + padding * 2)/2, bgColor);
    foregroundGraphics.lineStyle(length_seg /2, fgColor);
   
    for(let idx = 0; idx < snake_segs.length; idx++){
      let point = snake_segs[idx];
      if(idx==0){
        backgroundGraphics.moveTo(point[0] * length_seg, point[1] * length_seg);
        foregroundGraphics.moveTo(point[0] * length_seg, point[1] * length_seg);
      }else {
        backgroundGraphics.lineTo(point[0]  * length_seg, point[1]  * length_seg);
        foregroundGraphics.lineTo(point[0]  * length_seg, point[1]  * length_seg);
        
        }
    }
    backgroundGraphics.strokePath();
    foregroundGraphics.strokePath();
}

new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>

Disclaimer: there might be a better way to solve this, but this was the first that came to mind.

(personally I would try to adapt this example to my needs, since I like experimenting, and until now I never had the chance to use the follower and paths features in phaser. But I'm currently not sure, if this features, could be used for solving this task)

about 4 years ago · Juan Pablo Isaza Relatório

0

You can do this quite easily by first drawing the snake using it's outlinecolor with a line thickness of e.g. 18 pixels and afterwards redraw the same snake with it's body color and a smaller line thickness of say 15 pixels.

Unfortunately this would result in a solid shaped snake with sharp 90° corners. Luckily the CanvasRenderingContext2D has an additional property called lineCap which controls the look of line endpoints. If you set this to 'round' you have your pill-shaped-snake.

Here's an example:

var TILE_SIZE = 32;
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

snake_segs = [
  [9, 11],
  [10, 11],
  [10, 12],
  [11, 12],
  [11, 13],
  [12, 13],
  [12, 12]
];

ctx.fillStyle = "#00a000";
ctx.fillRect(0, 0, c.width, c.height)
ctx.lineCap = "round";

function drawSnake(color, thickness) {
  ctx.beginPath();
  ctx.lineWidth = thickness;
  ctx.strokeStyle = color;
  for (var i = 0; i < snake_segs.length - 1; i++) {
    var xpos1 = snake_segs[i][0] * TILE_SIZE;
    var ypos1 = snake_segs[i][1] * TILE_SIZE;

    var xpos2 = snake_segs[i + 1][0] * TILE_SIZE;
    var ypos2 = snake_segs[i + 1][1] * TILE_SIZE;

    ctx.moveTo(xpos1, ypos1);
    ctx.lineTo(xpos2, ypos2);
  }
  ctx.moveTo(xpos2, ypos2);
  ctx.closePath();
  ctx.stroke();
}

drawSnake("#ffff00", 18);
drawSnake("#00c000", 15);
<canvas id="myCanvas" width="500" height="500">
  </canvas>

about 4 years ago · Juan Pablo Isaza Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda