diff options
| author | Jules <jules@asdf.us> | 2016-09-11 10:19:07 -0400 |
|---|---|---|
| committer | Jules <jules@asdf.us> | 2016-09-11 10:19:07 -0400 |
| commit | fbf05115c17163d91e9f649a348e3e6800a22d5e (patch) | |
| tree | 14920be071bce59aa633be8dc47f8281a66a85db /js/lib | |
init latest version of harp
Diffstat (limited to 'js/lib')
| -rw-r--r-- | js/lib/app.js | 69 | ||||
| -rw-r--r-- | js/lib/canvas_loader.js | 150 | ||||
| -rw-r--r-- | js/lib/canvasmx.js | 35 | ||||
| -rw-r--r-- | js/lib/env.js | 123 | ||||
| -rw-r--r-- | js/lib/grid.js | 183 | ||||
| -rw-r--r-- | js/lib/snapmx.js | 33 | ||||
| -rw-r--r-- | js/lib/wavegrid.js | 136 |
7 files changed, 729 insertions, 0 deletions
diff --git a/js/lib/app.js b/js/lib/app.js new file mode 100644 index 0000000..2138025 --- /dev/null +++ b/js/lib/app.js @@ -0,0 +1,69 @@ + +var scene, cam, controls + +var app = (function() { + + var app = {} + var last_t = 0, initial_t = 0 + + app.init = function() { + app.bind() + app.build() + // check if we're in an iframe + if (window.self === window.top) { + app.ready() + } + } + + app.bind = function() { + $(window).resize(app.resize) + } + + app.build = function() { + if ($.browser.msie || ! has3d()) { return app.fallback() } + + scene = new MX.Scene().addTo('#scene') + cam = scene.camera + + app.resize() + + window.scrollTo(0,0) + environment.init() + } + + app.ready = function() { + if (last_t) return + setTimeout(function () { + $("html").removeClass("loading") + }, 100) + app.animate(0) + } + + app.animate = function (t) { + requestAnimationFrame(app.animate) + if (! initial_t) { + initial_t = t + return + } + t -= initial_t + var dt = t - last_t + last_t = t + environment.update(t) + } + + app.resize = function () { + scene.width = window.innerWidth + scene.height = window.innerHeight + scene.perspective = Math.min(window.innerWidth, scene.height) + scene.update() + } + + app.fallback = function(){ + $('body').addClass('fallback') + } + + return app + +})() + +document.addEventListener('DOMContentLoaded', app.init) diff --git a/js/lib/canvas_loader.js b/js/lib/canvas_loader.js new file mode 100644 index 0000000..c2ed31a --- /dev/null +++ b/js/lib/canvas_loader.js @@ -0,0 +1,150 @@ +var CanvasLoader = (function(){ + + var loader = new Loader (function(){}) + + function CanvasLoader (opt){ + this.opt = defaults(opt, { + rotate: true, + }) + this.canvas = document.createElement("canvas") + this.ctx = this.canvas.getContext('2d') + } + CanvasLoader.prototype.load_image = function(url, cb){ + loader.preloadImage(url, function(img){ + var w = this.w = img.naturalWidth + var h = this.h = img.naturalHeight + this.img = img + cb(img, w, h) + }.bind(this)) + } + CanvasLoader.prototype.scale_image_data = function(opt){ + var w, h + if (opt.scale) { + w = this.w * opt.scale + h = this.h * opt.scale + } + if (opt.w) { + w = opt.w + h = opt.w * this.h / this.w + } + else if (opt.h) { + w = opt.h * this.w / this.h + h = opt.h + } + this.canvas.width = w + this.canvas.height = h + this.ctx.save() + + if (this.opt.rotate) { + this.ctx.translate(w/2, h/2) + this.ctx.rotate( Math.PI ) + this.ctx.translate(-w/2, -h/2) + } + + this.ctx.drawImage(this.img, 0, 0, w, h) + this.ctx.restore() + return this.ctx.getImageData(0, 0, w, h) + } + CanvasLoader.prototype.gray_image_data = function(opt){ + var data = this.scale_image_data(opt) + var pixels = data.data + var len = data.width * data.height + var gray_data = new Uint8Array( len ) + var invert = this.opt.invert + + var gray, ii + for (var i = 0; i < len; i++) { + ii = i * 4 + gray = (255 - (pixels[ii] + pixels[ii+1] + pixels[ii+2]) / 3 ) * (pixels[ii+3]/255) + gray_data[i] = (invert) ? 255 - gray : gray + } + + return { + width: data.width, + height: data.height, + data: gray_data, + } + } + // in: imagedata [r,g,b,a] and a palette + // out: an array strided as [ palette offset, alpha ] + CanvasLoader.prototype.palette_image_data = function(opt){ + var data = this.scale_image_data(opt) + var pixels = data.data + var len = data.width * data.height + var palette_data = new Uint8Array( len*2 ) + var invert = this.opt.invert + + var palette = this.paletteToNumbers(opt.palette) + + var color, index, ii + for (var i = 0; i < len; i++) { + ii = i * 2 + jj = i * 4 + index = closest_to( palette, pixels.slice(jj, jj+3) ) + palette_data[ii] = index + palette_data[ii+1] = pixels[jj+3] + } + + return { + width: data.width, + height: data.height, + data: palette_data, + } + } + + CanvasLoader.prototype.paletteToStrings = function (palette){ + if (typeof palette[0] === "string") return palette + return palette.map(function(rgb){ + return "rgb(" + rgb.join(",") + ")" + }) + } + CanvasLoader.prototype.paletteToNumbers = function (palette){ + if (typeof palette[0] !== "string") return palette + var colors = palette.map(function(color){ + if (color[0] == "#") return hexToRgb(color) + return rgbStringToRgb(color) + }).filter(function(color){ return !! color }).map(function(rgb){ + return [rgb.r, rgb.g, rgb.b] + }) + return colors + } + + function hexToRgb(hex) { + // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, function(m, r, g, b) { + return r + r + g + g + b + b; + }); + + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } : null; + } + function rgbStringToRgb(rgb) { + var nums = rgb.replace(/rgba?\(/,"").replace(/\)/,"").split(",") + return { r: nums[0], g: nums[1], b: nums[2] } + } + function closest_to(palette, pixel){ + return palette.reduce(function(prev, curr, index) { + var d = distance(pixel, curr) + if (prev[0] > d) { + prev[0] = d + prev[1] = index + } + return prev + }, [Infinity, -1])[1] + } + function distance(u, v){ + if (! v) return Math.Infinity + var r = u[0] - v[0] + var g = u[1] - v[1] + var b = u[2] - v[2] + return Math.sqrt(r*r+g*g+b*b) + } + + return CanvasLoader + +})()
\ No newline at end of file diff --git a/js/lib/canvasmx.js b/js/lib/canvasmx.js new file mode 100644 index 0000000..4b53610 --- /dev/null +++ b/js/lib/canvasmx.js @@ -0,0 +1,35 @@ +var CanvasMX = (function(){ + + function CanvasMX (w, h) { + w = w || 100 + h = h || 100 + var canvas = this.canvas = document.createElement("canvas") + canvas.width = w + canvas.height = h + this.ctx = canvas.getContext('2d') + var mx = this.mx = new MX.Object3D () + this.el = mx.el + mx.el.appendChild(canvas) + scene.add(mx) + } + CanvasMX.prototype.resize = function(w, h){ + this.canvas.width = w + this.canvas.height = h + this.mx.width = w + this.mx.height = h + } + CanvasMX.prototype.clear = function(){ + this.snap.clear() + } + CanvasMX.prototype.show = function(){ + this.visible = true + this.mx.style.display = "block" + } + CanvasMX.prototype.hide = function(){ + this.visible = false + this.mx.style.display = "hide" + } + + return CanvasMX + +})()
\ No newline at end of file diff --git a/js/lib/env.js b/js/lib/env.js new file mode 100644 index 0000000..aecb61b --- /dev/null +++ b/js/lib/env.js @@ -0,0 +1,123 @@ +var environment = (function(){ + + var environment = {} + var width = 400 + var height = 600 + var grid_side = 50 + var grid_stroke = 2 + var sph + var left, right, top, bottom + var done_animating = false + + environment.init = function(){ + environment.ready() + + app.resize = function () { + scene.width = window.innerWidth + scene.height = window.innerHeight + scene.perspective = Math.min(window.innerWidth, scene.height) + scene.el.style[MX.perspectiveProp] = 200 + 'px' + scene.update() + } + } + + environment.ready = function(){ + controls = new MX.FollowingOrbitCamera({ + center: { x: 0, y: 0, z: 0 }, + radius: 1250, + radiusRange: [ 300, 2000 ], + rotationXRange: [ -0.05, 0.05 ], + rotationYRange: [ -0.05, 0.05 ], + wheelEase: 20, + ease: 10 + }) + controls.init() + controls.wheel.lock() + + top = environment.make_side() + top.mx.rotationY = Math.PI/2 + top.mx.x = -width/2 + + bottom = environment.make_side() + bottom.mx.rotationY = -Math.PI/2 + bottom.mx.x = width/2 + + left = environment.make_side() + left.mx.rotationX = Math.PI + left.mx.z = -width/2 + + right = environment.make_side() + right.mx.rotationX = 0 // Math.PI/2 + right.mx.z = width/2 + + var sphere = new Image + sphere.src = "http://dumpfm.s3.amazonaws.com/images/20101115/1289880948498-dumpfm-lolumad-wireframesphere.gif" + var mx = new MX.Object3D (sphere) + mx.width = 600 + mx.height = 400 + + mx.y = 100 + mx.rotationY = Math.PI + mx.rotationX = Math.PI/2 + mx.rotationZ = Math.PI/2 + scene.add(mx) + sph = mx + } + + environment.make_side = function(){ + var g = new Grid ({ + side: grid_side - grid_stroke, + strokeWidth: grid_stroke, + bg: "#ffffff", + stroke: [ "#000000" ], + duration: 1000, + delay: [ 0, 500 ], + width: width, + height: height, + draw_sides: true, + }) + var mx = new MX.Object3D (g.snap.node) + mx.width = width + mx.height = height + scene.add(mx) + return { g: g, mx: mx } + } + + environment.update = function(t){ + scene.update() + controls.update() + +// if (! done_animating) return + + t *= 0.05 + + top.g.animate({ + h: "right", + v: "down", + duration: 0, + v_offset: -t, + }) + bottom.g.animate({ + h: "left", + v: "down", + duration: 0, + v_offset: -t, + }) + left.g.animate({ + h: "left", + v: "up", + duration: 0, + v_offset: t, + }) + right.g.animate({ + h: "left", + v: "down", + duration: 0, + v_offset: -t, + }) + } + + return environment + +})() + diff --git a/js/lib/grid.js b/js/lib/grid.js new file mode 100644 index 0000000..aecaed8 --- /dev/null +++ b/js/lib/grid.js @@ -0,0 +1,183 @@ +var Grid = (function(){ + + var Grid = function(opt){ + var horizontal = this.horizontal = [] + var vertical = this.vertical = [] + var sides = this.sides = [] + var x, xmin, xmax + var y, ymin, ymax + var dx = opt.strokeWidth + opt.side + var dy = dx + var i + var s + + opt = this.opt = defaults(opt, { + bg: "#ffffff", + stroke: "#000000", + strokeWidth: 2, + side: 10, + delay: 0, + shuffleDelay: false, + randomizeDelay: false, + duration: 500, + width: 300, + height: 300, + halign: "left", + valign: "top", + draw_sides: false, + snap: null, + }) + if (typeof opt.stroke == 'string') opt.stroke = [opt.stroke] + if (typeof opt.delay == 'string') opt.delay = [opt.delay] + + if (opt.snap) { + s = this.snap = opt.snap + } else { + s = this.snap = Snap(opt.width, opt.height) + s.attr({ viewBox: "0 0 " + opt.width + " " + opt.height }) + } + + switch (opt.halign) { + case "right": + ymin = opt.height % dy + ymax = opt.height + 1 + break + default: + case "left": + ymin = 0 + ymax = opt.height + break + } + + switch (opt.valign) { + case "bottom": + xmin = opt.width % dx + xmax = opt.width + 1 + break + default: + case "top": + xmin = 0 + xmax = opt.width + break + } + + this.bg = s.rect(0, 0, "100%", "100%").attr({ fill: opt.bg }) + + this.pos = { + dx: dx, xmin: xmin, xmax: xmax, + dy: dy, ymin: ymin, ymax: ymax, + } + + for (i = 0, x = xmin; x < xmax; x += dx, i++) { + vertical.push( s.path().attr({ + strokeWidth: opt.strokeWidth, + stroke: opt.stroke[ i % opt.stroke.length ], + }) ) + } + for (i = 0, y = ymin; y < ymax; y += dy, i++) { + horizontal.push( s.path().attr({ + strokeWidth: opt.strokeWidth, + stroke: opt.stroke[ i % opt.stroke.length ], + }) ) + } + if (opt.draw_sides) { + sides.push( s.path().attr({ strokeWidth: opt.strokeWidth, stroke: opt.stroke[ ++i % opt.stroke.length ] }) ) + sides.push( s.path().attr({ strokeWidth: opt.strokeWidth, stroke: opt.stroke[ ++i % opt.stroke.length ] }) ) + sides.push( s.path().attr({ strokeWidth: opt.strokeWidth, stroke: opt.stroke[ ++i % opt.stroke.length ] }) ) + sides.push( s.path().attr({ strokeWidth: opt.strokeWidth, stroke: opt.stroke[ ++i % opt.stroke.length ] }) ) + } + } + + Grid.prototype.animate = function(opt, cb){ + opt = defaults(opt, { + h: "right", + v: "down", + h_offset: 0, + v_offset: 0, + duration: this.opt.duration, + }) + var duration = opt.duration + var height = this.opt.height, width = this.opt.width + var x = mod(this.pos.xmin + opt.h_offset, this.pos.dx) + var y = mod(this.pos.ymin + opt.v_offset, this.pos.dy) + var right = opt.h == "right" + var down = opt.v == "down" + + this.vertical.forEach(function(p, i){ + var start_path, end_path + if (down) { + start_path = vpath( x, 0, 0 ) + end_path = vpath( x, 0, height ) + } + else { + start_path = vpath( x, height, 0 ) + end_path = vpath( x, height, -height ) + } + if (duration == 0) { + p.attr({ d: end_path }) + } + else { + var delay = this.opt.randomizeDelay + ? choice(this.opt.delay) + : this.opt.delay[i % this.opt.delay.length] + p.attr({ d: start_path }) + setTimeout(function(){ + p.animate({ d: end_path }, duration) + }, delay) + } + x += this.pos.dx + }.bind(this)) + + this.horizontal.forEach(function(p, i){ + var start_path, end_path + if (right) { + start_path = hpath( 0, y, 0 ) + end_path = hpath( 0, y, width ) + } + else { + start_path = hpath( width, y, 0 ) + end_path = hpath( width, y, -width ) + } + + if (duration == 0) { + p.attr({ d: end_path }) + } + else { + var delay + if (this.opt.shuffleDelay) { + delay = randrange.apply(null, this.opt.delay) + } + else if (this.opt.randomizeDelay) { + delay = choice(this.opt.delay) + } + else { + delay = this.opt.delay[i % this.opt.delay.length] + } + p.attr({ d: start_path }) + setTimeout(function(){ + p.animate({ d: end_path }, duration) + }, delay) + } + y += this.pos.dy + }.bind(this)) + + if (this.opt.draw_sides) { + this.sides[0].attr({ d: hpath(0, 0, width) }) + this.sides[1].attr({ d: hpath(0, height - this.opt.strokeWidth/2, width) }) + this.sides[2].attr({ d: vpath(0, 0, height) }) + this.sides[3].attr({ d: vpath(width - this.opt.strokeWidth/2, 0, height) }) + } + + var max_delay = Math.max.apply(Math, this.opt.delay) + cb && setTimeout(cb, this.opt.duration + max_delay + 20) + } + + function hpath (x, y, w) { + return "M" + x + " " + y + "h" + w + } + function vpath (x, y, w) { + return "M" + x + " " + y + "v" + w + } + + return Grid +})() diff --git a/js/lib/snapmx.js b/js/lib/snapmx.js new file mode 100644 index 0000000..4deb729 --- /dev/null +++ b/js/lib/snapmx.js @@ -0,0 +1,33 @@ +var SnapMX = (function(){ + + function SnapMX (w, h) { + w = w || 100 + h = h || 100 + var snap = this.snap = new Snap (w, h) + var el = this.el = snap.node + var mx = this.mx = new MX.Object3D (el) + scene.add(mx) + } + SnapMX.prototype.resize = function(w, h){ + this.snap.attr({ + width: w, + height: h|0, + }) + this.mx.width = w + this.mx.height = h + } + SnapMX.prototype.clear = function(){ + this.snap.clear() + } + SnapMX.prototype.show = function(){ + this.visible = true + this.mx.style.display = "block" + } + SnapMX.prototype.hide = function(){ + this.visible = false + this.mx.style.display = "hide" + } + + return SnapMX + +})()
\ No newline at end of file diff --git a/js/lib/wavegrid.js b/js/lib/wavegrid.js new file mode 100644 index 0000000..532bbfc --- /dev/null +++ b/js/lib/wavegrid.js @@ -0,0 +1,136 @@ +var WaveGrid = (function(){ + + function WaveGrid (opt){ + this.opt = defaults(opt, { + stroke_width: 2, + stroke: "#ffffff", + fill: "#000000", + wave_spacing: 20, + wave_height: 6, + wave_height_scale: 4, + speed: 15, + wave_stagger: 8, + smooth_stagger: true, + face: null, + waves: true, + }) + if (! this.opt.face) { + console.error("WaveGrid: please supply a SnapMX face object") + } + this.loaded = false + } + WaveGrid.prototype.load = function(data, w, h){ + this.waves = [] + this.data = data + + var dx = this.opt.wave_spacing + var dy = this.opt.wave_height + var top_padding = dy * this.opt.wave_height_scale + this.opt.stroke_width + var wave + var x, y + + this.w = w + this.h = h + + this.opt.face.resize(w + dx*2, h + top_padding + dy*2) + this.group = this.opt.face.snap.g() + this.group.attr({ transform: "T" + (2.5*dx) + ","+ -top_padding/2 }) + + for (y = 0; y < h; y += dy) { + wave = this.group.path().attr({ + strokeWidth: this.opt.stroke_width, + stroke: this.opt.stroke, + fill: this.opt.fill, + }) + this.waves.push(wave) + } + this.loaded = true + this.update(0) + } + WaveGrid.prototype.update = function(t){ + if (! this.loaded) return + + t *= this.opt.speed / 1000 + + var wave_height_scale = this.opt.wave_height_scale + var dx = this.opt.wave_spacing + var dy = this.opt.wave_height + var dt = t % dx + var dtr = dt/dx + + var pixels = this.data.data + var pw = this.data.width + var ph = this.data.height + + var w = this.w + var xmin = (dx-dt) - dx + var xmax = w + (dx*2) + + var wave, path, x, xx, y, yy + var at, bt, ay, by, zt, zy + var pi, i, j = this.waves.length-1 + var xstart = 0 + + var smooth_stagger = this.opt.smooth_stagger + var stagger = this.opt.wave_stagger + var stagger_dtr + + var use_waves = this.opt.waves + + for (y = 0; y < this.h; y += dy, j--) { + pi = ((y/this.h) * ph)|0 + wave = this.waves[j] + path = "M" + (-dx) + "," + y + + if (smooth_stagger) { + dt = (t/stagger + ((j % stagger)/stagger * dx)) % dx + dtr = dt/dx + xmin = (dx-dt) - dx + } + else { + xstart = stagger == 1 ? 0 : ((j % stagger)/stagger) * dx + } + + for (x = xstart; x <= xmax; x += dx) { + xx = x + xmin + + jj = clamp( x / w, 0, 1 ) * (pw) + jjj = clamp( (x+dx) / w, 0, 1 ) * (pw) + + if (jjj >= pw) { + at = ((pi * pw) + pw-2)|0 + } + else { + at = ((pi * pw) + jj)|0 + } + + if (jjj >= pw) { + bt = ((pi * pw) + pw - 1)|0 + } + else { + bt = ((pi * pw) + jjj)|0 + } + + ay = (pixels[at]/255 * dy) * (dtr) * wave_height_scale + by = (pixels[bt]/255 * dy) * (1-dtr) * wave_height_scale + yy = y + (ay + by) + + if (use_waves) { + path += "C" + path += (xx).toFixed(3) + "," + (y).toFixed(3) + " " + path += (xx).toFixed(3) + "," + (yy + (ay+by)).toFixed(3) + " " + path += (xx).toFixed(3) + "," + (yy).toFixed(3) + " " + } + else { + path += "M" + (xx - 0.01).toFixed(3) + "," + (y).toFixed(3) + path += "L" + (xx).toFixed(3) + "," + (yy).toFixed(3) + path += "L" + (xx + 0.01).toFixed(3) + "," + (y).toFixed(3) + } + } + wave.attr({ d: path }) + } + } + + return WaveGrid + +})()
\ No newline at end of file |
