乐趣区

关于svelte:基于svelte3自定义虚拟滚动条组件svelte3scrollbar

前段时间有给大家分享两个 svelte3.x 系列组件。明天持续分享一个最新写的 svelte3-scrollbar 虚构丑化零碎滚动条组件。

svelte-layer:一款 svelte3.x 轻量级 PC 端弹窗组件
svelte-popup:一款 svelte3.x 挪动端弹层组件

svelte-scrollbar 一款轻量级网页自定义丑化零碎滚动条组件。反对 原生滚动条、自动隐藏、垂直 + 程度滚动、自定义滚动条大小 / 色彩 / 间隙 等性能。

在 lib 目录下新建一个 Scrollbar 组件。

引入组件

在须要用到滚动条性能的页面引入插件。

import Scrollbar from '$lib/Scrollbar'

疾速调用

通过如下形式调用,即可疾速生成一个虚拟化滚动条。

<!-- // 原生滚动条 -->
<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>

参数配置

svelte-scrollbar 反对如下参数配置。

<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>

模板及逻辑局部

<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>

另外还反对动静监听 scroll 事件。

<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,以上就是 svelte.js 自定义零碎丑化滚动条组件的分享。后续还会分享一个 svelte 桌面端实例我的项目。

最初附上一个 svelte3 聊天实战我的项目

svelte-chat 基于 svelte3.x 仿微信 APP 聊天我的项目

目前 svelte.js 稳定性还能够,生态也逐步完善,期待更多的开发小伙伴一起参加进来。

退出移动版