Creating Tools¶
Note
If your method of adding tools is to fork the project or edit the generated JS, please don’t. If you need custom tools for your particular application, you can do that without changing Literally Canvas’s source code. That is the entire purpose of this API.
If you want to contribute a tool to the project, then by all means, clone the repository and send us a pull request.
Literally Canvas has two APIs for creating tools: a simple API (old style) and a normal API (new style).
For both APIs, a tool is an object with a specific set of keys.
name
Unique identifier for the tool.
iconName
Name of the image to be used in the toolbar for this tool, not including the extension. The full URL of the image will be
{imageURLPrefix}/{iconName}.png
.optionsStyle
Which toolbar to show while this tool is active. There is a crappy API available to add new possibilities. The built-in values are:
null
or'null'
Show nothing.
'stroke-width'
Show a stroke width picker and set the tool’s
strokeWidth
property.'font'
Set the tool’s
font
property to a canvas-compatible font string.
var MyTool = function(lc) { // take lc as constructor arg
var self = this;
return {
name: 'MyTool',
iconName: 'line',
optionsStyle: 'stroke-width',
strokeWidth: lc.opts.defaultStrokeWidth,
// ...more to follow
}
};
LC.init(el, {
// Add me to the toolbar
tools: LC.defaultTools.concat([MyTool])
});
Simple API¶
To implement a tool using the “simple” API, you just need to implement three
lifecycle methods: begin
, continue
, and end
.
Here’s an alternate version of the line tool.
Note
This code block hasn’t been tested, so you might have to file a bug report and get it fixed.
var MyTool = function(lc) { // take lc as constructor arg
var self = this;
return {
name: 'MyTool',
iconName: 'line',
strokeWidth: lc.opts.defaultStrokeWidth,
optionsStyle: 'stroke-width',
begin: function(x, y, lc) {
self.currentShape = LC.createShape('Line', {
x1: x, y1: y, x2: x, y2: y,
self.strokeWidth, color: lc.getColor('primary')});
},
continue: function(x, y, lc) {
self.currentShape.x2 = x;
self.currentShape.y2 = y;
lc.setShapesInProgress([self.currentShape]);
},
end: function(x, y, lc) {
self.currentShape.x2 = x;
self.currentShape.y2 = y;
lc.setShapesInProgress([]);
lc.saveShape(self.currentShape);
}
}
};
LC.init(el, {
// Add me to the toolbar
tools: LC.defaultTools.concat([MyTool])
});
Normal API¶
If you want more sophisticated behavior than just touch-drag-release, you can attach event handlers and listen to pointer events and do anything you like.
Here’s the same tool implemented using the normal API.
var MyTool = function(lc) { // take lc as constructor arg
var self = this;
return {
usesSimpleAPI: false, // DO NOT FORGET THIS!!!
name: 'MyTool',
iconName: 'line',
strokeWidth: lc.opts.defaultStrokeWidth,
optionsStyle: 'stroke-width',
didBecomeActive: function(lc) {
var onPointerDown = function(pt) {
self.currentShape = LC.createShape('Line', {
x1: pt.x, y1: pt.y, x2: pt.x, y2: pt.y,
self.strokeWidth, color: lc.getColor('primary')});
lc.setShapesInProgress([self.currentShape]);
lc.repaintLayer('main');
};
var onPointerDrag = function(pt) {
self.currentShape.x2 = pt.x;
self.currentShape.y2 = pt.y;
lc.setShapesInProgress([self.currentShape]);
lc.repaintLayer('main');
};
var onPointerUp = function(pt) {
self.currentShape.x2 = pt.x;
self.currentShape.y2 = pt.y;
lc.setShapesInProgress([]);
lc.saveShape(self.currentShape);
};
var onPointerMove = function(pt) {
console.log("Mouse moved to", pt);
};
// lc.on() returns a function that unsubscribes us. capture it.
self.unsubscribeFuncs = [
lc.on('lc-pointerdown', onPointerDown),
lc.on('lc-pointerdrag', onPointerDrag),
lc.on('lc-pointerup', onPointerUp),
lc.on('lc-pointermove', onPointerMove)
];
},
willBecomeInactive: function(lc) {
// call all the unsubscribe functions
self.unsubscribeFuncs.map(function(f) { f() });
}
}
};
LC.init(el, {
// Add me to the toolbar
tools: LC.defaultTools.concat([MyTool])
});
Tools can call any method on the given LiterallyCanvas()
object.
Usually you’ll be drawing and adding shapes, but you can also set colors, pan,
zoom, trigger events, and more. If you’re feeling adventurous, you can add
new DOM nodes to lc.containerEl
with on-screen UI, like the text and
polygon tools do. Just make sure you clean up when your tool is deactivated!
There may eventually be a better API for adding UI to tools.
Options styles¶
Note
This is the roughest edge of the extensibility API. You may need to make minor changes to your custom tools in the future when we improve it.
When you activate a tool, the bottom toolbar changes to show the tool’s current
state. The two built-in options styles are 'stroke-width'
and font
.
The stroke-width
style will “magically” set tool.strokeWidth
on the
active tool when the user clicks an option.
The font
style will set tool.text
to the user-entered text, and
tool.font
to a canvas-compatible font string like italic 18px Garamond
.
To define your own options style, use this function:
-
LC.
defineOptionsStyle
(identifier, ReactComponent)¶ - Arguments
identifier – String used as the value of tool.optionsStyle to attach this options style to that tool.
ReactComponent – A React component taking the props
lc
andtool
.lc
is theLiterallyCanvas()
instance, andtool
is the tool instance. This component will be inserted into the bottom toolbar.
For examples, read the source files src/optionsStyles/stroke-width.coffee
and src/optionsStyles/font.coffee
. If you need additional assistance, and
already understand React.js, please ask the mailing list for help. Our
response time is great.
You should open GitHub issues if you would like specific UI or code improvements to the existing options styles, or if you think a new kind of generalized option style would helpful.