3.1.4 • Published 2 months ago

@bucky24/react-canvas v3.1.4

Weekly downloads
18
License
ISC
Repository
-
Last release
2 months ago

React-Canvas

This module is intended to be a library that allows an HTML5 <canvas> element to exist seamlessly with a React application, using React-like components to allow drawing onto the canvas using JSX.

Installation

With NPM

npm install --save @bucky24/react-canvas

With Yarn

yarn add @bucky24/react-canvas

ReactCanvas has a peer dependency on react. It requires version 18 at minimum.

Usage

The module exports a series of components that can be used in JSX. To use, declare a canvas of specific width and height inside your React app, then use the same nesting syntax as the rest of React to draw various elements. The components register and behave (with some exceptions) as normal React components, so most standard behaviors with components should work for them.

Warning

These modules must be inside a Canvas object to work properly, and normal HTML tags cannot be used inside them. So you can't nest ReactCanvas tags, then HTML tags, then more ReactCanvas tags.

Available Elements

Canvas

The root level element. This will actually create a <canvas> canvas tag on the page. However, it will also pass the 2D context object into React context of all its children. This allows the children to draw to the canvas object.

Parameters

ParameterDescription
widthThe width, in pixels, of the resulting canvas
heightThe height, in pixels, of the resulting canvas
captureAllKeyEventsIf this is set to false, any events not originating from the body (which is the default when no input field is selected) will be ignored. If this is set to true (default), all key events will be captured. You should set this to false if you have any dom elements outside of the canvas that need to handle key events.
drawWidthThe width, in pixels, that the canvas should use when drawing. Defaults to width
drawHeightThe height, in pixels, that the canvas should use when drawing. Defaults to height
Children

Accepts multiple children. Children should be valid ReactCanvas elements.

Example
return (<div>
	<Canvas
		width={300}
		height={300}
	>
	    {reactCanvasElements}
	</Canvas>
</div>);

Shape

The Shape element is simple-it draws a shape centered around a given point. Note that there have to be at least 3 entries in the points array

Parameters
ParameterDescription
xThe origin x coord of the shape
yThe origin y coord of the shape
pointsAn array containing objects with x and y parameters. This will form the body of the shape. Note that all coords in this array are relative to the x and y given as top level params.
colorA hex color code, which determines the color of the shape
filla boolean value, which determines if the shape is drawn as an outline (false) or a filled in shape (true)
closeA boolean value (default true) which determines if the shape's path is closed before drawing, or left empty. Closing the path means a line will be drawn from the last point to the first point.
Example
<Canvas
	width={300}
	height={300}
>
	<Shape
		x={40}
		y={0}
		points={[
			{ x: 10, y: 10},
			{ x: 100, y: 10 },
			{ x: 10, y: 100}
		]}
		color="#f00"
		fill={true}
	/>
</Canvas>

Text

Draws text to the screen at the given coordinates.

Parameters
ParameterDescription
xThe origin x coord of the text
yThe origin y coord of the text
colorthe color for the text (default black)
fontthe font for the text (default 12px Arial)
Children

Accepts a single child, which is the text to be displayed

Example
<Canvas
	width={300}
	height={300}
>
	<Text
		x={5}
		y={60}
	>
		Some text here
	</Text>
	<Text
		x={5}
		y={80}
		color="#00f"
		font="18px Times New Roman"
	>
		Blue Text
	</Text>
</Canvas>

Image

The Image element takes care of loading and displaying an image asset to the canvas.

Images are cached after first load, so re-using the same src will not cause the image to be loaded a second time.

Parameters
ParameterDescription
xThe origin x coord to draw the image at
yThe origin y coord to draw the image at
srcThe URL that the image can be found at. This can also be base-64 encoded image data
widthThe width to draw the image at
heightThe height to draw the image at
clipSee Image Clipping below
rotRotation angle in degrees
flipXBoolean, indicates if the image should be flipped on the X axis
flipYBoolean, indicates if the image should be flipped on the Y axis
onLoadFunction that is called when the image loads. If the image is already loaded, the function will not be called (note, using this function can cause optimization issues)
Example
<Canvas
	width={300}
	height={300}
>
	<Image
		src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"
		x={40}
		y={50}
		width={50}
		height={50}
	/>
</Canvas>
Base64 Encoded Example
const imageData = "data:image/png;base64,<some base 64 encoded data here>";
return <Canvas
	width={300}
	height={300}
>
	<Image
		src={imageData}
		x={40}
		y={50}
		width={50}
		height={50}
	/>
</Canvas>;
Image Clipping

The clip parameter allows drawing of only part of the image. It takes in the following parameters:

ParameterDescription
xX coord of where to start the clip
yY coord of where to start the clip
widthThe width of the clip
heightThe height of the clip

Note that these parameters are all translated into image space from draw space. So for example, if I had an image that was 50x50, but I was drawing it like this:

	<Image
		src={src}
		x={40}
		y={50}
		width={200}
		height={200}
		clip={{
			x: 100,
			y: 100,
			width: 50,
			height: 50,
		}}
	/>
</Canvas>;

The x coordinate is 50% of the final width of the image, meaning that when it is translated, it translates into half of the image width, so it becomes 25. This is to avoid the developer needing to know the size of the image before it's loaded (and becomes important because the Image component does not expose the image data). So when using clip, just use the width and height that you're passing into the Image component to make your calculations about how much to clip.

You can also observe the imageExample in the source to see how this is used.

Images

The Images element draws multiple images to the screen. This can be helpful when you need to draw a lot of images but don't want to have the overhead of a lot of React components. In general this component behaves exactly like the Image component.

Parameters
ParameterDescription
imagesA list containing objects that conform to the parameters for the Image component
onLoadFunction that is called when any images load. If all images are already loaded, the function will not be called. The callback function will be given a single param: the src of the image that was loaded. If the same image is listed multiple times in 'images', the callback will be called multiple times for the same src. Like Image, using this can cause slowness.
Example
<Canvas
	width={300}
	height={300}
>
	<Images
		images={[
			{
				src:"https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png",
				x:40,
				y:50,
				width:50,
				height:50,
			},
		]}
	/>
</Canvas>

Line

The Line element draws a line of a specific color between two given points.

Parameters
ParameterDescription
xThe origin x coord of the line
yThe origin y coord of the line
x2The destination x coord of the line
y2The destination y coord of the line
colorA hex color code, which determines the color of the line
Example
<Canvas
	width={300}
	height={300}
>
	<Line
		x={40}
		y={0}
		x2={100}
		y2={150}
		color="#f00"
	/>
</Canvas>

Rect

The Rect element is just a wrapper around Shape that returns a rectangle drawn between two points.

Parameters
ParameterDescription
xThe first x coord of the rectangle
yThe second y coord of the rectangle
x2The second x coord of the rectangle
y2The second y coord of the rectangle
colorA hex color code, which determines the color of the rectangle
fillBoolean, indicates if the rectangle should be filled or an outline
Example
<Canvas
	width={300}
	height={300}
>
	<Rect
		x={40}
		y={0}
		x2={100}
		y2={150}
		color="#f00"
		fill={false}
	/>
</Canvas>

Circle

Draws a circle with a specific position and radius.

Parameters
ParameterDescription
xThe x position of the circle center
yThe y position of the circle center
radiusThe radius of the circle
colorA hex color code, which determines the color of the circle
fillBoolean, indicates if the circle should be filled or an outline
Example
<Canvas
	width={300}
	height={300}
>
	<Circle
		x={40}
		y={0}
		radius={10}
		color="#f00"
		fill={false}
	/>
</Canvas>

Arc

Draws a semi-circle between two angles with a specific position and radius.

Parameters
ParameterDescription
xThe x position of the arc center
yThe y position of the arc center
radiusThe radius of the arc
startAngleThe start angle in radians of the arc
endAngleThe eng angle in radians of the arc
sectorBoolean, indicates if the drawn shape should be a slice of pie (true) or just the outer part of the circle (false)
colorA hex color code, which determines the color of the arc
fillBoolean, indicates if the arc should be filled or an outline
closedBoolean, determines if the arc should close its path (draw a line back to the start) or not
Example
<Canvas
	width={300}
	height={300}
>
	<Arc
		x={40}
		y={0}
		radius={10}
		startAngle={0}
		endAngle={Math.PI}
		color="#f00"
		fill={false}
		sector={false}
	/>
</Canvas>

Raw

Sometimes the basic elements contained in react-canvas aren't enough. For example, if you need to do a very complex shape with semi-circles and lines, currently there's no way to to do that, except creating a custom component with access to the raw canvas context.

That's where the Raw component comes in. It takes in a single prop, which is a callback it will call with the context as the first parameter, allowing you to access any low level functions or complex operations you need to.

Parameters
ParameterDescription
drawFnA callback that will be called on-render with a single param, which will be the context of the canvas. Note this is not a React context object. but rather an instance of CanvasRenderingContext2D
Example
<Canvas
	width={300}
	height={300}
>
	<Raw drawFn={(context) => {
		// do any low level canvas code here
	}} />
</Canvas>

Clip

Clip takes in parameters that define a rectangle, and a list of children. It will use the canvas context clip method to ensure that any part of those children that falls outside the rectangle is not drawn. This is different from the clip in Image, which determines the section of the source image to display.

Children

Acts as a container, it can handle any ReactCanvas based children.

Parameters
ParameterDescription
xThe start x of the clip rect
yThe start y of the clip rect
widthThe width of the clip rect
heightThe height of the clip rect
Example
<Canvas
	width={300}
	height={300}
>
	<Clip
		x={100}
		y={100}
		width={100}
		height={100}
	>
		<Text
			x={150}
			y={130}
		>
			Clipped text
		</Text>
    </Clip>
</Canvas>

Pattern

The Pattern element allows drawing an image in a repeated pattern in a rectangle.

Parameters
ParameterDescription
xX position to start the draw at
yY position to start the draw at
widthWidth of the rectangle
heightHeight of the rectangle
srcSimilar to image, a url, or raw image data to tile
Example
<Canvas
	width={300}
	height={300}
>
	<Pattern
        x={100}
        y={100}
        width={200}
        height={200}
        src="https://website.com/path/to/image"
    />
</Canvas>

CompoundElement

The CompoundElement is an attempt to improve rendering when there are a lot of objects on the screen. What it does is detect any time its children change. When this happens, it pre-renders its entire child list (using renderToCanvas) and then draws that rendered image moving forward. It can be given an x and y offset to move the drawn image around the screen. This component is very useful when you have a lot of objects all moving in the same direction that don't change very often (such as a background layer of a map).

Note that this component auto-detects the dimensions of its content. This requires two passes with the render which can be expensive. This also does not correctly handle every possible draw operation, and should be considered experimental.

Lastely, this component is not well named and the name may change in the future.

Children

Takes multiple children, must be ReactCanvas elements.

Parameters
ParameterDescription
xOffX position to offset the image draw at. Optional (defaults 0)
yOffY position to offset the image draw at. Optional (defaults 0)
extraDataAny extra data that gets passed into renderToCanvas. Use this when you need to pass context data to children. Note that any change to extraData will cause the element to re-render
zoomCurrent zoom level for the element. Optional (defaults 1)
maxZoomThe highest zoom level your component can go. Optional (defaults 1)
zoomAffectOffsetIf true, offsets will also be affected by zoom level (defaults false)
noZoomPositionIf true, position will not be affected by zoom level (defaults false)
zoomXOffX Offset to not zoom (see zoom below)
zoomYOffY Offset to not zoom (see zoom below)
Zoom

Zooming in and out of a component can be confused to not cause a re-render, similar to how the offset works. In the case of zooming, both zoom and maxZoom are treated such that a value of 1 is 100%, normal zoom level.

If you change the zoom parameter, the CompoundElement will not re-render its children, but rather will draw the pre-rendered image larger or smaller, as appropriate.

Because stretching images/text/etc tends to pixelate them, the maxZoom property can be used. When the maxZoom property is given, the resulting components are treated as if they were rendered at that level. This then allows the CompoundElement to only shrink the resulting image, and never expand it, which can improve pixelation.

Text does not perform well with small zoom levels.

Important: When using maxZoom, render all children of the CompoundElement as if that zoom was in effect. IE if a Rect is normally from (50,50) to (100,100), and your maxZoom is 2, render it from (100,100) to (200,200). The CompoundElement will take care of ensuring it draws the correct size.

Sometimes it becomes important to only zoom a portion of the image. For example, let's say you have a frame starting at (100, 100), and a rectangle at (125, 125) inside that frame. If you only want to zoom what is inside that frame, you don't want to have the zoom apply to the entire 125 of the rectangle's position, as that could cause it to move more than expected. That is what zoomXOff and zoomYOff are for. In this case you would set them to the coordinates of the frame, and only the last 25 of the rectangle would be subject to the zoom.

Example
<Canvas
	width={300}
	height={300}
>
	<CompoundElement
		width={400}
		height={400}
	>
		{images.map((image, index) => {
			return <Image key={index} src={sampleImage} x={image.x} y={image.y} width={50} height={50} />
		})}
	</CompoundElement>
</Canvas>

ImageStrip

The ImageStrip component is meant to be able to display certain portions of a sprite-sheet, one frame at a time.

Parameters
ParameterDescription
srcThe image to use as the spritesheet
xThe x coord of where to draw on the canvas
yThe y coord of where to draw on the canvas
widthHow wide the resulting image should be on the canvas
heightHow tall the resulting image shoudl be on the canvas
cellXThe x coord of the sprite on the spritesheet to draw
cellYThe y coord of the sprite on the spritesheet to draw
cellWidthHow wide each sprite in the sheet is
cellHeightHow tall each sprite in the sheet is
Example

This example renders the second sprite of the sheet, assuming a sheet with 75x75 px sprites. It renders the image at 100x100 at 50x50 resolution.

<Canvas
	width={300}
	height={300}
>
	<ImageStrip
		src={sampleImage}
		x={100}
		y={100}
		width={50}
		height={50}
		cellX={1}
		cellY={0}
		cellWidth={75}
		cellHeight={75}
	/>
</Canvas>

Animate

The Animate is meant to be able to automatically display the correct frame of a spritesheet, based on a global frame ticker.

The Animate component must be wrapped in an AnimationProvider to function.

Parameters

The following parameters are required but are the same as ImageStripe above: src, x, y, width, height, cellWidth, cellHeight

ParameterDescription
startXThe starting cellX on the sprite sheet
startYThe starting cellY on the sprit sheet
frameCountThe total number of frames in the animation
rowWidthThe number of spritesheet frames per row
Example

This example renders the animation

<Canvas
	width={300}
	height={300}
>
	<Animate
		src={sampleImage}
		x={100}
		y={100}
		width={50}
		height={50}
		startX={1}
		startY={0}
		cellWidth={75}
		cellHeight={75}
		frameCount={20}
		rowWidth={5}
	/>
</Canvas>

AnimationContext/AnimationProvider

The AnimationContext provides a way for the animation system to sychronize the animations as well as for your application to advance them.

The following values are exported by the context

ValueDescription
frameThe current animation frame. This continuously increases.
tickThis is a function. Call this function when you want the animation frames to move forward
Example
import React, { useContext, useEffect } from 'react';
import { AnimationContext } from '@bucky24/react-canvas';

import SampleImage from '../assets/sampleSpriteSheet.png';

export default function App() {
	const { tick } = useContext(AnimationContext);

	useEffect(() => {
		const interval = setInterval(tick, 100);

		return () => {
			clearInterval(interval);
		}
	});

	return <Canvas width={300} height={300}>
		<Animate
			src={SampleImage}
			x={100}
			y={100}
			width={50}
			height={50}
			startX={1}
			startY={0}
			cellWidth={75}
			cellHeight={75}
			frameCount={20}
			rowWidth={5}
		/>
	</Canvas>;
}

Events

Event List

The following events are available on the Canvas:

EventParams
onMouseMoveCoords
onMouseDownCoordsWithButton
onMouseUpCoordsWithButton
onKeyDownKeyData
onKeyUpKeyData
onWheelCoordsWithDirection

Coords

Coords is simply an object containing x and y as integers.

CoordsWithButton

CoordsWithButton contains x and y coordinates, as well as a button field that is one of the following:

Type
ButtonTypes.LEFT
ButtonTypes.MIDDLE
ButtonTypes.RIGHT

KeyData

KeyData is an object containing char which is the character of the key pressed, and code which is the key code.

CoordsWithDirection

CoordsWithDirection contains x and y coordinates, as well as an up field, which indicates if the scroll wheel was moved up (true) or down (false)

Handling on the Canvas

The quickest way to handle canvas events is via event handlers directly on the Canvas object. Any component containing a Canvas can listen for any events it emits via callbacks.

class MyComponent extends React.Component {
	constructor(props) {
		super(props);
	}
	render() {
		return (<div className={styles.appRoot}>
			<Canvas
				width={300}
				height={300}
				onMove={({ x, y, button }) => {
					console.log("mouse moved to", data.x, data.y);
				}}
				onMouseDown={({ x, y, button }) => {
				
				}}
				onMouseUp={({ x, y, button }) => {
				
				}}
			>
				{ /* some things here */ }
			</Canvas>
		</div>);
	}
};

CanvasComponent

If your components extend CanvasComponent (see below sections), you can handle events as native functions inside the component as React lifecycle methods.

If bounds is set on the child object (containing x, y, width, and height), the second parameter of the callback for all mouse events will be a boolean indicating if the operation took place within those bounds. If no bounds are set, then this boolean is always false.

Note that if you make a component a CanvasComponent but it is not nested inside a Canvas element, it will throw an error to your console because the child context doesn't exist.

Another thing of note: CanvasComponent consumes componentDidMount to do event handling. If you are using componentDidMount in your custom component, be sure to call super.componentDidMount() or else event handling will not work.

Example
import React from 'react';
import { CanvasComponent } from 'react-canvas';

class MyElement extends CanvasComponent {
	constructor(props) {
		super(props);
		
		this.bounds = {
			x: 100,
			y: 100,
			width: 20,
			height: 20
		};
	}
	onMouseMove({ x, y }, overMe) {
		// take some action
	}
	onMouseDown({ x, y, button }, overMe) {
		// take some action
	}
	onMouseUp({ x, y, button }, overMe) {
		// take some action
	}
	onKeyDown({ char, code }) {
		// do something
	}
	onKeyUp({ char, code }) {
		// do something
	}
	onWheel({ x, y, up }, overMe) {
		// take action
	}
	render() {
		// some rendering here
	}
}

export default MyElement;

Raw Event Handling

It is possible to hook into canvas events for your component by using the registerListener and unregisterListener functions on the Canvas context.

Both registerListener and unregisterListener take in two parameters: an EventType, and a closure that will be passed a data object when the event is triggered.

It is recommended that your component call registerListener only once, and that you call unregisterListener when the component is about to unmount, similar to any other listeners in react.

Event TypeTriggered byContents of Data
EventTypes.MOVEThe mouse moving across the canvasAn object containing x and y of the mouse on the canvas. May also contain a "touches" key if on a multi-touch device, which contains the x and y of all touches detected
EventTypes.MOUSE_UPThe mouse being releasedAn object containing x and y of the mouse on the canvas and a button corresponding to a ButtonType above
EventTypes.MOUSE_DOWNThe mouse being pressedAn object containing x and y of the mouse on the canvas and a button corresponding to a ButtonType above
EventTypes.KEY_DOWNA key being pressed or repeatedAn object containing the char value of the key and the key code
EventTypes.KEY_UPA key being releasedAn object containing the char value of the key and the key code
EventTypes.WHEELThe scroll wheel being spunAn object containing x and y of the mouse on the canvas, and a boolean "up" which indicates if the wheel is spinning up or down

Extending

You can easily create your own elements that have access to the canvas context.

Context

The following properties are available from the CanvasContext:

NamePurpose
contextThe raw canvas context. It is not recommend that you use this, see useWithContext below.
registerListenerFunction for event handling. See above for usage
unregisterListenerFunction for event handling. See above for usage
loadImageFunction that takes in a src and a cb function. If the image is already loaded, it will return the img object. If not, the cb function is called when the image is loaded
loadPatternFunction that takes in a src and a cb function. If the pattern is already loaded, it will return the canvas pattern object. If not, the cb function is called when the pattern is loaded
forceRenderCountThis is the number of times the forceRerender function has been called. This can be used to determine if a render is taking place because an image has loaded (as image loads call forceRerender by default)
widthThe width of the Canvas
heightThe height of the Canvas
forceRerenderForces the entire canvas to re-render
Example
import React, { useContext } from 'react';
import { CanvasContext } from 'react-canvas';

const MyElement = (props) => {
    const { context } = useContext(CanvasContext);
			
    if (!context) {
		return null;
	}
	context.save();

	// do context things here

	context.restore();
}

export default MyElement;
Rerendering

The canvas context also provides a function forceRerender which will essentially call this.forceUpdate() on the top level canvas. This can be useful in some situations to force the canvas to redraw.

renderToImage

React Canvas exports a method, renderToImage, that can take in a series of React Canvas components, and a width and height, then return a data string that represents the image that would be the result of those components being rendered to a canvas.

renderToImage does not need to be called from inside a component's render method.

context is not a required parameter, but it is recommended to pass in the context of the parent Canvas if possible. If you are trying to get the context from a top level component that exports the Canvas element, you can use a ref to the Canvas and call getMyContext to get the context object.

import React from 'react';
import {
    Line,
    renderToImage,
} from 'react-canvas';

const App() {
    const width = 300;
    const height = 100;

	const components = <>
		<Line
			x={10}
			y={10}
			x2={100}
			y2={10}
			color="#888"
		/>
		<Line
			x={10}
			y={10}
			x2={10}
			y2={100}
			color="#888"
		/>
	</>;

	const imageSource = renderToImage(components, width, height);

	return (<div>
		<img src={imageSource}>
	</div>);
};

export default App;

renderToCanvas

React Canvas exports a method, renderToCanvas, that does basically the same thing as renderToImage (and takes the same parameters), but instead of a base-64 data string, returns a canvas dom element that has had the given elements rendered to it. This is useful if you have images that are loaded from outside of your domain, as the browser will not allow these to be rendered to an image, but it will allow them to be rendered to a canvas.

The method returns an object containing the following properties:

PropertyTypeDescription
canvascanvasThe canvas object
dimsRectContains x, y, width, and height

This canvas can be passed into the src attribute of an Image element to render it.

This method does take the following additional properties:

PropertyTypeDescription
extraDatamixedUsed in case of a need to pass any sort of context data to rendered components. renderToCanvas breaks context chains. Any data passed in this method will be available in the RenderContext

blendImage

The blendImage function allows an existing image to be manipulated on the fly, generating a new image (as a Canvas) that can be drawn.

Parameters

NameDescription
srcThe source (url or base64 string) to blend
operationsAn array of BlendOperations. If empty, the original src is returned

Usage

blendImage(sampleImage, [
    {
        type: BLEND_TYPE.COLOR_SWAP,
        from: '#ffffff',
        to: '#0000ff',
    },
    {
        type: BLEND_TYPE.COLOR_SWAP,
        from: '#000000',
        to: '#00ff00',
    },
]).then((newImg) => {
    // newImg is a Canvas containing the rendered image, and can now be stored for drawing.
});

BlendOperation

Represents an operation that can be performed on an image

NameTypeDescription
typeBLEND_TYPEDescribes what operation to perform
**Other params determined by BLEND_TYPE, see below

BLEND_TYPE

COLOR_SWAP

BLEND_TYPE.COLOR_SWAP allows swapping one color in an image completely for another color. It expects the following parameers on the BlendOperation:

NameTypeDescription
fromHex ColorA string containing the RGB hex code to look for
toHex ColorA stirng containing the RGB hex code to use as a replacement

useWithContext

The useWithContext hook is the safest way to actually access the raw canvas context. This is because it takes into account any Clip elements that your component might be inside. It also protects against the context being null, so you don't have to worry about checking that in your components. It also handles the appropriate return value so that React doesn't complain about your component returning nothing.

Usage:

const React from 'react';
const { useWithContext } from '@bucky24/react-canvas';

export default function MyComponent() {
	const withContext = useWithContext();

	return withContext((context) => {
		// the context is now safe to use for raw drawing even if you're inside a clip region.
	});
}
3.1.3

2 months ago

3.1.2

2 months ago

3.1.4

2 months ago

3.1.1

2 months ago

3.1.0

2 months ago

3.0.2

12 months ago

3.0.1

12 months ago

3.0.0

12 months ago

2.2.1

1 year ago

2.2.0

1 year ago

2.2.2

1 year ago

2.0.0

1 year ago

1.9.2

2 years ago

1.9.1

2 years ago

1.9.0

2 years ago

1.8.0

2 years ago

1.7.3

2 years ago

1.7.2

2 years ago

1.7.1

2 years ago

1.7.0

3 years ago

1.6.5

3 years ago

1.6.4

4 years ago

1.6.3

4 years ago

1.6.2

4 years ago

1.6.1

4 years ago

1.6.0

4 years ago

1.5.9

4 years ago

1.5.8

4 years ago

1.5.7

4 years ago

1.5.6

4 years ago

1.5.5

4 years ago

1.5.4

4 years ago

1.5.3

4 years ago

1.5.2

4 years ago

1.5.1

4 years ago

1.5.0

4 years ago

1.4.0

4 years ago

1.3.1

4 years ago

1.3.0

4 years ago

1.2.3

4 years ago

1.2.2

4 years ago

1.2.1

4 years ago

1.2.0

4 years ago

1.1.4

4 years ago

1.1.3

4 years ago

1.1.2

4 years ago

1.1.1

4 years ago

1.1.0

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago

1.0.3

4 years ago

0.9.10

4 years ago

0.9.8

4 years ago

0.9.7

4 years ago

0.9.9

4 years ago

0.9.4

4 years ago

0.9.3

4 years ago

0.9.6

4 years ago

0.9.5

4 years ago

0.9.2

4 years ago

0.9.0

4 years ago

0.9.1

4 years ago

0.8.1

4 years ago

0.8.0

4 years ago

0.7.8

4 years ago

0.7.7

4 years ago

0.7.6

4 years ago

0.7.5

4 years ago

0.7.4

4 years ago

0.7.2

5 years ago

0.7.1

5 years ago

0.7.0

5 years ago

0.6.2

5 years ago

0.6.1

5 years ago

0.6.0

5 years ago

0.5.1

5 years ago

0.5.0

5 years ago

0.4.0

5 years ago

0.3.2

5 years ago

0.3.1

5 years ago

0.3.0

5 years ago