如何使用CSS创建自定义范围滑块
在本文中,我将展示如何使用现代CSS技术,仅使用原生的HTML
<input>
元素创建一个引人注目的自定义范围滑块。
范围滑块(<input type="range">
)允许用户在给定的范围内选择一个值,为<input type="number">
等其他输入类型提供了一种替代方式。
默认的范围滑块样式看起来并不出色。下面的图片展示了在Chrome、Firefox和Safari中,默认情况下我们将要样式化的范围滑块的显示效果。
但是<input>
元素很难进行样式设置。大多数在线的解决方案都依赖于JavaScript和一些混乱的代码来进行样式设置。更糟糕的是,其中一些技术还会破坏元素的可访问性。
因此,让我们看看如何更好地进行样式设置,仅使用CSS而不损害可访问性。下面的CodePen演示展示了我们将要构建的内容。
很酷,对吧?在文章的结尾,您还会找到这些样式的变种!
范围输入元素的结构
让我们首先解析范围输入元素的结构。它是一个原生元素,每个浏览器都有自己的实现方式。主要有两种不同的实现方式。
一种适用于Webkit和Blink浏览器,例如Chrome、Edge、Safari和Opera:
<input type="range" min="0" max="100" step="1" value="20">
<div>
<div pseudo="-webkit-slider-runnable-track" id="track">
<div id="thumb">
</div>
</div>
</div>
</input>
而另一种则适用于Firefox:
<input type="range" min="0" max="100" step="1" value="20">
<div></div>
<div></div>
<div></div>
</input>
虽然还有第三种适用于IE的实现方式,但幸运的是,这个浏览器几乎已经过时了!
浏览器之间的这种不一致性使得任务变得困难,因为我们需要为每个实现方式提供不同的样式。我不会深入探讨这个问题,因为这篇文章将永远不会结束,但我强烈推荐阅读Ana Tudor的这篇文章,以进行更深入的探索。
唯一需要记住的是,无论实现方式如何,我们始终有一个共同的组件,那就是“滑块按钮(thumb)”。
我只会为这个元素设置样式,这样我的自定义范围滑块就容易定制了。让我们直接跳进代码,看看这个神奇的过程。
自定义输入元素
第一步是通过使用appearance: none
和其他一些常用属性来重置并禁用所有浏览器的默认样式:
input {
appearance :none;
background: none;
cursor: pointer;
}
在更复杂的情况下,如果我们的元素被应用了其他默认样式,可能需要添加更多的代码。我们只需要确保元素是没有任何视觉样式的即可。
我们还可以定义一些CSS变量,这样我们就可以轻松地为范围滑块创建不同的变体:
input {
--c: orange; /* active color */
--g: 8px; /* the gap */
--l: 5px; /* line thickness*/
--s: 30px; /* thumb size*/
width: 400px; /* input width */
height: var(--s);
appearance :none;
background: none;
cursor: pointer;
}
在这一步中,只有滑块按钮是可见的,并具有其默认样式,就像下面的CodePen演示所展示的那样。
让我们来设置滑块按钮元素的样式。我们将从基本设置开始:
<thumb selector> {
height: var(--s);
aspect-ratio: 1;
border-radius: 50%;
box-shadow: 0 0 0 var(--l) inset var(--c);
appearance: none;
}
代码应该是不言自明的。到目前为止还没有什么花哨的东西,我们将得到如下所示的结果。
请注意在代码中使用了两个不同的选择器,正如我们在第一部分中所解释的那样:
/* Chrome, Edge, Safari, Opera */
input[type="range" i]::-webkit-slider-thumb { }
/* Firefox */
input[type="range"]::-moz-range-thumb { }
但是选择器应该如何使用呢?
我简单地使用浏览器的开发者工具检查了输入元素的代码,以查看每个浏览器用于样式化滑块按钮的选择器。我之前分享的文章将向您展示如何操作开发者工具以获取这些信息。
使用border-image添加一些魔法
现在,我们将使用一种神奇的CSS技巧来完成我们的滑块。它涉及使用border-image属性:
border-image: linear-gradient(90deg,var(--_c) 50%,#ababab 0) 1/0 100vw/0 calc(100vw + var(--g));
我知道这看起来有些吓人,但我们来分解一下这行代码,你会发现它并不那么难理解。上面的代码是以下代码的缩写形式:
border-image-source: linear-gradient(90deg,var(--c) 50%,#ababab 0);
border-image-slice: 1;
border-image-width: 0 100vw;
border-image-outset: 0 calc(100vw + var(--g));
从MDN页面上,我们可以了解到:
border-image CSS属性可以在给定元素周围绘制一张图像,取代元素的常规边框。
我们的图像将是一个渐变,由两种颜色组成 - 主要颜色(由--c定义)和灰色。我们需要边框图像水平覆盖整个输入框的空间,所以我们在左右宽度上使用了一个很大的值(100vw),同时将上下设置为(0)。
但是,border-image-width属性限制在元素的大小范围内。为了解决这个问题,我们还需要使用一个很大的值来增加border-image-outset属性的空间,以扩大边框图像的可用空间。再次参考MDN:
border-image-outset CSS属性设置了元素的边框图像与其边框框盒的距离。
使用border-image-outset渲染在元素的边框框盒之外的边框图像的部分不会触发溢出滚动条,并且不会捕捉鼠标事件。
当您第一次看到滑块时,它看起来像是在增加左侧的主要颜色,但实际上我们正在滑动一个固定的渐变,它超出了我们的元素。
以下演示提供了底层发生的概述。
拖动滑块并将其滑动,以查看事物如何移动。我使用了一个小值来设置宽度和outset,这样我们可以更容易地理解这个技巧。
此外,请注意outset需要大于宽度才能产生间隙。因此,它被定义为宽度加上间隙值。
通过将overflow: hidden添加到输入元素并使用一个很大的值,这种视觉效果是完美的,如下所示。
为u什么border-image-slice的值为1
这个属性有些棘手,但在使用border-image时是必需的。在我们的情况下,这个值并不是非常相关,一个较小的正值就足够了。如果您想了解更多信息,我有一个详细的Stack Overflow答案可以供您参考。
最后一步是缩小滑块条的大小,以匹配我们通过变量--l定义的大小。为此,我们将使用clip-path属性:
clip-path:
polygon(
0 calc(50% + var(--l)/2),
-100vw calc(50% + var(--l)/2),
-100vw calc(50% - var(--l)/2),
0 calc(50% - var(--l)/2),
0 0,100% 0,
100% calc(50% - var(--l)/2),
100vw calc(50% - var(--l)/2),
100vw calc(50% + var(--l)/2),
100% calc(50% + var(--l)/2),
100% 100%,0 100%);
下面的图像提供了一个概述,了解多边形形状的不同要点。
就是这样!我们使用几行代码创建了一个自定义范围滑块,您可以通过调整一些变量来轻松控制它。
如果我们与滑块进行交互时添加一些微妙的动画怎么样?它不需要很多代码,但可以提升滑块的用户体验。
首先,我们将在点击滑块按钮时将其从仅具有边框的圆形转变为完整的圆形。为此,我们增加了盒子阴影的扩展值。请记住,我们使用盒子阴影来定义滑块按钮的边框:
box-shadow: 0 0 0 var(--l) inset var(--c);
我们使用:active选择器和:focus-visible将var(--l)更新为var(--s)。后者与键盘导航相关,并允许我们无论使用鼠标还是键盘都具有相同的效果。
代码如下所示:
input[type="range" i]::-webkit-slider-thumb {
box-shadow: 0 0 0 var(--l) inset var(--c);
transition: .3s;
}
input[type="range" i]::-moz-range-thumb {
box-shadow: 0 0 0 var(--l) inset var(--c);
transition: .3s;
}
input:active::-webkit-slider-thumb,
input:focus-visible::-webkit-slider-thumb {
box-shadow: 0 0 0 var(--s) inset var(--c);
}
input:active::-moz-range-thumb,
input:focus-visible::-moz-range-thumb {
box-shadow: 0 0 0 var(--s) inset var(--c);
}
对于一个盒子阴影过渡来说,这个代码有点冗长,对吧?我们可以使用一个CSS变量来进行优化:
input[type="range" i]::-webkit-slider-thumb {
box-shadow: 0 0 0 var(--_b,var(--l)) inset var(--c);
transition: .3s;
}
input[type="range" i]::-moz-range-thumb {
box-shadow: 0 0 0 var(--_b,var(--l)) inset var(--c);
transition: .3s;
}
input:active,
input:focus-visible {
--_b: var(--s);
}
我使用一个变量来表示扩展值,并在:active和:focus-visible上简单地更新该变量。
我还将为颜色添加一点动画效果。我将在:hover时使其稍暗一些。为此,我不会更新颜色,而是使用新的color-mix()函数将其与黑色混合。这个技巧允许我们使用由--c定义的主要颜色,而不是为每个滑块手动定义一个新的深色。
--_c: color-mix(in srgb, var(--c), #000 var(--p,0%));
我定义了一个新的变量来替换代码中的--c。然后,通过使用变量--p调整黑色(#000)的百分比,我控制着颜色的“深浅”:
input:focus-visible,
input:hover{
--p: 25%;
}
这个功能还没有在每个浏览器中得到支持,所以强烈建议使用回退方案:
@supports not (color: color-mix(in srgb,red,red)) {
input {
--_c: var(--c); /* if no support we keep the color as defined */
}
}
现在我们的范围滑块非常完美!
结论
我们已经完成了,而且没有涉及任何复杂的与浏览器相关的实现!我们确定了滑块元素的选择器,并使用了一些CSS技巧来为整个范围滑块添加样式。让我们不要忘记,我们只是使用了<input>元素来完成这一切,因此我们不必担心任何可访问性问题,因为我们保留了原生功能。该滑块支持键盘导航,没有问题。
下面是使用相同技术的更多滑块示例。我将让您对它们的代码进行解析,作为一个练习。
转载自:https://juejin.cn/post/7244818201976062010