Created
September 7, 2023 12:53
-
-
Save zew13/a48b41505cbd52afa880435f9c115a25 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<style lang="stylus"> | |
.scrollbar | |
overflow auto | |
-ms-overflow-style none | |
scrollbar-width none | |
display flex | |
flex-direction row-reverse | |
position relative | |
.scrollbar::-webkit-scrollbar | |
width 0 | |
.scrollbar>div | |
margin auto | |
.scrollbar>div>div | |
min-height 100% | |
width 100% | |
display flex | |
flex-direction column | |
align-items center | |
justify-content center | |
.scrollbar>aside | |
user-select none | |
position sticky | |
height 100% | |
top 0 | |
left 100% | |
width 1.05rem | |
&, &>i | |
transition width 0.3s, opacity 1s, top 0.5s, background 0.3s, box-shadow 1s | |
&>i | |
position absolute | |
transition height 0.3s | |
display block | |
width 0.45rem | |
right 0.1rem | |
margin 2px 0 | |
border-radius 0.3rem | |
background rgba(0, 0, 0, 0.2) | |
.scrollbar > div | |
width 100% | |
.scrollbar > aside+div | |
margin-right -1.05rem | |
html.scroll | |
cursor grab | |
&>body | |
pointer-events none | |
.scrollbar>aside:hover, aside.scroll | |
background rgba(125, 125, 125, 0.15) | |
opacity 1 !important | |
box-shadow inset 0.2rem 0 0.2rem -0.2rem rgba(0, 0, 0, 0.3), inset -0.2rem 0 0.2rem -0.2rem rgba(0, 0, 0, 0.3) | |
&>i | |
background rgba(0, 0, 0, 0.3) | |
width 0.6rem | |
right 0.225rem | |
</style> | |
<template lang="pug"> | |
.scrollbar(ref="main") | |
aside(@click="click", ref="aside", v-if="turn") | |
i(ref="si", @mousedown="down") | |
div | |
div(ref="wrap") | |
slot | |
</template> | |
<script lang="coffee"> | |
import {onUnmounted, nextTick, ref, shallowRef, onMounted} from 'vue' | |
import _on from '~/coffee/$/_on' | |
import $on from '~/coffee/$/on' | |
Scroll = (elem) => | |
runing = 0 | |
{requestAnimationFrame, cancelAnimationFrame} = window | |
(to, duration=300)=> | |
cancelAnimationFrame runing | |
scrollCount = 0 | |
cosParameter = (elem.scrollTop - to) / 2 | |
toCos = to+cosParameter | |
oldTimestamp = 0 | |
step = (newTimestamp) -> | |
if oldTimestamp | |
# if duration is 0 scrollCount will be Infinity | |
scrollCount += Math.PI * (newTimestamp - oldTimestamp) / duration | |
if scrollCount >= Math.PI | |
elem.scrollTop = to | |
return | |
elem.scrollTop = toCos + cosParameter * Math.cos(scrollCount) | |
oldTimestamp = newTimestamp | |
runing = requestAnimationFrame step | |
return | |
if elem.scrollTop == to | |
return | |
runing = requestAnimationFrame step | |
return | |
SCROLL_CLS = "scroll" | |
HTML = document.documentElement | |
export default { | |
components:{ | |
} | |
setup:=> | |
main = shallowRef() | |
wrap = shallowRef() | |
si = shallowRef() | |
aside = shallowRef() | |
scrollTo = undefined | |
turn = ref 0 | |
_mouseunbind = undefined | |
mouseunbind = => | |
for i from [HTML, aside.value] | |
i?.classList.remove(SCROLL_CLS) | |
_mouseunbind?() | |
_mouseunbind = undefined | |
timer = undefined | |
onMounted => | |
mv = main.value | |
wv = wrap.value | |
scrollTo = Scroll mv | |
scroll = => | |
if not turn.value | |
return | |
{clientHeight,scrollHeight,scrollTop} = mv | |
height = Math.max(parseInt(clientHeight*clientHeight/scrollHeight)-4,48) | |
iv = si.value | |
av = aside.value | |
av.style.opacity = 1 | |
Object.assign( | |
iv.style | |
{ | |
height : height+"px" | |
top : parseInt(scrollTop/(scrollHeight-clientHeight)*(clientHeight-4-height))+"px" | |
} | |
) | |
clearTimeout timer | |
timer = setTimeout( | |
=> | |
av.style.opacity = 0 | |
1000 | |
) | |
return | |
resize = => | |
{clientHeight,scrollHeight} = mv | |
if scrollHeight <= clientHeight | |
turn.value = 0 | |
else | |
turn.value = 1 | |
nextTick scroll | |
ro = new ResizeObserver resize | |
ro.observe wv | |
$on( | |
mv | |
{ | |
scroll | |
} | |
) | |
$on( | |
window | |
{ | |
resize | |
} | |
) | |
onUnmounted => | |
ro.disconnect() | |
mouseunbind() | |
return | |
{ | |
turn | |
main | |
si | |
aside | |
wrap | |
click:(e)=> | |
if _mouseunbind | |
return | |
mv = main.value | |
{clientHeight,scrollHeight} = mv | |
scrollTo parseInt(e.offsetY/clientHeight * (scrollHeight-clientHeight)) | |
return | |
down:(e)=> | |
_mouseunbind?() | |
for i from [HTML, aside.value] | |
i?.classList.add(SCROLL_CLS) | |
mv = main.value | |
sv = si.value | |
_diff = 0 | |
Y = e.offsetY | |
_mouseunbind = _on document,{ | |
mouseup:mouseunbind | |
mousemove:({offsetY})=> | |
{scrollTop,clientHeight,scrollHeight} = mv | |
diff = offsetY - Y | |
if _diff * diff < 0 or diff == 0 # chrome 经常跳,防抖 | |
_diff = diff | |
return | |
ivh = sv.clientHeight + 4 | |
max = scrollHeight - clientHeight | |
st = (offsetY-mv.offsetTop-ivh/2)/(clientHeight-ivh) * max | |
if st < 0 | |
st = 0 | |
else if st > max | |
st = max | |
mv.scrollTop = st | |
Y = offsetY | |
return | |
} | |
} | |
} | |
</script> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment