Lines and arrows
Draw paths with as many points as you need, pick sharp, round or elbow, choose from 16 line ends, and bind arrows to shapes.
Lines and arrows are the shapes people draw the most, and the ones that feel wrong fastest when they're limited. In CanvasFlow both take any number of points, so you can sketch a chart that goes up and down, a zig-zag, or a curve that bends exactly where you want.
A live CanvasFlow editor runs here when JavaScript is on.
Drawing#
There are two ways to draw with the line tool (L) or the arrow tool (A):
Drag for a quick two-point line. Release and you're done.
Click to start a path, then keep clicking to add points:
| To… | Do this |
|---|---|
| Add a point | Click |
| Finish | Double-click, press Enter or Esc, or click the last point again |
| Remove the last point | ⌫ |
| Keep a segment on a 15° step | Hold ⇧ |
| Finish on a shape | Click the shape: the arrow binds to it and finishes by itself |
| Skip snapping and binding | Hold ⌘ (or Ctrl) |
A single click followed by Esc leaves nothing behind, and the whole path is one undo step.
Editing points#
Select a line or arrow to see its handles:
- Drag a point to move it. Hold ⇧ to snap the segment to 15° steps.
- Drag the small dot in the middle of a segment to add a point there. On round lines the dot sits on the curve itself.
- Double-click a point to remove it.
- Shift-click with the line or arrow tool to extend the selected one with a new point.
- Drag a line's last point onto its first to close it into a shape you can fill.
Line types#
Three types, chosen with arrowKind (the style panel calls it "Line type"):
arrowKind: 'straight'arrowKind: 'curved'arrowKind: 'elbow'| Value | Looks like | Notes |
|---|---|---|
straight |
Sharp corners at every point | The default |
curved |
A smooth curve through every point | The curve always passes through your points, so you control it directly |
elbow |
Right angles | Arrows only. Routed automatically between the two ends, around the shapes they connect |
Line ends#
Sixteen ends, for either end of a line or an arrow. The last five are crow's-foot marks for entity-relationship diagrams.
nonearrowtriangletriangle-outlinedotcircle-outlinediamonddiamond-outlinesquaresquare-outlinebaronemanyone-or-manyzero-or-onezero-or-manySet them with arrowheadStart and arrowheadEnd. New arrows start with an arrow at the end; new lines start without ends.
In code#
A line is a list of points. An arrow has a start, an end, and any number of mid points between them. All points are in the shape's local space, relative to its x and y.
// A small line chart: up, down, up, with a dot at the start and an arrow at the end.
editor.createShape({
type: 'line',
x: 100,
y: 300,
props: {
points: [
{ x: 0, y: 0 },
{ x: 80, y: -90 },
{ x: 160, y: -40 },
{ x: 240, y: -150 },
],
arrowheadStart: 'dot',
arrowheadEnd: 'arrow',
},
})
// An ER relation between two boxes: one user has many orders.
const users = editor.createShape({ type: 'geo', x: 0, y: 0, props: { w: 140, h: 80, text: 'Users' } })
const orders = editor.createShape({ type: 'geo', x: 400, y: 160, props: { w: 140, h: 80, text: 'Orders' } })
const relation = editor.createShape({
type: 'arrow',
x: 0,
y: 0,
props: {
start: { x: 140, y: 40 },
mid: [{ x: 270, y: 40 }],
end: { x: 400, y: 200 },
arrowheadStart: 'one',
arrowheadEnd: 'many',
},
})
editor.bindArrow(relation, 'start', users, { x: 0.5, y: 0.5 }, false)
editor.bindArrow(relation, 'end', orders, { x: 0.5, y: 0.5 }, false)
Bindings#
An arrow end can be bound to a shape. Move the shape and the arrow follows.
- A loose binding (
isPrecise: false) aims at the shape's center and stops at its outline, with a small gap. This is what you get when you drop an end anywhere inside a shape. - A precise binding (
isPrecise: true) sticks to an exact point on the shape, given as an anchor between 0 and 1 in each direction. You get one when you drop an end near the outline.
With middle points, a loose end aims along its own segment (toward the nearest point), so curved and multi-point arrows meet shapes naturally.
editor.getArrowBindings(arrowId) // { start?: ArrowBinding, end?: ArrowBinding }
editor.unbindArrow(arrowId, 'end')
Moving an arrow by itself releases its bindings. Moving it together with the shapes it connects keeps them.
Labels#
Double-click an arrow (or press Enter with it selected) to type a label. The line breaks around the label so dashes never run through the words. labelPosition (0 to 1) places it along the path.