Open Snoturky opened 7 years ago
I agree, it is problematic. It probably should keep updating continuously. Updating on mousemove makes scrolling dependent also on the rate browser sends updates, so it can't be relied upon.
Currently there's no solution for this proposed.
Not sure if this is anything related or not, but when using Slip for a Cordova application that I am putting together, the android build has had some terrible interactions. In can only move down the screen and not up, when recording list items. The selecting of the list item is very picky. If you scroll down have more list items than what is on the screen, it goes straight to the bottom of the list, and not smoothly in any fashion. These are just a few of the problems.
Here's a possible solution.
It works by dividing the triggerzone into 10 parts and use the offset within the zone as a multiplier for scroll animation duration. Currently animation is done using requestAnimationFrame
but can be polyfilled using setTimeout
. It still needs some fine-tuning but it will perform as it should.
Add following to prototype:
scrollEvt:{}, // object holding all required scroll params and scroll state
Replace following in prototype:
updateScrolling: function() {
if(!this.target || this.scrollEvt.scrolling){return};
var triggerOffset = 100,
duration=100,
offset = 0,
that=this;
var scrollable = this.target.scrollContainer,
containerRect = scrollable.getBoundingClientRect(),
targetRect = this.target.node.getBoundingClientRect(),
bottomOffset = Math.min(containerRect.bottom, window.innerHeight) - targetRect.bottom,
topOffset = targetRect.top - Math.max(containerRect.top, 0),
maxScrollTop = this.target.origScrollHeight - Math.min(scrollable.clientHeight, window.innerHeight);
if (bottomOffset < triggerOffset) {
offset = Math.min(triggerOffset, triggerOffset - bottomOffset);
}
else if (topOffset < triggerOffset) {
offset = Math.max(-triggerOffset, topOffset - triggerOffset);
}
if(offset){
this.scrollEvt.scrolling=true;
var speed=Math.abs(Math.ceil((offset*10)/triggerOffset)),speedarray=[5,5,5,4,4,3,3,2,2,1,1];
this.animateScrolling(scrollable,this.target.node.offsetHeight,scrollable.scrollTop,duration*speedarray[speed],offset<0?-1:1);
}
},
animateScrolling:function(scrollable,height,ypos,duration,direction,starttime,timestamp){
var that=this,progress=0;
if(timestamp){
var starttime=starttime||timestamp;progress=(timestamp-starttime)/duration;if(progress>1){progress=1};scrollable.scrollTop=(ypos+height*progress*direction);
this.state.onMove.call(this);
}
if(progress==1){this.cancelAnimateScrolling();this.updateScrolling();return};
this.scrollEvt.raf=requestAnimationFrame(function(timestamp){that.animateScrolling(scrollable,height,ypos,duration,direction,starttime,timestamp)});
},
cancelAnimateScrolling:function(){
this.scrollEvt.scrolling=false;
cancelAnimationFrame(this.scrollEvt.raf);
},
updatePosition: function(e, pos) {
if (this.target == null) {
return;
}
this.latestPosition = pos;
if (this.state.onMove && !this.scrollEvt.scrolling) {
if (this.state.onMove.call(this) === false) {
e&&e.preventDefault();
}
}
//sample latestPosition 100ms for velocity
if (this.latestPosition.time - this.previousPosition.time > 100) {
this.previousPosition = this.latestPosition;
}
},
onMouseUp: function(e) {
if (this.usingTouch || e.button !== 0) return;
if (this.state.onEnd && false === this.state.onEnd.call(this)) {
e.preventDefault();
}
this.cancelAnimateScrolling();
},
onTouchEnd: function(e) {
if (e.touches.length > 1) {
this.cancel();
} else if (this.state.onEnd && false === this.state.onEnd.call(this)) {
e.preventDefault();
}
this.cancelAnimateScrolling();
},
When scrolling, scrollEvt.scrolling=true
. This is useful to block other events and avoid duplicate calls. animateScrolling will call onmove since scrolling with a steady mouse will not, resulting in jagged movement.
TODO:
Update:
updateScrolling: function() {
if(!this.target || this.scrollEvt.scrolling){return};
var triggerOffset = 100,
duration=100,
offset = 0,
that=this;
var scrollable = this.target.scrollContainer,
containerRect = scrollable.getBoundingClientRect(),
targetRect = this.target.node.getBoundingClientRect(),
bottomOffset = Math.min(containerRect.bottom, window.innerHeight) - targetRect.bottom,
topOffset = targetRect.top - Math.max(containerRect.top, 0),
maxScrollTop = this.target.origScrollHeight - Math.min(scrollable.clientHeight, window.innerHeight);
if (bottomOffset < triggerOffset) {
offset = Math.min(triggerOffset, triggerOffset - bottomOffset);
}
else if (topOffset < triggerOffset) {
offset = Math.max(-triggerOffset, topOffset - triggerOffset);
}
if(!offset){return}
if(offset<0 && this.getTotalMovement().y>=-(triggerOffset/10)){return}
if(offset>0 && this.getTotalMovement().y<=(triggerOffset/10)){return}
this.scrollEvt.scrolling=true;
var speed=Math.abs(Math.ceil((offset*4)/triggerOffset));
this.animateScrolling(scrollable,this.target.node.offsetHeight,scrollable.scrollTop,duration*[4,4,3,2,1][speed],offset<0?-1:1)
},
TODO:
scrollTo
on document.body
Firefox autoscroll fix:
Add following after damnYouChrome
declaration:
var damnYouFirefox = /Firefox/.test(navigator.userAgent);
Add following after scrollContainer = scrollContainer || document.body;
:
if(damnYouFirefox && scrollContainer==document.body) {scrollContainer=document.documentElement};
@Snoturky
Could you do a proper pull request for your changes?
Is it even necessary to check for Firefox, or can we just always use documentElement?
Scrolling seems to work by looking at how close the cursor is to the viewable part of a scrollable parent on each mousemove event. The closer it is to the edge, the more it scrolls for that single fire of the event. It is intuitive that the scroll speed is based upon proximity to the edge, but it is not intuitive that scrolling happens only after each pixel-by-pixel move of the cursor.
That means that holding an element steady at the edge of a scrollable parent does not scroll at all, but dragging the element back into the center of the scrollable parent (which usually indicates that you have found what you were looking for and want to stop scrolling) will cause you to scroll away.
Is there a way to make the scrolling smooth, so it will continuously scroll at some constant speed depending upon where the element is held, instead of only scrolling as the mouse continues to move?
This is especially troublesome when you pick up an element near the edge, because just the act of picking it up and moving it toward the middle of the scrollable area will cause it to scroll.