1/** 2 * Initialises drag-to-scroll behaviour on all elements marked with 3 * [data-scroll-gallery]. Supports mouse (click & drag) and touch (swipe). 4 * 5 * Cursor position relative to the gallerys left edge 6 * used to calculate the distance moved during a drag/swipe. 7 * 8 * @returns {void} 9 */ 10 11const scrollGallery = function () { 12 const galleries = document.querySelectorAll("[data-scroll-gallery]"); 13 galleries.forEach((gallery) => { 14 const track = gallery.querySelector("[data-scroll-track]"); 15 if (!track) { 16 return; 17 } 18 19 let isDown = false; 20 let startX = 0; 21 let startTranslateX = 0; 22 23 const getTranslateX = () => { 24 const transform = window.getComputedStyle(track).transform; 25 if (transform === "none") { 26 return 0; 27 } 28 const match = transform.match(/matrix.*\((.+)\)/); 29 if (!match) { 30 return 0; 31 } 32 const values = match[1].split(", "); 33 return parseFloat(values[4]) || 0; 34 }; 35 36 const startDrag = (pageX) => { 37 isDown = true; 38 startX = pageX - gallery.offsetLeft; 39 startTranslateX = getTranslateX(); 40 track.classList.remove("animate-slide-x"); 41 track.style.transform = `translateX(${startTranslateX}px)`; 42 }; 43 44 const endDrag = () => { 45 if (!isDown) { 46 return; 47 } 48 isDown = false; 49 50 const currentTx = getTranslateX(); 51 52 const targetStr = 53 getComputedStyle(track).getPropertyValue("--translate-x") || "-50%"; 54 const targetFraction = parseFloat(targetStr) / 100; 55 const targetPx = targetFraction * track.scrollWidth; 56 const duration = 57 parseFloat(getComputedStyle(track).animationDuration) || 15; 58 59 let progress = targetPx !== 0 60 ? currentTx / targetPx 61 : 0; 62 progress = ((progress % 1) + 1) % 1; 63 64 track.style.animationDelay = `${-duration * progress}s`; 65 track.style.transform = ""; 66 track.classList.add("animate-slide-x"); 67 }; 68 69 gallery.addEventListener("mousedown", (e) => { 70 startDrag(e.pageX); 71 gallery.classList.add("cursor-grabbing"); 72 gallery.classList.remove("cursor-grab"); 73 }); 74 75 gallery.addEventListener("mouseleave", () => { 76 if (!isDown) { 77 return; 78 } 79 endDrag(); 80 gallery.classList.remove("cursor-grabbing"); 81 gallery.classList.add("cursor-grab"); 82 }); 83 84 gallery.addEventListener("mouseup", () => { 85 endDrag(); 86 gallery.classList.remove("cursor-grabbing"); 87 gallery.classList.add("cursor-grab"); 88 }); 89 90 gallery.addEventListener("mousemove", (e) => { 91 if (!isDown) { 92 return; 93 } 94 e.preventDefault(); 95 const xAxis = e.pageX - gallery.offsetLeft; 96 const walk = (xAxis - startX) * 1.5; 97 track.style.transform = `translateX(${startTranslateX + walk}px)`; 98 }); 99 100 gallery.addEventListener("touchstart", (e) => { 101 startDrag(e.touches[0].pageX); 102 }, { passive: true }); 103 104 gallery.addEventListener("touchmove", (e) => { 105 const xAxis = e.touches[0].pageX - gallery.offsetLeft; 106 const walk = (xAxis - startX) * 1.5; 107 track.style.transform = `translateX(${startTranslateX + walk}px)`; 108 }, { passive: true }); 109 110 gallery.addEventListener("touchend", () => { 111 endDrag(); 112 }, { passive: true }); 113 }); 114}; 115scrollGallery();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.