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

elevate the div with a floating effect

My goal is to create a chat feature for users that will only be visible for a limited time period. I was thinking of using PHP and timestamp to achieve this functionality, but I also want to make the chat visually disappear by having the message div float ...

Checking the conditional styling implemented with Material UI makeStyles: a step-by-step guide

For the past few weeks, I've been working on a React app where I heavily rely on Material UI components. One particular component changes its style based on the values of its props. To achieve this, I used the following approach: const useStyles = ...

Developing a react native library (create-react-native-library) incorporating a distinct react-native version within its designated Example directory

I'm looking to develop a React Native library, but the testing folder (example folder) it contains the latest version of React Native. However, I specifically need version 0.72.6 in the example folder. Is there a command for this? Current command: np ...

Certain iFrame instances are unable to display specific cross-domain pages

I'm currently working on a project to create a website that can embed external cross-domain sites, essentially like building a browser within a browser. I've been experimenting with using iFrames for this purpose: While it works for some pages, ...

What could be causing the issue with fetch not functioning in my mobile browser?

Recently, I created a basic NodeJS server that stores strings in a database when accessed via "/add", and then sends all those strings to my ReactJS front-end using the endpoint "/getall". The strings are displayed on the front-end using the map function. ...

Methods for delivering static resources on multiple routes in Express JS

Is there a way to effectively serve static resources for all routes in Express JS? I attempted to serve files with static resources using the following code snippet: app.use(Express.static(`${this.PATH}`)); app.use(Cors()); app.use(Express.json()); app.g ...

Issue with Ionic 2's infinite scroll not triggering method on second scroll attempt

I utilized the ion-tab element to showcase a page (inboxitem) which encompasses ion-list and makes use of ion-infinite-scroll. The following code snippet resides in inboxitem.html <ion-content class="inbox can-swipe-list"> <ion-list> ...

The ReactJS code encountered an error when attempting to access the 'location' property of an undefined or null reference

My Reactapp is encountering an error due to a specific file. import React from 'react'; import { Router, Route } from 'react-router'; import App from './components/App'; import About from './components/About'; im ...

Implement 2 new search options into the datatable plugin

Looking to enhance my existing panel by adding 2 search options: Here are the credentials you'll need: username: admin pass: Nopass1234 The additional features I want to include are: 2 search options: 1. from date 2. to date What will happen w ...

Troubleshooting: 404 Error When Trying to Send Email with AJAX in Wordpress

In the process of creating a unique theme, I encountered an interesting challenge on my contact page. I wanted to implement an AJAX function that would allow me to send emails directly from the page itself. After conducting some research, I managed to find ...

Mistakes in my async/await workflow: How am I incorrectly loading and injecting this external script?

Encountering a simple problem: some calls to refresh() cause window.grecaptcha to become undefined. It doesn't happen all the time, probably due to network delays. Debugging this issue is proving to be tricky, especially since I'm still new to th ...

When working with Next.js Components, be aware that using a return statement in a forbidden context can lead to

Whenever I try to add a new component to my Next.js project, I encounter an error that displays the following: `./components/GridMember.js Error: error: Return statement is not allowed here | 6 | return (test); | ^^^^^^^^^^^^^^^^^^^^^^^^^ Caused ...

There seems to be a glitch in my JavaScript for loop as it is not iterating for the correct amount that it should

It seems like my for loop is not always iterating 7 times as intended. Sometimes it runs with 5 iterations, other times with 4 or 3. This is the JavaScript code I am using: var start = new Date().getTime(); var end = new Date().getTime(); function timeT ...

Error: Unable to iterate through the {(intermediate value)}. It's not a function

Snippet of the original code: const genreOptions = [{{ genreOptions | json_encode | raw }}].map((type , label) => ({value: type, label: label})); Piece of debugging code: const genreOptions = { "Horror": "Kork ...

Troubleshooting the issue of React forms hook not functioning properly with Material UI Select input

How the Textfield below should load: How it actually loads: My Select component, created using Material UI and React form hook, is not loading the default value as expected. The component should start with a pre-selected value, which is provided in the c ...

What is the process for transferring an object to a different file?

Currently, I am working on a web application using Node.js. In my project, I have two main files. The first one is Server.js, where I handle server actions such as going online. The second file contains a large object filled with data. I successfully impo ...

Creating a password with two distinct numbers using regular expressions

In javascript I am struggling to create a password that meets the criteria of having at least eight characters, including two SEPARATE digits, one uppercase and one lowercase letter, as well as one special character (-, @, #, $, &, *, +) but not /, !, ? ...

An obstacle prevents the code injection process in the Chrome extension when a button is pressed

My basic chrome extension is not functioning correctly. More specifically, I am encountering an issue where the alert box does not appear when I click on the button in the popup. Here are the contents of my manifest.json file: {"manifest_version": 2, "n ...

Implement responsive data tables by setting a specific class for hiding columns

Having trouble assigning a specific class name to individual columns in datatables? It seems that when columns are hidden using the responsive extension, the desired class is not applied. Looking for a solution or workaround. Check out this example from D ...

Repetitive bouncing in a trampoline leads to the error message "Call stack has reached maximum size"

I have been delving into the world of blockchain and experimenting with a simple implementation of "proof of work". Proof of work: export function mineBlock(difficulty: number, block) { const prefix = Array(difficulty + 1).join("0"); function mine(b ...