纯CSS实现动态条纹背景效果
涛叔CSS-TRICKS 最近发表了一篇文章1用纯CSS实现了动态条纹背景效果。内容很基础,却也是非常好的入门学习材料。今天整理出来分享给大家。虽然不是原创,但我也没有简单地翻译原文内容。而是跟小鸟按钮2一样,自己重新实现了一把。读者可以直接在当前页面体验实际效果。另外原文中的组件尺寸比较大,直接嵌入博客正文不太美观,我把中间的绝对尺寸提取出来改为变量。这也算一点微小的创新吧😂
在开始之前先让大家体验一下整体效果,把鼠标悬停到下面的方形区域会看到:
现在我们开始逐步实现整个效果。首先画一个正方形:
<div></div>
div {width: 200px;
height: 200px;
background: beige;
}
然后我们使用背景色绘制条纹。基本原理就是给元素指定多种背景图片。CSS不支持设定多种前景颜色,但可以指定多个背景图片。但我们不需要图片,只需要纯色背景,但是background-image
不支持直接设定背景颜色。一个迂回的办法是使用linear-gradient
设置渐变色,但是把前后的颜色值设成一样的就能获得纯色背景。另外浏览器默认会将背景图铺满整个元素,所以我们要为每种颜色指定开始位置和宽高尺寸。所以完整 CSS 如下:
div {background-image:
linear-gradient(#ff0000, #ff0000),
linear-gradient(#00ff00, #00ff00),
linear-gradient(#0000ff, #0000ff),
linear-gradient(#ffff00, #ffff00),
linear-gradient(#00ffff, #00ffff);
background-position:
top 0px right,
top 40px right,
top 80px right,
top 120px right,
top 160px right;
background-size: 100% 40px;
background-repeat: no-repeat;
}
这里使用background-position
为每种背景颜色设置开始位置。因为各颜色的位置不同,所以需要分别设置。而指定背景大小则需要使用background-size
。又因为各背景色大小相同,可以统一设置为100% 40px
。其中100%
表示宽度,40px
表示高度。最后还需要使用background-repeat: no-repeat
告诉浏览器不要重复绘制背景图。显示效果如下:
如果想实现「锯齿」形条纹,则需要为每一个条纹单独设置宽度,顺便把高度调小一点留出间隙:
div {background-size:
60% 35px,
90% 35px,
70% 35px,
40% 35px,
10% 35px;
}
这样我们就把条纹的整体形状绘制出来了:
接下来我们实现动画效果。说白了是通过hover
伪元素来动态调整条纹宽度:
:hover {
divbackground-size:
100% 35px,
100% 35px,
100% 35px,
100% 35px,
100% 35px;
}
这样鼠标移上去条纹会自动变长,移走又会缩回去:
但这种变化太生硬了,我们给它添加一种动画效果:
div {transition: background-size 1s;
}
这里通过transition
给background-size
变化添加了1s
的渐变动画,效果如下:
现在我们开始调色。刚才为了方便演示,我给条纹添加了不同的颜色。但最终要显示的其实是种渐变色linear-gradient(to right, red, orange)
,从红色过渡到橙色。我们可以先看看展示效果:
div {background-image:
linear-gradient(to right, red, orange),
linear-gradient(to right, red, orange),
linear-gradient(to right, red, orange),
linear-gradient(to right, red, orange),
linear-gradient(to right, red, orange);
}
已经很接近最终效果了。但这种处理方式还是有点小瑕疵,即条纹在伸缩的时候,不同位置的色值会有变化。出现这个问题是因为渐变色的范围是条纹的宽度。鼠标移上去后条纹宽度会变长,而且不同条纹的初始长度又不一样,变化的幅度也不同,所以浏览器需要重新计算渐变色,从而导致颜色变化。
那如何解决这个问题呢?CSS-TRICKS 给出了mix-blend-mode
方案。简单来说就是人为设置两子元素,让它们都充满父元素,然后一个设置为渐变色,另一个绘制条纹实现颜色遮罩功能。
这里需要把mix-blend-mode
设置为screen
模式。在该模式下,浏览器会尝试将两个子元素的颜色混合。如果一个子元素的颜色是黑色,则会显示另外一个子元素的颜色;如果一个子元素的颜色是白色(默认色),则显示白色。更多细节请参考 MDN3。
为了实现遮罩效果,我们需要重构 HTML 结构:
<section>
<div></div>
<div></div>
<strong></strong>
</section>
我们在<section>
中设置三个子元素。第一个<div>
用来显示渐变色,第二个用来绘制条纹遮罩,最后的<strong>
用来显示文字。
先定义几个变量:
section {--size: 200px; /* 元素边长 */
--n: calc(var(--size) / 5); /* 条纹高度 */
--h: calc(var(--n) - 5px); /* 条纹实际高度 */
}
然后基于grid
将三个子元素重叠起来:
section {width: var(--size);
height: var(--size);
display: grid;
align-items: center;
justify-items: center;
cursor: pointer;
}> * {
section width: inherit;
height: inherit;
grid-area: 1 / 1;
transition: background-size 1s;
}
父元素<section>
的显示模式指定为grid
,并指定上下左右居中。然后通过section > *
将所有子元素都显示到第一个位置grid-area: 1/1
,这样子元素就会重叠起来。
我们使用:nth-child
伪元素指定子元素。给第一个<div>
设置渐变色:
> div:nth-child(1) {
section background: linear-gradient(to right, red, orange);
}
为第二个<div>
绘制动态条纹:
> div:nth-child(2) {
section --gt: linear-gradient(black, black);
background:
var(--gt) top right,
var(--gt) top var(--n) right,
var(--gt) top calc(var(--n) * 2) right,
var(--gt) top calc(var(--n) * 3) right,
var(--gt) top calc(var(--n) * 4) right,
beige;
background-repeat: no-repeat;
background-size: 60% var(--h), 90% var(--h), 70% var(--h), 40% var(--h), 10% var(--h);
mix-blend-mode: screen;
}:hover > div:nth-child(2) {
sectionbackground-size: 100% var(--h);
}
这次所有颜色都改为黑色,也就是linear-gradient(black,black)
,可以单独设置一个变量--gt
来简化 CSS 规则。这里还使用calc
动态计算每个条纹在上方的偏移量,方便后续调整。最后需要注意的点是把元素的mix-blend-mode
指定为screen
。
最后说一下文字样式。CSS-TRICKS 原文并没有讲到这部分。我自己本来也想略过。但实践发现没有文字显示效果就会打折,而且换个字体都不行。于是我又把原文的字体搞了过来。
strong {height: max-content;
text-align: center;
isolation: isolate;
line-height: 1.2em;
font-family: 'Bungee Shade';
font-size: calc(var(--size) / 5.5);
color: maroon;
}
第一个关键点是isolation: isolate
,它的意思是创建独立的stacking context
。其实我也不太明白是什么意思,但从效果上来看,是让<strong>
元素不受mix-blend-mode
的意识,独立渲染。大家可以参考 MDN4。
第二个点是需要给文字设置line-height
,不然多行文字会挤到一起。
每三个点是需要根据元素尺寸动态计算字体大小,我用的是calc(var(--size)/5.5)
,这个需要根据字体形状和个人偏好动态调整。如果不动态计算,个性--size
后文字可能超出元素边界。
这里用到的字体是 BungeeShade,大家可以从网上下载。我从 Google Font 下的是 TTF 格式,可以直接用作 Web Font。但转成 woff2 格式体积会减少一半,我就转了一下。个人博客能省则省😄
最后再给大家呈现最终效果: