怎么用JS实现一个可左右拉伸的滑块效果

本篇内容介绍了“怎么用JS实现一个可左右拉伸的滑块效果”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

目录
  • 前言

  • 需求

  • 方案

  • 源码

  • 后言


前言

上月我一朋友,让我帮他实现一个效果,话说是他公司产品觉得自家项目的制作gif功能用户体验差,看到了别人的效果,于是就“挥刀霍霍”向了我那朋友,具体我们看下效果。

别人家的效果

怎么用JS实现一个可左右拉伸的滑块效果

自家的效果

用的是 element ui的 滑块

怎么用JS实现一个可左右拉伸的滑块效果

果然还是别人家的效果,话说这个忙还是得帮,下面开始正题

需求

动手之前我们先捋一捋需求

滑块不能超出临界值(不能滑出轨道)

怎么用JS实现一个可左右拉伸的滑块效果

手动进行向左、向右、滑块整体左右滑动

鼠标对滑块左按钮、右按钮、中间按钮 分别可以向左拉伸、向右拉伸、滑块整体左右滑动

怎么用JS实现一个可左右拉伸的滑块效果

怎么用JS实现一个可左右拉伸的滑块效果

方案

需求貌似不多,选什么样的方案来实现呢? C3的 transform的 translateX 还是 定位position呢?由于之前我对定位的拖拽比较熟悉,于是我选择使用 定位position来实现。

具体思路:

  • 创建一个容器container、容器下面创建一个滑轨track、滑轨羡慕创建一个滑块 slider、滑块下面分别创建 左边按钮、中间按钮、右边按钮

  • 创建一个Slider类进行具体实现

源码

代码配有详尽的注释,以助于理解

html部分

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>slider</title>
</head>
<style>
    * {
        margin: 0;
        padding: 0;
    }
    .container {
        width: 400px;
        margin: 50px auto;
    }
    .track {
        box-sizing: content-box;
        position: relative;
        background: #d1d1d1;
        height: 30px;
        line-height: 30px;
        font-size: 12px;
    }
    .track::before,
    .track::after {
        content: '';
        position: absolute;
        top: 0;
        height: 100%;
        width: 12px;
        background: #d1d1d1;
    }
    .track::before {
        left: -12px;
        border-radius: 2px 0 0 2px;
    }
    .track::after {
        right: -12px;
        border-radius: 0 2px 2px 0;
    }
    .slider {
        position: absolute;
        top: 0;
        height: 100%;
        background: rgba(66, 133, 244, 0.25);
        text-align: center;
        color: #fff;
        font-size: 12px;
        border-radius: 2px;
        z-index: 1;
        cursor: pointer;
    }
    .middle {
        width: inherit;
        position: absolute;
        top: 0;
        height: 100%;
        background: rgba(66, 133, 244, 0.25);
        text-align: center;
        color: #fff;
        font-size: 12px;
        border-radius: 2px;
        z-index: 1;
        cursor: pointer;
    }
    .left {
        position: absolute;
        top: 0;
        height: 100%;
        width: 12px;
        background: #4285f4;
        border: 0;
        padding: 0;
        cursor: ew-resize;
        left: -12px;
        border-radius: 2px 0 0 2px;
    }
    .right {
        position: absolute;
        top: 0;
        height: 100%;
        width: 12px;
        background: #4285f4;
        border: 0;
        padding: 0;
        cursor: ew-resize;
        outline: none;
        right: -12px;
        border-radius: 0 2px 2px 0;
        z-index: 10;
    }
    .right:not(:hover):not(:active) > span,
    .left:not(:hover):not(:active) > span {
        visibility: hidden;
        color: #000;
    }
    .right > span,
    .left > span {
        position: absolute;
        bottom: 33px;
        transform: translate(-50%);
        left: 50%;
        visibility: visible;
    }
    .right > span > span,
    .left > span > span {
        display: block;
        background: #4285f4;
        height: 20px;
        padding: 0 4px;
        border-radius: 2px;
        line-height: 20px;
    }
    .left::before,
    .left::after,
    .right::before,
    .right::after {
        content: '';
        position: absolute;
        width: 1px;
        height: 8px;
        background: #fff;
        margin: 1px;
        top: 10px;
        right: 3px;
    }
    .left::before,
    .right::before {
        left: 3px;
    }
</style>
<body>
    <!-- 容器 -->
    <div class="container">
        <!-- 轨道 -->
        <div class="track">
            <!-- 滑块 -->
            <div class="slider" >
                <!-- 中间按钮 -->
                <div class="middle">0</div>
                <!-- 左边按钮 -->
                <div class="left">
                    <span>
                        <span  class="left-text">0</span>
                    </span>
                </div>
                <!-- 右边按钮 -->
                <div class="right">
                    <span>
                        <span  class="right-text">0</span>
                    </span>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

效果如图

怎么用JS实现一个可左右拉伸的滑块效果

下来我们来加上灵魂 js

想怎么用比较合适?

const slider = new Slider({
    slider_width: 50,// 滑块初始宽度
    track: "track", // 轨道class类名
    slider: "slider", // 滑块类名
    text: {
        left: "left-text", // 左边按钮文案class类名
        right: "right-text" // 右边按钮文案class类名
    },
    btn: {
        left: "left", // 左边按钮class类名
        right: "right",// 右边按钮class类名
        middle: "middle" //中间按钮class类名
    },
    range: {
        min: 50, // 滑块区间范围最小值
        max: 200 // 滑块区间范围最大值
    }
});
// 手动向右拉伸 滑块宽度至100
//slider.rightStretch(100) 
// 手动向左拉伸 滑块宽度至100
//slider.leftStretch(100)
// 手动移动滑块距离左边200
//slider.move(200)

想好了怎么用了,那么就是按怎么用进行逐步实现

第一步,我们要把传入的参数转化为构造函数 Slider 内部可以用的值

class Slider { 
    constructor(options) { 
        this._init(options)
    }
    _init(options){
         this.options = options // 传入的参数对象
        //
        this.init_width = options.slider_width // 初始滑块宽度

        options.range && options.range.min && (this.range_min = options.range.min) // 滑块宽度最小值
        options.range && options.range.max && (this.range_max = options.range.max) // 滑块宽度最大值
        //
        this.btn_left_slider = this._$(options.btn.left) // 滑块左边按钮类 - 用于向左边进行拉伸
        this.text_middle = this.btn_middle_slider = this._$(options.btn.middle) // 初始滑块宽度类  - 用于滑块整体左右移动  // 滑块中间按钮上面文案提示类
        this.btn_right_slider = this._$(options.btn.right) // 初始滑块宽度类  - 用于向右边边进行拉伸
        //
        this.track = this._$(options.track) // 滑块轨道类
        this.slider = this._$(options.slider) // 滑块轨道类
        //
        this.text_left = this._$(options.text.left) // 滑块左边按钮上面文案提示类
        this.text_right = this._$(options.text.right) // 滑块右边按钮上面文案提示类
        //
        this._initAttr() // 初始化属性值
        this._initevent() // 初始化事件
    }
}

第一步做了:同步用户传入参数于示例中、以及初始化滑块宽度、配置滑块的最大最小值、同步必须的dom、初始化属性值、初始化事件

为什么需要一开始就执行初始化属性(this._initAttr())呢?是dom里面都是默认的 0,用户传入的值需要同步初始化到dom中。

/**
 * @description: 初始化文案
 */
_initAttr() {
    this.slider.style.width = this.options.slider_width + 'px'
    this.text_middle.innerText = this.options.slider_width
}

然后会初始化事件,主要用到的事件是 鼠标在 滑块元素 mousedown 事件,然后借助事件代理,来区分我们作用的事件源对象的class类名,最后根据作用类名不同来执行不同的逻辑,这个是整个实现的重点

/**
 * @description: 初始化事件
 */
_initevent() {
    this.slider.onmousedown = ({ target: { className }, clientX }) => {
        // 滑块宽度 动态值
        const sliderWidth = this.slider.offsetWidth
        // 滑块 距离左边 动态值
        const sliderLeft = this.slider.offsetLeft

        switch (className) {
            case this.options.btn.middle:
                // 滑块 X 坐标计算
                this.disX = clientX - this.slider.offsetLeft
                // 滑块整体向右边移动 最大能移动的临界值
                const valid_right = this.track.offsetWidth - sliderWidth
                //
                document.onmousemove = ({ clientX }) => {
                    this.slider.style.left =
                        clientX - this.disX < 0
                            ? 0 // 如果是滑块整体向左移动 超出临界值 就是临界值
                            : clientX - this.disX > valid_right
                            ? valid_right + 'px' // 如果是滑块整体向右移动 超出临界值 就是临界值
                            : clientX - this.disX + 'px'
                    this._syncText()
                }
                break
            case this.options.btn.left:
                // 滑块 X 坐标计算
                this.disX = clientX
                // 滑块右边按钮 至 轨道 最左边变距离
                const rightWidth = this.slider.getBoundingClientRect().right - this.track.getBoundingClientRect().left
                //
                document.onmousemove = ({ clientX }) => {
                    // 两点间的 x 差值 // 向左 一直移动,临界值是 0 超出边界 就会变成 负数  ,如果一直向右移动,超出右边临界值,右边临界值在移动到右边按钮时产生
                    const diff = this.disX - clientX
                    //  如果 一直向左移动 对应的 临界值
                    let valid_left = sliderWidth + diff
                    // 如果 一直向右移动 对应的 临界值
                    let valid_right = sliderLeft - diff

                    if (this.range_min && valid_left <= this.range_min) {
                        // 如果有设置最小范围值 并且 此时 向左拉伸超出最小值,那么此时的 值 就是最小值  range_min
                        valid_left = this.range_min
                        valid_right = rightWidth - valid_left
                    } else if (this.range_max && valid_left >= this.range_max) {
                        // 如果右设置最大范围值 并且此时移动 valid_left 超出 设置的最大范围值
                        valid_left = this.range_max >= rightWidth ? rightWidth : this.range_max // 如果设置的最大范围值 超出有效值 rightWidth,那么就设置 valid_left 为 有效值 rightWidth ,否则就是设置为最大范围值
                        valid_right = rightWidth - valid_left
                    } else if (valid_left >= rightWidth) {
                        // 如果一直向左边移动 , 超出 有效值 rightWidth,(抵死在左边情形)
                        valid_left = rightWidth
                        valid_right = 0
                    } else if (valid_right >= rightWidth) {
                        // 如果一直向右边移动 , 超出 有效值 rightWidth
                        valid_left = 0
                        valid_right = rightWidth
                    }
                    this.slider.style.left = valid_right + 'px'
                    this.slider.style.width = valid_left + 'px'
                    this.btn_right_slider.style.left = valid_left + 'px'
                    this._syncText()
                }
                break
            case this.options.btn.right:
                // 滑块 X 坐标计算
                this.disX = clientX - this.btn_right_slider.offsetLeft
                //
                document.onmousemove = ({ clientX }) => {
                    // track 轨道宽度
                    const track_width = this.track.offsetWidth
                    // 滑块左边可滑动距离
                    const valid_left = this.slider.offsetLeft
                    // 滑块整体向右边移动 最大能移动的临界值
                    const valid_right = this.range_max ? (this.range_max >= track_width - valid_left ? track_width - valid_left : this.range_max) : track_width - valid_left
                    // 两点x坐标差值
                    const diff = clientX - this.disX
                    // 滑块右边按钮的 left 值 设置
                    this.btn_right_slider.style.left = (diff <= (this.range_min || 0) ? this.range_min || 0 : diff >= valid_right ? valid_right : diff) + 'px'
                    // 滑块的宽度 width 设置
                    this.slider.style.width = this.btn_right_slider.offsetLeft + 'px'
                    // 文案同步
                    this._syncText()
                }
                break
        }
        document.onmouseup = () => {
            document.onmousemove = null
            document.onmouseup = null
        }
        return false
    }
}

其余部分方法实现没有_initevent事件复杂,分别是以下方法:

  • 获取dom _$

  • 同步文案 _syncText

  • 手动控制滑块右边按钮向右拉伸 rightStretch

  • 手动控制滑块左边按钮向左拉伸 leftStretch

  • 手动控制滑块整体左右滑动 move

/**
 * @description: 获取dom
 * @param  { string } className
 */
_$(className) {
    return document.querySelector(`.${className}`)
}
/**
 * @description: 同步文案信息
 */
_syncText() {
    // 开始按钮距离 左边的距离
    const start = this.slider.offsetLeft
    // 结束按钮距离 左边的距离
    const end = this.slider.getBoundingClientRect().right - this.track.getBoundingClientRect().left
    // 容器的总宽度
    const total = this.track.offsetWidth
    // 刷新 左边按钮 tip 文案
    this.text_left.innerText = start
    // 刷新 右边按钮 tip 文案
    this.text_right.innerText = end
    // 刷新 内容按钮 tip 文案
    this.btn_middle_slider.innerText = end - start
}
/**
 * @description: 手动设置右边按钮拉伸到右边,
 * @param { Number } n:设置的值 范围 为: {0, rightMax:右边最大距离值}
 */
rightStretch(n) {
    // 边界处理
    const rightMax = this.track.offsetWidth - this.slider.offsetLeft
    this.slider.style.width = this.btn_right_slider.style.left = (n >= rightMax ? rightMax : n) + 'px'
    this._syncText()
    return this
}

/**
 * @description: 手动设置左边按钮拉伸到左边,
 * @param { Number } n:设置的值 范围 为: { leftMax:左边最大距离值,0}
 */
leftStretch(n) {
    // <左边最大距离值>
    const leftMax = this.slider.getBoundingClientRect().right - this.track.getBoundingClientRect().left
    // 如果左边按钮超出 最大距离左边边界值,则 就都设置成 <左边最大距离值>leftMax
    this.slider.style.width = this.btn_right_slider.style.left = (n >= leftMax ? leftMax : n) + 'px'
    // 边界处理
    this.slider.style.left = (n <= leftMax ? leftMax - n : 0) + 'px'
    this._syncText()
    return this
}

/**
 * @description: 手动设置滑块整体 向右 / 向右 移动
 * @param { Number } n :移动距离 {左边:0,右边:max }
 */
move(n) {
    const max = this.track.offsetWidth - this.slider.offsetWidth
    this.slider.style.left = (n >= max ? max : n) + 'px'
    this._syncText()
    return this
}

最后附上完整代码,和效果

class Slider {
    constructor(options) {
        this._init(options)
    }
    /**
        * @description: 初始化
        * @param {Object} options
        */
    _init(options) {
        this.options = options
        //
        this.init_width = options.slider_width || 0 // 初始滑块宽度

        options.range && options.range.min && (this.range_min = options.range.min) // 滑块宽度最小值
        options.range && options.range.max && (this.range_max = options.range.max) // 滑块宽度最大值
        //
        this.btn_left_slider = this._$(options.btn.left) // 滑块左边按钮类 - 用于向左边进行拉伸
        this.text_middle = this.btn_middle_slider = this._$(options.btn.middle) // 初始滑块宽度类  - 用于滑块整体左右移动  // 滑块中间按钮上面文案提示类
        this.btn_right_slider = this._$(options.btn.right) // 初始滑块宽度类  - 用于向右边边进行拉伸
        //
        this.track = this._$(options.track) // 滑块轨道类
        this.slider = this._$(options.slider) // 滑块轨道类
        //
        this.text_left = this._$(options.text.left) // 滑块左边按钮上面文案提示类
        this.text_right = this._$(options.text.right) // 滑块右边按钮上面文案提示类
        //
        this._initAttr() // 初始化属性值
        this._initEvent() // 初始化事件
    }
    /**
        * @description: 获取dom
        * @param  { string } className
        */
    _$(className) {
        return document.querySelector(`.${className}`)
    }
    /**
        * @description: 初始化文案
        */
    _initAttr() {
        this.slider.style.width = this.options.slider_width || 0 + 'px'
        this.text_middle.innerText = this.options.slider_width || 0
    }
   /**
 * @description: 初始化事件
 */
_initevent() {
    this.slider.onmousedown = ({ target: { className }, clientX }) => {
        // 滑块宽度 动态值
        const sliderWidth = this.slider.offsetWidth
        // 滑块 距离左边 动态值
        const sliderLeft = this.slider.offsetLeft

        switch (className) {
            case this.options.btn.middle:
                // 滑块 X 坐标计算
                this.disX = clientX - this.slider.offsetLeft
                // 滑块整体向右边移动 最大能移动的临界值
                const valid_right = this.track.offsetWidth - sliderWidth
                //
                document.onmousemove = ({ clientX }) => {
                    this.slider.style.left =
                        clientX - this.disX < 0
                            ? 0 // 如果是滑块整体向左移动 超出临界值 就是临界值
                            : clientX - this.disX > valid_right
                            ? valid_right + 'px' // 如果是滑块整体向右移动 超出临界值 就是临界值
                            : clientX - this.disX + 'px'
                    this._syncText()
                }
                break
            case this.options.btn.left:
                // 滑块 X 坐标计算
                this.disX = clientX
                // 滑块右边按钮 至 轨道 最左边变距离
                const rightWidth = this.slider.getBoundingClientRect().right - this.track.getBoundingClientRect().left
                //
                document.onmousemove = ({ clientX }) => {
                    // 两点间的 x 差值 // 向左 一直移动,临界值是 0 超出边界 就会变成 负数  ,如果一直向右移动,超出右边临界值,右边临界值在移动到右边按钮时产生
                    const diff = this.disX - clientX
                    //  如果 一直向左移动 对应的 临界值
                    let valid_left = sliderWidth + diff
                    // 如果 一直向右移动 对应的 临界值
                    let valid_right = sliderLeft - diff

                    if (this.range_min && valid_left <= this.range_min) {
                        // 如果有设置最小范围值 并且 此时 向左拉伸超出最小值,那么此时的 值 就是最小值  range_min
                        valid_left = this.range_min
                        valid_right = rightWidth - valid_left
                    } else if (this.range_max && valid_left >= this.range_max) {
                        // 如果右设置最大范围值 并且此时移动 valid_left 超出 设置的最大范围值
                        valid_left = this.range_max >= rightWidth ? rightWidth : this.range_max // 如果设置的最大范围值 超出有效值 rightWidth,那么就设置 valid_left 为 有效值 rightWidth ,否则就是设置为最大范围值
                        valid_right = rightWidth - valid_left
                    } else if (valid_left >= rightWidth) {
                        // 如果一直向左边移动 , 超出 有效值 rightWidth,(抵死在左边情形)
                        valid_left = rightWidth
                        valid_right = 0
                    } else if (valid_right >= rightWidth) {
                        // 如果一直向右边移动 , 超出 有效值 rightWidth
                        valid_left = 0
                        valid_right = rightWidth
                    }
                    this.slider.style.left = valid_right + 'px'
                    this.slider.style.width = valid_left + 'px'
                    this.btn_right_slider.style.left = valid_left + 'px'
                    this._syncText()
                }
                break
            case this.options.btn.right:
                // 滑块 X 坐标计算
                this.disX = clientX - this.btn_right_slider.offsetLeft
                //
                document.onmousemove = ({ clientX }) => {
                    // track 轨道宽度
                    const track_width = this.track.offsetWidth
                    // 滑块左边可滑动距离
                    const valid_left = this.slider.offsetLeft
                    // 滑块整体向右边移动 最大能移动的临界值
                    const valid_right = this.range_max ? (this.range_max >= track_width - valid_left ? track_width - valid_left : this.range_max) : track_width - valid_left
                    // 两点x坐标差值
                    const diff = clientX - this.disX
                    // 滑块右边按钮的 left 值 设置
                    this.btn_right_slider.style.left = (diff <= (this.range_min || 0) ? this.range_min || 0 : diff >= valid_right ? valid_right : diff) + 'px'
                    // 滑块的宽度 width 设置
                    this.slider.style.width = this.btn_right_slider.offsetLeft + 'px'
                    // 文案同步
                    this._syncText()
                }
                break
        }
        document.onmouseup = () => {
            document.onmousemove = null
            document.onmouseup = null
        }
        return false
    }
}
    /**
        * @description: 同步文案信息
        */
    _syncText() {
        // 开始按钮距离 左边的距离
        const start = this.slider.offsetLeft
        // 结束按钮距离 左边的距离
        const end = this.slider.getBoundingClientRect().right - this.track.getBoundingClientRect().left
        // 容器的总宽度
        const total = this.track.offsetWidth
        // 刷新 左边按钮 tip 文案
        this.text_left.innerText = start
        // 刷新 右边按钮 tip 文案
        this.text_right.innerText = end
        // 刷新 内容按钮 tip 文案
        this.btn_middle_slider.innerText = end - start
    }
    /**
        * @description: 手动设置右边按钮拉伸到右边,
        * @param { Number } n:设置的值 范围 为: {0, rightMax:右边最大距离值}
        */
    rightStretch(n) {
        // 边界处理
        const rightMax = this.track.offsetWidth - this.slider.offsetLeft
        this.slider.style.width = this.btn_right_slider.style.left = (n >= rightMax ? rightMax : n) + 'px'
        this._syncText()
        return this
    }

    /**
        * @description: 手动设置左边按钮拉伸到左边,
        * @param { Number } n:设置的值 范围 为: <leftMax> <左边最大距离值,0>
        */
    leftStretch(n) {
        // <左边最大距离值>
        const leftMax = this.slider.getBoundingClientRect().right - this.track.getBoundingClientRect().left
        // 如果左边按钮超出 最大距离左边边界值,则 就都设置成 <左边最大距离值>leftMax
        this.slider.style.width = this.btn_right_slider.style.left = (n >= leftMax ? leftMax : n) + 'px'
        // 边界处理
        this.slider.style.left = (n <= leftMax ? leftMax - n : 0) + 'px'
        this._syncText()
        return this
    }

    /**
        * @description: 手动设置滑块整体 向右 / 向右 移动
        * @param { Number } n :移动距离 <左边:0> <右边:max >
        */
    move(n) {
        const max = this.track.offsetWidth - this.slider.offsetWidth
        this.slider.style.left = (n >= max ? max : n) + 'px'
        this._syncText()
        return this
    }
}

怎么用JS实现一个可左右拉伸的滑块效果

“怎么用JS实现一个可左右拉伸的滑块效果”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!