Last active
October 9, 2024 08:48
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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