TremulaJS icon indicating copy to clipboard operation
TremulaJS copied to clipboard

Hammerjs_2.x Exception

Open vko-online opened this issue 9 years ago • 6 comments

Uncaught TypeError: Cannot read property 'stopDetect' of undefined when dragging, in

    "jquery": "~1.7.1",
    "tremulajs": "~1.2.4",
    "hammer.js": "~2.0.4",
    "jsbezier": "*"
switch(ev.type) {
            case 'mousewheel':
            case 'DOMMouseScroll':
            case 'wheel':
                _mw.call(this,ev);
                //dont break here -- keep evaluation...
            case '_mw': //map events over for processing by dragleft
                var wheelEvent = ev;//ev.originalEvent;
                var //wheel events for webkit|| new moz || old moz
                    dy = wheelEvent.wheelDeltaY*.5||-wheelEvent.deltaY||-wheelEvent.detail*3,
                    dx = wheelEvent.wheelDeltaX*.5||-wheelEvent.deltaX||-wheelEvent.detail*3;
                var nextScrollPos = this.scrollPos + (this.sx)?dx:dy;
                var maxScroll = this.trailingEdgeScrollPos;
                //isNextHeadMargin and isNextTailMargin add massive drag to input to simulate rubberband tension in scrollFrame
                var isNextHeadMargin = !this.hasMediumGridDimsSi && nextScrollPos>this.firstItemPos;
                var isNextTailMargin = !this.hasMediumGridDimsSi && nextScrollPos<maxScroll;
                //   || this.isInHeadMargin
                //add scroll tension if looping is OFF and the very next tick is going to put us beyound the first item or the last item
                if(!this.isLooping && (isNextHeadMargin||isNextTailMargin) ){
                    if(this.sx){
                        dx=Math.min(dx*.1,100);
                    }else{
                        dy=Math.min(dy*.1,100);
                    }
                }
                ev.gesture                  = ev.gesture || {};
                ev.gesture.deltaX           = dx;
                ev.gesture.deltaY           = dy;
                ev.gesture.center           = ev.gesture.center || {};
                ev.gesture.center.pageX = ev.pageX;//ev.originalEvent.pageX;
                ev.gesture.center.pageY = ev.pageY;//ev.originalEvent.pageY;
                //fingeredOffset = this.scrollPos; moved below...
                // if(this.isInHeadMargin){ev.gesture.deltaX = dx*.01}
                // ===>  NOTE: THERE IS NO BREAK HERE. MW EVENTS ARE NORMALIZED (as ev.gesture.*) ABOVE AND THEN PROCESSED AS DRAG EVENTS BELOW vvv

            case 'dragup':
            case 'dragdown':
            case 'dragright':
            case 'dragleft':
            // === manually block page scroll ===
            // if in horizontal config and the user is scrolling horizontally
            // or if in vertical config and the user is scrolling vertically
            if(ev.pointerType=="mouse"){
                ev.gesture.preventDefault();
                ev.gesture.stopPropagation();
            }else if(this.sx){//is horizontal config
                // if(ev.gesture.deltaX != 0){//this old bit was recently disabled
                    if(Math.abs(ev.gesture.deltaY/ev.gesture.deltaX) <= 1){ // if this ratio is 1 or less then the user is scrolling the scroll axis: so block native events
                        shuntEvent(ev);
                    }
                // }
            }else{// is vertical config
                // if(ev.gesture.deltaY != 0){//this old bit was recently disabled -- not needed now?
                    if(Math.abs(ev.gesture.deltaX/ev.gesture.deltaY) <= 1){ // if this ratio is 1 or less then the user is scrolling the scroll axis: so block native events
                        shuntEvent(ev);
                    }
                // }
            }// config case
            // === END: manually block page scroll  === 
                this.isTouching=true;
                //incase we are at the begining of a touch event or incase this is a fallthrough WheelEvent
                if(fingeredOffset==0 || /wheel|scroll/.test(ev.type)){
                    fingeredOffset = this.scrollPos;
                    lastD = 0;
                }
                // //incase we are at the begining of a touch event or incase this is a fallthrough WheelEvent
                // if(fingeredOffset_==0 ||  /wheel|scroll/.test(ev.type)){
                //  fingeredOffset_ = this.parentParentE.scrollTop;
                //  lastD_ = 0;
                // }
                var D = (this.sx)?ev.gesture.deltaX:ev.gesture.deltaY;
                var D_ = (!this.sx)?ev.gesture.deltaX:ev.gesture.deltaY;
                //if we are scrolling along the scrollaxis
                if(Math.abs(D)>Math.abs(D_)){
                    this.setScrollPos( D-lastD, true );
                    lastD = D;
                    this.oneShotPaint(ev);
                }
                this.tagLastUserEvent(ev);
                break;

            case 'swipeleft':
                if(!this.sx){return}
                ev.gesture.stopDetect();
                this.isTouching=false;
                //var m = this.momentum = -this.dMomentum;
                var m = -ev.gesture.velocityX;
                if(this.steppedScrolling)
                    this.easeToNextStepItem();
                else
                    this.startEasing(m,ev)
                this.tagLastUserEvent(ev);
                break;

            case 'swiperight':
                if(!this.sx){return}
                ev.gesture.stopDetect();
                this.isTouching=false;
                var m = ev.gesture.velocityX;
                if(this.steppedScrolling)
                    this.easeToPrevStepItem();
                else
                    this.startEasing(m,ev)
                this.tagLastUserEvent(ev);
                break;

            case 'swipeup':
                if(this.sx){return}
                ev.gesture.stopDetect();
                this.isTouching=false;
                var m = -ev.gesture.velocityY;
                if(this.steppedScrolling)
                    this.easeToNextStepItem();
                else
                    this.startEasing(m,ev)
                this.tagLastUserEvent(ev);
                break;

            case 'swipedown':
                if(this.sx){return}
                ev.gesture.stopDetect();
                this.isTouching=false;
                //var m = this.momentum = this.dMomentum;
                var m = ev.gesture.velocityY;
                if(this.steppedScrolling)
                    this.easeToPrevStepItem();
                else
                    this.startEasing(m,ev)
                this.tagLastUserEvent(ev);
                break;

            case 'touch':
                //u.log('touch: '+new Date().getMilliseconds())
                fingeredOffset = 0;
                fingeredOffset_ = 0;
                this.isTouching=true;
                this.oneShotPaint(ev);
                this.tagLastUserEvent(ev);
                break;
            case 'release':
                //u.log('release: '+new Date().getMilliseconds())
                //test for last event being a touch AND being OVER x ms ago.  Also make sure we're not in the middle of easing.
                var lastUserEvtMs = new Date() - this.lastUserEvent.time;
                var lastWasTouch = /touch/.test(this.lastUserEvent.evt.type,'i');
                if(!this.isEasing && lastWasTouch && lastUserEvtMs < 1000){
                    this.$e.trigger('tremulaItemSelect',ev);
                }
                this.isTouching=false;
                if(this.steppedScrolling){
                    var lastWasLegalTouch =  lastWasTouch && ev.target && ev.target.className && !/\bgridBox\b/.test(ev.target.className);

                    if(!lastWasTouch || lastWasLegalTouch){
                        this.easeToClosestStepItem();
                    };
                }else{
                    this.oneShotPaint();
                }
                this.tagLastUserEvent(ev);
                break;          
        }//switch

ev doesn't have property gesture in 'swipe***' case. ev.gesture.stopDetect(); causes error

copied from tremula codepen demo

    (function(){

  'use strict';

  angular.module('tremula', []).directive('tremula', function($timeout){

    return{
      restrict: 'EA',
      link: function($scope, elem){
        var tremulaBase;
        function createTremula(){

          var $tremulaContainer = elem;
          var tremula = new Tremula();
          var config = {
            itemConstraint      :150,//px
            itemMargins         :[10,10],//x (left & right), y (top & bottom) in px
            staticAxisOffset    :0,//px
            scrollAxisOffset    :20,//px
            scrollAxis          :'x',//'x'|'y'
            surfaceMap          :tremula.projections.xyPlain,
            staticAxisCount     :2,//zero based
            defaultLayout       :tremula.layouts.xyPlain,
            itemPreloading      :true,
            itemEasing          :false,
            isLooping           :false,
            itemEasingParams    :{
              touchCurve          :tremula.easings.easeOutCubic,
              swipeCurve          :tremula.easings.easeOutCubic,
              transitionCurve     :tremula.easings.easeOutElastic,
              easeTime            :500,
              springLimit         :40 //in px
            },
            onChangePub                 : doScrollEvents,
            data                : null,
            lastContentBlock        : {
              template :'<div class="lastContentItem"></div>',
              layoutType :'tremulaBlockItem',
              noScaling:true,
              w:300,
              h:300,
              isLastContentBlock:true,
              adapter:tremula.dataAdapters.TremulaItem
            },
            adapter             :null

          };
          tremula.init($tremulaContainer,config,this);
          return tremula;
        }
        function doScrollEvents(o){
          if(o.scrollProgress>.7){
            if(!tremula.cache.endOfScrollFlag){
              tremula.cache.endOfScrollFlag = true;
              pageCtr++;
              loadFlickr();
              console.log('END OF SCROLL!')
            }
          }
        }

        var pageCtr = 1;
        function loadFlickr(){
          var dataUrl = 'https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=c149b994c54c114bd7836b61539eec2e&tags=street+art&format=json&page='+pageCtr+'&extras=url_n';
          $.ajax({
            url:dataUrl
            ,dataType: 'jsonp'
            ,jsonp: 'jsoncallback'
          })
            .done(function(res){
              if (res.stat=='fail')alert('Dang. Looks like Flickr has lost its pancakes... '+res.message);
              var rs = res.photos.photo.filter(function(o,i){return o.height_n > o.width_n * .5});//filter out any with a really wide aspect ratio.
              tremulaBase.appendData(rs,flickrDataAdapter);//flicker
              tremulaBase.cache.endOfScrollFlag = false;
            })
            .fail( function(d,config,err){console.log('API FAIL. '+err) })}

        function flickrDataAdapter(data,env){
          this.data = data;
          this.w = this.width = data.width_n;
          this.h = this.height = data.height_n;
          this.imgUrl = data.url_n;
          this.auxClassList = "flickrRS";//stamp each mapped item with map ID
          this.template = this.data.template||('<img draggable="false" class="moneyShot" onload="imageLoaded(this)" src=""/><span class="span">desc </span>');
        }
        function applyBoxClick(){
          elem.on('tremulaItemSelect',function(gestureEvt,domEvt){
            console.log(gestureEvt,domEvt)
            var
              $e = $(domEvt.target);
            if($e.closest('.gridBox')[0]){
              var data = $.data(t).model.model.data;
            }
            if(data)alert(JSON.stringify(data));
          })
        }
        $timeout(function(){
          tremulaBase = createTremula();
          applyBoxClick();
          loadFlickr()
        }, 1);
      }
    }
  });

})();

Maybe hammer.js events not attached to DOM?

vko-online avatar Mar 11 '15 18:03 vko-online

Hi @vko-online, thanks for filing this. I think it may be a Hammer.js version issue. Hammer v2.x is implemented very differently-- could you please try swapping the version included in the Tremulajs demo to see if the issue clears up? Please let me know how that goes... Best, GS

garris avatar Mar 11 '15 22:03 garris

You are right @garris . Tremula doesn't work with latest 2.0.4/hammer.js same demo and same Uncaught TypeError: Cannot read property 'stopDetect' of undefined error Version of hammer used in tremula is too old, and not legacy Hammer.JS - v1.0.6dev - 2013-04-10 Please consider updating dependencies Cool plugin btw

vko-online avatar Mar 12 '15 03:03 vko-online

Just curious, did you happen to try hammer 1.1.3? I think that would probably work as designed.

And yes, I will hopefully find time soon to update TremulaJS to work with the latest 2.x version.

Anyhow I hope you can still implement your project without the latest hammerjs version. I will update the release when I get the chance to make the compatibility fix. Thanks again for filing the bug!

garris avatar Mar 12 '15 04:03 garris

Also, jquery dependency is redundant, few usages (found only 8), basically $.extend, $.each, $(), addClass/removeClass

//$.extend()
var __extends = function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};

//$.each()
array.forEach(iterator, fn)

//$()
document.querySelector(), getById/Class

//$().addClass()
var d = document.getElementById("div1");
d.className = d.className + " otherclass";

//$().parent()
var d = document.getElementById("div1");
d.parentNode

//http://stackoverflow.com/questions/507138/how-do-i-add-a-class-to-a-given-element#comment321539_507157
//Dragging in many thousands of lines of framework for a one-liner is not sensible. –  bobince

but maybe hammerjs still requires jquery (and maybe jsbezier)(both doesn't*) Also i think whole convertion to angular would pass easily. hammerjs => angular-touch, and i think you could get rid of jsbezier completly, you already have some in src/Easing.js(turns out they're just helpers) And yes @garris , 1.1.3 works perfectly

vko-online avatar Mar 12 '15 04:03 vko-online

Great analysis! Very true on all accounts. These are all things which would make TremulaJS much more attractive to AngularJS developers ( and I like angular ). I also think these optimizations are easily done by any JS developer and requires no specialized geometry or animation knowledge. Also, the Bėzier lib is very very small and can be built in. I would love to see this done. It is probably a good few hours of work -- I am sure there would be some happy Angular UI people...

garris avatar Mar 12 '15 07:03 garris

Hammer.js 2.0 changed it's API and that's why you are seeing issues. There is no drag event in Hammer.js 2.0.4 as there is a native drag and drop API, instead you would use pan events http://hammerjs.github.io/recognizer-pan/.

matijagrcic avatar May 29 '15 20:05 matijagrcic