When additional lines are drawn elsewhere on the HTML5 Canvas, the diagonal lines will gradually appear thicker and more pronounced

For horizontal and vertical lines, using a translation of 0.5 for odd stroke widths results in crisper and sharper lines. But what about diagonal lines?

Link to jsfiddle

<!DOCTYPE html>
<html lang="en">

<body style="background: black">
    <button id="btn">Draw Next Line</button>
    <br>
    <canvas style="border: 2px solid red" id="cnv"></canvas>
    <script>
        const ctx = document.getElementById("cnv").getContext("2d");

        debugger;

        const delta = 25;
        const color = 'white';

        const W = window.innerWidth - 80;
        const H = window.innerHeight - 100;
        ctx.canvas.width = W;
        ctx.canvas.height = H;

        ctx.lineWidth = 1;
        ctx.strokeStyle = color;

        // diagonal line.
        ctx.moveTo(0.5, 0);
        ctx.lineTo(W, H);


        ctx.stroke();

        // vertical lines
        let i = 0.5;
        document.getElementById("btn").onclick = () => {
            ctx.moveTo(i * delta, 0);
            ctx.lineTo(i * delta, H);
            ctx.stroke();
            i++;
        }


    </script>
</body>

</html>

Observing the demo reveals that adding another line after the diagonal lines causes them to become bolder or thicker. How can we achieve consistent thickness and sharpness regardless of whether the diagonal line is drawn first or last?

Answer №1

Make sure to include the ctx.beginPath(); call before each new line is drawn:

<!DOCTYPE html>
<html lang="en">

<body style="background: black">
    <button id="btn">Draw Next Line</button>
    <br> 
    <canvas style="border: 2px solid red" id="cnv"></canvas>
    <script>
        const ctx = document.getElementById("cnv").getContext("2d");

        debugger;

        const delta = 25;
        const color = 'white';

        const W = window.innerWidth - 80;
        const H = window.innerHeight - 100;
        ctx.canvas.width = W;
        ctx.canvas.height = H;

        ctx.lineWidth = 1;
        ctx.strokeStyle = color;

        // diagonal line.
        ctx.moveTo(0.5, 0);
        ctx.lineTo(W, H);


        ctx.stroke();
        
        // vertical lines
        let i = 0.5;
        document.getElementById("btn").onclick = () => {
            ctx.beginPath(); // Ensure this line is included before drawing a new line
            ctx.moveTo(i * delta, 0);
            ctx.lineTo(i * delta, H);
            ctx.stroke();
            i++;
        }


    </script>
</body>

</html>

Answer №2

To tackle this task, I would begin by conducting some research and then manually drawing a line using pixels along with an online algorithm. It is essential to utilize a special pixel function to ensure that the same effect is not repeated over each individual pixel. Discovering the draw_line function from this source was quite helpful in this process.

var canvas = document.querySelector("canvas");
var ctx = canvas.getContext('2d');
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
ctx.clearRect(0, 0, canvasWidth, canvasHeight);

var r = 255
var g = 255
var b = 255

function pixel(x, y) {
  var id = ctx.createImageData(1, 1); // only do this once per page
  var d = id.data; // only do this once per page
  d[0] = r;
  d[1] = g;
  d[2] = b;
  d[3] = 255;
  ctx.putImageData(id, x, y);
}

let draw_line = (x1, y1, x2, y2) => {
  // Iterators, counters required by algorithm
  let x, y, dx, dy, dx1, dy1, px, py, xe, ye, i;
  // Calculate line deltas
  dx = x2 - x1;
  dy = y2 - y1;
  // Create a positive copy of deltas (makes iterating easier)
  dx1 = Math.abs(dx);
  dy1 = Math.abs(dy);
  // Calculate error intervals for both axis
  px = 2 * dy1 - dx1;
  py = 2 * dx1 - dy1;
  // The line is X-axis dominant
  if (dy1 <= dx1) {
    // Line is drawn left to right
    if (dx >= 0) {
      x = x1;
      y = y1;
      xe = x2;
    } else { // Line is drawn right to left (swap ends)
      x = x2;
      y = y2;
      xe = x1;
    }
    pixel(x, y); // Draw first pixel
    // Rasterize the line
    for (i = 0; x < xe; i++) {
      x = x + 1;
      // Deal with octants...
      if (px < 0) {
        px = px + 2 * dy1;
      } else {
        if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) {
          y = y + 1;
        } else {
          y = y - 1;
        }
        px = px + 2 * (dy1 - dx1);
      }
      // Draw pixel from line span at
      // currently rasterized position
      pixel(x, y);
    }
  } else { // The line is Y-axis dominant
    // Line is drawn bottom to top
    if (dy >= 0) {
      x = x1;
      y = y1;
      ye = y2;
    } else { // Line is drawn top to bottom
      x = x2;
      y = y2;
      ye = y1;
    }
    pixel(x, y); // Draw first pixel
    // Rasterize the line
    for (i = 0; y < ye; i++) {
      y = y + 1;
      // Deal with octants...
      if (py <= 0) {
        py = py + 2 * dx1;
      } else {
        if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) {
          x = x + 1;
        } else {
          x = x - 1;
        }
        py = py + 2 * (dx1 - dy1);
      }
      // Draw pixel from line span at
      // currently rasterized position
      pixel(x, y);
    }
  }
}


for (var i = 0; i < 50; i++) {
  draw_line(0, 0, 50, 100)
}
<canvas></canvas>

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

Adjustable textarea with automatic height based on content and line breaks

Imagine you have a textarea with text that includes line breaks. If you style it like this: textarea { height: auto; overflow: hidden; resize: none; } Upon loading the page, the textarea will only adjust its height based on the content until it enc ...

using ng-show to display array elements

There is a syntax error showing up on the console for the code below, but it still functions as intended. Can someone help identify what I might be missing? <p class="light" data-ng-show="selectedAppType in ['A1','A2','A3' ...

Is there a way in Jest to restrict function calls to only those that are expected and disallow any other unauthorized calls

When working with Jest, you have the ability to mock out and spy on function calls within a function using functionalities like jest.spyOn and jest.fn() with .toHaveBeenCalledTimes(1) etc. However, in Spock framework testing, you can conclude your unit tes ...

tips for sending the chosen product to axios

How can I send the selected item from the dropdown menu to Axios in order to retrieve data? I need to pass the item itself, not just the ID, to the API. <label>City</label> <select @change="getArea()" v-model="key"> <option :valu ...

Integrating Flask with React for a cohesive frontend and backend connection

Recently, I decided to try my hand at setting up a webapp using Flask and React by following a YouTube tutorial. Despite encountering a few issues with virtual environments (which I ultimately resolved by not using one), I managed to closely follow the tut ...

Error: Firebase has encountered a network AuthError, which could be due to a timeout, interrupted connection, or an unreachable host. Please try again later. (auth/network-request-failed

I have set up my Angular app to utilize Firebase's emulators by following the instructions provided in this helpful guide. In my app.module.ts, I made the necessary configurations as shown below: import { USE_EMULATOR as USE_AUTH_EMULATOR } from &apos ...

How can I add seconds to the jquery datetimepicker plugin?

Looking to implement the datetimepicker plugin from here, but I'm having trouble finding information on how to include the seconds part. Is there a way to add seconds to this plugin? It's a crucial requirement, so any suggestions for another good ...

Creating a perpetual loop animation for two divs moving from right to left endlessly

Here is the code I have: HTML <div class="screen screen1"></div> <div class="screen screen2"></div> CSS .screen{ width: 100%; height: 50%; position: absolute; background-color:#001; background-image: radial- ...

Obtaining the text content of a <div> element when clicked using Selenium

I am trying to extract the email address from the code snippet below, but I am unsure of how to do it. Any assistance would be greatly appreciated! <div class="taLnk hvrIE6 fl" onclick="ta.trackEventOnPage('Listing', 'Email', 774169 ...

Create visual representations using a JSON structure that includes timestamps for various locations

I need to create a JavaScript graph based on a JSON dataset containing locations and timestamps. The data is structured like an array, and my goal is to visualize the time spent at each location with a comprehensive graph. ...

Convert a Material UI component into a string representation

I've been tasked with using a tool that creates a terminal interface within the browser. Our goal is to display HTML content, including Material components. The tricky part is that the tool requires input as strings. I discovered a package called jsx- ...

What is the best method for transferring states between components?

When it comes to React JS, I am a beginner looking to develop a ToDo list application. In my project, I have the main page App.js, where I created the component MainBlock. This MainBlock component structures the layout into left and right sides. On the rig ...

NodeJS introduces the nullish coalescing assignment operator (??=) for effective nullish value handling

Is it possible to use the Nullish coalescing assignment operator (??=) in NodeJS? const setValue = (object, path, value) => { const indices = { first: 0, second: 1 }, keys = path.replace(new RegExp(Object.keys(indices).join('| ...

Is there a method to refresh the entire DOM-based location without having to reload the browser window?

Is it possible to achieve smooth asynchronous page transitions without breaking existing JavaScript animations in a system like Webflow? I'm struggling to find a way to present new elements to the DOM without disrupting the animations that are already ...

The AJAX request is functioning properly, however, when I attempt to click the icon within the button, a 500 internal server error is triggered

Encountering a 500 internal server error specifically when attempting to click the thumbs-up icon within the like button: <button class="submit-btn like" id='{{ $image->id }}'> <i class="far fa-thumbs-up"></i> Like </ ...

Storing dynamically loaded images through PHP, AJAX, JSON, and jQuery for faster retrieval

I will provide as much detail as I can here. The application I am currently developing allows administrators to create folders and upload photos into them. The display is similar to Pinterest, where all the photos are loaded in a collage on one screen. It ...

Issues with functionality of JavaScript calculator in HTML forms

Currently, I am working on creating a basic perimeter calculator specifically designed for a projector screen. This code is intended to take the response from a radio button input and the diagonal length in order to accurately calculate the perimeter base ...

Easy steps to prevent window.onbeforeunload from triggering when submitting a form using Vue

Presently, I am utilizing a component named countdowntimer.vue, specifically designed as a countdown timer for an online examination platform. My goal is to implement an onbeforeunload event on the window object while ensuring that the timer automatically ...

Challenges encountered while compiling Node.js code with ts-node (Error: Cannot use import statement outside a module)

Trying to compile TypeScript code with NodeJS using this command: npx ts-node src/server.ts An error is thrown: SyntaxError: Cannot use import statement outside a module Following the error's instructions: Warning: To load an ES module, set " ...

Accessing data from a reactive source within a component

Upon building a component, I encountered an issue with returning a property containing an observable. While I was able to successfully display the property in the template, I realized that its presence there was unnecessary: {{(selectedOrder$ | async).ord ...