✨ Simple Pull to Refresh

🌟 Minimal Premium: Простое и стабильное обновление страницы потягиванием для браузера Via

当前为 2024-12-31 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name ✨ Simple Pull to Refresh
  3. // @namespace r1kov
  4. // @version 0.2-fix
  5. // @description 🌟 Minimal Premium: Простое и стабильное обновление страницы потягиванием для браузера Via
  6. // @match *://*/*
  7. // @grant none
  8. // @license MIT
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. const config = {
  15. threshold: 100, // Минимальное расстояние для срабатывания обновления
  16. elementId: 'simple-refresh',
  17. overlayId: 'simple-overlay',
  18. colors: {
  19. pull: '#333',
  20. release: '#ffcc00',
  21. loading: '#007aff'
  22. },
  23. messages: {
  24. pull: 'потяните вниз',
  25. release: 'отпустите',
  26. loading: 'обновление'
  27. }
  28. };
  29.  
  30. let state = {
  31. startY: 0,
  32. currentY: 0,
  33. isActive: false
  34. };
  35.  
  36. function injectStyles() {
  37. const style = document.createElement('style');
  38. style.textContent = `
  39. #${config.elementId} {
  40. position: fixed;
  41. top: 0;
  42. left: 0;
  43. right: 0;
  44. height: 64px;
  45. display: flex;
  46. align-items: center;
  47. justify-content: center;
  48. z-index: 999999;
  49. transform: translateY(-100%);
  50. transition: transform 0.3s ease-out;
  51. pointer-events: none;
  52. font-family: -apple-system, system-ui, sans-serif;
  53. }
  54.  
  55. #${config.elementId} .pill {
  56. display: flex;
  57. align-items: center;
  58. gap: 8px;
  59. padding: 12px 20px;
  60. background: ${config.colors.pull};
  61. color: white;
  62. border-radius: 26px;
  63. font-size: 14px;
  64. font-weight: 500;
  65. transition: background 0.3s ease-out;
  66. }
  67.  
  68. #${config.elementId} .icon {
  69. font-size: 18px;
  70. }
  71.  
  72. #${config.overlayId} {
  73. position: fixed;
  74. top: 0;
  75. left: 0;
  76. right: 0;
  77. bottom: 0;
  78. background: rgba(255, 255, 255, 0);
  79. backdrop-filter: blur(0px);
  80. -webkit-backdrop-filter: blur(0px);
  81. z-index: 999998;
  82. pointer-events: none;
  83. transition: all 0.3s ease-out;
  84. }
  85.  
  86. @keyframes rotate {
  87. to { transform: rotate(360deg); }
  88. }
  89. `;
  90. document.head.appendChild(style);
  91. }
  92.  
  93. function createElements() {
  94. const overlay = document.createElement('div');
  95. overlay.id = config.overlayId;
  96. document.body.appendChild(overlay);
  97.  
  98. const refreshEl = document.createElement('div');
  99. refreshEl.id = config.elementId;
  100. refreshEl.innerHTML = `
  101. <div class="pill">
  102. <span class="icon">↓</span>
  103. <span class="text">${config.messages.pull}</span>
  104. </div>
  105. `;
  106. document.body.appendChild(refreshEl);
  107.  
  108. return { overlay, refreshEl };
  109. }
  110.  
  111. function updateUI(distance) {
  112. const { overlay, refreshEl } = document.getElementById(config.overlayId) ?
  113. {
  114. overlay: document.getElementById(config.overlayId),
  115. refreshEl: document.getElementById(config.elementId)
  116. } : createElements();
  117.  
  118. const translateY = Math.min(distance * 0.5, 64);
  119.  
  120. refreshEl.style.transform = `translateY(${translateY - 64}px)`;
  121. overlay.style.background = `rgba(255, 255, 255, ${distance / config.threshold * 0.1})`;
  122. overlay.style.backdropFilter = `blur(${distance / config.threshold * 8}px)`;
  123. overlay.style.webkitBackdropFilter = `blur(${distance / config.threshold * 8}px)`;
  124.  
  125. if (distance > config.threshold) {
  126. refreshEl.querySelector('.pill').style.background = config.colors.release;
  127. refreshEl.querySelector('.text').textContent = config.messages.release;
  128. } else {
  129. refreshEl.querySelector('.pill').style.background = config.colors.pull;
  130. refreshEl.querySelector('.text').textContent = config.messages.pull;
  131. }
  132. }
  133.  
  134. function reset() {
  135. const refreshEl = document.getElementById(config.elementId);
  136. const overlay = document.getElementById(config.overlayId);
  137.  
  138. if (!refreshEl || !overlay) return;
  139.  
  140. refreshEl.style.transform = 'translateY(-100%)';
  141. overlay.style.background = 'rgba(255, 255, 255, 0)';
  142. overlay.style.backdropFilter = 'blur(0px)';
  143. overlay.style.webkitBackdropFilter = 'blur(0px)';
  144.  
  145. setTimeout(() => {
  146. refreshEl.remove();
  147. overlay.remove();
  148. }, 250);
  149. }
  150.  
  151. function refresh() {
  152. const refreshEl = document.getElementById(config.elementId);
  153. refreshEl.querySelector('.pill').style.background = config.colors.loading;
  154. refreshEl.querySelector('.icon').textContent = '↻';
  155. refreshEl.querySelector('.text').textContent = config.messages.loading;
  156. window.location.reload();
  157. }
  158.  
  159. function handleTouchStart(e) {
  160. if (window.scrollY === 0 && !e.target.matches('input, textarea, select')) {
  161. state.startY = e.touches[0].pageY;
  162. state.currentY = state.startY;
  163. state.isActive = true;
  164. }
  165. }
  166.  
  167. function handleTouchMove(e) {
  168. if (!state.isActive) return;
  169.  
  170. state.currentY = e.touches[0].pageY;
  171. const distance = state.currentY - state.startY;
  172.  
  173. if (distance > 0 && window.scrollY === 0) {
  174. e.preventDefault();
  175. requestAnimationFrame(() => updateUI(distance));
  176. }
  177. }
  178.  
  179. function handleTouchEnd() {
  180. if (!state.isActive) return;
  181.  
  182. const distance = state.currentY - state.startY;
  183.  
  184. if (distance > config.threshold) {
  185. refresh();
  186. } else {
  187. reset();
  188. }
  189.  
  190. state.isActive = false;
  191. }
  192.  
  193. injectStyles();
  194. document.addEventListener('touchstart', handleTouchStart, { passive: true });
  195. document.addEventListener('touchmove', handleTouchMove, { passive: false });
  196. document.addEventListener('touchend', handleTouchEnd, { passive: true });
  197. })();