├── .gitignore ├── demo ├── collision.wat ├── index.js └── motion.wat ├── index.js ├── package.json └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | out 4 | *.wasm 5 | -------------------------------------------------------------------------------- /demo/collision.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func $begin (import "canvas" "path_begin")) 3 | (func $close (import "canvas" "path_close")) 4 | (func $circle (import "canvas" "path_circle") (param f32 f32 f32)) 5 | (func $clear (import "canvas" "clear")) 6 | (func $stroke (import "canvas" "path_stroke")) 7 | (func $set_fill_color (import "canvas" "set_fill_color") (param i32 i32 i32)) 8 | (func $create_rect (import "canvas" "create_rect") (param i32 i32 f32 f32)) 9 | 10 | (global $border f32 (f32.const 1)) 11 | 12 | (global $x (mut f32) (f32.const 50)) 13 | (global $y (mut f32) (f32.const 50)) 14 | (global $size (mut f32) (f32.const 20)) 15 | 16 | (global $vx (mut f32) (f32.const 0.3)) 17 | (global $vy (mut f32) (f32.const 0.2)) 18 | (global $speed (mut f32) (f32.const 7)) 19 | 20 | (func (export "render") 21 | (param $canvas_width f32) 22 | (param $canvas_height f32) 23 | 24 | (set_global $x 25 | (f32.add 26 | (get_global $x) 27 | (f32.mul (get_global $speed) (get_global $vx)) 28 | ) 29 | ) 30 | 31 | (set_global $y 32 | (f32.add 33 | (get_global $y) 34 | (f32.mul (get_global $speed) (get_global $vy)) 35 | ) 36 | ) 37 | 38 | (if 39 | (i32.or 40 | (f32.gt 41 | (f32.add (get_global $x) (get_global $size)) 42 | (f32.sub (get_local $canvas_width) (get_global $border)) 43 | ) 44 | (f32.lt 45 | (f32.sub (get_global $x) (get_global $size)) 46 | (get_global $border) 47 | ) 48 | ) 49 | (then 50 | (set_global $vx (f32.mul (get_global $vx) (f32.const -1.0))) 51 | ) 52 | ) 53 | 54 | (if 55 | (i32.or 56 | (f32.gt 57 | (f32.add (get_global $y) (get_global $size)) 58 | (f32.sub (get_local $canvas_height) (get_global $border)) 59 | ) 60 | (f32.lt 61 | (f32.sub (get_global $y) (get_global $size)) 62 | (get_global $border) 63 | ) 64 | ) 65 | (then 66 | (set_global $vy (f32.mul (get_global $vy) (f32.const -1.0))) 67 | ) 68 | ) 69 | 70 | (call $clear) 71 | 72 | (call $set_fill_color (i32.const 0xDF) (i32.const 0xDF) (i32.const 0xEE)) 73 | (call $create_rect 74 | (i32.const 0) (i32.const 0) 75 | (get_local $canvas_width) (get_local $canvas_height) 76 | ) 77 | 78 | (call $begin) 79 | (call $circle (get_global $x) (get_global $y) (get_global $size)) 80 | (call $stroke) 81 | (call $close) 82 | ) 83 | 84 | (func (export "set_speed") 85 | (param $speed f32) 86 | (set_global $speed (get_local $speed)) 87 | ) 88 | 89 | (func (export "set_velocity") 90 | (param $new_vx f32) 91 | (param $new_vy f32) 92 | (set_global $vx (get_local $new_vx)) 93 | (set_global $vy (get_local $new_vy)) 94 | ) 95 | ) 96 | -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | 2 | var { h, app } = require('hyperapp') 3 | var create = require('../') 4 | 5 | // Cavnas Element 6 | var canvas = document.createElement('canvas') 7 | var context = canvas.getContext('2d') 8 | 9 | // Demo modules 10 | var modules = { 11 | motion: require('./motion.wat'), 12 | collision: require('./collision.wat'), 13 | } 14 | 15 | app({ 16 | state: { 17 | active: null, 18 | }, 19 | actions: { 20 | select (state, actions, name) { 21 | modules[name]({ 22 | canvas: create(context), 23 | math: Math 24 | }).then(mod => { 25 | Object.assign(global, mod) 26 | actions.activate(mod) 27 | }) 28 | }, 29 | activate (state, actions, mod) { 30 | if (mod.start) mod.start(canvas.width, canvas.height) 31 | return { active: mod } 32 | }, 33 | render (state, actions) { 34 | if (state.active && state.active.render) { 35 | state.active.render(canvas.width, canvas.height) 36 | } 37 | window.requestAnimationFrame(actions.render) 38 | } 39 | }, 40 | events: { 41 | load (state, actions) { 42 | actions.select('motion') 43 | window.requestAnimationFrame(actions.render) 44 | } 45 | }, 46 | view (state, actions) { 47 | var btns = Object.keys(modules).map(name => { 48 | return h('button', { onclick: e => actions.select(name) }, name) 49 | }) 50 | 51 | return h('div', { 52 | class: 'app', 53 | onupdate (el) { el.appendChild(canvas) } 54 | }, [ 55 | h('div', { class: 'modules' }, btns) 56 | ]) 57 | } 58 | }) 59 | 60 | -------------------------------------------------------------------------------- /demo/motion.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func $clear (import "canvas" "clear")) 3 | (func $begin (import "canvas" "path_begin")) 4 | (func $close (import "canvas" "path_close")) 5 | (func $circle (import "canvas" "path_circle") (param f32 f32 f32)) 6 | (func $arc (import "canvas" "path_arc") (param f32 f32 f32 f32 f32)) 7 | (func $stroke (import "canvas" "path_stroke")) 8 | (func $line (import "canvas" "path_line") (param f32 f32)) 9 | (func $move (import "canvas" "path_move") (param f32 f32)) 10 | (func $_sin (import "math" "sin") (param f32) (result f32)) 11 | (func $_cos (import "math" "cos") (param f32) (result f32)) 12 | 13 | (global $rotation (mut f32) (f32.const 0)) 14 | (global $radius (mut f32) (f32.const 50)) 15 | (global $ry (mut f32) (f32.const 80)) 16 | (global $rx (mut f32) (f32.const 80)) 17 | 18 | (global $size (mut f32) (f32.const 10)) 19 | 20 | (func $cos (param $radius f32) (param $angle f32) (param $offset f32) (result f32) 21 | (return (f32.add (f32.mul (get_local $radius) (call $_cos (get_local $angle))) (get_local $offset))) ) 22 | (func $sin (param $radius f32) (param $angle f32) (param $offset f32) (result f32) 23 | (return (f32.add (f32.mul (get_local $radius) (call $_sin (get_local $angle))) (get_local $offset))) 24 | ) 25 | 26 | (func (export "render") 27 | (param $canvas_width f32) 28 | (param $canvas_height f32) 29 | 30 | (set_global $rotation (f32.add (get_global $rotation) (f32.const 0.1))) 31 | 32 | (call $clear) 33 | 34 | (call $begin) 35 | (call $circle 36 | (call $cos (get_global $radius) (get_global $rotation) (get_global $rx)) 37 | (call $sin (get_global $radius) (get_global $rotation) (get_global $ry)) 38 | (get_global $size) 39 | ) 40 | (call $stroke) 41 | (call $close) 42 | ) 43 | ) 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | const LINE_CAP = ['butt', 'round', 'square'] 3 | const LINE_JOIN = ['round', 'bevel', 'miter'] 4 | 5 | const solid = (obj) => { 6 | for (var key in obj) { 7 | if (typeof obj[key] === 'function') { 8 | obj[key] = obj[key].bind(obj) 9 | } 10 | } 11 | } 12 | 13 | module.exports = target => solid(target) || ({ 14 | create_rect: (x, y, w, h) => { 15 | target.fillRect(x, y, w, h) 16 | // stroke ? 17 | }, 18 | 19 | path_begin: target.beginPath, 20 | path_close: target.closePath, 21 | path_move: target.moveTo, 22 | path_line: target.lineTo, 23 | path_fill: target.fill, 24 | path_stroke: target.stroke, 25 | path_bezier_curve: target.bezierCurveTo, 26 | path_quadradic_curve: target.quadraticCurveTo, 27 | path_circle: (x, y, radius) => target.arc(x, y, radius, 0, 2 * Math.PI), 28 | path_arc: target.arc, 29 | path_ellipse: target.ellipse, 30 | path_rect: target.rect, 31 | 32 | clear: () => target.clearRect(0, 0, target.canvas.width, target.canvas.height), 33 | clear_rect: target.clearRect, 34 | 35 | set_stroke_color: (r, g, b) => target.strokeStyle = `rgb(${r},${g},${b})`, 36 | set_fill_color: (r, g, b) => target.fillStyle = `rgb(${r},${g},${b})`, 37 | set_line_width:(x) => target.lineWidth = x, 38 | set_line_cap: (x) => target.lineCap = LINE_CAP[x], 39 | set_line_join: (x) => target.lineJoin = LINE_JOIN[x], 40 | set_shadow_blur: (x) => target.shadowBlur = x, 41 | set_shadow_color: (r, g, b, a) => target.shadowColor = `rgb(${r},${g},${b},${a})`, 42 | set_shadow_offset_x: (x) => target.shadowOffsetX = x, 43 | set_shadow_offset_y: (y) => target.shadowOffsetY = y, 44 | 45 | transform_rotate: (x) => target.rotate(x * Math.PI / 180), 46 | transform_scale: target.scale, 47 | transform_translate: target.translate, 48 | transform: target.transform, 49 | set_transform: target.setTransform, 50 | reset_transform: target.resetTransform, 51 | 52 | save: target.save, 53 | restore: target.restore, 54 | }) 55 | 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "canvas-wasm", 3 | "description": "Canvas functions suitable for WebAssembly", 4 | "author": "Jamen Marz", 5 | "license": "MIT", 6 | "version": "0.2.0", 7 | "repository": "jamen/canvas-wasm", 8 | "scripts": { 9 | "demo": "budo demo --live -- -t wasmify" 10 | }, 11 | "devDependencies": { 12 | "budo": "^10.0.4", 13 | "hyperapp": "^0.12.1", 14 | "wasmify": "^1.3.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # canvas-wasm 3 | 4 | > Canvas functions suitable for WebAssembly 5 | 6 | Canvas's [`CanvasRenderingContext2D`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) has several methods, setters, and getters **not** suitable for WebAssembly. Use this module to wrap a context for making it suitable. 7 | 8 | **Notice:** This is experimental and doesn't include a lot of things. Currently nothing using [memory](https://github.com/WebAssembly/design/blob/master/Semantics.md#linear-memory) is made. 9 | 10 | - [ ] Pixel/image manipulation 11 | - [ ] Gradients and patterns 12 | - [ ] Text objects and styling 13 | - [ ] Hit regions 14 | - [ ] Other non-standard APIs 15 | 16 | ## Install 17 | 18 | ```sh 19 | npm i canvas-wasm 20 | ``` 21 | 22 | **Note:** See [`browserify`](https://github.com/browserify/browserify) and [`wasmify`](https://github.com/jamen/wasmify) for an easy way to building WASM projects. 23 | 24 | ## Usage 25 | 26 | ### `create_*` 27 | 28 | Creating shapes without using paths 29 | 30 | - `create_rect(x, y, width, height)` 31 | 32 | ### `path_*` 33 | 34 | - `path_begin()` 35 | - `path_close()` 36 | - `path_move(x, y)` 37 | - `path_line(x, y)` 38 | - `path_fill()` 39 | - `path_stroke()` 40 | - `path_bezier_curve(c1x, c1y, c2x, c2y, x, y)` 41 | - `path_quadradic_curve(cx, cy, x, y)` 42 | - `path_circle(x, y, radius)` 43 | - `path_arc(x, y, radius, startAngle, endAngle)` 44 | - `path_ellipse(x, y, rx, ry, rot, startAngle, endAngle)` 45 | - `path_rect(x, y, width, height)` 46 | 47 | ### `clear_*` 48 | 49 | - `clear()` 50 | - `clear_rect(x, y, width, height)` 51 | 52 | ### `set_*` 53 | 54 | - `set_stroke_color(r, g, b)` 55 | - `set_fill_color(r, g, b)` 56 | - `set_line_width(w)` 57 | - `set_line_cap(0 | 1 | 2)` 58 | - `butt`, `join`, `square` respectively 59 | - `set_line_join(0 | 1 | 2)` 60 | - `round`, `bevel`, `miter` respectively 61 | - `set_shadow_blur(x)` 62 | - `set_shadow_color(r, g, b, a)` 63 | - `set_shadow_offset_x(x)` 64 | - `set_shadow_offset_y(y)` 65 | 66 | ### `transform_*` 67 | 68 | - `transform_rotate(deg)` 69 | - `transform_scale(x, y)` 70 | - `transform_translate(x, y)` 71 | - `transform(a, b, c, d, e, f)` 72 | - `set_transform(a, b, c, d, e, f)` 73 | - `reset_transform()` 74 | 75 | ### State functions 76 | 77 | - `save()` 78 | - `restore()` 79 | 80 | --------------------------------------------------------------------------------