Having trouble with ES6 in Canvas - why won't my code display correctly?

I'm currently working on developing a painting app using ES6. However, I'm facing issues with the positioning and line drawing on the canvas.

The lines are not being drawn in the correct position; for example, the top-left corner is formed when I click from the 0,0 corner of the canvas.

You can observe that the line does not start from the point where the cursor is pointing, and this discrepancy increases as we move from the TOP-LEFT corner to the BOTTOM-RIGHT corner.

https://i.stack.imgur.com/gzr3F.png

const TOOL_LINE = 'line';

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

class Paint {
  constructor(canvasId) {

    this.canvas = document.getElementById(canvasId);
    this.context = canvas.getContext("2d");
  }
  set activeTool(tool) {
    this.tool = tool;
  }
  init() {
    this.canvas.onmousedown = e => this.onMouseDown(e);
  }
  onMouseDown(e) {
    this.saveData = this.context.getImageData(0, 0, this.canvas.clientWidth, this.canvas.clientHeight);
    this.canvas.onmousemove = e => this.onMouseMove(e);
    document.onmouseup = e => this.onMouseUp(e);
    this.startPos = this.getMouseCoordinatesCanvas(e, this.canvas);
  }
  onMouseMove(e) {
    this.currentPos = this.getMouseCoordinatesCanvas(e, this.canvas);
    switch (this.tool) {
      case TOOL_LINE:
        this.drawShape();
        break;
      default:
        break;
    }
  }
  onMouseUp(e) {
    this.canvas.onmousemove = null;
    document.onmouseup = null;
  }
  drawShape() {
    this.context.putImageData(this.saveData, 0, 0);
    this.context.beginPath();
    this.context.moveTo(this.startPos.x, this.startPos.y);
    this.context.lineTo(this.currentPos.x, this.currentPos.y);
    this.context.stroke();
  }
  getMouseCoordinatesCanvas(e, canvas) {
    let rect = canvas.getBoundingClientRect();
    let x = e.clientX - rect.left;
    let y = e.clientY - rect.top;
    return new Point(x, y);
  }
}

var paint = new Paint("canvas");
paint.activeTool = TOOL_LINE;
paint.init();

document.querySelectorAll("[data-tools]").forEach(
  item => {
    item.addEventListener("click", e => {
      let selectedTool = item.getAttribute("data-tools");
      paint.activeTool = selectedTool;

    });
  }
);
#Container {
  background-color: lime;
  height: 310px;
}

.toolbox,
#canvas {
  display: inline-block;
}

.toolbox {
  background-color: gray;
  padding: 0px 15px 15px 15px;
  left: 10px;
  top: 11px;
}

.group {
  margin: 5px 2px;
}

#line {
  transform: rotate(-90deg);
}

.ico {
  margin: 3px;
  font-size: 23px;
}

.item:hover,
.item.active {
  background-color: rgba(160, 160, 160, 0.5);
  color: white;
}

#canvas {
  background-color: white;
  margin: 5px;
  float: right;
  width: 400px;
  height: 300px;
}
<script src="https://kit.fontawesome.com/c1d28c00bc.js" crossorigin="anonymous"></script>
<div class="container">
  <div id="Container">
    <div class="toolbox">
      <center>
        <div class="group tools">
          <div class="item active" data-tools="line">
            <i class="ico far fa-window-minimize" id="line" title="Line"></i>
          </div>
        </div>
      </center>
    </div>
    <canvas id="canvas"></canvas>
  </div>
</div>

Here is the link to the code repository.

Thank you in advance.

Answer №1

It appears that the issue could be related to one or both of the following factors:

Firstly, your canvas is being displayed at 400x300 dimensions, but it actually only has 300x150 pixels. Canvases have two sizes - the display size set with CSS and the actual pixel size set in code using canvas.width and canvas.height. The default pixel size is typically 300x150.

If you intend for them to have different sizes, then you need to adjust your mouse code accordingly. The correct code snippet would be:

getMouseCoordinatesCanvas(e, canvas) {
    let rect = canvas.getBoundingClientRect();
    let x = (e.clientX - rect.left) * canvas.width / rect.width;
    let y = (e.clientY - rect.top) * canvas.height / rect.height;
    return new Point(x, y);
}

Secondly, if you want the sizes to match, consider setting the size using CSS and adjusting the canvas size using code like this:

function resizeCanvasToDisplaySize(canvas) {
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
        canvas.width = width;
        canvas.height = height;
    }
    return needResize;
}

By making these adjustments, you can ensure that the canvas displays correctly and functions as intended.

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

Variable for Ajax success not set even though the other data was returned

UPDATE: While the main question remains unchanged, I have modified the approach to now return a getButtons() function instead of a global buttons array. I am utilizing an AJAX request to fetch data from the server in order to populate a popup box, which i ...

combining input fields for editing as a single unit instead of individually

Current Situation : At the moment, I have a form where individual records of input fields such as firstName, lastName, and email can be edited and saved without any errors. Requirement : However, I now want to be able to edit and save the firstName and ...

Is it necessary to include a promise in the test when utilizing Chai as Promised?

Documentation provided by Chai as Promised indicates the following: Note: when using promise assertions, either return the promise or notify(done) must be used. Examples from the site demonstrate this concept: return doSomethingAsync().should.eventua ...

Positioning social media icons directly below the Avatar component

I've been working on aligning my social media icons under the Avatar component. Despite trying multiple methods to center them in the JSS, I haven't had much success. I attempted different approaches but couldn't achieve the desired alignmen ...

Tips for automatically closing the Toggle Navigation feature in Vue JS when a user clicks outside of the navigation container

Is there a way to close the toggled navigation menu automatically when the user clicks outside of the navigation container? I have implemented two ul menus inside the navigation menu and would like the subNavActive, safNavAcitve, and repNavUl variables to ...

The EJS file is failing to display the stylesheet even though it is being pulled from the

Encountering a strange issue where the page routed to display additional information about a specific record from my database list on the homepage is not loading the stylesheets located in my partial/head, despite successfully passing the object informatio ...

When the "x" close icon is clicked, the arrow should toggle back to 0 degrees

I've been tackling the challenge of creating an accordion and I'm almost there. However, I'm facing an issue where the arrow doesn't return to its original position after clicking the close "x" icon. The toggle works fine but the arrow ...

Experience the feeling of releasing momentum by click and dragging the scroll

One feature I am looking for is the ability to control the scroll speed when dragging, and have the scroll continue moving slightly after release instead of stopping instantly. CodePen: https://codepen.io/rKaiser/pen/qGomdR I can adjust the speed adequat ...

Retrieve the node-postgres result and store it in a separate object beyond the callback function

Currently, I am in the process of converting a data profiling script originally written in Python to JavaScript while following the Wes Bos beginner Javascript Course. This script is designed to take database connection details and a specified target tabl ...

Executing several asynchronous functions in Angular 2

I am currently developing a mobile app and focusing on authentication. In order to display data to the user on my home page, I need to connect to various endpoints on an API that I have created. Although all endpoints return the correct data when tested i ...

Unable to determine why node.js express path is not working

const express = require("express"); const app = express(); app.use(express.static("public")); var dirname = __dirname; app.get("/:lang/:app",function(req,res){ console.log(req.params.lang + " " + req.params.app); ...

The Tauri JS API dialog and notification components are failing to function, resulting in a null return value

Currently, I am getting acquainted with the tauri framework by working on a small desktop application. While testing various tauri JS API modules, most of them have been functioning as expected except for the dialog and notification modules. Whenever I tes ...

An issue has occurred: Cannot access the properties of an undefined object (specifically 'controls')

I encountered an error message stating "TypeError: Cannot read property 'controls' of undefined" in the code that I am not familiar with. My goal is to identify the source of this error. Below is the HTML code snippet from my webpage: <div ...

JSON has difficulty retaining the value of the variable

I've been working on a piece of code that scans through a directory, extracts each file's name and content. The aim is to retrieve the data from these files, usually consisting of numbers or short pieces of text. var config = {}; config.liveProc ...

Fixed position not being maintained after clicking the button

Looking for some help with a fixed header issue on my website. The header is supposed to stay at the top while scrolling, which works well. However, when I switch to responsive view, click the menu button, and then go back to desktop view, none of the po ...

bash: The command "nodemon" could not be located

My experience with nodemon has been smooth for the past few months. However, today I encountered an error that I couldn't resolve. Despite uninstalling and reinstalling nodemon, as well as force installing it, I still received the same error message w ...

The error encountered is due to an invalid assignment on the left-hand side

I'm encountering the error below: Uncaught ReferenceError: Invalid left-hand side in assignment This is the problematic code: if (!oPrismaticMaterial = "") { for (var i = 0; i < oPrismaticMaterial.length; i++) { if (oPrismaticMater ...

"Activate the parent window by navigating using the accesskey assigned to the href

I have integrated a bank calculator tool into a website. The calculator opens in a new window, but I am encountering an issue. Users need a shortcut to open the calculator multiple times. I have discovered the accesskey feature, which works the first tim ...

Integration of Unlayer EmailEditor into React causes fatal errors in the application

I am attempting to integrate Unlayer into my React Application by using this guide: https://github.com/unlayer/react-email-editor. I have configured Webpack for this purpose. However, when I try to import EmailEditor in one of my modules: import React fr ...

What is the best way to save request URLs in JavaScript while following the DRY principle?

Is there a standard practice in JavaScript for storing the URLs of endpoints used in AJAX applications? Would you, for instance, consider creating a "Service" class to encapsulate the URLs? ...