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

magic-painting

v0.1.1

Published

JavaScript lightweight library for easy interactive beautiful artistic abstract panting

Downloads

37

Readme

Magic Painting project

JavaScript lightweight library for easy interactive beautiful artistic abstract panting.

See demo image demo image or web app.

Quick navigation

Table of contents

Overview

MagicPainting is a lightweight JavaScript library for easy interactive beautiful artistic abstract panting.

It uses HTML5 Canvas API and its 2D context. It provides a higher-level abstraction allowing a developer to focus on the actual painting.

Classes and features

MagicPainting

The MagicPainting class takes care of:

  • mouse/touch events
  • window to canvas coordinate transformation
  • schedules the painting (using requestAnimationFrame)
  • undo, redo, save, clear
  • communication with the primary Effect (settings, transformations, ...)
Context

The Context class is a wrapper around CanvasRenderingContext2D. Its main purpose is to automatically apply transformations (symmetry, rotation etc.), so if something is painted at one place, it is simultanously painted on other relevant places "out of the box", too. Currently implemented are:

  • symmetry along x axis
  • symmetry along y axis
  • symmetry along x axis, y axis and center
Effect

The Effect class is the base class for actual painting. Its subclasses implements what to do based on given events (i.e. their coordinates and times) Each effect can have a parent effect and descendant child effect(s).

Compatibility

Tested on Google Chrome 94

Usage

Node

Installation

npm install magic-painting --save-dev

Package contains source code as well as bundled and minified javascript files. The index.min.js file defines MagicPainting namespace, equivalent to module import * as MagicPainting from "..." counterpart.

Web

Have a look at examples/web-minimal directory.

Module import from source code

You can use module import from source code. Minimal example:

<script type="module">
import * as MagicPainting from "/path/to/src/index.js"
window.onload = () => {
    var painting = new MagicPainting.MagicPainting(document.body)
    painting.effect = new MagicPainting.effects.StreamLines()
}
</script>

Using global namespace from bundled file

You can load bundled script defining global namespace. The bundled script may be a local file

<script src="/path/to/index.min.js"></script>

or a file loaded from CDN (e.g. unpkg.com or similar made from NPM package).

<script src="https://unpkg.com/[email protected]/index.min.js"></script>

followed by

<script>
window.onload = () => {
    var painting = new MagicPainting.MagicPainting(document.body)
    painting.effect = new MagicPainting.effects.StreamLines()
}
</script>

Extending

Let's see how to implement a simple new Effect!

See it live here and its source code here or here .

This is just a minimal example to show the basic usage. Of course, adding colors, styles, opacity, curvature etc. etc. makes the Effect much more effective.

// example primary effect
class ExampleEffect extends MagicPainting.Effect {
    constructor() {
        super()
        // what to do on pointer start
        this.onPointerStart((x,y,t) => {
            // stores the coordinates
            this.xy = [{x,y}]
            // add new effect (which will be started just after)
            this.add(new ExampleStartEffect())
        })
        // what to do on pointer move
        this.onPointerMove((x,y,t) => {
            // updates coordinates
            var {x:x0,y:y0} = this.xy
            this.xy = {x,y}
            var dx = x - x0
            var dy = y - y0
            var context = this.background
            // draw line from previous to current coordinates
            context.stroke(() => {
                context.moveTo(x0,y0)
                context.lineTo(x,y)
                context.context.lineWidth = 10
                context.context.strokeStyle = "cyan"
            })
            // draw perpendicular lines
            context.stroke(() => {
                context.moveTo(x,y)
                context.lineTo(x+dy,y-dx)
                context.context.lineWidth = 5
                context.context.strokeStyle = "white"
            })
        })
        // what to do on pointer end
        this.onPointerEnd(t => {
            // finish is important for undo / redo
            this.finish()
        })
    }
}
// MagicPainting.effect must be a primary effect. The easiest is
ExampleEffect.bePrimary()

// example secondary effect
class ExampleStartEffect extends MagicPainting.Effect {
    constructor() {
        super()
        // what to do on pointer start
        this.onPointerStart((x,y,t) => {
            // stores coordinates and time
            this.xy = {x,y}
            this.t = t
        })
        // what to do on animation frame
        this.onAnimationFrameEnd(t => {
            // draw on foreground
            this.draw(this.foreground,t)
        })
        // what to do on pointer end
        this.onPointerEnd(t => {
            // draw on background
            this.draw(this.background,t)
            // remove itself from its parent
            this.remove()
        })
    }
    // what and how to draw (a circle whose radius depends on time elapsed from pointer start)
    draw(context,time) {
        var {x,y} = this.xy
        var r = 10*(1+Math.cos((time-this.t)/300))
        context.fill(() => {
            context.arc(x,y,r,0,2*Math.PI)
            context.context.fillStyle = "red"
        })
    }
}

For more information, see existing effects here and here and/or the documentation.

TODO (sorted by priority)

  • improve Context class w.r.t. transformations (only necessary is done yet)
  • add more transformations
  • add more effects
  • improve existing effects

Contributing

is welcome :-)

Ideas

Ideas about existing and new effects, UI/UX, ...

Bugs

issue tracker

Code

Please read CONTRIBUTING.md file

Maintainer

Jan Stránský

License

MIT