Skip to content
On this page

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.

js
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:

js
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:

js
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 MouseEvents 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 PointerEvents and of the deltaX and deltaY properties of the WheelEvents.

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()