Search code examples
javascripthtmlcanvas

Alternating ctx.strokeStyle while using requestAnimationFrame()


I'm trying to draw simple random motion as my landing page's background canvas, and I'm having a hard time getting the lines to have unique colors.

I've tried changing it after the moveTo as well, but didnt have success (just appeared to be a mix of the colors).

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
//ctx.canvas.width  = window.innerWidth;
//ctx.canvas.height = window.innerHeight*4;

const numSteps = ctx.canvas.width / 2 + 10;
const stepSize = 10;
const startX = 0;
const startY = canvas.height / 2;
const timeInterval = 15;

var Point = function(color_inp){
  this.x = startX;
  this.y = startY;
  this.color = color_inp;
}

const colors = [
  '#FF1493', // Pink
  '#FF00FF', // Magenta
  '#800080', // Purple
  '#4B0082', // Indigo
  '#0000FF', // Blue
  '#00FFFF', // Cyan
  '#00FF00', // Green
  '#FFFF00', // Yellow
  '#FF7F00', // Orange
  '#8B4513'  // Saddle Brown
];



function drawRandomWalk(stPoint, steps) {
  ctx.globalAlpha = 0.01;
  let stepCount = 0;
  ctx.beginPath();
  ctx.strokeStyle = stPoint.color;
  ctx.moveTo(stPoint.x, stPoint.y);
  setTimeout(drawStep, 10);
  
  function drawStep() {
    if (stepCount >= steps) {
      return;
    }

    ctx.moveTo(stPoint.x, stPoint.y);
    const direction = Math.random() < 0.5 ? -1 : 1;
    stPoint.y += stepSize * direction;
    stPoint.x = startX + stepCount * 2; 
    ctx.lineTo(stPoint.x, stPoint.y);
    ctx.lineWidth = 1;
    ctx.stroke();
    stepCount++;
    requestAnimationFrame(drawStep);
  }
}

for(const color of colors)
{
  const startPoint = new Point(color);
  drawRandomWalk(startPoint, numSteps);
}
<canvas id="myCanvas" width="600" height="600"></canvas>


Solution

  • The issue is each of the "pens" drawing the lines are sharing state – you're drawing a single long path that consists of many movetos/linetos over and over and again.

    If you want the globalAlpha fade effect (which relies on being able to draw the line over and over again), you'll need to separately keep track of the trail drawn by each pen, and draw it, like below.

    I got rid of the Point structure, since it's not really needed.

    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    
    const numSteps = ctx.canvas.width / 2 + 10;
    const stepSize = 10;
    const startX = 0;
    const startY = canvas.height / 2;
    const timeInterval = 15;
    
    const colors = [
      '#FF1493', // Pink
      '#FF00FF', // Magenta
      '#800080', // Purple
      '#4B0082', // Indigo
      '#0000FF', // Blue
      '#00FFFF', // Cyan
      '#00FF00', // Green
      '#FFFF00', // Yellow
      '#FF7F00', // Orange
      '#8B4513'  // Saddle Brown
    ];
    
    
    
    function drawRandomWalk(startX, startY, color, nSteps) {
      setTimeout(drawStep, 10);
      const steps = [[startX, startY]];
      
      function drawStep() {
        if (steps.length >= nSteps) {
          return;
        }
        // Draw current line
        ctx.globalAlpha = 0.01;
        ctx.beginPath();
        ctx.strokeStyle = color;
        ctx.lineWidth = 1;
        let x = 0, y = 0;
        for(let i = 0; i < steps.length; i++) {
          [x, y] = steps[i];
          if(i === 0) ctx.moveTo(x, y);
          else ctx.lineTo(x, y);
        }
        ctx.stroke();
        // Compute next point
        const direction = Math.random() < 0.5 ? -1 : 1;
        y += stepSize * direction;
        x = startX + steps.length * 2; 
        steps.push([x, y]);
        requestAnimationFrame(drawStep);
      }
    }
    
    for(const color of colors)
    {
      drawRandomWalk(startX, startY, color, numSteps);
    }
    <canvas id="myCanvas" width="600" height="600"></canvas>