Draggable Reference Line

Designed for reading long articles, it helps remember the current reading progress.

当前为 2021-09-14 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Draggable Reference Line
  3. // @namespace https://greasyfork.org/users/193469
  4. // @description Designed for reading long articles, it helps remember the current reading progress.
  5. // @license MIT
  6. // @version 1.1
  7. // @author Rui LIU (@liurui39660)
  8. // @match *://*/*
  9. // @icon https://icons.duckduckgo.com/ip2/example.com.ico
  10. // @run-at document-body
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. const height = 2; // px, height of the visible line
  17. const padding = 2; // px, additional draggable area above and below the line, not the sum of them
  18. const color = 'red'; // or #-string
  19.  
  20. const initPos = -padding - height / 2;
  21. const body = document.getElementsByTagName('body')[0];
  22. const line = document.createElement('div');
  23. line.textContent = '#DraggableReferenceLine';
  24. line.style = `margin: 0; padding: ${padding}px 0; cursor: n-resize; font-size: 0; color: #fff0; background: ${color}; background-clip: content-box; width: 100vw; height: ${height}px; position: absolute; top: ${localStorage[`DraggableLine_${window.location.pathname}${window.location.search}`] || initPos}px; z-index: 10;`;
  25.  
  26. const reset = () => {
  27. line.style.top = `${initPos}px`;
  28. localStorage.removeItem(`DraggableLine_${window.location.pathname}${window.location.search}`);
  29. }
  30. const set = top => {
  31. line.style.top = `${top}px`;
  32. localStorage[`DraggableLine_${window.location.pathname}${window.location.search}`] = top;
  33. }
  34. let callbackMouseMove = ev => {
  35. line.style.top = `${ev.pageY + initPos}px`; // initPos is also the offset to line center
  36. };
  37. let callbackMouseUp = ev => {
  38. if (ev.detail === 1) { // Don't overwrite dblclick
  39. body.style.cursor = null;
  40. const top = parseFloat(line.style.top);
  41. top > initPos ? set(top) : reset();
  42. [onmouseup, onmousemove, callbackMouseUp, callbackMouseMove] = [callbackMouseUp, callbackMouseMove, onmouseup, onmousemove];
  43. }
  44. };
  45. line.addEventListener('mousedown', ev => {
  46. if (ev.detail === 1) {
  47. ev.preventDefault(); // Don't select text
  48. body.style.cursor = 'n-resize'; // I know setPointerCapture, but see https://stackoverflow.com/questions/57566090/setpointercapture-behaves-differently-in-chrome-and-firefox
  49. [onmouseup, onmousemove, callbackMouseUp, callbackMouseMove] = [callbackMouseUp, callbackMouseMove, onmouseup, onmousemove];
  50. }
  51. });
  52. line.addEventListener('dblclick', reset);
  53. body.appendChild(line);
  54. })();