Skip to content

Instantly share code, notes, and snippets.

@hanubeki
Last active October 9, 2024 08:48
Show Gist options
  • Save hanubeki/11f8bd5f0b26dbff1114940849932fb6 to your computer and use it in GitHub Desktop.
Save hanubeki/11f8bd5f0b26dbff1114940849932fb6 to your computer and use it in GitHub Desktop.
Reinvent the wheel: alternative pull to refresh for hermit and other webview-based browsers
// ==UserScript==
// @name Swipe to Refresh mod
// @namespace https://jumpkick.io/
// @version 1.1.3
// @description based on https://gist.github.com/mhingston/17f3cebf80ad74615e9db7d06a8576dc
// @author Miles Hingston, hanubeki
// @match *://*/*
// @exclude *://*.youtube.com/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const img = document.createElement('img');
img.src = `data:image/svg+xml;base64,PHN2ZwogICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICAgd2lkdGg9IjIzMDguNTcxNSIKICAgIGhlaWdodD0iMjI1MS40Mjg1IgogICAgdmlld0JveD0iMCAwIDIzMDguNTcxNSAyMjUxLjQyODUiPgogICAgPGcKICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNzQuMjg1NjQ1LDEyNjEuOTI1NikiPgogICAgICAgIDxlbGxpcHNlCiAgICAgICAgc3R5bGU9ImZpbGw6I2Y5ZjlmOSIKICAgICAgICBjeD0iMTIyOC41NzE0IgogICAgICAgIGN5PSItMTM2LjIxMTQxIgogICAgICAgIHJ4PSIxMTU0LjI4NTgiCiAgICAgICAgcnk9IjExMjUuNzE0MiIgLz4KICAgICAgICA8cGF0aAogICAgICAgIGQ9Im0gMTk5NC40Mjg1LDE4LjA3NDMxNSBxIDAsNSAtMSw3IC02NCwyNjcuOTk5OTk1IC0yNjcuOTk5OSw0MzQuNDk5OTk1IC0yMDQsMTY2LjUgLTQ3OCwxNjYuNSAtMTQ2LDAgLTI4Mi41LC01NSAtMTM2LjUsLTU1IC0yNDMuNSwtMTU3IGwgLTEyOSwxMjkgcSAtMTksMTkgLTQ1LDE5IC0yNiwwIC00NSwtMTkgLTE5LC0xOSAtMTksLTQ1IGwgMCwtNDQ3Ljk5OTk5NSBxIDAsLTI2IDE5LC00NS4wMDAwMDAyIDE5LC0xOC45OTk5OTk4IDQ1LC0xOC45OTk5OTk4IGwgNDQ4LDAgcSAyNiwwIDQ1LDE4Ljk5OTk5OTggMTksMTkuMDAwMDAwMiAxOSw0NS4wMDAwMDAyIDAsMjYgLTE5LDQ1IGwgLTEzNywxMzYuOTk5OTk1IHEgNzEsNjYgMTYxLDEwMiA5MCwzNiAxODcsMzYgMTM0LDAgMjUwLC02NSAxMTYsLTY1IDE4NiwtMTc5IDExLC0xNyA1MywtMTE2Ljk5OTk5NTIgOCwtMjIuOTk5OTk5OCAzMCwtMjIuOTk5OTk5OCBsIDE5MS45OTk5LDAgcSAxMywwIDIyLjUsOS40OTk5OTk4IDkuNSw5LjUgOS41LDIyLjUwMDAwMDIgeiBtIDI1LC04MDAuMDAwMDM1IDAsNDQ4IHEgMCwyNiAtMTksNDUgLTE5LDE5IC00NSwxOSBsIC00NDcuOTk5OSwwIHEgLTI2LDAgLTQ1LC0xOSAtMTksLTE5IC0xOSwtNDUgMCwtMjYgMTksLTQ1IGwgMTM4LC0xMzggcSAtMTQ4LC0xMzcgLTM0OSwtMTM3IC0xMzQsMCAtMjUwLDY1IC0xMTYsNjUgLTE4NiwxNzkgLTExLDE3IC01MywxMTcgLTgsMjMgLTMwLDIzIGwgLTE5OSwwIHEgLTEzLDAgLTIyLjUsLTkuNSAtOS41LC05LjUgLTkuNSwtMjIuNSBsIDAsLTcgcSA2NSwtMjY4IDI3MCwtNDM0LjUgMjA1LC0xNjYuNSA0ODAsLTE2Ni41IDE0NiwwIDI4NCw1NS41IDEzOCw1NS41IDI0NSwxNTYuNSBsIDEyOS45OTk5LC0xMjkgcSAxOSwtMTkgNDUsLTE5IDI2LDAgNDUsMTkgMTksMTkgMTksNDUgeiIKICAgICAgICBpZD0icGF0aDMzMzgiIC8+CiAgICA8L2c+CiAgICA8L3N2Zz4=`;
img.style='width: 48px; height: 48px; z-index: 2147483647; position: fixed; left: calc(50% - 24px); top: -48px; border: 0; opacity: 0';
document.body.appendChild(img);
let startX, startY, topOfPage, triggered, prevented;
window.addEventListener('touchstart', function(e)
{
triggered = false;
prevented = false;
if(e.changedTouches.length === 1)
{
startX = e.changedTouches[0].clientX;
startY = e.changedTouches[0].clientY;
topOfPage = true;
const elem = e.changedTouches[0].target;
let parent = elem;
let obyCaught = false;
// 親要素を辿って全ての要素で一番上までスクロールされてるかチェック
do
{
if(parent.tagName === undefined)
{
// Shadow DOM対策
// Example: https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54V00007XFz6nSAD
if(parent.host !== undefined)
{
parent = parent.host;
continue;
}
else
{
break;
}
}
const compS = window.getComputedStyle(parent);
// phanpy.socialやCommaFeed等で動かなくなるが、他のブラウザでも同じ挙動のようなので仕様とする
if(compS.overscrollBehaviorY === 'contain')
{
obyCaught = true;
}
else if(compS.overscrollBehaviorY === 'none')
{
topOfPage = false;
}
if(compS.position === 'fixed')
{
obyCaught = false;
}
topOfPage &&= parent.scrollTop === 0;
parent = parent.parentNode;
}
while(topOfPage && parent);
topOfPage &&= !obyCaught;
// モーダル等によりbodyをスクロール防止している場合に発動させない
const bodyS = window.getComputedStyle(document.body);
topOfPage &&= bodyS.position !== 'fixed';
topOfPage &&= bodyS.overflowY !== 'hidden';
}
});
window.addEventListener('touchmove', function(e)
{
if(e.changedTouches.length === 1 && topOfPage)
{
// スワイプの方向が下以外の場合に発動させない
if(!prevented && (triggered || (e.changedTouches[0].clientY > startY && Math.abs(e.changedTouches[0].clientY - startY) > Math.abs(e.changedTouches[0].clientX - startX))))
{
triggered = true;
let lastY = e.changedTouches[0].clientY;
const clientY = Math.min(-48 + (lastY - startY), 128);
const opacity = (lastY - startY >= 128) ? 1 : (lastY - startY) / 512;
img.style.top = `${clientY}px`;
img.style.opacity = `${opacity}`;
img.style.transition = 'none';
e.preventDefault();
}
else
{
prevented = true;
}
}
},
{
passive: false
});
window.addEventListener('touchend', function(e)
{
// スワイプ発動後、離したときの距離が一定以上であればリロード
// URLに#を含むと動作しない (Hermit側の問題でありvia等、別のブラウザでは動作する模様)
if(topOfPage && !prevented && e.changedTouches.length === 1 && e.changedTouches[0].clientY - startY >= 128)
{
location.reload();
}
else
{
img.style.top = '-48px';
img.style.opacity = '0';
img.style.transition = 'top 0.2s, opacity 0.2s';
}
triggered = false;
prevented = false;
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment