CSS 实现带tooltip的slider

开发 前端
这次我们来用 CSS 实现一个全功能的滑动输入器,也就是各大组件库都有的slider。特别是在拖动时,tooltip还能跟随拖动的方向和速度呈现不同的倾斜角度,这些是如何通过CSS实现的呢?一起来看看吧!

现代 CSS 强大的令人难以置信。

这次我们来用 CSS 实现一个全功能的滑动输入器,也就是各大组件库都有的slider,效果如下:

还可以改变一下样式,像这样。

特别是在拖动时,tooltip还能跟随拖动的方向和速度呈现不同的倾斜角度,这些是如何通过CSS实现的呢?一起来看看吧~

一、自定义input range

首先来看滑动输入器的最原始形态。

<input type="range">

效果如下:

要自定义样式,一般要修改这几个伪元素。

::-webkit-slider-container{
  /*容器*/
}
::-webkit-slider-runnable-track{
  /*轨道*/
}
::-webkit-slider-thumb{
  /*手柄*/
}

这里可以很轻松的改变轨道的宽高,拖拽手柄的大小等等。

[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    margin: 0;
    outline: 0;
    background-color: transparent;
    width: 400px;
    overflow: hidden;
    height: 20px;
}
[type="range"]::-webkit-slider-runnable-track {
    height: 4px;
    background: #eee;
    border-radius: 4px;
}
[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background-color: #9747FF;
    transform: translateY(-50%);
    margin-top: 2px;
}

效果如下:

相信大家很容易做到这一步,因为只需要自定义这几个伪元素就行了。

这里还有一个难点,就是左边滑过的区域,如何也自定义颜色呢?毕竟没有专门的选择器(Firefox有,这里主要讨论Chrome),接下来请继续看。

二、自定义滑过区域的颜色

在之前,曾经通过border-image实现过类似的效果,主要原理是border-image可以在绘制元素之外,在拖拽手柄左侧绘制一个足够长的条条就行了。

不过这种实现有一个局限,由于是通过超出隐藏的方式裁剪掉多出的部分,使得滑动条边缘是“一刀切”的,无法实现圆角

回到这里,可以想想,要实现自定义左边滑过区域的样式,本质是需要知道当前的滑动进度,假设进度是--progress,那么轨道滑过区域的背景色可以这样来表示。

[type="range"]::-webkit-slider-runnable-track {
    height: 4px;
    background: linear-gradient(#9747FF 0 0) 0 0/calc(var(--progress) * 1%) 100% no-repeat #eee;
    border-radius: 4px;
}

那么,如何实时获取这个进度呢?

在以往,可以借助 JS实时更新这样一个自定义变量,这也是目前最好的实现方式。

而现在,有了更好的方式来彻底实现这样一个功能,那就是滚动驱动动画。

有些同学可能无法理解,这里又没有滚动,怎么会和这个特性有关呢?别急,滚动驱动有两种类型,一个是 scroll()、还有一个是view(),我们这里要用到的就是view(),其实也就是利用这一点来监听元素在视区的位置。

具体怎么做呢?

首先,通过@property定义一个CSS变量,整数类型。

@property --progress {
    syntax: "<integer>";
    initial-value: 0;
    inherits: true;
}

然后定一个动画,从0到100就行了,表示进度。

@keyframes slider {
    to {
        --progress: 100;
    }
}

由于是需要监听拖拽手柄,也就是::-webkit-slider-thumb 的位置,所以要给这个伪元素添加view-timeline,但是我们需要通过::-webkit-slider-thumb改变父级轨道::-webkit-slider-runnable-track的样式,所以需要用到time-scope(可以跨层级关联),具体实现如下:

[type="range"]{
    timeline-scope: --slider;
    animation: slider linear 3s reverse;
    animation-timeline: --slider;    
}
[type="range"]::-webkit-slider-thumb {
    /**/
    view-timeline: --slider inline;
}

这样一来,拖拽手柄的位置就通过动画实时映射到了input 上,效果如下:

这样就是实现了自定义滑动区域的样式,是不是非常神奇?

三、实时显示滑动进度

由于前面实现了--progress的实时变化,现在展示出来就比较容易了,需要用CSS计数器。

为了方便表示,我们可以单独用一个标签来展示进度,结构如下:

<label class="slider">
  <input type="range">
  <output class="tooltip"></output>
</label>

然后将--progress通过伪元素呈现出来。

.tooltip::before{
  content: counter(num);
  counter-reset: num var(--progress);
}

效果如下:

数字已经可以正常显示了,但是有个问题,起始点不对,不是0和100,我们把拖拽手柄透明度降低,可以看到进度其实是这样的。

这是由于默认的animation-range是cover,除了这种方式,还有其他几种方式。

很明显,我们这里应该需要contain,因为滑块是始终在轨道内的。

.slider{
    position: relative;
    timeline-scope: --slider;
    animation: slider linear 3s reverse;
    animation-timeline: --slider;
    animation-range: contain; /*设置animation-range*/
}

效果如下:

这样进度就正常了。

四、tooltip自动跟随滑块

首先美化一下,小三角可以用另一个伪元素实现。

.tooltip{
  position: absolute;
  margin: 0 0 20px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
  font-size: 14px;
  border-radius: 4px;
  padding: 10px;
  color: #f0f0f0;
  background-color: #333;
  transform-origin: center bottom;
  filter: drop-shadow(4px 4px 4px rgba(50, 50, 50, 0.3));
}
.tooltip::after{
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: black transparent transparent transparent;
}

效果如下:

那么,如何让这个tooltip自动跟随滑块呢?

一种方式是直接通过--progress来计算left值。

.tooltip{
  position: absolute;
  left: calc(var(--progress) * 1%);
}

不过这种计算是有偏差的,这是因为定位是相对于轨道位置的,而不是滑块中心。

为了修复这个问题,我们可以给input一个负的margin。

[type="range"] {
    /*  */
    margin: 0 -10px;
}

这样就可以了。

不过这样还有有尺寸方面的问题的,如下:

除了这种方式,还可以用锚点定位的方式实现。

实现非常简单,只需要给滑块一个anchor-name。

[type="range"]::-webkit-slider-thumb {
    /**/
    anchor-name: --thumb;
}

然后给tooltip设置锚点定位。

.tooltip{
  position: absolute;
  position-anchor: --thumb;
  inset-area: top;
}

这样就完美实现了,也不用担心定位偏差的问题。

五、自动跟随拖拽方向

还有最后一个效果,可以自动跟随拖拽方向,这是如何实现的呢?

重新定义一个CSS变量。

@property --progress2 {
    syntax: "<integer>";
    initial-value: 0;
    inherits: true;
}

然后给这个变量设置为--progress,但是要给一个过渡时间。

.tooltip{
  --progress2: var(--progress);
  transition: --progress2 .1s ease-out;
}

由于有过渡时间,所以这两个变量就会有一个差值,类似于这样。

根据这个差值,我们不就可以知道拖动方向和速度了吗,然后给一个旋转角度。

.tooltip{
  transform-origin: center bottom;
  rotate: calc((var(--progress2) - var(--progress))*2deg);
}

这样就实现了文章开头所示效果:

完整代码可以查看以下链接

  • CSS slider (juejin.cn)[1]
  • CSS slider (codepen.io)[2]

还可以改变一下样式。

[type="range"]::-webkit-slider-runnable-track {
    height: 20px;
    background: linear-gradient(#9747FF 0 0) 0 0/calc(var(--progress) * 1% + 20px * (50 - var(--progress))/100) 100% no-repeat #eee;
    border-radius: 24px;
}
[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background-color: #fff;
    border: 3px solid #9747FF;
    view-timeline: --slider inline;
    anchor-name: --thumb;
}

这样可以得到下面的效果:

完整代码可以查看以下链接

  • CSS custom slider (juejin.cn)[3]
  • CSS custom slider (codepen.io)[4]

六、兼容性和总结

这个实现主要依赖于滚动驱动动画,所以兼容性要求Chrome 115+,如果需要tooltip的锚点定位,则需要Chrome 125+,下面总结一下。

  • Chrome 没有专门的选择器来自定义滑过区域的样式。
  • 通过border-image可以自定义滑过区域的样式,但是不能实现圆角。
  • 滚动驱动动画的视图滚动可以监听滑块的位置。
  • 通过CSS变量和滚动驱动动画可以将实时进度传递给轨道,从而通过线性渐变绘制滑过区域颜色。
  • 借助CSS计数器和伪元素可以将CSS变量显示在页面。

当然,不兼容的浏览器也可以采用回退措施,比如不支持滚动驱动动画,我们可以用JS来动态赋值--progress变量,不支持锚定定位,我们可以用绝对定位,配合left也能实现,虽然复杂一点,但也是现阶段比较好的处理方式了,剩下的就交给时间吧。

[1]CSS slider (juejin.cn): https://code.juejin.cn/pen/7409224431194603574。

[2]CSS slider (codepen.io): https://codepen.io/xboxyan/pen/eYwxxdQ。

[3]CSS custom slider (juejin.cn): https://code.juejin.cn/pen/7415566353493000219。

[4]CSS custom slider (codepen.io): https://codepen.io/xboxyan/pen/OJeddWm。

责任编辑:姜华 来源: 前端侦探
相关推荐

2012-06-14 15:49:59

Slider

2023-07-03 08:51:41

选择器detailssummary

2012-02-14 15:42:32

CSS3

2023-04-04 08:14:17

CSSloading 动画

2009-07-02 14:42:55

ExtJS Grid

2024-08-15 08:56:17

2021-07-16 05:59:27

CSS 技巧带圆角的三角形

2017-04-27 14:05:59

CSS动画前端

2022-05-31 15:27:11

CSS动画

2021-01-19 12:16:10

CSS前端UI

2022-10-24 17:57:06

CSS容器查询

2009-12-24 16:20:43

WPF Tooltip

2015-04-09 14:08:17

jQueryjQuery Tool

2011-07-20 10:31:49

Cocoa Slider 颜色

2011-04-11 14:14:29

checkboxlistviewAndroid

2022-06-21 11:23:15

API鸿蒙JS开发

2010-09-01 15:28:11

CSSexpression

2010-09-13 14:09:35

CSS文字

2011-05-25 16:30:05

CSSHTML

2016-10-20 19:21:46

transformcss3css
点赞
收藏

51CTO技术栈公众号