Guest User

Untitled

a guest
Feb 17th, 2015
271
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* Impetus.js
  2.  * http://chrisbateman.github.com/impetus
  3.  * Copyright (c) 2014 Chris Bateman
  4.  * Licensed under the MIT license */
  5.  
  6. (function() {
  7.     var Impetus = function(cfg) {
  8.         'use strict';
  9.  
  10.         var sourceEl, updateCallback, boundXmin, boundXmax, boundYmin, boundYmax, pointerLastX, pointerLastY, pointerCurrentX, pointerCurrentY, pointerId, decVelX, decVelY;
  11.         var targetX = 0;
  12.         var targetY = 0;
  13.         var multiplier = 1;
  14.         var friction = 0.92;
  15.         var stopThresholdDefault = 0.4;
  16.         var stopThreshold = stopThresholdDefault;
  17.         var ticking = false;
  18.         var pointerActive = false;
  19.         var paused = false;
  20.         var decelerating = false;
  21.         var trackingPoints = [];
  22.  
  23.         var touchstartTime = null;
  24.         var touchendTime = null;
  25.         var flickMultiplier = 1;
  26.         var flickMultiplierMax = 5;
  27.         var flickAccelation = .5;
  28.         var flickThreshold = 350; //ms
  29.         var flickDeceleration = 0;
  30.  
  31.  
  32.         /**
  33.          * @see http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
  34.          */
  35.         var requestAnimFrame = (function(){
  36.             return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) {
  37.                     window.setTimeout(callback, 1000 / 60);
  38.                 };
  39.         })();
  40.  
  41.         /**
  42.          * Disable movement processing
  43.          * @public
  44.          */
  45.         this.pause = function() {
  46.             pointerActive = false;
  47.             paused = true;
  48.         };
  49.  
  50.         /**
  51.          * Enable movement processing
  52.          * @public
  53.          */
  54.         this.resume = function() {
  55.             paused = false;
  56.         };
  57.  
  58.         /**
  59.          * Update the current x and y values
  60.          * @public
  61.          * @param {Number} x
  62.          * @param {Number} y
  63.          */
  64.         this.setValues = function(x, y) {
  65.             if (typeof x === 'number') {
  66.                 targetX = x;
  67.             }
  68.             if (typeof y === 'number') {
  69.                 targetY = y;
  70.             }
  71.         };
  72.  
  73.         /**
  74.          * Update the multiplier value
  75.          * @public
  76.          * @param {Number} val
  77.          */
  78.         this.setMultiplier = function(val) {
  79.             multiplier = val;
  80.             stopThreshold = stopThresholdDefault * multiplier;
  81.         };
  82.  
  83.         /**
  84.          * Executes the update function
  85.          */
  86.         var callUpdateCallback = function() {
  87.             updateCallback.call(sourceEl, targetX, targetY);
  88.         };
  89.  
  90.         /**
  91.          * Creates a custom normalized event object from touch and mouse events
  92.          * @param  {Event} ev
  93.          * @returns {Object} with x, y, and id properties
  94.          */
  95.         var normalizeEvent = function(ev) {
  96.             if (ev.type === 'touchmove' || ev.type === 'touchstart' || ev.type === 'touchend') {
  97.                 var touch = ev.targetTouches[0] || ev.changedTouches[0];
  98.                 return {
  99.                     x: touch.clientX,
  100.                     y: touch.clientY,
  101.                     id: touch.identifier
  102.                 };
  103.             } else { // mouse events
  104.                 return {
  105.                     x: ev.clientX,
  106.                     y: ev.clientY,
  107.                     id: null
  108.                 };
  109.             }
  110.         };
  111.  
  112.         /**
  113.          * Initializes movement tracking
  114.          * @param  {Object} ev Normalized event
  115.          */
  116.         var onDown = function(ev) {
  117.             var event = normalizeEvent(ev);
  118.             if (!pointerActive && !paused) {
  119.                 touchstartTime = Date.now();
  120.  
  121.                 if(decelerating && touchendTime && (touchstartTime - touchendTime) < flickThreshold){
  122.                     flickMultiplier += flickMultiplier >= flickMultiplierMax ? 0 : flickAccelation;
  123.                 }
  124.                 else {
  125.                     flickMultiplier -= flickMultiplier <= 1 ? 0 : flickAccelation;
  126.                 }
  127.  
  128.                 pointerActive = true;
  129.                 decelerating = false;
  130.                 pointerId = event.id;
  131.  
  132.                 pointerLastX = pointerCurrentX = event.x;
  133.                 pointerLastY = pointerCurrentY = event.y;
  134.                 trackingPoints = [];
  135.                 addTrackingPoint(pointerLastX, pointerLastY);
  136.  
  137.                 document.addEventListener('touchmove', onMove);
  138.                 document.addEventListener('touchend', onUp);
  139.                 document.addEventListener('touchcancel', stopTracking);
  140.                 document.addEventListener('mousemove', onMove);
  141.                 document.addEventListener('mouseup', onUp);
  142.             }
  143.         };
  144.  
  145.         /**
  146.          * Handles move events
  147.          * @param  {Object} ev Normalized event
  148.          */
  149.         var onMove = function(ev) {
  150.             ev.preventDefault();
  151.             var event = normalizeEvent(ev);
  152.  
  153.             if (pointerActive && event.id === pointerId) {
  154.                 pointerCurrentX = event.x;
  155.                 pointerCurrentY = event.y;
  156.                 addTrackingPoint(pointerLastX, pointerLastY);
  157.                 requestTick();
  158.             }
  159.         };
  160.  
  161.         /**
  162.          * Handles up/end events
  163.          * @param  {Object} ev Normalized event
  164.          */
  165.         var onUp = function(ev) {
  166.             var event = normalizeEvent(ev);
  167.  
  168.             if (pointerActive && event.id === pointerId) {
  169.                 stopTracking();
  170.             }
  171.         };
  172.  
  173.         /**
  174.          * Stops movement tracking, starts animation
  175.          */
  176.         var stopTracking = function() {
  177.             pointerActive = false;
  178.             addTrackingPoint(pointerLastX, pointerLastY);
  179.             touchendTime = Date.now();
  180.             startDecelAnim();
  181.  
  182.             document.removeEventListener('touchmove', onMove);
  183.             document.removeEventListener('touchend', onUp);
  184.             document.removeEventListener('touchcancel', stopTracking);
  185.             document.removeEventListener('mouseup', onUp);
  186.             document.removeEventListener('mousemove', onMove);
  187.         };
  188.  
  189.         /**
  190.          * Records movement for the last 100ms
  191.          * @param {number} x
  192.          * @param {number} y [description]
  193.          */
  194.         var addTrackingPoint = function(x, y) {
  195.             var time = Date.now();
  196.             while (trackingPoints.length > 0) {
  197.                 if (time - trackingPoints[0].time <= 100) {
  198.                     break;
  199.                 }
  200.                 trackingPoints.shift();
  201.             }
  202.  
  203.             trackingPoints.push({
  204.                 x: x,
  205.                 y: y,
  206.                 time: time
  207.             });
  208.         };
  209.  
  210.         /**
  211.          * Calculate new values, call update function
  212.          */
  213.         var update = function() {
  214.             targetX += (pointerCurrentX - pointerLastX) * multiplier;
  215.             targetY += (pointerCurrentY - pointerLastY) * multiplier;
  216.  
  217.             checkBounds();
  218.             callUpdateCallback();
  219.  
  220.             pointerLastX = pointerCurrentX;
  221.             pointerLastY = pointerCurrentY;
  222.             ticking = false;
  223.         };
  224.  
  225.         /**
  226.          * prevents animating faster than current framerate
  227.          */
  228.         var requestTick = function() {
  229.             if (!ticking) {
  230.                 requestAnimFrame(update);
  231.             }
  232.             ticking = true;
  233.         };
  234.  
  235.         /**
  236.          * Initializes the bound values
  237.          */
  238.         var initBounds = function() {
  239.             if (cfg.boundX) {
  240.                 boundXmin = cfg.boundX[0];
  241.                 boundXmax = cfg.boundX[1];
  242.             }
  243.             if (cfg.boundY) {
  244.                 boundYmin = cfg.boundY[0];
  245.                 boundYmax = cfg.boundY[1];
  246.             }
  247.         };
  248.  
  249.         /**
  250.          * Keep values in bounds, if available
  251.          */
  252.         var checkBounds = function() {
  253.             if (boundXmin !== undefined && targetX < boundXmin) {
  254.                 targetX = boundXmin;
  255.             }
  256.             if (boundXmax !== undefined && targetX > boundXmax) {
  257.                 targetX = boundXmax;
  258.             }
  259.             if (boundYmin !== undefined && targetY < boundYmin) {
  260.                 targetY = boundYmin;
  261.             }
  262.             if (boundYmax !== undefined && targetY > boundYmax) {
  263.                 targetY = boundYmax;
  264.             }
  265.         };
  266.  
  267.         /**
  268.          * Initialize animation of values coming to a stop
  269.          */
  270.         var startDecelAnim = function() {
  271.             var firstPoint = trackingPoints[0];
  272.             var lastPoint = trackingPoints[trackingPoints.length - 1];
  273.  
  274.             var xOffset = lastPoint.x - firstPoint.x;
  275.             var yOffset = lastPoint.y - firstPoint.y;
  276.             var timeOffset = lastPoint.time - firstPoint.time;
  277.  
  278.             var D = (timeOffset / 15) / multiplier;
  279.  
  280.             decVelX = xOffset / D * flickMultiplier;
  281.             decVelY = yOffset / D * flickMultiplier;
  282.  
  283.             if (Math.abs(decVelX) > 1 || Math.abs(decVelY) > 1) {
  284.                 decelerating = true;
  285.                 requestAnimFrame(stepDecelAnim);
  286.             }
  287.         };
  288.  
  289.         /**
  290.          * Animates values slowing down
  291.          */
  292.         var stepDecelAnim = function() {
  293.             if (!decelerating) {
  294.                 return;
  295.             }
  296.  
  297.             decVelX *= friction;
  298.             decVelY *= friction;
  299.  
  300.             if (Math.abs(decVelX) > stopThreshold || Math.abs(decVelY) > stopThreshold) {
  301.                 targetX += decVelX;
  302.                 targetY += decVelY;
  303.  
  304.                 // Slowly move flickMultiplier to 1
  305.                 var lastTouchDiff = Date.now() - touchendTime,
  306.                     lastSpanned = Math.floor(lastTouchDiff / flickThreshold);
  307.                 if(flickDeceleration != lastSpanned){
  308.                     flickMultiplier -= flickMultiplier <= 1 ? 0 : flickAccelation;
  309.                     flickDeceleration = lastSpanned;
  310.                 }
  311.  
  312.                 checkBounds();
  313.                 callUpdateCallback();
  314.  
  315.                 requestAnimFrame(stepDecelAnim);
  316.             } else {
  317.                 decelerating = false;
  318.                 flickMultiplier = 1;
  319.                 flickDeceleration = 0;
  320.             }
  321.         };
  322.  
  323.  
  324.         /**
  325.          * Initialize instance
  326.          */
  327.         (function init() {
  328.             if (cfg.source) {
  329.                 sourceEl = (typeof cfg.source === 'string') ? document.querySelector(cfg.source) : cfg.source;
  330.                 if (!sourceEl) {
  331.                     throw new Error('IMPETUS: source not found.');
  332.                 }
  333.             } else {
  334.                 sourceEl = document;
  335.             }
  336.  
  337.             if (cfg.update) {
  338.                 updateCallback = cfg.update;
  339.             } else {
  340.                 throw new Error('IMPETUS: update function not defined.');
  341.             }
  342.  
  343.             if (typeof cfg.multiplier !== 'undefined') {
  344.                 multiplier = cfg.multiplier || multiplier;
  345.                 stopThreshold = stopThresholdDefault * multiplier;
  346.             }
  347.             if (typeof cfg.friction !== 'undefined') {
  348.                 friction = cfg.friction || friction;
  349.             }
  350.  
  351.             if (cfg.initialValues) {
  352.                 if (cfg.initialValues[0]) {
  353.                     targetX = cfg.initialValues[0];
  354.                 }
  355.                 if (cfg.initialValues[1]) {
  356.                     targetY = cfg.initialValues[1];
  357.                 }
  358.                 callUpdateCallback();
  359.             }
  360.  
  361.             initBounds();
  362.  
  363.             sourceEl.addEventListener('touchstart', onDown);
  364.             sourceEl.addEventListener('mousedown', onDown);
  365.  
  366.         })();
  367.     };
  368.  
  369.     // AMD
  370.     if (typeof define === 'function' && define.amd) {
  371.         define(function() {
  372.             return Impetus;
  373.         });
  374.     } else {
  375.         this.Impetus = Impetus;
  376.     }
  377.  
  378. })();
Advertisement
Add Comment
Please, Sign In to add comment