1

Write your name in the canvas below, the code saves the drawing coordinates in the array. then I want to replay the drawing using this coordinates but it doesn't work. I think the issue is it draws it fast!!

How would you do this?

Here is a sample of what we want.

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>

1 Answers1

0
  1. Separate the part that adds the dot to the canvas into its own function called add (for example), and for both the play and draw functions call it with the current coordinates.

  2. Assign the canvas to a variable so that you can reset it prior to drawing the saved image.

  3. Only run the animation while step < coordinates.length.

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

const coordinates = [];

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

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

let step = 0;

function play() {

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

  steps();

  function steps() {

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

    step++;

    // Stop the animation if the end of the array
    // is reached
    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` that adds 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);
      //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;
}
  <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>
Andy
  • 61,948
  • 13
  • 68
  • 95
  • What a great job!! Bravo.. But as you noted when you write Andy with separate characters the play results in continues Andy right? any improvement? – user21188932 Aug 18 '23 at 21:21
  • I guess play around with some of the settings in `add`. Maybe drop the `ctx.lineWidth` down to `1`, comment out the `shadowBlur` line. Have a play around and see what works. I don't have a lot of experience working with `requestAnimationFrame` but [it looks like you can throttle it](https://stackoverflow.com/questions/19764018/controlling-fps-with-requestanimationframe) which might make it smoother (if that's what you were referring to.) – Andy Aug 18 '23 at 21:27
  • Please read the updated comment above, there is a bug in the code.. – user21188932 Aug 18 '23 at 21:28
  • _"But as you noted when you write Andy with separate characters"_: where did I write that? – Andy Aug 18 '23 at 21:31
  • Sorry, if you write Andy... Language barriers!!! – user21188932 Aug 18 '23 at 21:33
  • Hah, I understand. I'm just about to head out so can't write an update to my answer but I will point you in the right direction. At the moment you have one array that's create when you `mousedown`. For individual letters you'll probably want to create a main array and add a _new_ array to it when you `mouseup` and then `mousedown` again. And then it's a matter of iterating over the nested arrays to ensure that you get the right output. Have a go yourself, and if you run into problems, post the code again as a new question and I'm sure someone will take a look. – Andy Aug 18 '23 at 21:44
  • 1
    Thanks, So I choose you answer... try myself and if create new question.. – user21188932 Aug 18 '23 at 21:46
  • 1
    Please don't remove your comments they could be a reference for me.. – user21188932 Aug 18 '23 at 21:47