Some time ago, I shared with you two svelte3.x series components. Today, I will continue to share a newly written svelte3-scrollbar virtual beautification system scroll bar component.

svelte-layer: a svelte3.x lightweight PC-side pop-up window component
svelte-popup: a svelte3.x mobile terminal popup component

svelte-scrollbar is a lightweight web page custom beautification system scroll bar component. Support 原生滚动条、自动隐藏、垂直+水平滚动、自定义滚动条大小/颜色/间隙 and other functions.

Create a new Scrollbar component in the lib directory.

import components

Introduce plugins to pages that require scroll bar functionality.

 import Scrollbar from '$lib/Scrollbar'

quick call

Called in the following way to quickly generate a virtualized scroll bar.

 <!-- //原生滚动条 -->
<Scrollbar native>
    <div>自定义内容信息。</div>
</Scrollbar>

<!-- //自动隐藏 -->
<Scrollbar autohide={true}>
    <div>自定义内容信息。</div>
</Scrollbar>

<!-- //水平滚动(支持滚轮滑动) -->
<Scrollbar mousewheel>
    <div>自定义内容信息。</div>
</Scrollbar>

<!-- //自定义高度/最大高度 -->
<Scrollbar height="200" maxHeight="350">
    <div>自定义内容信息。</div>
</Scrollbar>

<!-- //自定义大小/间隙/颜色 -->
<Scrollbar size="10px" gap="5" color="#09f">
    <div>自定义内容信息。</div>
</Scrollbar>

parameter configuration

svelte-scrollbar supports the following parameter configurations.

 <script>
    // 是否开启原生滚动条
    export let native = false
    // 是否自动隐藏滚动条
    export let autohide = false
    // 滚动条尺寸
    export let size = undefined
    // 滚动条颜色
    export let color = ''
    // 滚动条层叠
    export let zIndex = null
    // 滚动条区域高度
    export let height = undefined
    // 滚动条区域最大高度
    export let maxHeight = undefined
    // 滚动条间隙
    export let gap = 0
    // 是否开启水平滚轮滚动控制
    export let mousewheel = false
    
    ...
</script>

Template and logic section

 <div class="vui__scrollbar" bind:this={el} on:mouseenter={handleMouseEnter} on:mouseleave={handleMouseLeave}>
    <div class="vscroll__wrap" class:hidenative={!bool(native)} bind:this={wrap} on:scroll={handleScroll} on:mousewheel={handleMouseWheel} style="{wrapStyle}">
        <slot />
    </div>
    <div class="vscroll__bar vertical" class:ishide={!data.isShowBar} on:mousedown={e => handleClickTrack(e, 0)} >
        <div class="vscroll__thumb" bind:this={barY} style="background: {color}; height: {data.barHeight}px; width: {addUnit(size)}" on:mousedown={e => handleDragThumb(e, 0)}></div>
    </div>
    <div class="vscroll__bar horizontal" class:ishide={!data.isShowBar} on:mousedown={e => handleClickTrack(e, 1)}>
        <div class="vscroll__thumb" bind:this={barX} style="background: {color}; width: {data.barWidth}px; height: {addUnit(size)}" on:mousedown={e => handleDragThumb(e, 1)}></div>
    </div>
</div>

<script>
    /**
     * @Desc     svelte3虚拟滚动条组件svelte-scrollbar
     * @Time     andy by 2022-05
     * @About    Q:282310962  wx:xy190310
     */
     
    // ...

    import { onMount, afterUpdate, createEventDispatcher, tick } from 'svelte'
    const dispatch = createEventDispatcher()

    import util from './util'

    $: data = {
        // 滚动条宽度
        barWidth: 0,
        // 滚动条高度
        barHeight: 0,
        // 滚动条水平偏移率
        ratioX: 1,
        // 滚动条垂直偏移率
        ratioY: 1,
        // 鼠标是否按住滚动条
        isTaped: false,
        // 鼠标是否悬停于滚动区域
        isHover: false,
        // 显示滚动条
        isShowBar: !bool(autohide)
    }

    const bool = (boolean) => JSON.parse(boolean) ? true : false
    const addUnit = (val) => val ? parseInt(val) + 'px' : null

    let observeTimer = null
    let c = {}
    // 滚动条对象
    let el
    let wrap
    let barX
    let barY

    $: wrapStyle = `height: ${addUnit(height)}; max-height: ${addUnit(maxHeight)}`
    $: GAP = addUnit(gap)

    onMount(() => {
        console.log('监听滚动条开启...')

        updated()

        let observer = new MutationObserver(mutation => {
            updated()
        })

        observer.observe(wrap, {
            attributes: true,
            childList: true,
            subtree: true,
            attributeFilter: [
                'style', 'class'
            ]
        })

        window.addEventListener('resize', util.throttle(updated))

        return () => {
            observer.disconnect()
            window.removeEventListener('resize', updated)
            console.log('监听滚动条关闭...')
        }
    })

    afterUpdate(() => {
        // console.log('监听dom更新...')
    })

    // 鼠标滑入
    function handleMouseEnter() {
        data.isHover = true
        data.isShowBar = true
        updated()
    }

    // 鼠标滑出
    function handleMouseLeave() {
        data.isHover = false
        if(!data.isTaped && bool(autohide)) {
            data.isShowBar = false
        }
    }

    // 拖动滚动条
    function handleDragThumb(e, index) {
        // ...
    }

    // 点击滚动条插槽
    function handleClickTrack(e, index) {
        if(index == 0) {
            wrap.scrollTop = (Math.abs(e.target.getBoundingClientRect().top - e.clientY) - barY.offsetHeight / 2) * data.ratioY
            barY.style.transform = `translateY(${wrap.scrollTop / data.ratioY}px)`
        }else {
            wrap.scrollLeft = (Math.abs(e.target.getBoundingClientRect().left - e.clientX) - barX.offsetWidth / 2) * data.ratioX
            barX.style.transform = `translateX(${wrap.scrollLeft / data.ratioX}px)`
        }
    }

    // 更新滚动区
    async function updated() {
        // ...
    }

    // 鼠标滚动事件
    function handleScroll(e) {
        let target = e.target
        let status
        if(target.scrollTop == 0) {
            status = 'top' // 滚动至顶部
        }else if(target.scrollTop + target.offsetHeight >= target.scrollHeight) {
            status = 'bottom' // 滚动至底部
        }

        /**
         * 父组件调用 const { target, status, scrollTop, scrollLeft } = e.detail
         */
        dispatch('scroll', {
            target, // 滚动对象
            status, // 滚动状态(记录滚动位置)
            scrollTop: target.scrollTop,
            scrollLeft: target.scrollLeft
        })
        updated()
    }

    // 控制滚轮水平滚动
    function handleMouseWheel(e) {
        if(!bool(mousewheel)) return
        e.preventDefault()
        if(wrap.scrollWidth > wrap.offsetWidth) {
            wrap.scrollLeft += e.deltaY
        }
    }

    // 滚动到一组特定坐标
    export async function scrollTo(arg1, arg2) {
        await tick()

        if(typeof arg1 == 'object') {
            wrap.scrollTo(arg1)
        }else if(!isNaN(arg1) && !isNaN(arg2)) {
            wrap.scrollTo(arg1, arg2)
        }
    }

    // 设置滚动条到顶部的距离
    export async function setScrollTop(value) {
        await tick()

        wrap.scrollTop = value == 'top' ? 0 : value == 'bottom' ? wrap.scrollHeight : parseInt(value)
        barY.style.transform = `translateY(${wrap.scrollTop / data.ratioY}px)`
    }

    // 设置滚动条到左边的距离
    export async function setScrollLeft(value) {
        await tick()

        wrap.scrollLeft = value == 'left' ? 0 : value == 'right' ? wrap.scrollWidth : parseInt(value)
        barX.style.transform = `translateX(${wrap.scrollLeft / data.ratioX}px)`
    }
</script>

<style>
    @import 'Scrollbar.scss';
</style>

Also supports dynamic monitoring of scroll events.

 <Scrollbar on:scroll={handleScroll}>
    <div>自定义内容信息。</div>
</Scrollbar>

<script>
    // 监听滚动事件
    function handleScroll(e) {
        const { target, status, scrollTop, scrollLeft } = e.detail
        scrollTopVal = scrollTop
        scrollLeftVal = scrollLeft

        // 判断滚动状态(方法1)
        /*if(scrollTop == 0) {
            scrollStatus = '滚动至顶部'
        }else if(scrollTop + target.offsetHeight >= target.scrollHeight) {
            scrollStatus = '滚动底部'
        }else {
            scrollStatus = '滚动中'
        }*/

        // // 判断滚动状态(方法2)
        if(status == 'top') {
            scrollStatus = '滚动至顶部'
        }else if(status == 'bottom') {
            scrollStatus = '滚动底部'
        }else {
            scrollStatus = '滚动中'
        }
    }
</script>

OK, the above is the sharing of the svelte.js custom system beautification scroll bar component. A svelte desktop example project will be shared in the future.

Finally, attach a svelte3 chat actual project

svelte-chat is based on the svelte3.x imitation WeChat APP chat project

At present, the stability of svelte.js is OK, and the ecology is gradually improving. We look forward to more development partners participating together.


xiaoyan2017
765 声望318 粉丝

web前端开发爱好者,专注于前端h5、jquery、vue、react、angular等技术研发实战项目案例。