纯 CSS 打造炫酷自定义范围滑块:无需 JavaScript,也能玩转样式!这篇文章将向你展示如何使用现代 CSS 技
这篇文章将向你展示如何使用现代 CSS 技术,仅用原生 HTML <input>
元素创建一个吸睛的自定义范围滑块。
范围输入元素的结构
首先,我们来分析一下范围输入元素的结构。它是一个原生元素,每个浏览器都有自己的实现方式。主要有两种不同的实现:
一种用于 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 的实现,但好在 IE 浏览器现在已经基本消失了!
浏览器之间的这种不一致性使得这项任务变得困难,因为我们需要为每种实现提供不同的样式。我不会深入探讨这个问题,否则这篇文章就写不完了,但我强烈建议你阅读 Ana Tudor 的 这篇文章,以进行更深入的探索。
你需要记住的是,无论实现方式如何,我们总是有一个共同的组件:“滑块”。
我只打算为这个元素设置样式,这将使我的自定义范围滑块易于定制。让我们直接进入代码,看看其中的奥妙。
自定义输入元素
第一步是使用 appearance: none
和其他一些常见属性来重置和禁用所有浏览器的默认样式:
input {
appearance: none;
background: none;
cursor: pointer;
}
在更复杂的情况下,我们可能需要添加更多代码,以防其他默认样式应用于我们的元素。只需确保我们有一个“裸”元素,没有任何视觉样式。
我们还定义了一些 CSS 变量,以便我们可以轻松地为范围滑块创建不同的变体:
input {
--c: orange; /* 活动颜色 */
--g: 8px; /* 间隙 */
--l: 5px; /* 线宽 */
--s: 30px; /* 滑块大小 */
width: 400px; /* 输入宽度 */
height: var(--s);
appearance: none;
background: none;
cursor: pointer;
}
在这一步,只有滑块及其默认样式是可见的。
为滑块元素添加样式
让我们为滑块元素添加样式。我们将从基本设置开始:
<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 页面 我们可以读到:
CSS 属性
border-image
在给定元素周围绘制图像。它替换了元素的常规边框。
我们的图像将是一个具有两种颜色的渐变——主要颜色(由 --c
定义)和灰色。我们需要边框图像水平覆盖输入的整个空间,因此我们为左右宽度使用一个较大的值 (100vw
),同时将上下保持为 (0
)。
但是 border-image-width
受限于元素大小。为了克服这个问题,我们还需要为 border-image-outset
使用一个较大的值,以增加边框图像的可用空间。再次来自 MDN:
CSS 属性
border-image-outset
设置元素的边框图像从其边框框中设置出的距离。使用
border-image-outset
在元素边框框外部渲染的边框图像部分不会触发溢出滚动条,也不会捕获鼠标事件。
当你第一次看到滑块时,它看起来像是我们在左边增加主色,但实际上我们是在滑动一个超出我们元素的固定渐变。
通过向输入元素添加 overflow: hidden
并使用一个较大的值,幻觉是完美的。
如果你想深入了解 border-image
并了解更多关于“宽度”和“外延”的内容,请查看 我的文章,我在其中剖析了 border-image 属性,以便了解它的所有秘密并理解我正在使用的值。
就这样!我们用几行代码创建了一个自定义范围滑块,你可以通过调整几个变量轻松控制它。
<input type="range" min="0" max="100" step="1" value="20">
<input type="range" min="0" max="100" step="1" style="--c: lightblue;--l: 6px;--g:12px;">
<input type="range" min="0" max="100" step="1" value="70" style="--c: red;--l: 4px;">
input {
--c: orange; /* active color */
--g: 8px; /* the gap */
--l: 5px; /* line thickness*/
--s: 30px; /* thumb size*/
width: 400px;
height: var(--s); /* needed for Firefox*/
-webkit-appearance :none;
-moz-appearance :none;
appearance :none;
background: none;
cursor: pointer;
overflow: hidden;
}
/* chromium */
input[type="range" i]::-webkit-slider-thumb{
height: var(--s);
aspect-ratio: 1;
border-radius: 50%;
box-shadow: 0 0 0 var(--l) inset var(--c);
border-image: linear-gradient(90deg,var(--c) 50%,#ababab 0) 0 1/calc(50% - var(--l)/2) 100vw/0 calc(100vw + var(--g));
-webkit-appearance: none;
appearance: none;
}
/* Firefox */
input[type="range"]::-moz-range-thumb {
height: var(--s);
width: var(--s);
background: none;
border-radius: 50%;
box-shadow: 0 0 0 var(--l) inset var(--c);
border-image: linear-gradient(90deg,var(--c) 50%,#ababab 0) 0 1/calc(50% - var(--l)/2) 100vw/0 calc(100vw + var(--g));
-moz-appearance: none;
appearance: none;
}
/**/
body {
margin: 0;
min-height: 100vh;
display: grid;
gap: 40px;
place-content: center;
background: repeating-linear-gradient(-45deg, #fff 0 20px, #f9f9f9 0 40px)
}
添加一些动画
当我们与滑块交互时,添加一些微妙的动画怎么样?它不需要很多代码,并且可以增强滑块的用户体验。
首先,当我们点击滑块时,我们将把滑块从一个只有边框的圆形变成一个完整的圆形。为此,我们增加 box-shadow
的扩展值。请记住,我们使用 box-shadow
来定义滑块的边框:
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);
}
对于 box-shadow
过渡来说,它有点冗长,对吧?我们可以使用 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); /* 如果不支持,我们将保持颜色不变 */
}
}
我们的范围滑块现在完美了!
input {
--c: orange; /* active color */
--g: 8px; /* the gap */
--l: 5px; /* line thickness*/
--s: 30px; /* thumb size*/
width: 400px;
height: var(--s); /* needed for Firefox*/
--_c: color-mix(in srgb, var(--c), #000 var(--p,0%));
-webkit-appearance :none;
-moz-appearance :none;
appearance :none;
background: none;
cursor: pointer;
overflow: hidden;
}
input:focus-visible,
input:hover{
--p: 25%;
}
input:active,
input:focus-visible{
--_b: var(--s)
}
/* chromium */
input[type="range" i]::-webkit-slider-thumb{
height: var(--s);
aspect-ratio: 1;
border-radius: 50%;
box-shadow: 0 0 0 var(--_b,var(--l)) inset var(--_c);
border-image: linear-gradient(90deg,var(--_c) 50%,#ababab 0) 0 1/calc(50% - var(--l)/2) 100vw/0 calc(100vw + var(--g));
-webkit-appearance: none;
appearance: none;
transition: .3s;
}
/* Firefox */
input[type="range"]::-moz-range-thumb {
height: var(--s);
width: var(--s);
background: none;
border-radius: 50%;
box-shadow: 0 0 0 var(--_b,var(--l)) inset var(--_c);
border-image: linear-gradient(90deg,var(--_c) 50%,#ababab 0) 0 1/calc(50% - var(--l)/2) 100vw/0 calc(100vw + var(--g));
-moz-appearance: none;
appearance: none;
transition: .3s;
}
@supports not (color: color-mix(in srgb,red,red)) {
input {
--_c: var(--c);
}
}
/**/
body {
margin: 0;
min-height: 100vh;
display: grid;
gap: 40px;
place-content: center;
background: repeating-linear-gradient(-45deg, #fff 0 20px, #f9f9f9 0 40px)
}
结论
我们已经完成了,而且不必处理任何与浏览器相关的复杂实现!我们确定了滑块元素的选择器,并使用一些 CSS 技巧,用它设置了整个范围滑块的样式。别忘了,我们只使用了 <input>
元素执行此操作。
以下是一些使用相同技巧的滑块示例。我将把剖析它们的代码作为练习留给你。
转载自:https://juejin.cn/post/7403627317334147098