Last active
April 20, 2017 03:09
-
-
Save nicksheffield/9bc64b77b692311627b376a254f2dfd6 to your computer and use it in GitHub Desktop.
My own approach at a p5 style canvas drawing library
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ---------------------------------------------------- | |
| // p6 Object | |
| // ---------------------------------------------------- | |
| window.p6 = { | |
| running: false, | |
| ratio: 1, | |
| size: { | |
| w: 100, | |
| h: 100 | |
| }, | |
| settings: {} | |
| } | |
| window.gui = {} | |
| // ---------------------------------------------------- | |
| // Variables | |
| // ---------------------------------------------------- | |
| window.canvasWidth = 100 | |
| window.canvasHeight = 100 | |
| window.mouseDown = false | |
| window.mouseOX = null | |
| window.mouseOY = null | |
| window.mouseX = 0 | |
| window.mouseY = 0 | |
| window.mouseSpeed = 0 | |
| window.mouseAngle = 0 | |
| // ---------------------------------------------------- | |
| // Constants | |
| // ---------------------------------------------------- | |
| window.FULL_CANVAS = 'full' | |
| // ---------------------------------------------------- | |
| // Canvas Control | |
| // ---------------------------------------------------- | |
| function createCanvas(w, h, parent) { | |
| if(!parent) parent = document.body | |
| p6.canvas = document.createElement('canvas') | |
| p6.ctx = p6.canvas.getContext('2d') | |
| parent.appendChild(p6.canvas) | |
| resizeCanvas(w, h) | |
| p6.canvas.addEventListener('mousemove', (e) => mousemove(e, p6.ctx)) | |
| p6.canvas.addEventListener('mousedown', (e) => mousedown(e, p6.ctx)) | |
| p6.canvas.addEventListener('mouseup', (e) => mouseup(e, p6.ctx)) | |
| p6.canvas.addEventListener('mousedown', (e) => window.mouseDown = true) | |
| p6.canvas.addEventListener('mouseup', (e) => window.mouseDown = false) | |
| } | |
| function resizeCanvas(w, h) { | |
| if(w) p6.size.w = w | |
| if(h) p6.size.h = h | |
| save() | |
| if(w == 'full') { | |
| p6.size.w = window.innerWidth | |
| p6.size.h = window.innerHeight | |
| } | |
| p6.canvas.width = p6.size.w * p6.ratio | |
| p6.canvas.height = p6.size.h * p6.ratio | |
| p6.canvas.style.width = p6.size.w + 'px' | |
| p6.canvas.style.height = p6.size.h + 'px' | |
| canvasHeight = p6.canvas.height | |
| canvasWidth = p6.canvas.width | |
| restore() | |
| } | |
| function pixelDensity(val) { | |
| p6.ratio = val | |
| resizeCanvas() | |
| } | |
| // ---------------------------------------------------- | |
| // Drawing Functions | |
| // ---------------------------------------------------- | |
| function rect(x, y, w, h) { | |
| if(x == 'full') { | |
| x = 0 | |
| y = 0 | |
| w = canvasWidth | |
| h = canvasHeight | |
| } | |
| p6.ctx.beginPath() | |
| p6.ctx.rect(x * p6.ratio, y * p6.ratio, w * p6.ratio, h * p6.ratio) | |
| } | |
| function circle(cx, cy, r) { | |
| p6.ctx.beginPath() | |
| p6.ctx.arc(x * p6.ratio, y * p6.ratio, r * p6.ratio, 0, 2 * Math.PI, false) | |
| } | |
| function polygon(cx, cy, sides, rad) { | |
| if(!sides || sides <= 2) return | |
| var points = [] | |
| for(let i=0; i<sides; i++) { | |
| points.push(at.step(360/sides*i, rad)) | |
| } | |
| p6.ctx.beginPath() | |
| p6.ctx.moveTo(0 - points[0].x, 0 - points[0].y) | |
| for(let i=1; i<points.length; i++) { | |
| p6.ctx.lineTo(0 - points[i].x, 0 - points[i].y) | |
| } | |
| } | |
| function line(x1, y1, x2, y2) { | |
| p6.ctx.beginPath() | |
| p6.ctx.moveTo(x1 * p6.ratio, y1 * p6.ratio) | |
| p6.ctx.lineTo(x2 * p6.ratio, y2 * p6.ratio) | |
| } | |
| function fill(c) { | |
| if(c) p6.ctx.fillStyle = c | |
| p6.ctx.fill() | |
| } | |
| function stroke(c) { | |
| if(c) p6.ctx.strokeStyle = c | |
| p6.ctx.stroke() | |
| } | |
| function lineWidth(val) { | |
| p6.ctx.lineWidth = val * p6.ratio | |
| } | |
| function lineCap(val) { | |
| p6.ctx.lineCap = val | |
| } | |
| function lineJoin(val) { | |
| p6.ctx.lineJoin = val | |
| } | |
| function clear() { | |
| p6.ctx.clearRect(0, 0, canvasWidth, canvasHeight) | |
| } | |
| function fade(c, a) { | |
| save() | |
| color(c) | |
| alpha(a) | |
| rect(FULL_CANVAS) | |
| fill() | |
| alpha(1) | |
| restore() | |
| } | |
| function translate(x, y) { | |
| p6.ctx.translate(x * p6.ratio, y * p6.ratio) | |
| } | |
| function rotate(deg) { | |
| p6.ctx.rotate((Math.PI / 180) * deg) | |
| } | |
| function rotateCanvas(x, y, deg, cb) { | |
| save() | |
| translate(x, y) | |
| rotate(deg) | |
| cb() | |
| restore() | |
| } | |
| // ---------------------------------------------------- | |
| // Utility Functions | |
| // ---------------------------------------------------- | |
| function random(a,b,c){ | |
| if(a instanceof Array) { | |
| return a[random(0, a.length)-1] | |
| } else { | |
| return parseFloat((Math.random()*((a?a:1)-(b?b:0))+(b?b:0)).toFixed(c?c:0)) | |
| } | |
| } | |
| function randomColor() { | |
| return '#' + ("000000" + Math.random().toString(16).slice(2, 8).toUpperCase()).slice(-6); | |
| } | |
| function deviceDensity() { | |
| return window.devicePixelRatio | |
| } | |
| function increase(obj1, obj2) { | |
| for(var prop in obj2) { | |
| if(obj1[prop] !== null) { | |
| obj1[prop] += obj2[prop] | |
| } | |
| } | |
| } | |
| function save() { | |
| p6.ctx.save() | |
| p6.settings.fillStyle = p6.ctx.fillStyle | |
| p6.settings.strokeStyle = p6.ctx.strokeStyle | |
| p6.settings.globalAlpha = p6.ctx.globalAlpha | |
| p6.settings.lineWidth = p6.ctx.lineWidth | |
| p6.settings.lineCap = p6.ctx.lineCap | |
| p6.settings.lineJoin = p6.ctx.lineJoin | |
| } | |
| function restore() { | |
| p6.ctx.restore() | |
| for(var prop in p6.settings) { | |
| p6.ctx[prop] = p6.settings[prop] | |
| } | |
| } | |
| var at = { | |
| angle: function(a,b){return Math.atan2(b.y-a.y,b.x-a.x)/Math.PI*180}, | |
| dist: function(a,b){return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y))}, | |
| step: function(a,s){return {x:s*Math.cos(a*Math.PI/180),y:s*Math.sin(a*Math.PI/180)}} | |
| } | |
| // ---------------------------------------------------- | |
| // Color Functions | |
| // ---------------------------------------------------- | |
| function color(c) { | |
| p6.color = c | |
| p6.ctx.fillStyle = c | |
| p6.ctx.strokeStyle = c | |
| } | |
| function alpha(a) { | |
| p6.ctx.globalAlpha = a | |
| } | |
| function hsl(h, s, l) { | |
| if(!h) h = 0 | |
| if(!s) s = 100 | |
| if(!l) l = 50 | |
| return 'hsl(' + [h, s+'%', l+'%'].join(',') + ')' | |
| } | |
| function rgb(r, g, b) { | |
| return 'rgb(' + [r, g, b].join(',') + ')' | |
| } | |
| // ---------------------------------------------------- | |
| // User Defined Functions | |
| // ---------------------------------------------------- | |
| function draw() {} | |
| function setup() {} | |
| function windowResized() {} | |
| function mousemove() {} | |
| function mousedown() {} | |
| function mouseup() {} | |
| // ---------------------------------------------------- | |
| // Mouse Tracking | |
| // ---------------------------------------------------- | |
| window.addEventListener('mousedown', updateMousePos) | |
| window.addEventListener('mousemove', updateMousePos) | |
| function updateMousePos(event) { | |
| mouseOX = mouseX | |
| mouseOY = mouseY | |
| mouseX = event.pageX | |
| mouseY = event.pageY | |
| if(mouseOX !== null) { | |
| mouseSpeed = at.dist({x: mouseOX, y: mouseOY}, {x: mouseX, y: mouseY}) | |
| mouseAngle = at.angle({x: mouseOX, y: mouseOY}, {x: mouseX, y: mouseY}) | |
| } | |
| } | |
| // ---------------------------------------------------- | |
| // Animation Loop | |
| // ---------------------------------------------------- | |
| ;(function animLoop() { | |
| if(p6.running) draw(p6.ctx) | |
| requestAnimationFrame(animLoop) | |
| })() | |
| // ---------------------------------------------------- | |
| // p6 Initaliser | |
| // ---------------------------------------------------- | |
| setTimeout(function() { | |
| setup() | |
| p6.running = true | |
| p6.color = black | |
| window.addEventListener('resize', (e) => windowResized(e, p6.ctx)) | |
| }, 10) | |
| // ---------------------------------------------------- | |
| // List Object | |
| // ---------------------------------------------------- | |
| function List(initArray) { | |
| this.items = initArray || [] | |
| this.markedForRemove = [] | |
| this.push = (item) => this.items.push(item) | |
| this.forEach = (cb) => { this.items.forEach(cb); this.removeMarked() } | |
| this.pop = (item) => this.items = this.items.filter((i) => i !== item) | |
| this.remove = (item) => this.markedForRemove.push(item) | |
| this.removeMarked = () => this.markedForRemove.forEach((i) => this.pop(i)) | |
| this.empty = () => this.items.length = 0 | |
| return this | |
| } | |
| // ---------------------------------------------------- | |
| // Dat.GUI Helper | |
| // ---------------------------------------------------- | |
| function datGUI(controls) { | |
| if(typeof dat === 'undefined') return | |
| var gui = new dat.GUI() | |
| for(var prop in controls) { | |
| var control = controls[prop] | |
| window.gui[prop] = control.val | |
| var GUIcontrol = gui.add(window.gui, prop, control.values) | |
| if(control.min !== undefined) GUIcontrol = GUIcontrol.min(control.min) | |
| if(control.max !== undefined) GUIcontrol = GUIcontrol.max(control.max) | |
| if(control.step !== undefined) GUIcontrol = GUIcontrol.step(control.step) | |
| if(control.human !== undefined) GUIcontrol = GUIcontrol.name(control.human) | |
| if(control.onChange !== undefined) GUIcontrol = GUIcontrol.onChange(control.onChange) | |
| } | |
| } | |
| // ---------------------------------------------------- | |
| // Color Constants | |
| // ---------------------------------------------------- | |
| const aliceblue = '#F0F8FF' | |
| const antiquewhite = '#FAEBD7' | |
| const aqua = '#00FFFF' | |
| const aquamarine = '#7FFFD4' | |
| const azure = '#F0FFFF' | |
| const beige = '#F5F5DC' | |
| const bisque = '#FFE4C4' | |
| const black = '#000000' | |
| const blanchedalmond = '#FFEBCD' | |
| const blue = '#0000FF' | |
| const blueviolet = '#8A2BE2' | |
| const brown = '#A52A2A' | |
| const burlywood = '#DEB887' | |
| const cadetblue = '#5F9EA0' | |
| const chartreuse = '#7FFF00' | |
| const chocolate = '#D2691E' | |
| const coral = '#FF7F50' | |
| const cornflowerblue = '#6495ED' | |
| const cornsilk = '#FFF8DC' | |
| const crimson = '#DC143C' | |
| const cyan = '#00FFFF' | |
| const darkblue = '#00008B' | |
| const darkcyan = '#008B8B' | |
| const darkgoldenrod = '#B8860B' | |
| const darkgray = '#A9A9A9' | |
| const darkgrey = '#A9A9A9' | |
| const darkgreen = '#006400' | |
| const darkkhaki = '#BDB76B' | |
| const darkmagenta = '#8B008B' | |
| const darkolivegreen = '#556B2F' | |
| const darkorange = '#FF8C00' | |
| const darkorchid = '#9932CC' | |
| const darkred = '#8B0000' | |
| const darksalmon = '#E9967A' | |
| const darkseagreen = '#8FBC8F' | |
| const darkslateblue = '#483D8B' | |
| const darkslategray = '#2F4F4F' | |
| const darkslategrey = '#2F4F4F' | |
| const darkturquoise = '#00CED1' | |
| const darkviolet = '#9400D3' | |
| const deeppink = '#FF1493' | |
| const deepskyblue = '#00BFFF' | |
| const dimgray = '#696969' | |
| const dimgrey = '#696969' | |
| const dodgerblue = '#1E90FF' | |
| const firebrick = '#B22222' | |
| const floralwhite = '#FFFAF0' | |
| const forestgreen = '#228B22' | |
| const fuchsia = '#FF00FF' | |
| const gainsboro = '#DCDCDC' | |
| const ghostwhite = '#F8F8FF' | |
| const gold = '#FFD700' | |
| const goldenrod = '#DAA520' | |
| const gray = '#808080' | |
| const grey = '#808080' | |
| const green = '#008000' | |
| const greenyellow = '#ADFF2F' | |
| const honeydew = '#F0FFF0' | |
| const hotpink = '#FF69B4' | |
| const indianred = '#CD5C5C' | |
| const indigo = '#4B0082' | |
| const ivory = '#FFFFF0' | |
| const khaki = '#F0E68C' | |
| const lavender = '#E6E6FA' | |
| const lavenderblush = '#FFF0F5' | |
| const lawngreen = '#7CFC00' | |
| const lemonchiffon = '#FFFACD' | |
| const lightblue = '#ADD8E6' | |
| const lightcoral = '#F08080' | |
| const lightcyan = '#E0FFFF' | |
| const lightgoldenrodyellow = '#FAFAD2' | |
| const lightgray = '#D3D3D3' | |
| const lightgrey = '#D3D3D3' | |
| const lightgreen = '#90EE90' | |
| const lightpink = '#FFB6C1' | |
| const lightsalmon = '#FFA07A' | |
| const lightseagreen = '#20B2AA' | |
| const lightskyblue = '#87CEFA' | |
| const lightslategray = '#778899' | |
| const lightslategrey = '#778899' | |
| const lightsteelblue = '#B0C4DE' | |
| const lightyellow = '#FFFFE0' | |
| const lime = '#00FF00' | |
| const limegreen = '#32CD32' | |
| const linen = '#FAF0E6' | |
| const magenta = '#FF00FF' | |
| const maroon = '#800000' | |
| const mediumaquamarine = '#66CDAA' | |
| const mediumblue = '#0000CD' | |
| const mediumorchid = '#BA55D3' | |
| const mediumpurple = '#9370DB' | |
| const mediumseagreen = '#3CB371' | |
| const mediumslateblue = '#7B68EE' | |
| const mediumspringgreen = '#00FA9A' | |
| const mediumturquoise = '#48D1CC' | |
| const mediumvioletred = '#C71585' | |
| const midnightblue = '#191970' | |
| const mintcream = '#F5FFFA' | |
| const mistyrose = '#FFE4E1' | |
| const moccasin = '#FFE4B5' | |
| const navajowhite = '#FFDEAD' | |
| const navy = '#000080' | |
| const oldlace = '#FDF5E6' | |
| const olive = '#808000' | |
| const olivedrab = '#6B8E23' | |
| const orange = '#FFA500' | |
| const orangered = '#FF4500' | |
| const orchid = '#DA70D6' | |
| const palegoldenrod = '#EEE8AA' | |
| const palegreen = '#98FB98' | |
| const paleturquoise = '#AFEEEE' | |
| const palevioletred = '#DB7093' | |
| const papayawhip = '#FFEFD5' | |
| const peachpuff = '#FFDAB9' | |
| const peru = '#CD853F' | |
| const pink = '#FFC0CB' | |
| const plum = '#DDA0DD' | |
| const powderblue = '#B0E0E6' | |
| const purple = '#800080' | |
| const rebeccapurple = '#663399' | |
| const red = '#FF0000' | |
| const rosybrown = '#BC8F8F' | |
| const royalblue = '#4169E1' | |
| const saddlebrown = '#8B4513' | |
| const salmon = '#FA8072' | |
| const sandybrown = '#F4A460' | |
| const seagreen = '#2E8B57' | |
| const seashell = '#FFF5EE' | |
| const sienna = '#A0522D' | |
| const silver = '#C0C0C0' | |
| const skyblue = '#87CEEB' | |
| const slateblue = '#6A5ACD' | |
| const slategray = '#708090' | |
| const slategrey = '#708090' | |
| const snow = '#FFFAFA' | |
| const springgreen = '#00FF7F' | |
| const steelblue = '#4682B4' | |
| const tan = '#D2B48C' | |
| const teal = '#008080' | |
| const thistle = '#D8BFD8' | |
| const tomato = '#FF6347' | |
| const turquoise = '#40E0D0' | |
| const violet = '#EE82EE' | |
| const wheat = '#F5DEB3' | |
| const white = '#FFFFFF' | |
| const whitesmoke = '#F5F5F5' | |
| const yellow = '#FFFF00' | |
| const yellowgreen = '#9ACD32' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>p6.js - Rotation</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| overflow: hidden; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.4/dat.gui.min.js"></script> | |
| <script src="p6.js"></script> | |
| <script> | |
| const list = new List() | |
| let hue = 0 | |
| let hueRotation = 1 | |
| let rotationSpeed = 1 | |
| function setup() { | |
| createCanvas(FULL_CANVAS) | |
| pixelDensity(deviceDensity()) | |
| datGUI({ | |
| rotationMul: { val: 0, min: -2, max: 2, step: 0.1, human: 'Rotation' }, | |
| sides: { val: 4, min: 3, max: 10, step: 1, human: 'Sides' } | |
| }) | |
| } | |
| function windowResized() { | |
| resizeCanvas(FULL_CANVAS) | |
| } | |
| function mousemove() { | |
| if(mouseDown) { | |
| list.push({ | |
| x: mouseX, | |
| y: mouseY, | |
| size: 100, | |
| rotation: 0, | |
| spin: rotationSpeed, | |
| color: rgb(random(0,255), random(0,50), random(0,50)) | |
| // color: hsl(hue += hueRotation) | |
| }) | |
| } | |
| } | |
| function draw(ctx) { | |
| clear() | |
| list.forEach((p, i) => { | |
| rotateCanvas(p.x, p.y, p.rotation + (i * gui.rotationMul), () => { | |
| // rect(0 - p.size / 2, 0 - p.size / 2, p.size, p.size) | |
| polygon(0, 0, gui.sides, p.size) | |
| fill(p.color) | |
| }) | |
| p.rotation += p.spin | |
| }) | |
| } | |
| </script> | |
| </body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>p6.js</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| overflow: hidden; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.4/dat.gui.min.js"></script> | |
| <script src="p6.js"></script> | |
| <script> | |
| const particles = list() | |
| const options = { | |
| hue: random(0, 360), | |
| hueRotation: 1, | |
| wobble: 90, | |
| size: 20, | |
| speed: 0.3, | |
| shrink: 0.5, | |
| clear: false, | |
| density: deviceDensity(), | |
| wipe: function() { | |
| particles.empty() | |
| clear() | |
| } | |
| } | |
| function setup() { | |
| createCanvas('full') | |
| pixelDensity(options.density) | |
| lineCap('round') | |
| datGUI({ | |
| options, | |
| controls: { | |
| 'hueRotation': { min: 0.1, max: 360, step: 1, human: 'Hue Rotation' }, | |
| 'size': { min: 1, max: 50, step: 1, human: 'Size' }, | |
| 'shrink': { min: 0, max: 1, step: 0.1, human: 'Shrink' }, | |
| 'wobble': { min: 0, max: 360, step: 1, human: 'Wobble' }, | |
| 'speed': { min: 0, max: 2, step: 0.1, human: 'Mouse Speed' }, | |
| 'density': { min: 0.1, max: deviceDensity(), step: 0.1, human: 'Pixel Density', | |
| onChange: function() { console.log('onchange'); pixelDensity(options.density) } | |
| }, | |
| 'clear': { human: 'Clear Frames' }, | |
| 'wipe': { human: 'Clear Screen'} | |
| } | |
| }) | |
| } | |
| function windowResized() { | |
| resizeCanvas('full') | |
| } | |
| function mousemove() { | |
| if(mouseDown) { | |
| particles.push({ | |
| x: mouseX, | |
| y: mouseY, | |
| angle: mouseAngle, | |
| speed: mouseSpeed * options.speed, | |
| wobble: options.wobble, | |
| size: options.size, | |
| color: 'hsl(' + options.hue + ', 100%, 50%)' | |
| }) | |
| options.hue = options.hue + options.hueRotation < 360 ? options.hue + options.hueRotation : options.hue - 360 | |
| } | |
| } | |
| function draw() { | |
| if(options.clear) clear() | |
| particles.forEach((p) => { | |
| let step = at.step(p.angle, p.speed) | |
| lineWidth(p.size) | |
| line(p.x, p.y, p.x + step.x, p.y + step.y) | |
| stroke(p.color) | |
| increase(p, step) | |
| p.angle += random(-p.wobble, p.wobble) | |
| if((p.size -= options.shrink) <= 0) particles.remove(p) | |
| }) | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment