打造专属插件之EasySliderBar

这是一款简单易用的 Slider Bar,原生 HTML + CSS + JS 编写, 亲测兼容 Chrome, Firefox, IE 9+ , 欢迎一起探讨、交流。

image

引用

<link rel="stylesheet" type="text/css" href="./index.css">
<div id="slider"></div>
<script type="text/javascript" src="./index.js"></script>
<script type="text/javascript">
    var sb = new EasySliderBar({
        el: 'slider'
    })
</script>

参数

Name Type Constraint Comment
el String required 容器节点
max Number default 100 上限
min Number default 0 下限
scale Integer default 0 小数位数
trackHeight Integer default 10 轨道高度
type enum['circle', 'square', 'diamond'] default 'circle' slider bar类型
barNumber Integer default 1 slider bar数量
positions Array default [min...] slider bar初始位置
  • 当参数 el 非法,slider bar 取消创建
  • 当其他参数非法,取默认值

方法

Name Parameter Return Comment
setBarPosition pos, index void 设置 index 指定的 slider bar 的位置为 pos,如果index === undefined,则设置所有 slider bar 的位置为 pos
getBarPosition index Number Array 获取 index 指定的 slide rbar 的位置,如果index === undefined,则返回所有 slider bar 的位置数组

源码

index.js

/**
 * Created by lonelydawn on 2017-11-20.
 * EasySliderBar - Easy slider bar
 */

// Polyfill
Array.prototype.forEach = Array.prototype.forEach || function (cb) {
    for (var i = 0; i < this.length; i++) {
        cb(this[i], i)
    }
}
Array.prototype.filter = Array.prototype.filter || function (cb) {
    var arr = []
    for (var i = 0; i < this.length; i++) {
        if (cb[this[i], i]) {
            arr.push(this[i])
        }
    }
    return arr
}

var EasySliderBar = function (options) {
    // Add class, and return the changed className
    var getPushedClass = function (className, value) {
        return typeof className === 'string' && typeof value === 'string'
            ? className.split(' ').concat(value).join(' ') : className 
    }
    // Remove class, and return the changed className
    var getRemovedClass = function (className, value) {
        if (typeof className === 'string' && typeof value === 'string') {
            return className.split(' ').filter(function (item) {
                return item !== '' && item !== value
            }).join(' ')
        }
        return className
    }
    // Get parallel pixel to scale
    var getInterval = function () {
        return track.clientWidth / (max - min)
    }
    // Limit bars' moving range
    var getValidLeft = function (value) {
        if (value < 0) {
            return 0
        } else if (value > track.clientWidth) {
            return track.clientWidth
        } else {
            return value
        }
    }
    // Add trigger on slidebar
    var setBarMove = function (bar, index) {
        bar.onmousedown = function (e) {
            var start = (e || window.event).clientX
            var left = Math.floor((bar.position - min) * getInterval())
            bar.label.className = getRemovedClass(bar.label.className, 'hidden')
            // Execute callback
            onBarMoveBegin({
                index: index,
                position: Number(bar.position)
            })
            document.onmousemove = function (e) {
                var movingLeft = getValidLeft(left + (e || window.event).clientX - start)
                var movingPosition = (min + movingLeft / getInterval()).toFixed(scale)
                bar.label.innerHTML = movingPosition
                bar.style.left = movingLeft - barOffset + 'px'
                onBarMoving({
                    index: index,
                    position: Number(movingPosition)
                })
            }
            document.onmouseup = function (e) {
                bar.position = (min + getValidLeft(left + (e || window.event).clientX - start) / getInterval()).toFixed(scale)
                bar.label.className = getPushedClass(bar.label.className, 'hidden')
                // Execute callback
                onBarMoveEnd({
                    index: index,
                    position: Number(bar.position)
                })
                document.onmousemove = null
                document.onmouseup = null
                bar.releaseCapture && bar.releaseCapture()
            }
            bar.setCapture && bar.setCapture()
            return false
        }
    }
    var el = options.el
    var scale = options.scale || 0
    var min = !isNaN(options.min) ? options.min : 0
    var max = !isNaN(options.max) ? (options.max > min ? options.max : min + 100) : 100
    var barNumber = options.barNumber || 1
    var positions = Object.prototype.toString.call(options.positions) === '[object Array]' ? options.positions : []
    for (var i = 0; i < barNumber; i++) {
        positions[i] = positions[i] && positions[i] >= min && positions[i] <= max ? positions[i] : min
    }
    var type = options.type || 'circle'
    // Set bars' width and height by type
    var trackHeight = !isNaN(options.trackHeight) ? options.trackHeight : 10
    var barWidth = {
        square: trackHeight * 2,
        circle: trackHeight * 3,
        diamond: 20
    }[type]
    var barHeight = {
        square: trackHeight * 3,
        circle: trackHeight * 3,
        diamond: trackHeight
    }[type]
    var barOffset = barWidth / 2
    var labelWidth = (30 + 7 * scale) || 30
    var labelOffset = labelWidth / 2 - barOffset
    // Get customized callback
    var onBarMoveBegin = options.onBarMoveBegin || new Function()
    var onBarMoving = options.onBarMoving || new Function()
    var onBarMoveEnd = options.onBarMoveEnd || new Function()

    var wrapper = document.getElementById(el)
    if (!wrapper) {
        return
    }
    // Init wrapper height
    wrapper.style.padding = barOffset + 5 + 'px'
    wrapper.style.height = trackHeight + 'px'
    // Empty container
    var childs = wrapper.childNodes
    for (var i = childs.length - 1; i > -1; i--) {
        wrapper.removeChild(childs[i])
    }
    // Create track
    var track = document.createElement('div')
    track.className = 'slider-track'
    wrapper.appendChild(track)
    // Create slider bar
    var barStack = []
    for (var counter = 0; counter < barNumber; counter++) {
        var bar = document.createElement('div')
        bar.className = 'slider-bar ' + type
        bar.position = positions[counter]
        bar.style.width = barWidth + 'px'
        bar.style.height = barHeight + 'px'
        bar.style.top = (trackHeight - barHeight) / 2 + 'px'
        bar.style.left = Math.floor((positions[counter] - min) * getInterval()) - barOffset + 'px'
        setBarMove(bar, counter)
        // Create slider bar label
        var label = document.createElement('div')
        label.className = 'slider-label hidden'
        label.style.width = labelWidth + 'px'
        label.style.left = -labelOffset + 'px'
        label.innerHTML = positions[counter].toFixed(scale)
        bar.label = label
        // Create DOM structure
        bar.appendChild(label)
        track.appendChild(bar)
        barStack.push(bar)
    }
    return {
        // Set some bars' position. (If index is not defined, set all.)
        setBarPosition: function (pos, index) {
            var set = function (bar) {
                if (pos >= min && pos <= max) {
                    bar.position = pos
                    bar.style.left = Math.floor((pos - min) * getInterval()) - barOffset + 'px'
                    bar.label.innerHTML = Number(pos).toFixed(scale)
                }
            }
            index ? set(barStack[index]) : barStack.forEach(function (item) {
                set(item)
            })
        },
        // Return some bars' position. (If index is not defined, return all.)
        getBarPosition: function (index) {
            return index >= 0 ? barStack[index].position : barStack.map(function (item) {
                return item.position
            })
        }
    }
}

index.css

.slider-track {
    width: 100%;
    height: 100%;
    position: relative;
    border-radius: 100px;
    font-family: Roboto;
    background-color: #e4e7ed;
}
.slider-label {
    padding: 3px 0;
    position: absolute;
    top: -40px;
    color: #fff;
    font-size: 12px;
    text-align: center;
    border-radius: 5px;
    background-color: #303133;
    transition: all 300ms linear;
}
.slider-label:after {
    width: 0px;
    height: 0px;
    border: 6px transparent solid;
    border-top-color: #303133;
    position:absolute;
    top: 100%;
    left: calc(50% - 6px);
    content:""
}
.slider-label.hidden {
    display: none;
}
.slider-bar {
    position: absolute;
    box-sizing: border-box;
}
.slider-bar:hover .slider-label {
    display: block;
}

.slider-bar.circle,
.slider-bar.square {
    border: 2px solid #409EFF;
    background-color: #FFF;
}
.slider-bar.circle {
    border-radius: 50%;
}
.slider-bar.square {
    border-radius: 3px;
}
.slider-bar.diamond {
    background-color: #409EFF;
}
.slider-bar.diamond:before {
    width: 0px;
    height: 0px;
    border: 10px transparent solid;
    border-bottom-color: #409EFF;
    position:absolute;
    bottom: 100%;
    left: 0;
    content:""
}
.slider-bar.diamond:after{
    width: 0px;
    height: 0px;
    border: 10px transparent solid;
    border-top-color: #409EFF;
    position:absolute;
    top: 100%;
    left: 0;
    content:""
}

源码奉上,任君 Hack 。

下载地址

GitHub

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,727评论 1 92
  • 前端开发面试题 <a name='preface'>前言</a> 只看问题点这里 看全部问题和答案点这里 本文由我...
    自you是敏感词阅读 752评论 0 3
  • You Don't Need jQuery 前端发展很快,现代浏览器原生 API 已经足够好用。我们并不需要为了操...
    残丶梦阅读 1,422评论 0 2
  • 说在最前:“每天一本书,还要写500到600字的10条阅读清单?坚持15天。看起来不太容易…但为什么不试试呢?20...
    嘟鼓鼓阅读 264评论 12 12
  • 晚上,27接了最后一杯水,对于又没了水的事实,大家都很绝望。 虾:其实,这次算撑得久的了。都快一周了。 我:嗯,对...
    浮游酱阅读 221评论 1 3