MX.MobileMovements = function (cam) { var touching = true, moving = false, startTime = null, v = 12, vr = Math.PI * 0.012, vx = vy = vz = 0; var directionLocked = false, directionLockThreshold = 5 var pos = { x: 0, y: viewHeight, z: 0, rotationX: 0, rotationY: 0 } var pointX, pointY, deltaX, deltaY, distX = 0, distY = 0, absDistX = 0, absDistY = 0, startTime var rotationX = 0, rotationY = 0, destRotationX = 0, destRotationY = 0 var rotationSum = 0 var rotationMedian = 0 var orientationMax = 0 var samples = 0 var sampleThreshold = 120 var lastAlpha var is_portrait var exports = { init: function () { exports.orientationchange() document.addEventListener("touchstart", exports.touchstart) document.addEventListener("touchmove", exports.touchmove) document.addEventListener("touchend", exports.touchend) window.addEventListener('orientationchange', exports.orientationchange) window.addEventListener("devicemotion", exports.devicemotion) window.addEventListener("deviceorientation", exports.deviceorientation) }, touchstart: function(e){ if (e.touches.length == 1) { touching = true startTime = Date.now() var point = event.touches[0] pointX = point.pageX pointY = point.pageY distX = distY = 0 pos.x = cam.x pos.z = cam.z pos.rotationY = cam.rotationY } }, touchmove: function(e){ e.preventDefault() if (e.touches.length == 1) { var timestamp = Date.now() var point = event.touches[0] deltaX = point.pageX - pointX deltaY = point.pageY - pointY pointX = point.pageX pointY = point.pageY distX += deltaX distY += deltaY absDistX = abs(distX) absDistY = abs(distY) } }, touchend: function(e){ e.preventDefault() if (e.touches.length == 0) { touching = directionLocked = false var timestamp = Date.now() var duration = startTime - timestamp if (duration < 300) { } } }, orientationchange: function(e){ is_portrait = window.innerWidth < window.innerHeight if (is_portrait) { lastAlpha = 0 } }, devicemotion: function(e) { if (! is_portrait) return; var rotationBeta = e.rotationRate.alpha; // weird! rotationSum += rotationBeta; samples += 1; }, deviceorientation: function (e) { if (! lastAlpha) { lastAlpha = e.alpha } is_portrait ? exports.portraitorientation(e) : exports.landscapeorientation(e) }, portraitorientation: function(e) { // compass gives most accurate orientation in portrait mode var alpha, dx = 0, dy = 0 if (e.webkitCompassHeading) { alpha = 180 - e.webkitCompassHeading; } else { alpha = 180 - e.alpha; } // android rotates in reverse if (is_android) { alpha = 360 - alpha } // use rotationRate to gauge if we've tilted the screen past vertical // for looking at ceiling if (e.beta > orientationMax) { orientationMax = e.beta rotationMedian = rotationSum } // this number was only going to 83 max.. not 90.. weird var beta = e.beta + 7; // if we've got enough motion data, we should be able to determine // if we've tilted backwards. otherwise, lock to the horizon. if (! is_android && samples > sampleThreshold) { dx = rotationSum > rotationMedian ? e.beta - 90 : 90 - e.beta } else { dx = 0 } // avoid jumping around in a circle if (Math.abs(alpha - lastAlpha) < 100 || Math.abs(alpha - lastAlpha) > 300) { dy = alpha - lastAlpha lastAlpha = alpha } // avoid jumping around in a circle #2 if (dy > 300) { dy -= 360 } else if (dy < -300) { dy += 360 } destRotationX = MX.toRad(dx) destRotationY += MX.toRad(dy) }, landscapeorientation: function (e) { var dx, dy dx = e.gamma > 0 ? 90 - e.gamma : 90 + e.gamma dy = e.alpha - lastAlpha lastAlpha = e.alpha // avoid the sudden jump from 0 to -360 if (dy > 300) { dy -= 360 } else if (dy < -300) { dy += 360 } destRotationX = dx > 45 ? 0 : MX.toRad(dx) destRotationY += MX.toRad(dy) }, update: function () { var drx, dry dry = (destRotationY - rotationY) / 6 drx = (destRotationX - rotationX) / 6 rotationY += dry rotationX += drx cam.rotationY = pos.rotationY += dry cam.rotationX = pos.rotationX += drx if (distX || distY) { var oldDistY = absDistY, oldDistX = absDistX absDistY = avg(absDistY, 0, 5) var dy = (oldDistY - absDistY) * sign(distY) * 2 absDistX = avg(absDistX, 0, 5) var dx = (oldDistX - absDistX) * sign(distX) * 2 distY = sign(distY) * absDistY distX = sign(distX) * absDistX pos.x -= dy * Math.cos(pos.rotationY + Math.PI / 2) pos.z -= dy * Math.sin(pos.rotationY + Math.PI / 2) cam.rotationY = pos.rotationY += dx / (window.innerWidth) * Math.PI / 2 app.tube("move", pos) } }, lock: function(){ locked = true }, unlock: function(){ locked = false }, scale: function(n){ if (n) scale = n; return scale }, resetScale: function(n){ scale = DEFAULT_SCALE }, gravity: function(g){ return typeof g == "boolean" ? gravity = g : gravity }, velocity: function(n){ v = clamp(n, 1, 50) }, jumpVelocity: function(n){ jumpV = clamp(n, 1, 50) }, } return exports } // function momentum (current, start, time, lowerMargin, wrapperSize, deceleration) { // var distance = current - start, // speed = Math.abs(distance) / time, // destination, // duration; // // deceleration = deceleration === undefined ? 0.0006 : deceleration; // // destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 ); // duration = speed / deceleration; // // if ( destination < lowerMargin ) { // destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin; // distance = Math.abs(destination - current); // duration = distance / speed; // } else if ( destination > 0 ) { // destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0; // distance = Math.abs(current) + destination; // duration = distance / speed; // } // // return { // destination: Math.round(destination), // duration: duration // }; // }