Canvas Designer is a tool aimed to give you a full-fledged drawing surface and also auto generate appropriate code for you in different formats!
It targets Canvas 2D Context – i.e. it gives you a built-in IDE for Canvas 2D APIs!
Obviously, it is developed & designed solely by Muaz Khan!
Each task or feature has a unique JavaScript file. It simplifies not only coding but also later edits and additions!
Common task like output in textarea, global variables/objects etc. have been placed in common.js file.
Dragging relevant code has been placed in drag-helper.js and drawing relevant code has been placed in draw-helper.js file. (Note: draw-helper.js is most important file in this project because functions it contains are repeatedly called from all portions of the project!)
events-handler.js is used to handle global events like mousemove, mouseup, mousedown and their touch relevant!
There is a file decorator.js which is aimed to decorate the designer; i.e. to decorate the design surface, toolbox, etc.
There is a global variable named points of type array which stores all x/y coordinates plus extra values in the following style:
var points = [['shape', [x, y], [lineWidth, strokeStyle, fillStyle, globalAlpha, globalCompositeOperation, lineCap, lineJoin]]];
i.e.
var points = [path-name, path-coordinates, path-options];
Where:
path-name
can be line/arc/rect/quadratic/bezier etc.
path-coordinates
are the shape's x/y coordinates + width, height, radius etc.
path-options
are line-width, stroke/fill styles, global alpha/composite-operation, line cap/join etc.
draw-helper.js defines a method named "redraw
" that is one of the most called functions in this project. redraw
aimed to refresh all points on the drawing surface! By default, this function is called on each mousedown
/mouseup
event; however, in some cases, it is also called in mousemove
event. (See drag-helper.js file)
common.js defines second most called function "updateTextArea
" which is aimed to update the output accordingly.
In the top of common.js file; there is an object "is
" which contains Boolean
properties for selected toolbox shape(s). Mouse/Touch events in the events-handler.js are using these properties to call appropriate objects!
You can use Ctrl + C
for copy and Ctrl + V
for paste. You can also use Ctrl + MouseDown
for directly copy and paste!
You can copy in two formats:
Remember: Ctrl + MouseDown
will only work if one option is selected: DragAllPaths
or DragLastPath
(from tool-box!)
You can undo using Ctrl + Z.
You can also use Ctrl + A to select all drawing for dragging purpose!
If you want to add new features in the Canvas Designer; follow these steps:
You've to add something through which end-users can use/access your feature – you can add any HTML element for this purpose (e.g. checkbox, radio-button, button, etc.) – however I recommend you add new toolbox icon instead! (which is a <canvas>
element!)
<div id="tool-box" class="tool-box">
........................................
<canvas id="icon-unique-id" width="40" height="40"></canvas>
</div>
After adding above HTML code, if you refresh the page; you'll see an empty icon appended in the bottom of the toolbox. Now you've to decorate this icon so end users can understand it!
Open decorator.js file and append following code anywhere in the file.
function givenName() { var context = getContext('icon-unique-id'); // 2d context relevant code here! (decoration code!!!) bindEvent(context, 'FeatureSelected'); } givenName();
FeatureSelected
is a global property used to understand the current state of your new toolbox icon (whether icon is selected or not). (Open common.js file)
var is = { ...................... isFeatureSelected: false, set: function (shape) { ...................... = is.isFeatureSelected = false; ............................................. } };
Now you've to create a new javascript file where you'll put all your "new feature" relevant code! Name the file like this: feature-handler.js
var featureHandler = {
ismousedown: false,
mousedown: function(e) {
this.ismousedown = true;
},
mouseup: function(e) {
this.ismousedown = false;
},
mousemove: function(e) {
if(this.ismousedown) { ... }
}
};
Now you've to bind mouse events to your newly created object. Open events-handler.js and append following code:
addEvent(canvas, isTouch ? 'touchstart' : 'mousedown', function (e) { ............................................................ else if (is.isFeatureSelected) featureHandler.mousedown(e); ............................................................ }); addEvent(document, isTouch ? 'touchend' : 'mouseup', function (e) { ............................................................ else if (is.isFeatureSelected) featureHandler.mouseup(e); ............................................................ }); addEvent(canvas, isTouch ? 'touchmove' : 'mousemove', function (e) { ............................................................ else if (is.isFeatureSelected) featureHandler.mousemove(e); ............................................................ });
Now you are 80% done! I strongly recommend you place your feature's drawing relevant code in the draw-helper.js file as I did for other features! - Reusability!!
This is the last step. You've to handle the output for textarea.
Open common.js file; there is a function "updateTextArea" in the "common" object – which is aimed to output into textarea element.
You don't have to change "updateTextArea". For simplicity purpose, code is separated in different functions/properties that you've to edit:
common.forLoop
common.absoluteNOTShortened
common.relativeShortened
common.relativeNOTShortened
Wait! – You've to edit "init
" function and drag-helper.js file too! – See below div.
This function is aimed to draw little transparent-red circles around the stretchable points of the shape so end-user can stretch the shape using those points! (To test it; draw a line, then click arrow [first-icon] on toolbox!) – This function is called on mousemove
event.
if (p[0] === 'your-shape') { tempContext.beginPath(); tempContext.arc(point[0], point[1], 10, Math.PI * 2, 0, !1); tempContext.arc(point[2], point[3], 10, Math.PI * 2, 0, !1); // For additional circles, copy above line and only change point's index! tempContext.fill(); }
You've to edit other two functions in the drag-helper.js file (It is absolutely necessary because it helps change points accordingly while dragging the shape!):
dragHelper.dragLastPath
dragHelper.dragAllPaths
/*≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡*/ // in "dragAllPaths": if (p[0] === 'your-shape-name') { points[i] = [p[0], [ getPoint(x, prevX, point[0]), getPoint(y, prevY, point[1]), ], p[2]]; } /*≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡*/ // in "dragLastPath": if (p[0] === 'your-shape-name') { if (g.pointsToMove === 'moveTo-points' || isMoveAllPoints) { point[0] = getPoint(x, prevX, point[0]); point[1] = getPoint(y, prevY, point[1]); } points[points.length - 1] = [p[0], point, p[2]]; } /*≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡*/ // in "mousedown" event (in the same file!) if (p[0] === 'your-shape-name') { if (dHelper.isPointInPath(x, y, point[0], point[1])) { g.pointsToMove = 'moveTo-points'; } } /*≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡*/
Now you're 100% done!
I did a detailed post about this: Dragging/Moving shapes smoothly using Canvas 2d APIs
In the drag-helper.js file – "global
" object contains following three properties:
prevX
prevY
pointsToMove
prevX/prevY
aimed to save previous mousedown
points so we can extract the dragged distance accordingly!
pointsToMove
aimed to understand which point of the shape is to move – e.g. first control points of the last Bezier curve or second control points (or moving/ending points!)
dragHelper.getPoint: function (point, prev, otherPoint) {
if (point > prev) point = otherPoint + (point - prev);
else point = otherPoint - (prev - point);
return point;
}
dragHelper.getPoint
returns the dragged distance!
By default, output in the textarea is provided in absolute coordinates. However, you can choose relative option too. Relative means points are relative to each other. If one or two points changes; all subsequent also changes, accordingly. Relative coordinates help you drag/move shape(s) at any portion of the screen without any bit of disturbance in the original shape!
You can set pageX/pageY (or offset top/left) values to x-y objects and move whole shape on mousemove/mouseup/mousedown or at any other event!
You can get shortened code for both formats: relative and absolute! Normal code is also provided there!