Guide
This guide explains a number of concepts that are particular to InfiniteCanvas
. It also mentions some of InfiniteCanvas
's limitations.
Infinity
On a usual <canvas>, a path can only contain finite points, i.e. points with finite x
and y
coordinates. On an InfiniteCanvas
, however, paths can also contain "points" that are at infinity. To describe such paths, two methods have been added to the InfiniteCanvasRenderingContext2D
, namely moveToInfinityInDirection(x, y)
and lineToInfinityInDirection(x, y)
.
Start a path at infinity
On a regular <canvas>, we can start a path at some point by calling moveTo(x, y)
. If we then call lineTo(x2, y2)
, we end up with a path that consists of a line segment connecting (x, y)
and (x2, y2)
. But on an InfiniteCanvas
, we can start a path at infinity by calling moveToInfinityInDirection(x, y)
. Once we have done this, the starting point of our current path is at infinity in the direction indicated by the vector with coordinates (x, y)
. For example, if we call moveToInfinityInDirection(1, 0)
, the starting point of our path will be at infinity to the right. If we now call lineTo(x3, y3)
, we end up with a path that consists of a ray from the point at (x3, y3)
towards infinity to the right.
ctx.lineWidth = 3;
ctx.strokeStyle = '#0f0';
ctx.beginPath();
ctx.moveTo(150, 50); // starting point of the green path
ctx.lineTo(50, 50); // end point of the green path
ctx.stroke();
ctx.strokeStyle = '#f00'
ctx.beginPath();
ctx.moveToInfinityInDirection(1, 0); // starting "point" of the red path
ctx.lineTo(50, 150); // end point of the red path
ctx.stroke();
Result:
Lines to infinity
On an InfiniteCanvas
, we can not only start paths at infinity, we can also extend our path by adding a line to infinity. We do this by calling lineToInfinityInDirection(x, y)
. This will add a ray that starts at the point where our path currently is, and that extends to infinity in the direction given by the vector (x, y)
. Consider this example:
ctx.lineWidth = 3;
ctx.strokeStyle = '#090';
ctx.fillStyle = '#00990044';
ctx.beginPath(); // begin the green path
ctx.moveTo(10, 10);
ctx.lineTo(10, 110);
ctx.lineTo(110, 110);
ctx.fill(); // fill the green path, which has only finite lines
ctx.stroke();
ctx.strokeStyle = '#900';
ctx.fillStyle = '#99000044';
ctx.beginPath(); // begin the red path
ctx.moveTo(10, 140);
ctx.lineTo(10, 240);
ctx.lineToInfinityInDirection(1, 0);
ctx.fill(); // fill the red path, which contains a line to infinity
ctx.stroke();
Result:
Note the filling of the red path: because the starting point of the path is at the top left, and the end "point" of the path is at infinity to the right, the filled area is the result of "connecting" the starting point to the end "point", which means adding another ray, but one that starts at the starting point at the top left and that extends to infinity to where the path starts.
Infinite rectangles
The methods rect(x, y, width, height)
, fillRect(x, y, width, height)
, strokeRect(x, y, width, height)
and clearRect(x, y, width, height)
of the InfiniteCanvasRenderingContext2D
accept Infinity
and -Infinity
as values for x
, y
, width
and height
.
Consider this example:
ctx.fillStyle = '#00009966'; // blue
ctx.fillRect(30, 30, 30, -Infinity) // A rectangle that extends upwards indefinitely
ctx.fillStyle = '#99000066'; // red
ctx.fillRect(60, 60, -Infinity, 30) // A rectangle that extends to the left indefinitely
ctx.fillStyle = '#00990066'; // green
ctx.fillRect(90, 60, Infinity, 30) // A rectangle that extends to the right indefinitely
ctx.fillStyle = '#99990066'; // yellow
ctx.fillRect(30, 120, 30, Infinity) // A rectangle that extends downwards indefinitely
ctx.fillStyle = '#99009966'; // magenta
ctx.fillRect(-Infinity, 150, Infinity, 30) // A rectangle that extends both left and right indefinitely
ctx.fillStyle = '#00999966'; // cyan
ctx.fillRect(120, -Infinity, 30, Infinity) // A rectangle that extends both upwards and downwards indefinitely
Result:
WARNING
Be careful about supplying Infinity
or -Infinity
as values for x
or y
in the rect(x, y, width, height)
method. This method is different from the others in that it does not draw anything, but only modifies the current path. In doing so, it will try to ensure that the end point of the current path will end up at the point specified by x
and y
. But it can only do that if x
and y
together specify either a point (i.e. x
and y
are both finite), or if they specify a "point" at infinity (i.e. one of x
and y
is finite and the other is not). If neither x
nor y
is finite, rect(x, y, width, height)
will throw an error.
Filling or clearing the entire canvas
The entire InfiniteCanvas
can be filled or cleared by calling fillRect(-Infinity, -Infinity, Infinity, Infinity)
or clearRect(-Infinity, -Infinity, Infinity, Infinity)
, respectively.
Events
InfiniteCanvas
exposes events in roughly the same way that an HTML element does. This means that you can add and remove event listeners using the same addEventListener()
method that is present on regular HTML elements.
Mouse events
One particularity about the MouseEvent
s that are emitted by InfiniteCanvas
(i.e. those that are passed to event listeners to 'click'
, 'mouseover'
, 'pointerdown'
etc.) is the fact that some of the properties have values that are different from those on the corresponding MouseEvents
that are emitted by the <canvas>
element. For MouseEvents
emitted by an InfiniteCanvas
, the values of the offsetX
, offsetY
, movementX
and movementY
properties are relative to the transformed coordinate system of the InfiniteCanvas
, and not relative to the coordinate system of the <canvas>
element. The same is true for the width
and height
properties of the PointerEvent
s and of the deltaX
and deltaY
properties of the WheelEvent
s.
Touch events
Every Touch
object that is present on a TouchEvent
that is emitted by InfiniteCanvas
has two additional properties, infiniteCanvasX
and infiniteCanvasY
, which represent the x and y coordinates, respectively, of the touch in question as seen from the transformed coordinate system of the InfiniteCanvas
.
Preventing defaults
When the left mouse button is pressed down while the mouse is over an InfiniteCanvas
, the InfiniteCanvas
will emit a 'mousedown'
event. Under normal circumstances, the InfiniteCanvas
will now begin to pan once the mouse is moved. If, however, an event listener for 'mousedown'
has been added to the InfiniteCanvas
, and this event listener has called preventDefault()
on the MouseEvent
that was emitted, panning will be cancelled. The same is true for the 'pointerdown'
event that is also emitted in this case: calling preventDefault()
on that one will also cancel panning.
The same applies
- with respect to rotating and the
'mousedown'
and'pointerdown'
events for the second mouse button, - with respect to zooming and the
'wheel'
event and - with respect to all transformations and the
'touchstart'
event.
In all these cases, calling preventDefault()
with true
as first and only argument will also call preventDefault()
on the corresponding event that was emitted by the <canvas>
element.
Limitations
The one thing that InfiniteCanvas
does is to add infinity to the 2D surface on which you can draw. But this comes at a cost: every time you pan, zoom or rotate, the drawing has to be redrawn. This means the drawing has to be kept in memory, which also means that replacing the entire drawing with a new drawing takes a little bit more time than if you were simply drawing on a regular <canvas>. In other words: animations are expensive on InfiniteCanvas
.
WARNING
InfiniteCanvasRenderingContext2D
currently does not support getImageData()
, createImageData()
, isPointInPath()
, isPointInStroke()
, drawFocusIfNeeded()
and scrollPathIntoView()