Skip to content

Instantly share code, notes, and snippets.

@nicksheffield
Last active April 20, 2017 03:09
Show Gist options
  • Save nicksheffield/9bc64b77b692311627b376a254f2dfd6 to your computer and use it in GitHub Desktop.
Save nicksheffield/9bc64b77b692311627b376a254f2dfd6 to your computer and use it in GitHub Desktop.
My own approach at a p5 style canvas drawing library
// ----------------------------------------------------
// 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'
<!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>
<!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