<template><div> <div ref="container"> <slot v-bind="$attrs"></slot> </div> <div ref="scrollbar" style="width: 100%; height: 16px; overflow-x: scroll;"> <div ref="scrolltrack" style="width: 100%; background: #ccc;"></div> <div ref="scrollbarhandle" style="width: 24px; height: 16px; background: #666; border-radius: 8px; position: absolute; left: 0;"></div> </div> </div> </template><script> import { ref } from '@vue/reactivity'; import { onmounted, onunmounted } from '@vue/runtime-core';
export default { setup() { const container = ref(null); const scrolltrack = ref(null); const scrollbarhandle = ref(null); const scrollbar = ref(null);
let scrollleft = 0; let handleleft = 0; let handlewidth = 0;
function updatescrollbar() { if (!container.value || !scrolltrack.value || !scrollbarhandle.value) return; const containerwidth = container.value.clientwidth; const scrolltrackwidth = scrolltrack.value.clientwidth; handlewidth = (containerwidth * scrolltrackwidth) / scrollbar.value.scrollwidth; handleleft = (scrollleft / scrollbar.value.scrollwidth) * scrolltrackwidth; if (handlewidth < 24) { handlewidth = 24; } scrollbarhandle.value.style.width = `${handlewidth}px`; scrollbarhandle.value.style.left = `${handleleft}px`; }
function onscroll() { if (!container.value || !scrolltrack.value || !scrollbarhandle.value) return; scrollleft = scrollbar.value.scrollleft; updatescrollbar(); }
function onhandlemousedown(e) { if (!container.value || !scrolltrack.value || !scrollbarhandle.value) return; const scrollbarwidth = scrollbar.value.clientwidth; const handlewidth = scrollbarhandle.value.clientwidth; const scrollbarhandleminleft = 0; const scrollbarhandlemaxleft = scrollbarwidth - handlewidth;
e.preventdefault(); e.stoppropagation();
document.addeventlistener('mousemove', onhandlemousemove); document.addeventlistener('mouseup', onhandlemouseup);
const onhandlemousemove = (e) => { const offset = e.clientx - container.value.offsetleft - handlewidth / 2; if (offset < scrollbarhandleminleft) { offset = scrollbarhandleminleft; } else if (offset > scrollbarhandlemaxleft) { offset = scrollbarhandlemaxleft; } const newscrollleft = (offset / scrollbarwidth) * scrollbar.value.scrollwidth;
scrollbar.value.scrollleft = newscrollleft; e.preventdefault(); e.stoppropagation(); };
const onhandlemouseup = () => { document.removeeventlistener('mousemove', onhandlemousemove); document.removeeventlistener('mouseup', onhandlemouseup); }; }
onmounted(() => { scrollbar.value = scrollbar.value || container.value.parentelement; updatescrollbar(); scrollbar.value.addeventlistener('scroll', onscroll); scrollbarhandle.value.addeventlistener('mousedown', onhandlemousedown); });
onunmounted(() => { scrollbar.value.removeeventlistener('scroll', onscroll); scrollbarhandle.value.removeeventlistener('mousedown', onhandlemousedown); });
return { container, scrolltrack, scrollbarhandle, }; }, }; </script><style> .container { display: flex; overflow: hidden; width: 100%; height: 100%; }
.scroll-track { position: relative; flex-grow: 1; }
.scrollbar-handle { position: absolute; z-index: 100; cursor: pointer; } </style>
|