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

What is the correct way to initialize and assign an observable in Angular using AngularFire2?

Currently utilizing Angular 6 along with Rxjs 6. A certain piece of code continuously throws undefined at the ListFormsComponent, until it finally displays the data once the Observable is assigned by calling the getForms() method. The execution of getForm ...

Development and staging setups tailored specifically for a JavaScript SDK

Currently, I am working with a Javascript SDK that is available on NPM. Alongside this, I have a Vue application utilizing the SDK and it's crucial for me to test them together across various pre-production environments (such as staging). Here are the ...

Leveraging the power of the three.js library on the client-side within a vue.js/n

I'm facing a challenge with incorporating the three.js library (installed via npm) to display 3D models on the client side within my nuxt.js application. Despite multiple attempts, I seem to be hitting a roadblock with the import not functioning prope ...

Creating a merged object from a split string array in TypeScript

I possess an array containing objects structured as follows; const arr1 = [ {"name": "System.Level" }, {"name": "System.Status" }, {"name": "System.Status:*" }, {"name": "System.Status:Rejected" }, {"name": "System.Status:Updated" } ] My object ...

The code malfunctions following the maintenance

Recently, I started learning JavaScript and trying to improve the readability of my code by moving away from inline functions. I have a piece of code that iterates through a JSON array containing comments and then appends those comments to the DOM. Strange ...

Automate the process of saving information to Google Sheets using Google AppScript

I have a Sheet named 'Automatic' where I've imported a set of data using IMPORTXML. My goal is to update this data list daily at the same time to create a database with various stock quotes over time. Is there a way to accomplish this usin ...

Generating a React User-Object in JSON Format

Imagine there is a back-end system using Node.js that allows the creation of users with specific attributes as shown below: POST http://localhost:8080/user Authorization: {{adminToken}} Content-Type: application/json { "userID": "test" ...

Retrieve the element located within a "block" element that is relative to the user's click event, without the

I'm pondering whether it's feasible, but here's my concept: Within my page, there are multiple identical blocks with the same classes, differing only in content. I am unable or unwilling to assign IDs because these blocks are dynamically g ...

MUI Grid with Custom Responsive Ordering

Can I achieve a responsive grid layout like this example? Check out the image here I have already coded the desktop version of the grid: <Grid container spacing={2}> <Grid item sm={12} lg={6} order={{ sm: 2, lg: 1 }}> ...

What are the best practices for effectively utilizing the nodejs Stream API?

I am currently working on an application that reads input from various sources, including files, sockets, and other parts of the same program that produce buffers or strings. While I have successfully handled sockets and files using node's Stream API ...

Issue with React occurring when attempting to delete an input component

I seem to be facing a challenge that I can't quite figure out whether it's related to React or not. To help illustrate the issue, I've created a simple example outside of my project: https://codepen.io/as3script/pen/VMbNdz?editors=1111 Wit ...

JavaScript code to generate a UTF8 string from UTF codes

I am in possession of the byte representation of UTF8, such as: 195, 156 for "Ü" (capital U Umlaut) I am struggling to generate a JavaScript-compatible string from these numbers - all my attempts have been unsuccessful. Every method I have tried has mis ...

The type 'void | Observable<User>' does not include the property 'subscribe'. Error code: ts(2339)

authenticate() { this.auth.authenticate(this.username, this.password).subscribe((_: any) => { this.router.navigateByUrl('dashboard', {replaceUrl: true}); }); } I'm puzzled by this error message, I've tried a few solu ...

SPFx WebPart - Tabbed Interface

I am new to developing SPFX WebParts and currently working on creating a Tab WebPart. The HTML appears to be rendering correctly, but I'm facing issues with the Javascript functionality not firing as expected. Any assistance or guidance on how to prop ...

Utilizing Typescript to Inject Generics and Retrieve the Name of an ES6 Module

I am currently working on developing a versatile repository using: Typescript ES6 Angular 1.x However, I am facing challenges in determining the correct way to inject the Entity and retrieve its module name. The main reason for needing the name: I adh ...

Issue arises when fastify/websocket is being used and an argument of type '{ websocket: boolean; }' is not compatible or able to be assigned to a parameter

I am facing an issue with my new project that involves fastify and Typescript. The error I am encountering is as follows: Argument of type '{ websocket: boolean; }' is not assignable to parameter of type 'RouteShorthandOptions ...ts(2345) B ...

Emptying the trumbowyg editor within an Angular environment

I am currently experiencing issues with the trumbowyg editor in a project I'm working on. I am able to update the editor by changing its model but cannot seem to clear its content using $scope.editorModel = '';. For a more detailed explanati ...

Tips for inserting an HTML element within an exported constant

I need help formatting an email hyperlink within a big block of text. Here is the code snippet: const myEmail = '<a href="mailto:<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2e4b564f435e424b6e4b564f435e424b004d41 ...

Tips for deleting a button from a DataTable file upload feature

I currently have Datatable set up to upload images using the code snippet below: { label: "Images:", name: "files[].id", type: "uploadMany", display: function ( fileId, counter ) { re ...

Sending a object as an argument to a function

Could someone please help me understand the purpose of passing an object as a function parameter? I have been trying to learn Next.js and they frequently use this method in their code. If anyone could provide a brief explanation of why this is done, it wo ...