0

I am trying to make an 'undo' action like Ctrl + Z.

I made a simple canvas paint example to make it easier to understand what I would like. When the user moves the mouse without releasing mouse1, something will be drawn on the canvas. Then, when they stop pressing mouse1, the drawing ends. The Ctrl + Z shortcut will undo these drawings.

Here is the code:

//var
const canvas = document.getElementById('canvas');

const cC = canvas.getContext('2d');

//trigger for write or not
var pressedQ = false;

//

function getMousePosition(evt) {    
  var rect = canvas.getBoundingClientRect();
  var root = document.documentElement;
  var mouseX = evt.clientX - rect.left - root.scrollLeft;
  var mouseY = evt.clientY - rect.top - root.scrollTop;
  return {
    x: mouseX,
    y: mouseY
  };
}

function writeCircle(posX, posY, size, color) {
  cC.fillStyle = 'black';
  cC.beginPath();
  cC.arc(posX, posY, size, 0, Math.PI*2, true);
  cC.fill();
}

function pencil(evt) { 
  if (pressedQ == true) {
    var mousePos = getMousePosition(evt);
    writeCircle(mousePos.x, mousePos.y, 50);
  }
  else{}
}

function activate(textmode, evt) {
  pressedQ = true;
  console.log('start');
}

function deactivate() {
  pressedQ = false;
  console.log('finish');
}

window.onload = function() {
  cC.clearRect(0, 0, canvas.width, canvas.height);
  canvas.addEventListener('mousedown', activate);
  canvas.addEventListener('mousemove', pencil);
  canvas.addEventListener('mouseup', deactivate);
}
<canvas id="canvas" width="700" height="490"></canvas>

Thanks!

Note: I cant understand ES2015+ syntax

BCDeWitt
  • 4,540
  • 2
  • 21
  • 34
Willian
  • 98
  • 11

1 Answers1

1

What you're looking for is the command design pattern. Since you're using functions here, you just need to store the function name and its parameters. Then, you can use that data to call the function again later. The following example isn't perfect, but it should demonstrate the basic idea. (I tried to avoid any JavaScript syntax that was added after the 2015 update)

var canvas = document.getElementById('canvas');
var cC = canvas.getContext('2d');

var pressedQ = false; //trigger for write or not

var commands = [];

var commandTypes = {
  drawCircle: function (posX, posY, size, color) {
    cC.fillStyle = 'black';
    cC.beginPath();
    cC.arc(posX, posY, size, 0, Math.PI * 2, true);
    cC.fill();
  }
};

function execute() {
  var commandType = arguments[0];
  var data = Array.prototype.slice.call(arguments, 1);
  
  if (!commandTypes.hasOwnProperty(commandType))
    throw new Error(commandType + ' is not a real command');

  commandTypes[commandType].apply(null, data);
}

function pushAndExecute() {
  commands.push(arguments);
  execute.apply(null, arguments);
}

function getMousePosition(evt) {
  var rect = canvas.getBoundingClientRect();
  var root = document.documentElement;
  return {
    x: evt.offsetX - rect.left - root.scrollLeft,
    y: evt.offsetY - rect.top - root.scrollTop
  };
}

function pencil(evt) { 
  if (!pressedQ) return;
  var mousePos = getMousePosition(evt);
  pushAndExecute('drawCircle', mousePos.x, mousePos.y, 50);
}

function activate(evt) {
  pressedQ = true;
  // console.log('start');
  pencil(evt);
}

function deactivate() {
  pressedQ = false;
  // console.log('finish');
}

function handleKeys(evt) {
  if (evt.ctrlKey && evt.key === 'z') {
    // console.log('undo');

    // Remove the most recent command from the list
    commands.splice(-1, 1);

    // Clear canvas
    cC.clearRect(0, 0, canvas.width, canvas.height);

    // Re-play all commands (re-draw canvas from scratch)
    commands.forEach(function (command) {
      execute.apply(null, command);
    });
  }
}

window.onload = function() {
  cC.clearRect(0, 0, canvas.width, canvas.height);
  canvas.addEventListener('mousedown', activate);
  canvas.addEventListener('mousemove', pencil);
  canvas.addEventListener('mouseup', deactivate);
  window.addEventListener('keydown', handleKeys);
}
<canvas id="canvas" width="700" height="490"></canvas>
BCDeWitt
  • 4,540
  • 2
  • 21
  • 34