There is not much we have to do add undo and redo functionality to a Canvas application. Here is simple application to demo how we can achieve that by saving the state after each event in an array and then add that to another array after undo so that we can redo. These are simple JavaScript arrays where we are storing the images using push() and pop() methods. The canvas image is retrieved using the context.toDataURL("image/png") method and when we undo or redo we take that image and paint it again on the canvas after clearing the current canvas. Just to complete the function we have added the clear button also to reset the canvas.
Application starting point - index.html
<!DOCTYPE html>
<html>
<head>
<title>HTML5 canvas - Undo and Redo buttons</title>
<link rel="stylesheet" type="text/css" href="extjs/resources/css/ext-all.css">
<script type="text/javascript" src="extjs/ext-all-debug.js"></script>
<script type="text/javascript" src="app.js"></script>
</head>
<body>
</body>
</html>
Application JavaScript file - app.js
Ext.Loader.setConfig({
enabled: true
});
Ext.application({
name: 'MyApp',
appFolder: 'app',
controllers: [
'UndoRedo'
],
launch: function() {
Ext.create('Ext.container.Viewport', {
margin: 10,
defaults: {
margin: 10,
},
items: [{
xtype: 'label',
html: '<b>HTML5 canvas with Undo and Redo functions</b>'
},
{
xtype: 'myCanvas',
},
{
xtype: 'button',
text: 'Clear Canvas',
id: 'clear'
},
{
xtype: 'button',
text: 'Undo Action',
id: 'undo',
disabled: true,
},
{
xtype: 'button',
text: 'Redo Action',
id: 'redo',
disabled: true,
}]
});
}
});
HTML5 canvas view panel - MyCanvas.js
Ext.define('MyApp.view.MyCanvas', {
extend: 'Ext.Container',
alias : 'widget.myCanvas',
layout: {
type: 'vbox',
align: 'stretch'
},
width: 600,
height: 300,
border: 1,
style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
items: [
{
xtype: 'panel',
id: 'myCanvasPanel',
html: '<canvas id="html5Canvas" width="600" height="300">no canvas support</canvas>'
}]
});
Application controller file - UndoRedo.js
Ext.define('MyApp.controller.UndoRedo', {
extend : 'Ext.app.Controller',
//define the views
views : ['MyCanvas'],
//define refs
refs: [{
ref: 'myCanvasPanel',
selector: 'panel[id="myCanvasPanel"]'
}],
init : function() {
this.control({
'myCanvas' : {
afterrender : this.onPanelRendered
},
'viewport button[id=clear]' : {
click : this.onClearCanvas
},
'viewport button[id=undo]' : {
click : this.onUndoCanvas
},
'viewport button[id=redo]' : {
click : this.onRedoCanvas
}
});
},
//clear the canvas
onClearCanvas: function(button) {
console.log('Clear CANVAS button clicked!');
canvasPad.width = canvasPad.width;
canvasPadContext.lineWidth = 3;
//disable the undo and redo buttons
undoButton.disable();
redoButton.disable();
//clear the arrays
savedImages = [];
removedImages = [];
},
onUndoCanvas: function(button) {
console.log('Undo CANVAS button clicked!');
//save the current canvas in redo array
this.removeImage();
canvasPad.width = canvasPad.width;
canvasPadContext.lineWidth = 3;
//create an image object and paint
var imageObj = new Image();
imageObj.onload = function(){
canvasPadContext.drawImage(imageObj, 0, 0);
};
//get from array the source for the image object
imageObj.src = savedImages.pop();
//if the stack is empty then disable the undo button
if (savedImages.length === 0) {
undoButton.disable();
}
},
onRedoCanvas: function(button) {
console.log('Redo CANVAS button clicked!');
//save the current canvas in undo array
this.saveImage();
//clear the canvas
canvasPad.width = canvasPad.width;
canvasPadContext.lineWidth = 3;
//create an image object and paint
var imageObj = new Image();
imageObj.onload = function(){
canvasPadContext.drawImage(imageObj, 0, 0);
};
//get from array the source for the image object
imageObj.src = removedImages.pop();
//if the stack is empty then disable the redo button
if (removedImages.length === 0) {
redoButton.disable();
}
},
onPanelRendered: function(panel) {
console.log('HTML5 canvas rendered, get ready to Sign!');
var view = panel.up('viewport');
undoButton = view.down('button[id=undo]');
redoButton = view.down('button[id=redo]');
savedImages = [];
removedImages = [];
//get reference to the canvas element and its 2d context
canvasPad = Ext.getDom("html5Canvas");
if (canvasPad && canvasPad.getContext) {
canvasPadContext = canvasPad.getContext('2d');
}
if (!canvasPad || !canvasPadContext) {
alert('Error creating canvas pad.');
return;
}
canvasPad.width = this.getMyCanvasPanel().getWidth();
canvasPad.height = this.getMyCanvasPanel().getHeight();
canvasPadContext.lineWidth = 3;
//add listener to the mousedown event
Ext.get("html5Canvas").addListener('mousedown',this.onMouseDown, this);
},
onMouseDown: function(event) {
this.saveImage();
//get the mouse click coordinates
var mySign = Ext.get("myCanvasPanel");
event._x = event.getX() - mySign.getX();
event._y = event.getY() - mySign.getY();
//create a circle
canvasPadContext.beginPath();
canvasPadContext.arc(event._x, event._y,10,0*Math.PI,2*Math.PI);
canvasPadContext.stroke();
},
saveImage: function(){
//save the canvas image to undo array
var imgSrc = canvasPad.toDataURL("image/png");
savedImages.push(imgSrc);
undoButton.enable();
},
removeImage: function(){
//save the canvas image to redo array
var imgSrc = canvasPad.toDataURL("image/png");
removedImages.push(imgSrc);
redoButton.enable();
}
});
No comments:
Post a Comment
NO JUNK, Please try to keep this clean and related to the topic at hand.
Comments are for users to ask questions, collaborate or improve on existing.