Scroller.js: improvements to scrolling by keyboard
This commit is contained in:
@@ -162,31 +162,55 @@ class Scroller
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.modelPositionY = 0;
|
this.modelPositionY = 0;
|
||||||
this.modelPositionX = 0;
|
this.modelPositionX = 0;
|
||||||
|
|
||||||
|
this.modelPositionXVel = 0;
|
||||||
|
this.modelPositionYVel = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollBy(xOffset, yOffset) {
|
clampHorizontal(xOffset) {
|
||||||
var initialPositionY = this.modelPositionY;
|
return Math.max(
|
||||||
var initialPositionX = this.modelPositionX;
|
0, Math.min(
|
||||||
|
xOffset, document.documentElement.scrollWidth - window.visualViewport.width
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
clampVertical(yOffset) {
|
||||||
|
return Math.max(
|
||||||
|
0, Math.min(
|
||||||
|
yOffset, document.documentElement.scrollHeight - window.visualViewport.height
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeModelPosition() {
|
||||||
if (this.timerIdentifier == undefined) {
|
if (this.timerIdentifier == undefined) {
|
||||||
this.startTimer();
|
this.startTimer();
|
||||||
|
|
||||||
initialPositionY = window.scrollY;
|
this.modelPositionY = window.scrollY;
|
||||||
initialPositionX = window.scrollX;
|
this.modelPositionX = window.scrollX;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modelPositionY = initialPositionY + yOffset;
|
scrollBy(xOffset, yOffset) {
|
||||||
this.modelPositionY = Math.max(
|
this.initializeModelPosition();
|
||||||
0, Math.min(
|
|
||||||
this.modelPositionY, document.documentElement.scrollHeight - window.visualViewport.height
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.modelPositionX = initialPositionX + xOffset;
|
this.modelPositionY += yOffset;
|
||||||
this.modelPositionX = Math.max(
|
this.modelPositionY = this.clampVertical(this.modelPositionY);
|
||||||
0, Math.min(
|
|
||||||
this.modelPositionX, document.documentElement.scrollWidth - window.visualViewport.width
|
this.modelPositionX += xOffset;
|
||||||
)
|
this.modelPositionX = this.clampHorizontal(this.modelPositionX);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
setScrollVelocity(xVelocity, yVelocity) {
|
||||||
|
this.initializeModelPosition();
|
||||||
|
|
||||||
|
this.modelPositionXVel = xVelocity;
|
||||||
|
this.modelPositionYVel = yVelocity;
|
||||||
|
|
||||||
|
if (this.timerIdentifier == undefined) {
|
||||||
|
this.startTimer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startTimer() {
|
startTimer() {
|
||||||
@@ -199,17 +223,29 @@ class Scroller
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTimer(self) {
|
updateTimer(self) {
|
||||||
|
self.modelPositionX = self.clampHorizontal(self.modelPositionX + self.modelPositionXVel);
|
||||||
|
self.modelPositionY = self.clampVertical(self.modelPositionY + self.modelPositionYVel);
|
||||||
|
|
||||||
const accelerationFactor = 0.05;
|
const accelerationFactor = 0.05;
|
||||||
let positionOffsetY = (self.modelPositionY - window.scrollY) * accelerationFactor;
|
let positionOffsetY = (self.modelPositionY - window.scrollY) * accelerationFactor;
|
||||||
let positionOffsetX = (self.modelPositionX - window.scrollX) * accelerationFactor;
|
let positionOffsetX = (self.modelPositionX - window.scrollX) * accelerationFactor;
|
||||||
window.scrollBy(positionOffsetX, positionOffsetY);
|
window.scrollBy(positionOffsetX, positionOffsetY);
|
||||||
|
|
||||||
if (Math.abs(positionOffsetX) <= 1.0 && Math.abs(positionOffsetY) <= 1.0) {
|
var atRest = Math.abs(positionOffsetX) <= 1.0 && Math.abs(positionOffsetY) <= 1.0;
|
||||||
|
var noVelocity = self.modelPositionXVel == 0 && self.modelPositionYVel == 0;
|
||||||
|
if (atRest && noVelocity) {
|
||||||
self.stopTimer();
|
self.stopTimer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ScrollDirection = {
|
||||||
|
Up: 'k',
|
||||||
|
Down: 'j',
|
||||||
|
Left: 'h',
|
||||||
|
Right: 'l',
|
||||||
|
};
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
let tagger = new ElementTagger();
|
let tagger = new ElementTagger();
|
||||||
let scroller = new Scroller();
|
let scroller = new Scroller();
|
||||||
@@ -240,20 +276,25 @@ class Scroller
|
|||||||
tagger.clickLinkWithTag(keyName);
|
tagger.clickLinkWithTag(keyName);
|
||||||
tagger.untagDocument();
|
tagger.untagDocument();
|
||||||
}
|
}
|
||||||
} else {
|
} else if (event.shiftKey) {
|
||||||
// Otherwise, interpret as a single character command
|
// Otherwise, interpret as a single character command
|
||||||
|
|
||||||
let lowerKey = keyName.toLowerCase();
|
let lowerKey = keyName.toLowerCase();
|
||||||
let scrollAmount = event.shiftKey ? window.visualViewport.height : 30;
|
let scrollAmount = event.shiftKey ? window.visualViewport.height : 30;
|
||||||
|
|
||||||
if (lowerKey == 'j') {
|
switch (lowerKey) {
|
||||||
|
case ScrollDirection.Down:
|
||||||
scroller.scrollBy(0, scrollAmount);
|
scroller.scrollBy(0, scrollAmount);
|
||||||
} else if (lowerKey == 'k') {
|
break;
|
||||||
|
case ScrollDirection.Up:
|
||||||
scroller.scrollBy(0, -scrollAmount);
|
scroller.scrollBy(0, -scrollAmount);
|
||||||
} else if (lowerKey == 'h') {
|
break;
|
||||||
|
case ScrollDirection.Left:
|
||||||
scroller.scrollBy(-scrollAmount, 0);
|
scroller.scrollBy(-scrollAmount, 0);
|
||||||
} else if (lowerKey == 'l') {
|
break;
|
||||||
|
case ScrollDirection.Right:
|
||||||
scroller.scrollBy(scrollAmount, 0);
|
scroller.scrollBy(scrollAmount, 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -268,6 +309,24 @@ class Scroller
|
|||||||
tagger.tagDocument();
|
tagger.tagDocument();
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
} else {
|
||||||
|
let lowerKey = event.key.toLowerCase();
|
||||||
|
let velocity = 5;
|
||||||
|
|
||||||
|
switch (lowerKey) {
|
||||||
|
case ScrollDirection.Down:
|
||||||
|
scroller.setScrollVelocity(0, velocity);
|
||||||
|
break;
|
||||||
|
case ScrollDirection.Up:
|
||||||
|
scroller.setScrollVelocity(0, -velocity);
|
||||||
|
break;
|
||||||
|
case ScrollDirection.Left:
|
||||||
|
scroller.setScrollVelocity(-velocity, 0);
|
||||||
|
break;
|
||||||
|
case ScrollDirection.Right:
|
||||||
|
scroller.setScrollVelocity(velocity, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -279,8 +338,9 @@ class Scroller
|
|||||||
if (event.key == " " && tagger.isTagged) {
|
if (event.key == " " && tagger.isTagged) {
|
||||||
tagger.untagDocument();
|
tagger.untagDocument();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
} else {
|
||||||
|
scroller.setScrollVelocity(0, 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user