TremulaJS
TremulaJS copied to clipboard
Hammerjs_2.x Exception
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?
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
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
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!
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 (turns out they're just helpers)
And yes @garris , src/Easing.js
1.1.3
works perfectly
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...
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/.