diff options
| author | pepperpepperpepper <pepper@scannerjammer.com> | 2015-10-28 17:00:52 -0700 |
|---|---|---|
| committer | pepperpepperpepper <pepper@scannerjammer.com> | 2015-10-28 17:00:52 -0700 |
| commit | 3d17f2b534c04ffa3996cd309056180e72408c01 (patch) | |
| tree | f420b255bb567fabb91fef31d5b98f06cdd87a7b /assets/javascripts/mx | |
Diffstat (limited to 'assets/javascripts/mx')
| -rw-r--r-- | assets/javascripts/mx/extensions/mx.scene.js | 165 | ||||
| -rw-r--r-- | assets/javascripts/mx/extensions/mx.unclampedOrbitCamera.js | 130 | ||||
| -rw-r--r-- | assets/javascripts/mx/mx.skew.js | 610 | ||||
| -rw-r--r-- | assets/javascripts/mx/primitives/mx.image.js | 50 |
4 files changed, 955 insertions, 0 deletions
diff --git a/assets/javascripts/mx/extensions/mx.scene.js b/assets/javascripts/mx/extensions/mx.scene.js new file mode 100644 index 0000000..8f11fb0 --- /dev/null +++ b/assets/javascripts/mx/extensions/mx.scene.js @@ -0,0 +1,165 @@ +// NOTE +// +// This is not a fully functional 3d scene as you might expect. +// The camera can only do pitch (rotationX) and yaw (rotationY), but no roll (rotationZ) +// because I haven't implemented alternative euler orders or quaternions. +// +// For serious 3D scenes with more functionalities you should use +// THREE.js with CSS3D Renderer. + +MX.Camera = MX.Object3D.extend({ + + init: function(){ + this.el = null + this.type = "Camera" + }, + + move: function(s){ + for (var i in s) { + this[i] = s[i] + } + }, + + toString: function(){ + var params = "x y z rotationX rotationY".split(" ") + return this.__toString(params, "scene.camera.move") + }, + + getCameraEuler: function (target) { + var dx = target.x - this.x, + dy = target.y - this.y, + dz = target.z - this.z + r = {} + r.y = Math.atan2(-dx, dz) + r.x = Math.atan2(-dy, Math.sqrt(dx*dx + dz*dz)) + r.z = 0 + if (MX.rotationUnit === 'deg') { + r.x = MX.toDeg(r.x) + r.y = MX.toDeg(r.y) + } + return r + } + +}) + +MX.Scene = (function () { + + var add = MX.Object3D.prototype.add, + remove = MX.Object3D.prototype.remove + + function Scene () { + + this.el = document.createElement('div') + this.el.classList.add('mx-scene') + + var s = this.el.style + + s[MX.transformProp] = 'preserve-3d' + + s.webkitPerspectiveOrigin = '50% 50%' + s.mozPerspectiveOrigin = '50% 50%' + s.perspectiveOrigin = '50% 50%' + + s.webkitUserSelect = 'none' + s.mozUserSelect = 'none' + s.userSelect = 'none' + + s.overflow = 'hidden' + + this.inner = new MX.Object3D().addTo(this.el) + this.inner.el.style.width = '0' + this.inner.el.style.height = '0' + + var self = this + var width, height, perspective + + Object.defineProperty(this, 'width', { + get: function () { + return width + }, + set: function (val) { + width = val + self.el.style.width = val + 'px' + } + }) + + Object.defineProperty(this, 'height', { + get: function () { + return height + }, + set: function (val) { + height = val + self.el.style.height = val + 'px' + } + }) + + Object.defineProperty(this, 'perspective', { + get: function () { + return perspective + }, + set: function (val) { + perspective = val + self.el.style[MX.perspectiveProp] = val + 'px' + self.inner.z = -val - self.camera.z + self.inner.rotationOrigin.z = -val + } + }) + + var cam = this.camera = new MX.Camera() + + this.inner.rotationOrigin = { x:0, y:0, z:0 } + + this.perspective = 0 + } + + Scene.prototype = { + + constructor: Scene, + + add: function () { + add.apply(this.inner, arguments) + return this + }, + + remove: function () { + remove.apply(this.inner, arguments) + return this + }, + + addTo: function (target) { + if (typeof target === 'string') { + target = document.querySelector(target) + } + if (target instanceof HTMLElement && target.appendChild) { + target.appendChild(this.el) + } else { + console.warn('You can only add a Scene to an HTML element.') + } + return this + }, + + update: function () { + // update inner based on camera + + var i = this.inner, + c = this.camera + + c.update() + + i.z = -this.perspective - c.z + i.x = -c.x + i.y = -c.y + + i.rotationX = -c.rotationX + i.rotationY = -c.rotationY + //i.rotationZ = -c.rotationZ + + i.update() + return this + }, + + } + + return Scene + +})()
\ No newline at end of file diff --git a/assets/javascripts/mx/extensions/mx.unclampedOrbitCamera.js b/assets/javascripts/mx/extensions/mx.unclampedOrbitCamera.js new file mode 100644 index 0000000..28b1aac --- /dev/null +++ b/assets/javascripts/mx/extensions/mx.unclampedOrbitCamera.js @@ -0,0 +1,130 @@ +MX.OrbitCamera = function(opt){ + var exports = {}, bound = false + exports.opt = opt = defaults(opt, { + el: window, // object to bind events on + camera: scene.camera, // camera object we'll be moving + radius: 100, + radiusRange: [ 10, 1000 ], + rotationX: PI/2, + rotationY: 0, + center: { x: 0, y: 0, z: 0 }, + sensitivity: 10, // moving 1 pixel is like moving N radians + wheelSensitivity: 10, + ease: 10, + wheelEase: 10, + }) + var rx, ry, radius, px, py, epsilon = 1e-10 + exports.dragging = false + exports.init = function(){ + ry = opt.rotationY + rx = opt.rotationX + radius = opt.radius + exports.wheel = new wheel({ + el: opt.el, + update: function(e, delta){ + opt.radius = clamp( opt.radius + delta * opt.wheelSensitivity, opt.radiusRange[0], opt.radiusRange[1] ) + }, + }) + exports.bind() + } + exports.toggle = function(state){ + if (state) exports.bind() + else exports.unbind() + } + exports.bind = function(){ + if (bound) return; + bound = true + opt.el.addEventListener("mousedown", down) + window.addEventListener("mousemove", move) + window.addEventListener("mouseup", up) + opt.el.addEventListener("touchstart", touch(down)) + window.addEventListener("touchmove", touch(move)) + window.addEventListener("touchend", touch(up)) + exports.wheel.unlock() + } + exports.unbind = function(){ + if (! bound) return; + bound = false + opt.el.removeEventListener("mousedown", down) + window.removeEventListener("mousemove", move) + window.removeEventListener("mouseup", up) + exports.wheel.lock() + } + function cancelable (fn) { + return function(e){ + e.preventDefault() + fn(e) + } + } + function touch (fn){ + return function(e){ + fn(e.touches[0]) + } + } + function down (e) { + px = e.pageX + py = e.pageY + exports.dragging = true + } + function move (e) { + if (! exports.dragging) return + exports.delta(px- e.pageX, py - e.pageY) + px = e.pageX + py = e.pageY + } + function up (e) { + exports.dragging = false + } + exports.delta = function(x,y){ + opt.rotationY += x/window.innerWidth * opt.sensitivity + opt.rotationX = opt.rotationX + y/window.innerHeight * opt.sensitivity + } + exports.move = function(y, x){ + opt.rotationY = y + if (typeof x == "number") { opt.rotationX = x } + } + exports.zoom = function(r){ + opt.radius = r + } + exports.setZoom = function(r){ + radius = opt.radius = r + } + exports.zoomPercent = function(n){ + opt.radius = lerp(n, opt.radiusRange[0], opt.radiusRange[1]) + } + exports.zoomDelta = function(r){ + opt.radius += r + } + exports.pause = function(){ + var sy = sign(opt.rotationY-ry) + var sx = sign(opt.rotationX-rx) + opt.rotationY = ry + sy * 0.1 + opt.rotationX = rx + sx * 0.1 + } + exports.update = function(){ + if (abs(ry - opt.rotationY) > epsilon) { + ry = avg(ry, opt.rotationY, opt.ease) + } + else { + ry = opt.rotationY + } + if (abs(rx - opt.rotationX) > epsilon) { + rx = avg(rx, opt.rotationX, opt.ease) + } + else { + rx = opt.rotationX + } + if (abs(radius - opt.radius) > epsilon) { + radius = avg(radius, opt.radius, opt.wheelEase) + } + else { + radius = opt.radius + } + opt.camera.x = opt.center.x + radius * sin(rx) * cos(ry) + opt.camera.z = opt.center.y + radius * sin(rx) * sin(ry) + opt.camera.y = opt.center.z + radius * cos(rx) + opt.camera.rotationX = PI/2 - rx + opt.camera.rotationY = ry + PI/2 + } + return exports +} diff --git a/assets/javascripts/mx/mx.skew.js b/assets/javascripts/mx/mx.skew.js new file mode 100644 index 0000000..73296bd --- /dev/null +++ b/assets/javascripts/mx/mx.skew.js @@ -0,0 +1,610 @@ +/** + * Copyright (C) 2013 by Evan You + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +var MX = MX || (function (undefined) { + + var MX = { + version: '0.1.0', + prefix: undefined, + rotationUnit: 'rad', + } + + var floatPrecision = 5 + + // ======================================================================== + // Setup & Compatibility + // ======================================================================== + + var transformProp, + transitionProp, + transformOriginProp, + transformStyleProp, + perspectiveProp, + transitionEndEvent + + var positionAtCenter = true, // whether to auto center objects + centeringCSS // styles to inject for center positioning + + document.addEventListener('DOMContentLoaded', setup) + + function setup () { + + // sniff prefix + + var s = document.body.style + + MX.prefix = + 'webkitTransform' in s ? 'webkit' : + 'mozTransform' in s ? 'moz' : + 'msTransform' in s ? 'ms' : '' + + transformProp = MX.transformProp = addPrefix('transform') + transitionProp = MX.transitionProp = addPrefix('transition') + transformOriginProp = MX.transformOriginProp = addPrefix('transformOrigin') + transformStyleProp = MX.transformStyleProp = addPrefix('transformStyle') + perspectiveProp = MX.perspectiveProp = addPrefix('perspective') + transitionEndEvent = MX.transitionEndEvent = MX.prefix === 'webkit' ? 'webkitTransitionEnd' : 'transitionend' + + // shiv rAF + + var vendors = ['webkit', 'moz', 'ms'] + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'] + window.cancelAnimationFrame = + window[vendors[x]+'CancelAnimationFrame'] || + window[vendors[x]+'CancelRequestAnimationFrame'] + } + + // inject centering css + + centeringCSS = document.createElement('style') + centeringCSS.type = 'text/css' + centeringCSS.innerHTML = + '.mx-object3d {' + + 'position: absolute;' + + 'top: 50%;' + + 'left: 50%;}' + injectCenteringCSS() + + window.scrollTo(0,0) + } + + function injectCenteringCSS () { + document.head.appendChild(centeringCSS) + } + + function removeCenteringCSS () { + document.head.removeChild(centeringCSS) + } + + // ======================================================================== + // Utils + // ======================================================================== + + function toDeg (rad) { + return rad / Math.PI * 180 + } + + function toRad (deg) { + return deg / 180 * Math.PI + } + + function buildRotationTranslation (obj) { + + // used when rotationOrigin is set + + var origin = obj.rotationOrigin + if (!origin) { + return + } else { + var dx = origin.x - obj.x, + dy = -(origin.y - obj.y), + dz = -(origin.z - obj.z) + return { + before: 'translate3d(' + dx.toFixed(floatPrecision) +'px,' + dy.toFixed(floatPrecision) + 'px,' + dz.toFixed(floatPrecision) + 'px) ', + after: 'translate3d(' + (-dx).toFixed(floatPrecision) + 'px,' + (-dy).toFixed(floatPrecision) + 'px,' + (-dz).toFixed(floatPrecision) + 'px) ' + } + } + } + + function addPrefix (string) { + if (MX.prefix) { + string = MX.prefix + string.charAt(0).toUpperCase() + string.slice(1) + } + return string + } + + // ======================================================================== + // Base Object3D + // ======================================================================== + + function Object3D (el) { + + this.setupDomElement(el) + this.setCSSTransformStyle('preserve-3d') + this.el.classList.add('mx-object3d') + + this.parent = undefined + this.children = [] + this.updateChildren = true + + this.inverseLookAt = false + + this.persisted = true + + this.reset() + +// this.quaternion = new MX.Quaternion () +// this.quaternion._euler = this + + var width, height, + self = this + + Object.defineProperty(this, 'width', { + get: function () { + return width + || parseInt(self.el.style.width, 10) * app_devicePixelRatio + || 0 + }, + set: function (val) { + width = val + this.el.style.width = (width/app_devicePixelRatio) + 'px' + } + }) + + Object.defineProperty(this, 'height', { + get: function () { + return height + || parseInt(self.el.style.height, 10) * app_devicePixelRatio + || 0 + }, + set: function (val) { + height = val + this.el.style.height = (height/app_devicePixelRatio) + 'px' + } + }) + } + + Object3D.prototype = { + + constructor: Object3D, + + reset: function () { + this.x = this.__x = 0 + this.y = this.__y = 0 + this.z = this.__z = 0 + this.rotationX = this.__rotationX = 0 + this.rotationY = this.__rotationY = 0 + this.rotationZ = this.__rotationZ = 0 + this.scaleX = this.__scaleX = 1 + this.scaleY = this.__scaleY = 1 + this.scaleZ = this.__scaleZ = 1 + this.skewX = this.__skewX = 0 + this.skewY = this.__skewY = 0 + this.scale = this.__scale = 1 + this.perspective = this.__perspective = 0 + this.rotationOrigin = undefined + this.followTarget = undefined +// this.quaternion = new MX.Quaternion() + this.dirty = true + this.update() + }, + + setupDomElement: function (el) { + this.el = undefined + if (el instanceof HTMLElement) { + this.el = el + } else if (typeof el === 'string') { + var tag = el.match(/^[^.#\s]*/)[1], + id = el.match(/#[^.#\s]*/), + classes = el.match(/\.[^.#\s]*/g) + this.el = document.createElement(tag || 'div') + if (id) { + this.el.id = id[0].slice(1) + } + if (classes) { + var i = classes.length + while (i--) { + this.el.classList.add(classes[i].slice(1)) + } + } + } else { + this.el = document.createElement('div') + } + }, + + update: function () { + + if (this.updateChildren) { + var i = this.children.length + while (i--) { + this.children[i].update() + } + } + + if (this.followTarget) { + this.lookAt(this.followTarget, false) + } + + if (this.scaleX !== this.__scaleX || + this.scaleY !== this.__scaleY || + this.scaleZ !== this.__scaleZ) { + this.__scaleX = this.scaleX + this.__scaleY = this.scaleY + this.__scaleZ = this.scaleZ + this.dirty = true + } + + if (this.skewX !== this.__skewX || + this.skewY !== this.__skewY) { + this.__skewX = this.skewX + this.__skewY = this.skewY + this.dirty = true + } + + if (this.scale !== this.__scale) { + this.scaleX = + this.scaleY = + this.scaleZ = + this.__scaleX = + this.__scaleY = + this.__scaleZ = + this.__scale = + this.scale + this.dirty = true + } + + if (this.rotationX !== this.__rotationX || + this.rotationY !== this.__rotationY || + this.rotationZ !== this.__rotationZ) { + this.__rotationX = this.rotationX + this.__rotationY = this.rotationY + this.__rotationZ = this.rotationZ + this.dirty = true + } + + if (this.x !== this.__x || + this.y !== this.__y || + this.z !== this.__z) { + this.__x = this.x + this.__y = this.y + this.__z = this.z + this.dirty = true + } + + if (this.perspective !== this.__perspective) { + this.__perspective = this.perspective + this.dirty = true + } + + if (this.dirty && this.el) { + + var rotationTranslation = buildRotationTranslation(this), + rotation = 'rotateX(' + this.rotationX.toFixed(floatPrecision) + MX.rotationUnit + ') ' + + 'rotateY(' + this.rotationY.toFixed(floatPrecision) + MX.rotationUnit + ') ' + + 'rotateZ(' + this.rotationZ.toFixed(floatPrecision) + MX.rotationUnit + ') ' + + var transformString = + (MX.positionAtCenter ? 'translate3d(-50%, -50%, 0) ' : '') + + (this.perspective ? 'perspective(' + this.perspective + 'px) ' : '') + + 'translate3d(' + + this.x.toFixed(floatPrecision || 0) + 'px,' + + (-this.y).toFixed(floatPrecision) + 'px,' + + (-this.z).toFixed(floatPrecision) + 'px) ' + + 'scale3d(' + + (app_devicePixelRatio * this.scaleX).toFixed(floatPrecision) + ',' + + (app_devicePixelRatio * this.scaleY).toFixed(floatPrecision) + ',' + + (app_devicePixelRatio * this.scaleZ).toFixed(floatPrecision) + ') ' + + 'skew(' + + (app_devicePixelRatio * this.skewX).toFixed(floatPrecision) + 'rad,' + + (app_devicePixelRatio * this.skewY).toFixed(floatPrecision) + 'rad) ' + + if (rotationTranslation) { + transformString += rotationTranslation.before + + rotation + + rotationTranslation.after + + } else { + transformString += rotation + } + + this.el.style[transformProp] = transformString + this.dirty = false + } + + return this + + }, + + // taken from three.js + setFromQuaternion: function ( q, order, update ) { + // q is assumed to be normalized + + // http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m + + var sqx = q.x * q.x; + var sqy = q.y * q.y; + var sqz = q.z * q.z; + var sqw = q.w * q.w; + + this.rotationX = Math.atan2( 2 * ( q.x * q.w - q.y * q.z ), ( sqw - sqx - sqy + sqz ) ); + this.rotationY = Math.asin( clamp( 2 * ( q.x * q.z + q.y * q.w ), -1, 1 ) ); + this.rotationZ = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw + sqx - sqy - sqz ) ); + }, + + lookAt: function (target, update) { + var r = this.getLookAtEuler(target) + this.setRotation(r) + if (update !== false) this.update() + return this + }, + + getLookAtEuler: function (target) { + // euler order XYZ + var r = {}, + dx = target.x - this.x, + dy = target.y - this.y, + dz = target.z - this.z + if (this.inverseLookAt) { + dx = -dx + dy = -dy + dz = -dz + } + if (dz === 0) dz = 0.001 + r.x = -Math.atan2(dy, dz) + var flip = dz > 0 ? 1 : -1 + r.y = flip * Math.atan2(dx * Math.cos(r.x), dz * -flip) + r.z = Math.atan2(Math.cos(r.x), Math.sin(r.x) * Math.sin(r.y)) - Math.PI / 2 + if (MX.rotationUnit === 'deg') { + r.x = toDeg(r.x) + r.y = toDeg(r.y) + r.z = toDeg(r.z) + } + return r + }, + + add: function () { + if (!this.el) return + var parent = this + Array.prototype.forEach.call(arguments, function (child) { + if (!child instanceof Object3D) return + parent.el.appendChild(child.el) + if (!parent.children) parent.children = [] + parent.children.push(child) + child.parent = parent + }) + return this + }, + + remove: function () { + var parent = this + Array.prototype.forEach.call(arguments, function (child) { + var index = parent.children.indexOf(child) + if (index !== -1) { + parent.children.splice(index, 1) + parent.el.removeChild(child.el) + child.parent = undefined + } + }) + return this + }, + + addTo: function (target) { + if (typeof target === 'string') { + target = document.querySelector(target) + } + if (target instanceof HTMLElement && target.appendChild) { + target.appendChild(this.el) + } else if (target instanceof Object3D || target instanceof MX.Scene) { + target.add(this) + } + return this + }, + + removeElement: function () { + if (this.el.parentNode) { + this.el.parentNode.removeChild(this.el) + } + }, + + setPosition: function (tar) { + this.x = (tar.x || tar.x === 0) ? tar.x : this.x + this.y = (tar.y || tar.y === 0) ? tar.y : this.y + this.z = (tar.z || tar.z === 0) ? tar.z : this.z + }, + + setRotation: function (tar) { + this.rotationX = (tar.x || tar.x === 0) ? tar.x : this.rotationX + this.rotationY = (tar.y || tar.y === 0) ? tar.y : this.rotationY + this.rotationZ = (tar.z || tar.z === 0) ? tar.z : this.rotationZ + }, + + setScale: function (tar) { + this.scaleX = (tar.x || tar.x === 0) ? tar.x : this.scaleX + this.scaleY = (tar.y || tar.y === 0) ? tar.y : this.scaleY + this.scaleZ = (tar.z || tar.z === 0) ? tar.z : this.scaleZ + }, + + setSkew: function (tar) { + this.skewX = (tar.x || tar.x === 0) ? tar.x : this.skewX + this.skewY = (tar.y || tar.y === 0) ? tar.y : this.skewY + }, + + setCSSTransformOrigin: function (origin) { + this.el && (this.el.style[transformOriginProp] = origin) + return this + }, + + setCSSTransformStyle: function (style) { + this.el && (this.el.style[transformStyleProp] = style) + return this + }, + + setCSSTransition: function (trans) { + this.el && (this.el.style[transitionProp] = trans) + return this + }, + + setCSSPerspective: function (pers) { + this.el && (this.el.style[perspectiveProp] = pers) + return this + }, + + move: function(ops){ + var layer = this + layer.ops = defaults(ops, layer.ops) + for (var i in ops) { + layer[i] = ops[i] + } + layer.dirty = true + layer.update() + }, + + onTransitionEnd: function (callback) { + this.cancelTransitionEnd() + var el = this.el + el.addEventListener(transitionEndEvent, onEnd) + function onEnd () { + el.removeEventListener(transitionEndEvent, onEnd) + callback() + } + }, + + cancelTransitionEnd: function () { + this.el.removeEventListener(transitionEndEvent) + }, + + toString: function(params){ + params = params || "id width height depth x y z rotationX rotationY rotationZ scale".split(" ") + return this.__toString(params) + }, + + __toString: function(params, func){ + this.id = this.id || 'undef' // _.uniqueId() + var list = [], + obj = {}, + type = this.type || "Object3d", + name = type.toLowerCase(), + val, + param + for (var i in params) { + param = params[i] + val = this[param] + if (val === 0 && ! func) continue; + if (typeof val == "number") { + if (param.indexOf("rotation") != -1) { + obj[param] = Number(val.toFixed(3)) + } + else { + obj[param] = ~~val + } + } + else { + obj[param] = val + } + } + + return (func || "var " + name + " = new MX." + type ) + "(" + + JSON.stringify(obj, undefined, 2) + + ")\n" + (func ? "" : "scene.add(" + name + ")") + }, + + contains: function(x,y,z){ + var containsX = false, + containsY = false, + containsZ = false + + if (x === null) { + containsX = true + } + else { + containsX = abs(this.x - x) <= this.width/2 + } + + if (y === null) { + containsY = true + } + else { + containsY = abs(this.y - y) <= this.height/2 + } + + if (z === null) { + containsZ = true + } + else { + containsZ = abs(this.z - z) <= this.depth/2 + } + + return (containsX && containsY && containsZ) + } + + } + + // ======================================================================== + // Inheritance + // ======================================================================== + + Object3D.extend = extend.bind(Object3D) + + function extend (props) { + var Super = this + var ExtendedObject3D = function () { + Super.call(this) + props.init && props.init.apply(this, arguments) + } + ExtendedObject3D.prototype = Object.create(Super.prototype) + for (var prop in props) { + if (props.hasOwnProperty(prop) && prop !== 'init') { + ExtendedObject3D.prototype[prop] = props[prop] + } + } + ExtendedObject3D.extend = extend.bind(ExtendedObject3D) + return ExtendedObject3D + } + + // ======================================================================== + // Expose API + // ======================================================================== + + MX.Object3D = Object3D + MX.toRad = toRad + MX.toDeg = toDeg + + // center positioning getter setter + Object.defineProperty(MX, 'positionAtCenter', { + get: function () { + return positionAtCenter + }, + set: function (val) { + if (typeof val !== 'boolean') return + positionAtCenter = val + if (positionAtCenter) { + injectCenteringCSS() + } else { + removeCenteringCSS() + } + } + }) + + return MX + +})()
\ No newline at end of file diff --git a/assets/javascripts/mx/primitives/mx.image.js b/assets/javascripts/mx/primitives/mx.image.js new file mode 100644 index 0000000..39bb0b5 --- /dev/null +++ b/assets/javascripts/mx/primitives/mx.image.js @@ -0,0 +1,50 @@ +MX.Image = MX.Object3D.extend({ + init: function (ops) { + + this.type = "Image" + this.media = ops.media + this.width = 0 + this.height = 0 + this.x = ops.x || 0 + this.y = ops.y || 0 + this.z = ops.z || 0 + this.scale = ops.scale || 1 + this.backface = ops.backface || false + + ops.className && this.el.classList.add(ops.className) + this.backface && this.el.classList.add("backface-visible") + this.el.classList.add("image") + this.el.classList.add("mx-scenery") + + this.el.style.backgroundRepeat = 'no-repeat' + + this.load(ops) + }, + + load: function(ops){ + var layer = this + layer.ops = defaults(ops, layer.ops) + + var image = new Image() + image.onload = function(){ + if (! layer.ops) return + layer.scale = layer.ops.scale || 1 + layer.width = layer.ops.width || image.naturalWidth + layer.height = layer.ops.height || image.naturalHeight +// layer.x = layer.ops.x || 0 +// layer.y = layer.ops.y || 0 +// layer.z = layer.ops.z || 0 +// layer.rotationX = layer.ops.rotationX || 0 +// layer.rotationY = layer.ops.rotationY || 0 +// layer.rotationZ = layer.ops.rotationZ || 0 + layer.el.style.backgroundImage = "url(" + image.src + ")" + layer.el.classList.add('image') + layer.dirty = true + layer.ops.onload && layer.ops.onload( image ) + layer.update() + } + image.src = ops.src; + if (image.complete) setTimeout(image.onload) + }, + +}) |
