Canvas Designer Help

Try: HTML5 Canvas Designer/Tool

Introducing the tool!

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 !

Note: Source code viewers can view this HTML file!

How files are arranged?

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.

How I store coordinates?

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:

  1. path-name can be line/arc/rect/quadratic/bezier etc.
  2. path-coordinates are the shape's x/y coordinates + width, height, radius etc.
  3. path-options are line-width, stroke/fill styles, global alpha/composite-operation, line cap/join etc.

Digging into the code!

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!

How to copy and paste?

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:

  1. Copy last path
  2. Copy all paths (at a time!)

Remember: Ctrl + MouseDown will only work if one option is selected: DragAllPaths or DragLastPath (from tool-box!)

How to Undo?

You can undo using Ctrl + Z.

You can also use Ctrl + A to select all drawing for dragging purpose!

How to Contribute?

If you want to add new features in the Canvas Designer; follow these steps:

1st Step: Add new toolbox icon!

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>

2nd Step: Decorate the icon!

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;
		.............................................
	}
};

3rd Step: Create new JavaScript file

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) { ... }
	}
};

4th Step: Bind mouse events

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!!

5th Step: Handle the output (textarea!)

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:

  1. common.forLoop
  2. common.absoluteNOTShortened
  3. common.relativeShortened
  4. common.relativeNOTShortened

Wait! – You've to edit "init" function and drag-helper.js file too! – See below div.

What "init" function in drag-helper.js do?

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!):

  1. dragHelper.dragLastPath
  2. 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!

How I drag shapes?

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:

  1. prevX
  2. prevY
  3. 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!

Explaining relative and absolute coordinates

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!