Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Hammerjs_2.x Exception #17

Open
vko-online opened this issue Mar 11, 2015 · 6 comments
Open

Hammerjs_2.x Exception #17

vko-online opened this issue Mar 11, 2015 · 6 comments

Comments

@vko-online
Copy link

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?

@garris
Copy link
Owner

garris commented Mar 11, 2015

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

@vko-online
Copy link
Author

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 vko-online changed the title Exception on Angularjs Hammer.js Exception Mar 12, 2015
@garris
Copy link
Owner

garris commented Mar 12, 2015

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!

@vko-online
Copy link
Author

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

@garris
Copy link
Owner

garris commented Mar 12, 2015

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...

@vko-online vko-online changed the title Hammer.js Exception Hammer.2.x.js Exception Mar 12, 2015
@vko-online vko-online changed the title Hammer.2.x.js Exception Hammerjs_2.x Exception Mar 12, 2015
@matijagrcic
Copy link

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/.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants