DoubleSlider双向滑块

实现了一个双滑块选择器。用户可以通过拖动左右两个滑块来选择一个区间,也可以点击选择器的条来整体移动所选区间。(后续会扩展更多功能如:鼠标直接选中某一区间)

技术栈:vue3、js

主要代码

DoubleSlider.vue

<template>
    <div class="a_slider">
        <div
            class="a_slider_bar"
            ref="sliderBarRef"
            @mousedown="handleMouseDown"
            @mousemove="handleMouseMove"
            @mouseup="clickBar"
        ></div>
        <div
            class="a_min_bar"
            :style="{
                left: props.modelValue.left + '%',
                width: props.modelValue.width + '%',
            }"
            @mousedown="handleMouseDown"
            @mousemove="handleMouseMove"
            @mouseup="clickBar"
        ></div>
        <div
            class="a_slider__button"
            v-diy-drag
            :style="{ left: props.modelValue.left + '%' }"
            id="dragButtonLeft"
            ref="dragButtonLeftRef"
        ></div>
        <div
            class="a_slider__button"
            v-diy-drag
            :style="{
                left: props.modelValue.left + props.modelValue.width + '%',
            }"
            id="dragButtonRight"
            ref="dragButtonRightRef"
        ></div>
    </div>
</template>

<script setup>
import { reactive, onMounted, onBeforeUnmount, ref } from "vue";
const emits = defineEmits(["update:modelValue", "change"]);
const sliderBarRef = ref();
const dragButtonLeftRef = ref();
const dragButtonRightRef = ref();
let drag = ref(false);

const handleMouseDown = () => (drag.value = false);
const handleMouseMove = () => (drag.value = true);

const vDiyDrag = {
    beforeMount(el) {
        const onMouseDown = (e) => {
            const id = e.target.id;
            if (id !== "dragButtonLeft" && id !== "dragButtonRight") {
                return;
            }
            const initialMouseX = e.clientX;
            const initialDomX = el.offsetLeft;
            const clientWidth = sliderBarRef.value.clientWidth;
            let minX = 0,
                maxX = 0;
            // 按钮宽度占比
            const buttonWidth = (6 / clientWidth) * 100;
            const dragButtonLeft = {
                minX: 0,
                maxX:
                    parseFloat(dragButtonRightRef.value.style.left) -
                    buttonWidth,
            };
            const dragButtonRight = {
                minX:
                    parseFloat(dragButtonLeftRef.value.style.left) +
                    buttonWidth,
                maxX: 100,
            };
            if (id === "dragButtonLeft") {
                minX = dragButtonLeft.minX;
                maxX = dragButtonLeft.maxX;
            } else {
                minX = dragButtonRight.minX;
                maxX = dragButtonRight.maxX;
            }

            const onMouseMove = (e) => {
                const deltaX = e.clientX - initialMouseX;
                let newX = ((initialDomX + deltaX) / clientWidth) * 100;
                // 边界控制
                if (newX < minX) newX = minX;
                else if (newX > maxX) newX = maxX;
                el.style.left = newX + "%";
                change();
            };

            const onMouseUp = () => {
                document.removeEventListener("mousemove", onMouseMove);
                document.removeEventListener("mouseup", onMouseUp);
            };

            document.addEventListener("mousemove", onMouseMove);
            document.addEventListener("mouseup", onMouseUp);
            return false;
        };

        el.addEventListener("mousedown", onMouseDown);

        onBeforeUnmount(() => {
            el.removeEventListener("mousedown", onMouseDown);
        });
    },
};

const props = defineProps({
    modelValue: {
        type: Object,
        default: () => {
            return {
                left: 0,
                width: 100,
            };
        },
    },
});

const state = reactive({});

onMounted(() => {});

onBeforeUnmount(() => {});

function clickBar(e) {
    console.log(drag.value);
    const clientWidth = sliderBarRef.value.clientWidth;
    const offsetX = (e.offsetX / clientWidth) * 100;
    const rightButton = parseFloat(dragButtonRightRef.value.style.left);
    const leftButton = parseFloat(dragButtonLeftRef.value.style.left);
    const middle = (rightButton + leftButton) / 2;

    let diff = offsetX - middle;
    if (diff > 0) {
        if (rightButton + diff > 100) {
            diff = 100 - rightButton;
        }
        // 右边按钮
        dragButtonRightRef.value.style.left = rightButton + diff + "%";
        // 左边按钮
        dragButtonLeftRef.value.style.left = leftButton + diff + "%";
    } else if (diff < 0) {
        if (leftButton + diff < 0) {
            diff = 0 - leftButton;
        }
        // 左边按钮
        dragButtonLeftRef.value.style.left = leftButton + diff + "%";
        // 右边按钮
        dragButtonRightRef.value.style.left = rightButton + diff + "%";
    }
    change();
}

function change() {
    let newValue = {
        left: parseFloat(dragButtonLeftRef.value.style.left),
        width:
            parseFloat(dragButtonRightRef.value.style.left) -
            parseFloat(dragButtonLeftRef.value.style.left),
    };
    emits("update:modelValue", newValue);
    emits("change", newValue);
}
</script>

<style lang="less" scoped>
.a_slider {
    position: relative;
    width: 100%;
    .a_slider_bar {
        // position: absolute;
        width: 100%;
        height: 8px;
        background: rgba(255, 255, 255, 0.2);
        border: 1px solid rgba(255, 255, 255, 0.4);
        cursor: crosshair;
    }
    .a_min_bar {
        pointer-events: none;
        position: absolute;
        top: 5px;
        width: 100%;
        height: 12px;
        background: rgba(16, 29, 47, 1);
        border: 1px solid rgba(255, 255, 255, 0.4);
        transform: translateY(-50%);
    }
    .a_slider__button {
        position: absolute;
        top: 5px;
        width: 6px;
        height: 18px;
        background: rgba(0, 38, 101, 1);
        border-radius: 2px;
        cursor: ew-resize;
        box-sizing: border-box;
        border: 1px solid rgba(255, 255, 255, 1);
        transform: translateY(-50%);
    }
    .a_slider__button:hover {
        background: rgba(54, 124, 232, 1);
    }
}
</style>

效果图:

posted @   YAY_tyy  阅读(2)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示