Experience an enhanced replay of canvas drawings with advanced line smoothing technology

Enter your name on the canvas below, and the code will save the drawing coordinates in an array. However, I am having trouble replaying the drawing using these coordinates. It seems to be drawing too quickly!

What would be your approach to resolve this issue?

Check out a sample of what we are aiming for here.

const coordinates = [];

document.getElementById('coordinatesBtn').addEventListener('click', () => {
  console.log(coordinates)
});


document.getElementById('coordinatesDrawBtn').addEventListener('click', () => {
  play()
});

let step = 0;

function play() {

  steps();

  function steps() {

    draw(null, coordinates[step].x, coordinates[step].y);
    step++;
    window.requestAnimationFrame(steps);

  }
}


var surface, ctx, target, inProgress, cp1x, cp1y, cp2x, cp2y, skip1, skip2;

function $(e) {
  return document.getElementById(e);
}

function draw(e, aa, bb) {
  var x = e ? e.offsetX : aa;
  var y = e ? e.offsetY : bb;

  target.x.innerHTML = x;
  target.y.innerHTML = y;

  coordinates.push({
    x,
    y
  });

  //ctx.globalCompositeOperation = 'source-over';
  ctx.shadowColor = "rgba(0,0,0,.5)";
  ctx.shadowBlur = 2;
  ctx.lineCap = 'round';
  ctx.lineJoin = 'round';
  ctx.lineWidth = 2;
  ctx.strokeStyle = 'red';
  if (!inProgress) {
    ctx.beginPath();
    ctx.moveTo(x, y);
    inProgress = true;
    skip1 = true;
    skip2 = false;
  } else {
    if (skip1) {
      cp1x = x;
      cp1y = y;
      skip1 = false;
      skip2 = true;
    }
    if (skip2) {
      cp2x = x;
      cp2y = y;
      skip1 = false;
      skip2 = false;
    } else {
      //ctx.lineTo(x,y);
      ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
      //ctx.quadraticCurveTo(cp1x, cp1y, x, y);
      skip1 = true;
      skip2 = false;
    }
  }
  ctx.stroke();
}

function captureTouch() {
  surface.addEventListener('selectstart', function(e) {
    e.preventDefault();
    return false;
  }, false);
  surface.addEventListener('dragstart', function(e) {
    e.preventDefault();
    return false;
  }, false);
  surface.addEventListener('mousedown', function() {
    $('msg').style.setProperty('display', 'none');
    surface.addEventListener('mousemove', draw, false);
  }, false);
  surface.addEventListener('mouseup', function() {
    surface.removeEventListener('mousemove', draw, false);
    inProgress = false;
    ctx.save();
  }, false);
}

function init() {
  surface = $('surface');
  surface.width = document.body.clientWidth;
  surface.height = window.innerHeight;
  ctx = surface.getContext('2d');
  target = {
    x: $('x'),
    y: $('y')
  };
  captureTouch();
  document.body.addEventListener('touchmove', function(e) {
    e.preventDefault();
  }, false);
}

document.addEventListener('DOMContentLoaded', init, false);
* {
    -webkit-user-select:none;
}
body {
    margin:0;
    padding:0;
    width:100%;
    height:100%;
    overflow:hidden;
}
#msg {
    color:red;
    font-weight:bold;
}
#coords{ 
    background:#ffc; 
    border-bottom:1px solid #fc0;
}
#container {
    position:relative;
}
#surface {
    background:white;
}
.tp{
    visibility:hidden;
    position:absolute;
    width:30px;
    height:30px;
    background:#999;
    -webkit-border-radius:15px;
    -webkit-box-shadow:inset white 0 0 15px 5px, white 0 0 2px 2px;
}
<!DOCTYPE HTML>
<html>
    <head>
        <title>Canvas Draw</title>
    </head>
    <body>
        <div id="coords">
            <div id="msg">Start drawing!</div>
            X: <span id="x"></span>
            Y: <span id="y"></span>
        </div>
        <button id="coordinatesBtn">Show coordinates of drawing</button>
         <button id="coordinatesDrawBtn">Use coordinates to draw</button>
        <div id="container">
            <canvas id="surface"></canvas>
        </div>
    </body>
</html>

Answer №1

  1. Segment the section responsible for adding the dot to the canvas into a separate function named add (e.g.,), and invoke it with the current coordinates in both the play and draw functions.

  2. Declare a variable for the canvas so you can clear it before drawing the saved image.

  3. Ensure that the animation only runs while step is less than coordinates.length.

// Save reference to the canvas element
const canvas = document.querySelector('#surface');
const context = canvas.getContext('2d');

// Array to store coordinates
const coordinates = [];

document.getElementById('coordinatesBtn').addEventListener('click', () => {
  console.log(coordinates)
});

document.getElementById('coordinatesDrawBtn').addEventListener('click', () => {
  play()
});

let step = 0;

function play() {

  // Clear the canvas before writing a new image
  context.clearRect(0, 0, canvas.width, canvas.height);

  steps();

  function steps() {

    // Invoke the `add` function with the current coords
    add(coordinates[step].x, coordinates[step].y);

    step++;

    // Stop the animation at the end of the array
    if (step < coordinates.length) {
      window.requestAnimationFrame(steps);
    }
  }
}

var surface, ctx, target, inProgress, cp1x, cp1y, cp2x, cp2y, skip1, skip2;

function $(e) {
  return document.getElementById(e);
}

function draw(e, aa, bb) {
  var x = e ? e.offsetX : aa;
  var y = e ? e.offsetY : bb;

  target.x.innerHTML = x;
  target.y.innerHTML = y;

  coordinates.push({
    x,
    y
  });

  // Call add with the current coords
  add(x, y);
}

// New function `add` to add the dot to the canvas
function add(x, y) {
  ctx.shadowColor = "rgba(0,0,0,.5)";
  ctx.shadowBlur = 2;
  ctx.lineCap = 'round';
  ctx.lineJoin = 'round';
  ctx.lineWidth = 2;
  ctx.strokeStyle = 'red';
  if (!inProgress) {
    ctx.beginPath();
    ctx.moveTo(x, y);
    inProgress = true;
    skip1 = true;
    skip2 = false;
  } else {
    if (skip1) {
      cp1x = x;
      cp1y = y;
      skip1 = false;
      skip2 = true;
    }
    if (skip2) {
      cp2x = x;
      cp2y = y;
      skip1 = false;
      skip2 = false;
    } else {
      //ctx.lineTo(x,y);
      ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
      
      skip1 = true;
      skip2 = false;
    }
  }
  ctx.stroke();
}

function captureTouch() {
  surface.addEventListener('selectstart', function(e) {
    e.preventDefault();
    return false;
  }, false);
  surface.addEventListener('dragstart', function(e) {
    e.preventDefault();
    return false;
  }, false);
  surface.addEventListener('mousedown', function() {
    $('msg').style.setProperty('display', 'none');
    surface.addEventListener('mousemove', draw, false);
  }, false);
  surface.addEventListener('mouseup', function() {
    surface.removeEventListener('mousemove', draw, false);
    inProgress = false;
    ctx.save();
  }, false);
}

function init() {
  surface = $('surface');
  surface.width = document.body.clientWidth;
  surface.height = window.innerHeight;
  ctx = surface.getContext('2d');
  target = {
    x: $('x'),
    y: $('y')
  };
  captureTouch();
  document.body.addEventListener('touchmove', function(e) {
    e.preventDefault();
  }, false);
}

document.addEventListener('DOMContentLoaded', init, false);
* {
  -webkit-user-select: none;
}

body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

#msg {
  color: red;
  font-weight: bold;
}

#coords {
  background: #ffc;
  border-bottom: 1px solid #fc0;
}

#container {
  position: relative;
}

#surface {
  background: white;
}

.tp {
  visibility: hidden;
  position: absolute;
  width: 30px;
  height: 30px;
  background: #999;
  -webkit-border-radius: 15px;
  -webkit-box-shadow: inset white 0 0 15px 5px, white 0 0 2px 2px;
}
<div id="coords">
    <div id="msg">Start drawing!</div>
    X: <span id="x"></span> Y: <span id="y"></span>
  </div>
  <button id="coordinatesBtn">Show coordinates of drawing</button>
  <button id="coordinatesDrawBtn">Use coordinates to draw</button>
  <div id="container">
    <canvas id="surface"></canvas>
  </div>

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

Drawing a personalized cursor using JavaScript on a canvas

I've been working on creating a canvas that allows for drawing in vanilla JavaScript. Although I've successfully enabled drawing on the canvas, I'm now looking to implement a feature where a preview dot shows up when the user presses down be ...

What is the best way to include numerous attributes to an element using JavaScript?

Attributes can be included within a string in the following format: let attr = ' min="0" step="5" max="100" '; or let attr = ' min="2019-12-25T19:30" '; and so on. Is there a function available ...

Embedding a countdown timer in basic HTML code

Attempting to embed the following into an HTML website: The issue I am facing is that when I run it, the timer does not appear. However, when I run it in fsFiddle, I do see the countdown timer. What could be causing this problem? <!DOCTYPE html> & ...

What could be the reason for my button to update only at the completion of the "each" loop?

Is there a way to update the button text before the loop, so that users can receive feedback while it's running? Currently, the text only updates after the loop has completed. In my scenario, the button displays "Updating please wait..." after the lo ...

How can I generate codegen types using typeDefs?

I have been exploring the capabilities of graphql-codegen to automatically generate TypeScript types from my GraphQL type definitions. However, I encountered an issue where I received the following error message: Failed to load schema from code file " ...

React: Unexpected error occurs with invalid element type

I encountered the following issue while attempting to utilize a component Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forg ...

Incorporating image links within React components

I am currently in the process of learning React. Within my components folder, located inside the src folder of my project, there is a file called website_index.js. Additionally, there is an images folder within the src directory. I am attempting to link im ...

Understanding the user's preference for text alignment and managing how the text is displayed in a text area (or text field) within a ReactJS application

My latest project is an app that features multiple text areas. Personally, I use the app with two languages - English and another language that is aligned to the right. However, the default setting has the text-areas aligning to the left. To change this ...

Pass SASS/SCSS variables to Javascript without the need to export them to CSS files

Context Let's take a look at the _variables.scss file below: /* Setting up color variables */ $white: #fff; $black: #000; $grey: #ccc; // and so on... // Export the color palette for Javascript accessibility :export { white: $white; ...

A guide on utilizing MongoDB command line tools in the railway environment

A current MERN stack project I am working on involves resetting two documents in my MongoDB database to default data at midnight every day to revert changes made on the live website. I achieve this by using the MongoDB CLI database tools to import .json fi ...

JSON - When an Object's Value Matches

Within my JSON file, I have an object that contains an array of nested objects. I am looking for a way to trigger a function based on the value of a specific object. If the value matches a certain number, I want to display only those objects within a desig ...

Leveraging Angular 2 for incorporating jQuery-based JavaScript ajax functionality

It seems like I'm attempting to force a square peg into a round hole as I work with Angular2 and Typescript. I've created a Javascript module that serves as an API client library for the API I'm utilizing. This library simplifies tasks such ...

Automatically change the height of the UI element in HTML

I'm currently working on building a table view using the ul tag in HTML. Within the link provided, I've utilized two li tags to create a two-column layout. To visually represent the spaces occupied by the li tags, column dots, and row dots, I&apo ...

Click on a button to completely remove all JavaScript from your website using jQuery

I'm currently experiencing some difficulties with my website Concept Studio. On a specific page, I have a typing animation within a form and I'd like to include a button that allows users to skip the animation. However, I'm unsure of how to ...

Troubleshooting problems with TranslateZ performance on mobile devices

While attempting to incorporate the code found at http://codepen.io/keithclark/pen/JycFw, I encountered significant flickering and delays in Chrome when using mobile devices. #slide1:before { background-image: url("http://lorempixel.com/output/abstract ...

The PHP script is displaying all content following the echo statement

Currently in the process of constructing my inaugural website. Starting off by creating it on my laptop and will eventually upload it to my hosting service. The main focus of this website is to provide a searchable database containing information about fam ...

Arranging based on the initial elements of the array

Could I please receive the arrangement ['H','H','A','A'] from the given input ['H','A','H','A'] Is there a way to organize it according to the first occurrence of each charact ...

innerhtml does not display the entire array contents

I'm facing an issue with my innerHTML output where it seems to be displaying one less value than expected from the array. I've been unable to pinpoint the exact problem. await fetch(urlbody, bodyrequestOptions) .then((response) => response ...

How can I add a blank selection at the bottom of my ng-options dropdown?

At the moment, my setup looks something like this (in a simplified form): <select ng-model=model.policyHolder ng-options="person.index as person.name for person in model.insurance.persons"> <option value>Someone else </select> This co ...

Using Javascript within a PHP file to generate JSON output

Can you integrate Javascript code within a PHP file that utilizes header('Content-Type: application/json'); to produce output in JSON format? UPDATE: I'm attempting to modify the color of a CSS class when $est = 'Crest', but the J ...