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