1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-04-21 22:31:32 +00:00

DynaView plugin: add optional scroll position preservation

This commit is contained in:
Jermolene
2019-01-11 17:50:52 +00:00
parent af9f90e8cd
commit e14e69bedc
10 changed files with 107 additions and 24 deletions

View File

@@ -13,6 +13,12 @@ The components of this plugin include:
! Scroll Features
!! Scroll Position Preservation
Some recent browsers have a feature called "scroll anchoring" whereby they suppress the apparent scrolling that occurs when elements are inserted or removed above the current viewport. (See https://github.com/WICG/ScrollAnchoring for more details).
~DynaView can optionally polyfill this behaviour for older browsers by setting the configuration tiddler $:/config/DynaView/PreserveScrollPosition to `yes`.
!! Set tiddler field when visible
The background task detects when elements with the class `tc-dynaview-set-tiddler-when-visible` scroll into view. The first time that they do, the background task assigns the value in the attribute `data-dynaview-set-value` to the tiddler whose title is in the attribute `data-dynaview-set-tiddler`. This assignment can be tied to a reveal widget to cause content to be displayed when it becomes visible. If the class `tc-dynaview-expand-viewport` is set then the viewport is expanded so that the processing occurs when elements move near the viewport.

View File

@@ -24,11 +24,21 @@ var isWaitingForAnimationFrame = 0, // Bitmask:
ANIM_FRAME_CAUSED_BY_RESIZE = 4; // Animation frame was requested because of window resize
exports.startup = function() {
var topmost = null, lastScrollY;
window.addEventListener("load",onLoad,false);
window.addEventListener("scroll",onScroll,false);
window.addEventListener("resize",onResize,false);
$tw.hooks.addHook("th-page-refreshing",function() {
if(shouldPreserveScrollPosition()) {
topmost = findTopmostTiddler();
}
lastScrollY = window.scrollY;
});
$tw.hooks.addHook("th-page-refreshed",function() {
checkTopmost();
if(lastScrollY === window.scrollY) { // Don't do scroll anchoring if the scroll position got changed
scrollToTiddler(topmost);
}
updateAddressBar();
checkVisibility();
saveViewportDimensions();
});
@@ -60,7 +70,7 @@ function worker() {
saveViewportDimensions();
}
setZoomClasses();
checkTopmost();
updateAddressBar();
checkVisibility();
isWaitingForAnimationFrame = 0;
}
@@ -134,22 +144,11 @@ function checkVisibility() {
});
}
function checkTopmost() {
function updateAddressBar() {
if($tw.wiki.getTiddlerText("$:/config/DynaView/UpdateAddressBar") === "yes") {
var elements = document.querySelectorAll(".tc-tiddler-frame[data-tiddler-title]"),
topmostElement = null,
topmostElementTop = 1 * 1000 * 1000;
$tw.utils.each(elements,function(element) {
// Check if the element is visible
var elementRect = element.getBoundingClientRect();
if((elementRect.top < topmostElementTop) && (elementRect.bottom > 0)) {
topmostElement = element;
topmostElementTop = elementRect.top;
}
});
if(topmostElement) {
var title = topmostElement.getAttribute("data-tiddler-title"),
hash = "#" + encodeURIComponent(title) + ":" + encodeURIComponent("[list[$:/StoryList]]");
var top = findTopmostTiddler();
if(top.element) {
var hash = "#" + encodeURIComponent(top.title) + ":" + encodeURIComponent("[list[$:/StoryList]]");
if(title && $tw.locationHash !== hash) {
$tw.locationHash = hash;
window.location.hash = hash;
@@ -158,6 +157,51 @@ function checkTopmost() {
}
}
/*
tiddlerDetails: {title: <title of tiddler to scroll to>, offset: <offset in pixels from the top of the tiddler>}
*/
function scrollToTiddler(tiddlerDetails) {
if(shouldPreserveScrollPosition() && !$tw.pageScroller.isScrolling() && tiddlerDetails) {
var elements = document.querySelectorAll(".tc-tiddler-frame[data-tiddler-title]"),
topmostTiddlerElement = null;
$tw.utils.each(elements,function(element) {
if(element.getAttribute("data-tiddler-title") === tiddlerDetails.title) {
topmostTiddlerElement = element;
}
});
if(topmostTiddlerElement) {
var rect = topmostTiddlerElement.getBoundingClientRect(),
scrollY = Math.round(window.scrollY + rect.top + tiddlerDetails.offset);
if(scrollY !== window.scrollY) {
window.scrollTo(window.scrollX,scrollY);
}
}
}
}
function shouldPreserveScrollPosition() {
return $tw.wiki.getTiddlerText("$:/config/DynaView/PreserveScrollPosition") === "yes";
}
function findTopmostTiddler() {
var elements = document.querySelectorAll(".tc-tiddler-frame[data-tiddler-title]"),
topmostElement = null,
topmostElementTop = 1 * 1000 * 1000;
$tw.utils.each(elements,function(element) {
// Check if the element is visible
var elementRect = element.getBoundingClientRect();
if((elementRect.top < topmostElementTop) && (elementRect.bottom > 0)) {
topmostElement = element;
topmostElementTop = elementRect.top;
}
});
return {
element: topmostElement,
offset: -topmostElementTop,
title: topmostElement.getAttribute("data-tiddler-title")
};
}
var previousViewportWidth, previousViewportHeight;
function saveViewportDimensions() {

View File

@@ -1,8 +1,19 @@
title: $:/plugins/tiddlywiki/dynaview/styles
tags: $:/tags/Stylesheet
\define if-tiddler-is(title,value,text)
<$reveal stateTitle=<<__title__>> text=<<__value__>> type="match">
<$text text=<<__text__>>/>
</$reveal>
\end
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline
<<if-tiddler-is title:"$:/config/DynaView/PreserveScrollPosition" value:"yes" text:"""
body {
overflow-anchor: none; /* Turn off browser scroll anchoring */
}
""">>
body.tc-dynaview .tc-dynaview-zoom-visible-1-and-above,
body.tc-dynaview .tc-dynaview-zoom-visible-1a-and-above,
body.tc-dynaview .tc-dynaview-zoom-visible-1b-and-above,