Changing the stroke color in between drawing on an HTML5 Canvas

I have been experimenting with a basic JavaScript snippet to showcase an unusual HTML5 canvas behavior I've encountered.

Every 100ms, I am drawing the same set of strokes but in a different sequence. Strangely, some of the strokes change color intermittently. This oddity occurs only when I shuffle the order of drawing between calls, even though the lines are consistently drawn in the same position and color each frame.

const canvasWidth = 500;
const gapBetweenLines = 5;
const nbrLines = canvasWidth / gapBetweenLines;
const canvasHeight = 500;

const canvas = document.getElementById('map');
canvas.width = canvasWidth;
canvas.height = canvasHeight;

// generate an array of line objects, each assigned a random color
let lines = [];
for (let i = 0; i < nbrLines; i++) {
  lines.push({
    index: i,
    x: i * gapBetweenLines,
    color: '#' + Math.floor(Math.random() * 16777215).toString(16)
  });
}

// utility function to shuffle the given array
function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
}

// draw lines on the canvas periodically using random colors
function drawLines() {
  const shuffledLines = [...lines];
  shuffle(shuffledLines);

  let ctx = canvas.getContext('2d');
  for (let i = 0; i < nbrLines; i++) {
    const line = shuffledLines[i];
    ctx.strokeStyle = line.color;
    // ctx.save();
    ctx.beginPath();
    ctx.moveTo(line.x, 0);
    ctx.lineTo(line.x, canvasHeight);
    ctx.stroke();
    // ctx.restore();
  }
}

// invoke the drawLines function every 100ms
setInterval(drawLines, 100);
<!DOCTYPE html>
<html>
  <body>
    <h1>Fluctuating Lines</h1>
    <canvas id="map"></canvas>
    <div id="lineinfo"></div>
  </body>
</html>

In the last day, I've dedicated quite a bit of time trying to pinpoint the root cause of this issue. Is there something fundamental about HTML5 Canvas drawing that eludes me?

Interestingly, preserving and restoring the context between each stroke yields no discernible difference.

Answer №1

It seems like the issue lies in the way you are generating colors.

color: '#' + Math.floor(Math.random() * 16777215).toString(16)

The problem here is that Number#toString(16) doesn't add leading zeroes to the generated string:

console.log(12..toString(16)) // "c", not "0C"

This could result in some lines having invalid HEX values assigned to their color property (like a five or two-character HEX code).

To solve this, you can ensure that your color generator always produces a six-digit HEX value by padding with zeroes using the String#padStart() method.

const canvasWidth = 500;
const gapBetweenLines = 5;
const nbrLines = canvasWidth / gapBetweenLines;
const canvasHeight = 500;

const canvas = document.getElementById('map');
canvas.width = canvasWidth;
canvas.height = canvasHeight;

// create an array of line objects, each with a randomly generated color
let lines = [];
for (let i = 0; i < nbrLines; i++) {
  lines.push({
    index: i,
    x: i * gapBetweenLines,
    color: '#' + Math.floor(Math.random() * 16777215).toString(16)
      .padStart(6, "0")
  });
}

// function to shuffle the given array in place
function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
}

// draw lines on the canvas at specific intervals with the random colors
function drawLines() {
  const shuffledLines = [...lines];
  shuffle(shuffledLines);

  let ctx = canvas.getContext('2d');
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for (let i = 0; i < nbrLines; i++) {
    const line = shuffledLines[i];
    ctx.strokeStyle = line.color;
    ctx.beginPath();
    ctx.moveTo(line.x, 0);
    ctx.lineTo(line.x, canvasHeight);
    ctx.stroke();
  }
}

// call the drawLines function every 100ms
setInterval(drawLines, 100);
<!DOCTYPE html>
<html>
  <body>
    <h1>Flickering Lines</h1>
    <canvas id="map"></canvas>
    <div id="lineinfo"></div>
  </body>
</html>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing ReactJS to make an Ajax request

I am trying to transfer data to individuals and then display it using the array map function. export default class App extends Component { constructor(props) { super(props); this.state = { persons: [], }; } componentDidMount() { $ ...

Issue TS7053 occurs when trying to access any index of the target of a React.FormEvent<HTMLFormElement>

I've been working on adapting this tutorial to React and TypeScript. Here is the code snippet I have implemented for handling the onSubmit event: const handleSignUp = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); ...

Does the entire state get replaced every time a change is made if the state is immutable?

Is it necessary to replace the entire state if it is immutable? Otherwise, wouldn't mutating the state occur? Are top-level keys maintained as distinct immutable objects? Wouldn't changing anything necessitate replacing the entire state by defin ...

Link rows to dictionary keys and show their corresponding values

In my React component, I have a list of dictionaries stored in the props as follows: console.log(fruits): [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…} ] The dictionary entries are: 0: name: 'Apple' color: 'Red&apos ...

OpenStreetMap is failing to display the full map within the designated div

Here is the code snippet for displaying a map when clicking on a Span text, but not showing it in full: var latitude = document.querySelector('#lati').value; var longitude = document.querySelector('#longi').value; var open_address = doc ...

Exploring the capabilities of a Vue.js component

I am currently facing some challenges while trying to test a Vue.js component. My main issue lies in setting a property for the component and verifying that it has been set correctly. For context, the module has been loaded with exports and the JavaScrip ...

React Nextjs implementation of a fixed navigation bar to stick at the top

Hello, I am experiencing some issues setting up a sticky navbar in Next.js. The current navbar doesn't seem to be functioning as expected. Is there anyone who can assist me with the code provided below? import React, { useEffect } from 'react&apo ...

Performing a JavaScript AJAX request to send a complex object containing an array of other complex objects within it

My issue arises from encountering an empty array of objects at the backend. To illustrate, I have established two classes at the backend... public class ScoreModel { public string Subject { get; set; } public float Score { get; set; } ...

Cannot assign border to an undefined element - JavaScript

Currently, I am attempting to validate some code using javascript. However, I am encountering a frustrating issue where I keep getting an "Uncaught TypeError: Cannot set property 'border' of undefined". Being relatively new to javascript, I ...

The Integration of Google Books API with Ajax Technology

When a user enters an ISBN number in the search box, it triggers a display of information from the Google Books API within a div. This information is fetched from a JSON file in the standard format and includes details like title, subtitle, author, and des ...

Frontend experiencing issues with Laravel Echo Listener functionality

I have recently developed a new event: <?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; use Illuminate&bs ...

How to properly handle sending an empty post request in Angular 2

My current issue revolves around attempting to send data from my application to the server using a POST request, however, the server seems to be receiving empty POST data. This is the function responsible for sending the data: private headers = new Heade ...

Reduce the use of if statements

Is there a way to optimize this function by reducing the number of if statements? The currentFeatures are determined by a slider in another file. The cost is updated if the currentFeatures do not match the previousFeatures, and if the user changes it back ...

Showing options in the navigation bar upon user authentication with Passport in NodeJS

When a user is connected, I want the navbar to display "Profile of: {USER}", otherwise it should show a set of Sign up/Login tabs. The challenge lies in using EJS with separate "head.ejs" and "header.ejs" sections placed in a /partials folder within the / ...

transferring information from child to parent with the help of Vue.js and Laravel

As a newcomer to vue.js, I have a child component called 'test' and a parent component called 'showdata'. My issue arises when I try to emit data from the child to the parent - while the emission is successful, displaying the data in th ...

Displaying an array of objects in the MUI Datagrid interface

I have integrated Redux into my project to retrieve data from the API, and this is a snapshot of the data structure: https://i.stack.imgur.com/jMjUF.png My current challenge lies in finding an effective way to display the information stored within the &a ...

The three.js raycaster is able to detect objects both in front of and behind the specific object I am trying to select

I am currently working on rendering a 3D model of a house using Three.js and encountering an issue with the raycaster in my code. The problem I'm facing is that it's selecting every object both behind and in front of the specific object I want to ...

Utilize the Bootstrap responsive grid system to ensure that every row is filled with content, creating

I am working with a list of 8 results that I display in a responsive bootstrap grid. However, I want to only show the results that fill up entire rows. For instance, on a medium-sized screen, it appears as 2 rows with 4 results each. On a smaller screen, ...

How to efficiently transfer dynamically generated table row data to JavaScript for database submission using the POST method

Currently, I am working with Laravel and facing an issue related to dynamic rows in my form. Each row has its own form with unique values. My goal is to pass the form data along with values to JavaScript when the submit button is clicked without refreshin ...

Convert the text inside a span element into a key-value object in JavaScript

How can I extract and save the text from a span element as key value pairs within a div? <div class="data-diff-span__composite-list-item__18c5zip data-diff-core__highlight-area__19c0zip"> <div class="data-diff-basic__class-row__4n ...