简体   繁体   中英

How to step back in HTML5 Canvas history

I have an image cropper using Imgly HTML5 Canvas plugin. I need to be able to setup a history stack for the cropper to be able to undo a crop operation. Currently, I can clear the canvas on button click, but I need to be able to retain the original image, and just move back through a history of changes of the image in the canvas, in the event that a cropping step is done incorrectly.

I have the following which simply clears the canvas:

$("#renderButton").click(function() {
        var elem = $(".imgly-canvas");
        var canvas = elem.get(0);
        var context = canvas.getContext("2d");
        $('#file').val('');

        context.clearRect(0, 0, canvas.width, canvas.height);
        context.beginPath();
    });

The plugin creates the canvas element on image load with:

Utils.getImageDataForImage = function(image) {
  var canvas, context;
  canvas = document.createElement("canvas");
  canvas.width = image.width;
  canvas.height = image.height;
  context = canvas.getContext("2d");
  context.drawImage(image, 0, 0);
  return context.getImageData(0, 0, image.width, image.height);
};

And this is used on resize:

    Utils.cloneImageData = function(imageData) {
  var i, newImageData, _i, _ref;
  newImageData = this.sharedContext.createImageData(imageData.width, imageData.height);
  for (i = _i = 0, _ref = imageData.data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
    newImageData.data[i] = imageData.data[i];
  }
  return newImageData;
};

/*
  @param {Object} dimensions
  @param {Integer} dimensions.width
  @param {Integer} dimensions.height
  @returns {HTMLCanvasElement}
*/


Utils.newCanvasWithDimensions = function(dimensions) {
  var canvas;
  canvas = document.createElement("canvas");
  canvas.width = dimensions.width;
  canvas.height = dimensions.height;
  return canvas;
};

/*
  @param {imageData} imageData
  @returns {HTMLCanvasElement}
*/


Utils.newCanvasFromImageData = function(imageData) {
  var canvas, context;
  canvas = document.createElement("canvas");
  canvas.width = imageData.width;
  canvas.height = imageData.height;
  context = canvas.getContext("2d");
  context.putImageData(imageData, 0, 0);
  return canvas;
};

So I'm not sure how to build a call stack to reference each change and move back through a history of modifications to an image in the canvas.

The HTML5 canvas nicely converts to JSON which can then be used to reload the canvas. You can store this in a global object.

var myObj = window.myObj || {};

myObj = {
    history: [],
    canvas: null
};

Get the canvas data:

myObj.canvas = document.getElementById('canvas-id');
var ctx = myObj.canvas.getContext('2d');
var data = JSON.stringify(ctx.getImageData(0, 0, myObj.canvas.width, myObj.canvas.height));

myObj.history.push(data);

Reload data:

var reloadData = JSON.parse(myObj.history[someIndex]);
var ctx = myObj.canvas.getContext('2d');
ctx.putImageData(reloadData, 0, 0);

Once you can store/load data the tricky part is managing the myObj.history array.

You should look at the command pattern . Basically, you need to write a function for every action that the user can do. When they click a button or load an image, don't call the function right away. Instead, create a command object with all the information needed to execute the command plus the information needed to undo it.

Commands are applied to a data model (the image and the crop marks). A command "load image" needs to record the new and the previous image URL so you can load the correct image when you move through the history.

For crop commands, you need to store the old and new crop rectangles - if you keep a copy of the original image around. When the command is executed, you apply the new crop rectangle on the original and draw that on the canvas.

For undo, you use the original image and the previous crop rectangle.

So the trick is to define a data model which contains all the information how the UI looks like (which is often hard to get from the UI directly - you can't get crop information after rendering the cropped image to the canvas). The commands then manipulate this state (so the next command can save it for undo) and update the UI.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM