magic-painting
TypeScript icon, indicating that this package has built-in type declarations

0.1.1 • Public • Published

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/magic-painting@0.1.1/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

Readme

Keywords

Package Sidebar

Install

npm i magic-painting

Weekly Downloads

6

Version

0.1.1

License

MIT

Unpacked Size

6.83 MB

Total Files

75

Last publish

Collaborators

  • stranskyjan