vue3 process-bar 可拖拽的进度条

1,493 阅读1分钟

<template>
  <div
    class="process-bar-outer"
    ref="outer"
    :style="outerStyle"
    @mousedown="handleOuter">
    <div class="process-bar-inner" :style="innerStyle"></div>
    <div class="process-bar-slider" :style="sliderStyle"></div>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, ref,defineProps, defineEmits, computed, onUnmounted } from "vue";

    const props = defineProps({
        modelValue: {
            type: Number,
            default: 0
        },
        width: {
            type: Number,
            default: 200
        },
        height: {
            type: Number,
            default: 6
        },
        sliderWidth: {
            type: Number,
            default: 10
        }
    })
    const emit = defineEmits(['update:modelValue'])
    const currPos = ref(Math.round(props.width * props.modelValue / 100));
    const space = (props.sliderWidth - props.height) / 2;
    let outerOffsetLeft: number;
    let outer = ref(null);
    let sliding = false;

    const outerStyle = {
        width: props.width + 'px',
        height: props.height + 'px',
        borderRadius: props.height + 'px'
    }
    const innerStyle = computed(() => {
        return {
            width: currPos.value + 'px',
            borderRadius: props.height + 'px'
        }
    })
    const sliderStyle = computed(() => {
        return {
            width: props.sliderWidth + 'px',
            height: props.sliderWidth + 'px',
            top: -space + 'px',
            left: currPos.value - (props.sliderWidth / 2) + 'px'
        }
    })
    
    const handleOuter = (e: MouseEvent) => {
        sliding = true;
        currPos.value = handleBoundary(e.clientX - outerOffsetLeft)
    }

    const handleBoundary = (value: number) => {
        value = Math.min(props.width, value);
        value = Math.max(0, value);
        emit('update:modelValue', Math.round(value / props.width * 100))
        return value;
    }

    const handlMousemove = (e:MouseEvent) => {
           if (sliding) {
                currPos.value = handleBoundary(e.clientX - outerOffsetLeft)
           }
       }

    const handlMouseup = (e:MouseEvent) => {
            sliding = false;
           currPos.value = handleBoundary(e.clientX - outerOffsetLeft)
       }

    onMounted(() => {
        outerOffsetLeft = (outer.value as any).getBoundingClientRect().left;
        document.addEventListener('mousemove', handlMousemove)
        document.addEventListener('mouseup', handlMouseup)
    })

    onUnmounted(() => {
        document.removeEventListener('mousemove', handlMousemove)
        document.removeEventListener('mouseup', handlMouseup)
    })
</script>

<style lang="scss" scoped>
div {
    box-sizing: border-box;
}
.process-bar-outer {
    caret-color: rgba(0,0,0,0);
    position: relative;
    background-color: #ebeef5;
    

    .process-bar-inner {
       height: 100%;
       background-color: #409eff;
    }

    .process-bar-slider {
        position: absolute;
        border-radius: 50%;
        border: 1.6px solid #409EFF;
        background-color: #ffffff;
    }
}
</style>