npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

puppet-canvas

v0.1.0

Published

HTML5 Canvas implementation for NodeJS backed by puppeteer

Downloads

8,877

Readme

puppet-canvas

puppet-canvas is a Puppeteer backed implementation of HTML Canvas API for NodeJS.

Installation

$ npm install puppet-canvas --save

Usage

Following example draws a house and grabs the data url of the image.

import { createCanvas, close } from 'puppet-canvas';
// OR const { createCanvas, close } = require('puppet-canvas')

async(() => {

const canvas = await createCanvas(400, 400);
const ctx = await canvas.getContext('2d');

// Draw a house
ctx.lineWidth = 10;
ctx.strokeRect(75, 140, 150, 110);
ctx.fillRect(130, 190, 40, 60);
ctx.moveTo(50, 140);
ctx.lineTo(150, 60);
ctx.lineTo(250, 140);
ctx.closePath();
ctx.stroke();

// Get the image as a data url
const dataUrl = await canvas.toDataURL();

// Release
await close();

})();

house image

Using images

To use external images in your canvas, first load the image using the loadImage method.

import { createCanvas, loadImage } from 'puppet-canvas';

const canvas = await createCanvas(400, 400);
const ctx = await canvas.getContext('2d');
const image = await loadImage('https://....', canvas);
await ctx.drawImage(image, 100, 100);

Using complex objects

Any non-serializable object returned by puppeteer is automatically proxied as well. So one can use/pass more complex objects like a gradient:

const gradient = await ctx.createLinearGradient(20, 0, 220, 0);
gradient.addColorStop(0, 'green');
gradient.addColorStop(.5, 'cyan');
gradient.addColorStop(1, 'green');
ctx.fillStyle = gradient;
ctx.fillRect(20, 20, 200, 100);

Using custom fonts

When using a custom font for rendering text, you can ensure the font is loaded by calling the loadFont methods.

import { createCanvas, loadFont } from 'puppet-canvas';

const canvas = await createCanvas(400, 400);
const ctx = await canvas.getContext('2d');

await loadFont(
  'Bangers',
  'https://fonts.gstatic.com/s/bangers/v12/FeVQS0BTqb0h60ACH55Q2J5hm24.woff2',
  canvas
);
ctx.font = `bold 48px Bangers`;
ctx.fillText('Hello world', 50, 100);

Implementation

puppet-canvas creates a canvas on a puppeteer instance, and exposes the API via a JavaScript Proxy.

A side effect of this is that all calls, essentially become async. For normal drawing, one doesn't need to await each command unless a value is being returned.

A proxied solution is somewhat better than alternate ones because, firstly, the rendering is exactly what the browser would have rendered (Chrome). Secondly, this is mostly future-proof since all methods are just proxied to the actual instance. So, any new API added to the Canvas, should automagically work.

Implentation Shortcomings

Though a proxied implementation is simpler, smaller, and more flexible, it can be slower than alternative native implementations. For example, manipulating lots of pixels, one pixel at a time, may be slow and not recommended.

The other shortcoming is from a dev experience, everything is an async call. So it creates more code for getting child object and properties. The following code:

const imageDataLength = ctx.createImageData(10, 10).data.length;

has to be refactored as

const imageData = await ctx.createImageData(10, 10);
const imageDataLength = await (await imageData.data).length;

Full API

createCanvas(width: number, height: number) => Promise<HTMLCanvasElement>

Creates a canvas instance with the specified width and height (in pixels)

linkCanvas(canvas: ElementHandle<HTMLCanvasElement>) => Promise<HTMLCanvasElement>

Say, you want to use this with an existing instance of puppeteer, you can pass in the ElementHandle of the canvase in your page.

close() => Promise

Close associated puppeteer instance. Usually called at the end.

releaseCanvas(canvas: HTMLCanvasElement) => Promise

Release the canvas instance, if you do not want puppet-canvas to proxy it anymore, but still want to keep the canvas instance around

screenshotCanvas(canvas: HTMLCanvasElement, options?: ScreenshotOptions) => Promise<string | Buffer>

Take a screenshot of the canvas. The method optionally takes in ScreenshotOptions which are the same options as described in Puppeteer screenshot method

loadFont(name: string, url: string, canvas: HTMLCanvasElement) => Promise

Load a font for the canvas element to use. name is the font-family name of the font. url is the url to the font file (like a woff file)

loadImage(url: string, canvas: HTMLCanvasElement, page?: Page) => Promise<HTMLImageElement>

Load an image from a URL that could be used by the canvas, e.g. for drawing the image on the canvas.

License

MIT License (c) Preet Shihn