@joyday/vue-loop-scroll
Version:
A high-performance Vue component for loop scrolling, supporting large data sets, adaptive resizing, real-time data updates, and flexible scrolling controls.
3 lines (2 loc) • 10.1 kB
JavaScript
(function(S,o){typeof exports=="object"&&typeof module<"u"?o(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],o):(S=typeof globalThis<"u"?globalThis:S||self,o(S.VueLoopScroll={},S.Vue))})(this,function(S,o){"use strict";var Ve=Object.defineProperty;var je=(S,o,_)=>o in S?Ve(S,o,{enumerable:!0,configurable:!0,writable:!0,value:_}):S[o]=_;var ne=(S,o,_)=>je(S,typeof o!="symbol"?o+"":o,_);var _=document.createElement("style");_.textContent=`.scroll-loop-viewport[data-v-17f09cf5]{overflow:hidden}.scroll-loop-viewport.direction-forward>.scroll-loop-track[data-v-17f09cf5]:before{content:""}.scroll-loop-viewport.direction-backward>.scroll-loop-track[data-v-17f09cf5]:after{content:""}.scroll-loop-viewport.direction-vertical[data-v-17f09cf5]{height:100%}.scroll-loop-viewport.direction-vertical>.scroll-loop-track[data-v-17f09cf5]{display:flow-root}.scroll-loop-viewport.direction-horizontal>.scroll-loop-track[data-v-17f09cf5]{display:flex;width:max-content}
/*$vite$:1*/`,document.head.appendChild(_);class T extends Error{constructor(s){super(s);ne(this,"cancelled");this.cancelled=!0,Object.setPrototypeOf(this,T.prototype)}}const ie=r=>{let t=0,s=!1;return{execute:async function(...p){const x=t;s=!0;try{const W=await r.apply(this,p);if(x!==t)throw new T("Async operation cancelled");return W}finally{s=!1}},cancel:()=>{s&&(t++,s=!1)},isCancellable:()=>s,isCancelledError:p=>p instanceof T}},se=()=>{const r=o.ref(0);return{updateCounter:r,triggerUpdate:()=>{r.value++}}};function $(r,t){let s=null;const l=o.computed(()=>"ResizeObserver"in window),a=()=>{s&&(s.disconnect(),s=null)},d=o.watch(()=>o.toValue(r),p=>{a(),p&&l.value&&(s=new window.ResizeObserver(t),s.observe(p))},{immediate:!0,flush:"post"});return o.onBeforeUnmount(()=>{d(),a()}),{isSupported:l,stop:()=>{d()}}}function H(r){if(!(r!=null&&r.offsetParent))return{width:0,height:0};const t=r.getBoundingClientRect(),s=window.getComputedStyle(r),l=P=>parseFloat(P)||0,a=l(s.borderLeftWidth)+l(s.borderRightWidth),d=l(s.borderTopWidth)+l(s.borderBottomWidth);return{width:t.width-a,height:t.height-d}}let ae=0;const re=()=>String(ae++),ce=(r,t,s="forward")=>{const l=r.length-1;let a=0;if(s==="forward")for(;a<=l;)t(r[a],a),a++;else{let d=l;for(;d>=0;)t(r[d],d),d--}};function le(r){const t=typeof r;return r!=null&&t==="object"}const ue=(r,t)=>{const s=setTimeout(()=>t==null?void 0:t(),r);return()=>clearTimeout(s)},D=new WeakMap;function de(r,t){let s=!0;const{resolve:l,promise:a}=pe(),P=ue(r,()=>{l(!1)}),p=Object.defineProperty(a,"isRunning",{get:()=>s});return D.set(p,()=>{s&&(s=!1,P(),l(!0))}),a.finally(()=>{s=!1,D.delete(p)}),p}function fe(r){var t;(t=D.get(r))==null||t()}function pe(){let r,t;return{promise:new Promise((l,a)=>{r=l,t=a}),resolve:r,reject:t}}const he=["data-uid","data-key"],ge=((r,t)=>{const s=r.__vccOpts||r;for(const[l,a]of t)s[l]=a;return s})(o.defineComponent({__name:"index",props:{dataSource:{},itemKey:{},direction:{default:"up"},speed:{default:1},waitMode:{default:"item"},waitTime:{default:0},pausedOnHover:{type:Boolean,default:!0},loadCount:{default:1}},setup(r){const t=r,s=o.ref(null),l=o.ref(null),a=o.shallowRef([]);let d,P,p=[];const x=o.reactive({viewportWidth:0,viewportHeight:0,trackWidth:0,trackHeight:0}),W={scrollOffset:0,scrollOffsetInPageMode:0,isScrolling:!1,isPaused:!1},f=o.reactive({...W}),me=()=>{Object.assign(f,{...W})},k=o.computed(()=>["up","down"].includes(t.direction)),b=o.computed(()=>["up","left"].includes(t.direction)),O=o.computed(()=>k.value?x.viewportHeight:x.viewportWidth),q=o.computed(()=>k.value?x.trackHeight:x.trackWidth),we=o.computed(()=>q.value>O.value),ve=o.computed(()=>{let e=f.scrollOffset;return b.value?e*=-1:e+=O.value-q.value,{transform:`translate3D(${k.value?`0, ${e}px, 0`:`${e}px, 0, 0`})`}}),B=o.computed(()=>t.waitTime>0),V=o.computed(()=>Math.max(Math.min(t.speed,O.value),Number.MIN_VALUE)),{execute:X,cancel:Se,isCancellable:ye}=ie(o.nextTick),{updateCounter:Ie,triggerUpdate:j}=se(),xe=()=>{if(!l.value)return 0;const{height:n,width:i}=H(l.value);return k.value?n:i},ke=()=>{const e=p[p.length-1];return(e==null?void 0:e.key)??""},be=()=>{t.pausedOnHover&&!f.isPaused&&f.isScrolling&&(f.isPaused=!0,E())},Oe=()=>{t.pausedOnHover&&f.isPaused&&f.isScrolling&&(f.isPaused=!1,U(d==null?void 0:d.isRunning))},Y=async()=>(d=de(t.waitTime),await d),Ce=()=>{d!=null&&d.isRunning&&(fe(d),d=null)},J=e=>{try{return le(e)&&t.itemKey&&e[t.itemKey]!=null?String(e[t.itemKey]):JSON.stringify(e)}catch{return""}},L=e=>({value:e,uid:re(),key:J(e)}),N=()=>{var e;return Array.from(((e=l.value)==null?void 0:e.children)??[])},G=e=>{const{totalWidth:n,totalHeight:i}=e.reduce((c,g)=>(c.totalHeight+=g.height,c.totalWidth+=g.width,c),{totalWidth:0,totalHeight:0});return k.value?i:n},Pe=()=>H(s.value),ze=()=>H(l.value),Q=()=>{const e=Pe(),n=ze();Object.assign(x,{viewportHeight:e.height,viewportWidth:e.width,trackHeight:n.height,trackWidth:n.width})},Z=e=>t.dataSource.findIndex(n=>J(n)===e),Me=e=>{p=ee(e)},Re=()=>{P&&(cancelAnimationFrame(P),P=void 0)},E=()=>{Re(),Ce()},F=()=>{E(),me(),p=[]},ee=e=>{const n=[],i=l.value;if(!i)return n;const c=xe();let g;const{top:u,left:v}=i.getBoundingClientRect(),z=h=>{const m=b.value?h.bottom-u:c-(h.top-u),w=b.value?h.right-v:c-(h.left-v);return{bottom:m,right:w}},y=h=>{const m=b.value?h.previousElementSibling:h.nextElementSibling;return m?z(m.getBoundingClientRect()):{bottom:0,right:0}};return ce(e,h=>{const m=h.getBoundingClientRect();let{bottom:w,right:M}=g??y(h);const I=w,C=M,{bottom:R,right:K}=z(m),He=R-I,De=K-C;g={bottom:R,right:K},n.push({uid:h.dataset.uid||"",key:h.dataset.key||"",width:De,height:He,top:I,bottom:R,left:C,right:K})},b.value?"forward":"reverse"),n},A=async(e,n,i)=>{const c=t.dataSource.length,g=a.value.length,u=i==="next";let v=0;const z=t.dataSource.length*2,y=async()=>{if(v++>z)return;let h=t.loadCount;const m=[];for(;h-- >0;){e=(e+c)%c;const R=b.value?"push":"unshift";m[R](L(t.dataSource[e])),u?e++:e--}const w=b.value&&u||!b.value&&!u;a.value=w?a.value.concat(m):m.concat(a.value),await X();const M=w?N().slice(g):N().slice(0,a.value.length-g),I=ee(M),C=G(I);M.length>0&&n>C?await y():(Me(N()),Q())};await y()},U=async(e=!0)=>{if(f.isScrolling=!0,f.isPaused)return;const n=(u,v)=>{const z=p.length,y=I=>k.value?I.height:I.width;let h=v,m=0,w=0;const M=t.dataSource.length;for(;!(w++>M);){const I=y(p[h]);m+=I;const C=(h+1)%z,R=m+y(p[C]);if(u<m)break;if(u<R)return{hasCrossedItem:!0,nextItemIndex:C,remainingOffset:u-m};h=C}return{hasCrossedItem:!1,nextItemIndex:v,remainingOffset:u}};let{remainingOffset:i,nextItemIndex:c}=n(f.scrollOffset,0);const g=async()=>{f.scrollOffset+=V.value,i+=V.value,f.scrollOffsetInPageMode+=V.value;const{hasCrossedItem:u,remainingOffset:v,nextItemIndex:z}=n(i,c);if(u){c=z,i=v;const m=t.waitMode==="item"&&B.value,w=t.waitMode==="page"&&B.value&&f.scrollOffsetInPageMode>=O.value;(m||w)&&(i-=v,f.scrollOffset-=v),w&&(f.scrollOffsetInPageMode-=v)}if(f.scrollOffset>=O.value){const m=b.value?a.value.slice(c):a.value.slice(0,-c),w=p.slice(c),M=G(w),I=O.value*2-(M-i);if(I>0){a.value=m,p=w,c=0,f.scrollOffset=i;const C=Z(ke()),R=C>-1?C+1:0;await A(R,I,"next")}}const y=t.waitMode==="item"&&u&&B.value,h=t.waitMode==="page"&&u&&f.scrollOffsetInPageMode>=O.value&&B.value;h&&(f.scrollOffsetInPageMode=0),!((y||h)&&await Y())&&await new Promise((m,w)=>{P=requestAnimationFrame(async()=>{try{await g(),m()}catch(M){w(M)}})})};B.value&&e&&await Y()||await g()},_e=async(e,n,i)=>{const c=L(e);a.value=[c];const{marginBefore:g,marginAfter:u}=i;g>0&&await A(n-1,g,"pre"),u>0&&await A(n+1,u,"next");const z=(()=>{const y=p.find(h=>h.uid===c.uid);return y?k.value?y.top:y.left:0})()-g;f.scrollOffset=z},Be=()=>{let e=f.scrollOffset;const n=p.length,i=[];let c=!1,g=0;for(;g<n;){const u=p[g];c&&i.push(u);const v=k.value?u.height:u.width;if(e-=v,e<0){if(c)break;i.push(u),c=!0,e+=O.value}g++,g>=n&&(g=0)}return i},Te=e=>{const n=k.value?e.height:e.width,c=(k.value?e.top:e.left)-f.scrollOffset,u=O.value-c-n+O.value;return{marginBefore:c,marginAfter:u}},We=()=>{const e=Be();for(const[,n]of e.entries()){const i=Z(n.key);if(i>-1)return{status:"found",item:t.dataSource[i],index:i,margins:Te(n)}}return{status:"not-found"}},Ee=async()=>{const e=We();e.status==="found"?(await _e(e.item,e.index,e.margins),await U(d==null?void 0:d.isRunning)):(F(),a.value=[],await te())},te=async()=>{await A(0,O.value*2,"next"),await U()},oe=e=>e.map(n=>L(n)),Ae=async()=>{const e=Math.ceil(t.dataSource.length/t.loadCount);let n=0;const i=a.value;for(;n++<e;){const c=t.dataSource.slice(0,t.loadCount*(n+1));if(b.value||c.reverse(),a.value=oe(c),await X(),Q(),we.value)return a.value=i,!0}return a.value=oe(t.dataSource),!1};return o.onMounted(()=>{o.watch(()=>[t.dataSource,Ie.value],async()=>{try{if(ye()&&Se(),f.isScrolling&&E(),!await Ae()){F();return}f.isScrolling?await Ee():await te()}catch{}},{immediate:!0,deep:!0}),o.watch(()=>t.direction,()=>{F(),j()}),$(s,e=>{const{blockSize:n,inlineSize:i}=e[0].contentBoxSize[0];(n!==x.viewportHeight||i!==x.viewportWidth)&&j()}),$(l,e=>{const{blockSize:n,inlineSize:i}=e[0].contentBoxSize[0];(n!==x.trackHeight||i!==x.trackWidth)&&j()})}),o.onBeforeUnmount(()=>{E()}),(e,n)=>a.value.length>0?(o.openBlock(),o.createElementBlock("div",{key:0,ref_key:"scrollViewportRef",ref:s,class:o.normalizeClass(["scroll-loop-viewport",[k.value?"direction-vertical":"direction-horizontal",b.value?"direction-forward":"direction-backward"]]),onMouseenter:be,onMouseleave:Oe},[o.createElementVNode("div",{ref_key:"scrollTrackRef",ref:l,class:"scroll-loop-track",style:o.normalizeStyle(ve.value)},[(o.openBlock(!0),o.createElementBlock(o.Fragment,null,o.renderList(a.value,i=>(o.openBlock(),o.createElementBlock("div",{class:"scroll-loop-item","data-uid":i.uid,"data-key":i.key,key:i.uid},[o.renderSlot(e.$slots,"default",o.mergeProps({ref_for:!0},{item:i.value}),()=>[o.createTextVNode(o.toDisplayString(i.value),1)],!0)],8,he))),128))],4)],34)):o.createCommentVNode("",!0)}}),[["__scopeId","data-v-17f09cf5"]]);S.LoopScroll=ge,Object.defineProperty(S,Symbol.toStringTag,{value:"Module"})});