离谱的 CSS!从表盘刻度到艺术剪纸

简介: 离谱的 CSS!从表盘刻度到艺术剪纸

某日,群里有这样一个问题,如何实现这样的表盘刻度:


d2fe22d719e14ce28e36ccde86d9d4ef_tplv-k3u1fbpfcp-zoom-1.png

这其实是个挺有意思的问题,方法也有很多。


单标签,使用 conic-gradient 实现表盘刻度



最简单便捷的方式,就是利用角向渐变的方式 conic-gradient,代码也非常简单,首先,我们实现一个重复角向渐变:


<div></div>
div {
    width: 300px;
    height: 300px;
    border-radius: 50%;
    background: repeating-conic-gradient(
        #000 0, #000 .8deg, transparent 1deg, transparent calc((360 / 60) * 1deg)
    );
}


其实比较难理解的是 calc((360 / 60) * 1deg),这是因为表盘一共通常有 60 个刻度。效果大概是这样:

fd4da1f8a76e4f43bd6e2aff34b83fe2_tplv-k3u1fbpfcp-zoom-1.png


接下来,只需要将中间镂空即可。如果背景色是白色,直接叠加一个圆形即可,当然,更好的方式是通过 mask 属性进行镂空:


{
    background: repeating-conic-gradient(
        #000 0, #000 .8deg, transparent 1deg, transparent calc(360 / 60 * 1deg)
    );
    mask: radial-gradient(transparent 0, transparent 140px, #000 140px)
}


这样,我们就得到了一个表盘刻度:


1ebba33831274359b724b4de2e07a6ca_tplv-k3u1fbpfcp-zoom-1.png


这是使用一个标签就能实现的方式,当然,缺点也很明显:


  1. 锯齿感严重,渐变的通病
  2. 由于是使用的角向渐变,刻度存在头重脚轻的现象,越向内部,宽度越窄(刻度愈大,差异愈加明显)



ce0b02918a0e405cbedccbacbb9486c7_tplv-k3u1fbpfcp-zoom-1.png



使用多个标签实现



如果不介意使用太多的标签,那么通常而言,更容易想到的方法就是利用 60 个标签,加上旋转实现:


<div class="g-container">
        <div class="g-item"></div>
        // ... 一共 60 个
        <div class="g-item"></div>
    </div>
.g-item {
    position: absolute;
    width: 4px;
    height: 12px;
    background: #000;
    left: 0;
    top: 0;
    transform-origin: 0 150px;
}
@for $i from 1 through 60 { 
    .g-item:nth-child(#{$i}) {
        transform: rotate(#{($i - 1) * 6deg});
    }
}


像是这样,我们通过 60 个 div 标签,利用 SASS 的 for 语法减少重复的代码量,批量实现每个元素逐渐绕一点旋转一定的角度,也是可以实现一个表盘刻度的:


18d6ceb22e2743dfa0c8b4a6bcade478_tplv-k3u1fbpfcp-zoom-1.png



这个方案的好处是,每个刻度粗细一致,并且,不会产生锯齿。


借助 -webkit-box-reflect 减少标签数



当然,上述方案的缺点就在于,使用了 60 个标签去完成这样一个简单的图形,有点太奢侈了。


我们希望尽可能的优化优化标签的个数。此时,我们很容易的想到 -- -webkit-box-reflect,倒影效果属性。


-webkit-box-reflect 是一个非常有意思的属性,它让 CSS 有能力像镜子一样,反射我们元素原本绘制的内容。


-webkit-box-reflect 的语法非常简单,最基本的用法像是这样:


div {
    -webkit-box-reflect: below;
}


其中,below 可以是 below | above | left | right 代表下上左右,也就是有 4 个方向可以选。


假设我们有如下一张图片:


<div></div>
div {
    background-image: url('https://images.pokemontcg.io/xy2/12_hires.png');
}


9b6604077f7549b6bc607c7474ae56b4_tplv-k3u1fbpfcp-zoom-1.png


加上 -webkit-box-reflect: right,也就是右侧的倒影:


div {
    background-image: url('https://images.pokemontcg.io/xy2/12_hires.png');
    -webkit-box-reflect: right;
}


效果如下,生成了一个元素右侧的镜像元素:

7cf3060b72594df8bbfa2f8d4a9823a8_tplv-k3u1fbpfcp-zoom-1.png


借助 -webkit-box-reflect: right,我们至少可以从 60 个标签减少到 15 个标签的使用。简单的嵌套两层即可。


我们简单改变一下 HTML 结构:


<div class="g-parent">
    <div class="g-container">
        <div class="g-item"></div>
        // ... 一共 16 个
        <div class="g-item"></div>
    </div>
</div>

这一次,我们只需要实现 1/4 圆的刻度即可:


@for $i from 1 through 16 { 
    .g-item:nth-child(#{$i}) {
        transform: rotate(#{($i - 1) * 6deg});
    }
}


我们可以得到这样一个图形:


bb10a865fb264f9f9ed71bc0e0b5defd_tplv-k3u1fbpfcp-zoom-1.png

基于这个图形,我们只需要先向左侧倒影一次,再向下倒影一次即可:


.g-container {
    -webkit-box-reflect: below;
}
.g-parent {
    -webkit-box-reflect: left;
}


效果如下:


2625705c80bb4de9b1da56c78f36fb55_tplv-k3u1fbpfcp-zoom-1.png


大致的效果就出来了,当然,0点、3点、6点、9点上左下右 4 个刻度有点问题。不过 -webkit-box-reflect 也提供距离调整功能,再简单修改下代码:


.g-container {
    -webkit-box-reflect: below 4px;
}
.g-parent {
    -webkit-box-reflect: left -4px;
}


这次,效果就是我们想要的最终效果:


a6d57225b56246388a801cf45f0d6121_tplv-k3u1fbpfcp-zoom-1.png


我们就成功地借助 -webkit-box-reflect 节约了 3/4 的标签数量。完整的代码:CodePen Demo -- Clock ticks


-webkit-box-reflect 与剪纸艺术



到这里,我不由得想到,这种对折、再对折,镜像再镜像的方式,与我们小时候的折纸艺术非常的类似。


那么,基于这样一个模板:


<div class="g-parent">
    <div class="g-container">
        <div class="g-item"></div>
    </div>
</div>
.g-container {
    -webkit-box-reflect: below;
}
.g-parent {
    -webkit-box-reflect: left;
}


我只需要绘制 .g-item 里面的内容,将他通过 2 次 -webkit-box-reflect 镜像,就能得到一个剪纸图形。


而如何得到随机有意思的不规则图形呢?


clip-path 是个很不错的选择,我们通过 clip-path 随机对一个矩形进行裁剪:


.g-item {
    width: 150px;
    height: 150px;
    background: #000;
    clip-path: polygon(25% 0%,71% 66%,59% 0%,79% 23%,95% 4%,100% 40%,77% 100%,38% 100%,47% 71%,36% 30%,23% 60%,0% 100%,5% 37%);
}
CSS 复制 全屏


效果如下:


164232217-4ff9437f-d7b3-4c75-9136-5fdb25b1994a.png


经过两次镜像后的效果如下:

25bc55395a134fc895ebbaab4e7c2c6b_tplv-k3u1fbpfcp-zoom-1.png


是不是有那么点意思了?可以随机利用 clip-path 多尝试几次,可以得到不同的效果:


ca04654b176a4dd2aa70ade38f09945c_tplv-k3u1fbpfcp-zoom-1.png


CodePen Demo -- Pure CSS Page Cutting


-webkit-box-reflect 配合 clip-path 配合 mask



但是上面的图形看着还是太简单了,几个原因,一是对折的次数和角度不够,缺少对折次数和不同角度的对折,二是图形不够负责。


我又想起了之前看到过的一篇类似的剪纸相关的文章 -- Paper Snowflakes: Combining Clipping and Masking in CSS


再上述的基础上,还使用了 mask,将图形切割的更细。


我们再来一次,还是同样的结构,当然,为了得到更负责的图形,我们设置了 4 个 .g-item:


<div class="g-parent">
    <div class="g-container">
        <div class="g-item"></div>
        <div class="g-item"></div>
        <div class="g-item"></div>
        <div class="g-item"></div>
    </div>
</div>


首先,还是设置一个 clip-path 切割后的图形:


.g-item:nth-child(1) {
    width: 150px;
    height: 150px;
    background: #000;
    clip-path: polygon(17% 41%,6% 39%,16% 91%,18% 78%,56% 11%,28% 71%,99% 67%,25% 65%,69% 72%,46% 28%,90% 76%,67% 34%,48% 30%,79% 36%,59% 15%,23% 92%,16% 1%,32% 81%,72% 38%,50% 59%,71% 98%,66% 87%,83% 14%,36% 71%,49% 7%,9% 25%,52% 76%,10% 83%,17% 41%);
}


效果如下:

888f9228a65a4e8ea1a694f90b209e31_tplv-k3u1fbpfcp-zoom-1.png


这个图可能它会特别的奇怪,没有问题,我们继续。


如果,我们将一个矩形,从左下角开始算,分为 4 份,那么每一个份就是 90° / 4 = 22.5°,我们希望切割得到其中一份:


144ffdd6fbd64beaae64e3b23a67d22e_tplv-k3u1fbpfcp-zoom-1.png

我们可以借助 mask 去完成这个切割:


.g-item:nth-child(1) {
    width: 150px;
    height: 150px;
    background: #000;
    clip-path: polygon(.....);
    mask: conic-gradient(from 0turn at 0 100%, #000, #000 22.5deg, transparent 22.5deg, transparent);
}


上述的图形,就被切割成了这样:

7c677e6f3d8741518cfbc2e769eda836_tplv-k3u1fbpfcp-zoom-1.png

OK,基于此,我们可以得到同样的第二份图形,但是我们给它加一个 rotateY(180deg):


.g-item:nth-child(2) {
    width: 150px;
    height: 150px;
    background: #000;
    clip-path: polygon(.....);
    mask: conic-gradient(from 0turn at 0 100%, #000, #000 22.5deg, transparent 22.5deg, transparent);
    transform: rotateY(180deg);
}


效果如下:

ff59dfee19414979a7f8a9755febf4e1_tplv-k3u1fbpfcp-zoom-1.png


我们再通过 rotateZ(),给第二图形旋转一定的角度,让他和第一个贴合在一起:


.g-item:nth-child(2) {
    clip-path: polygon(.....);
    mask: conic-gradient(from 0turn at 0 100%, #000, #000 22.5deg, transparent 22.5deg, transparent);
    transform: rotateY(180deg) rotateZ(-45deg);
}


就得到了一个斜向角度的镜像图像:

fa28e47014514a2d847cdeb1dccf74cc_tplv-k3u1fbpfcp-zoom-1.png

因为 .g-item 被切割成了 4 份,所以第 3、4 个图形如法炮制即可,这样,整个 .g-item 的效果如下:

a1ed820cd38e45c5ac9774c166bd2a5c_tplv-k3u1fbpfcp-zoom-1.png


再打开 -webkit-box-reflect,整个的图形效果如下:

0a943c24f46249c8ba880f0a7d385ad6_tplv-k3u1fbpfcp-zoom-1.png


这样,一个剪纸图形就诞生啦!


当然,为了得到不一样的效果,我们可以借助 JavaScript 去随机生成 CSS 中的各类参数,完整的代码,大概是这样:


<div class="g-parent">
    <div class="g-container">
        <div class="g-item"></div>
        <div class="g-item"></div>
        <div class="g-item"></div>
        <div class="g-item"></div>
    </div>
</div>


.g-container,
.g-parent {
    position: relative;
    display: flex;
    width: 150px;
    height: 150px;
}
.g-item {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    background: #000;
    transform-origin: 0 100%;
    clip-path: var(--polygon, polygon(40% 0%,0% 91%,52% 100%,0% 37%,77% 23%,77% 76%,43% 22%,55% 88%,100% 100%,100% 10%));
}
.g-item {
    mask: conic-gradient(from 0turn at 0 100%, #000, #000 22.5deg, transparent 22.5deg, transparent);
}
@for $i from 1 through 5 { 
    .g-item:nth-child(#{$i}) {
        transform: rotateZ(calc(22.5deg * #{$i - 1}));
    }
}
.g-item:nth-child(2) {
    transform: rotateY(180deg) rotateZ(-60deg);
}
.g-item:nth-child(4) {
    transform: rotateY(180deg) rotateZ(-105deg);
}
.g-container {
    -webkit-box-reflect: below;
}
.g-parent {
    -webkit-box-reflect: left;
}
const ele = document.querySelectorAll('.g-item');
document.addEventListener('click', function(e) {
    let num = Math.floor(Math.random() * 30 + 10);
    const maskR =  Math.floor(Math.random() * 22.5 + 22.5 ) + 'deg';
    const r1 = Math.floor(Math.random() * 100) + '%';
    const r2 = Math.floor(Math.random() * 100) + '%'; 
    let polygon = 'polygon(' + r1 + ' ' + r2 + ',';
    for (let i=0; i<num; i++) {
         const newR1 = Math.floor(Math.random() * 100) + '%';
         const newR2 = Math.floor(Math.random() * 100) + '%';
        polygon += newR1 + ' ' + newR2 + ','
    }
    polygon += r1 + ' ' + r2 + ')';
    [...ele].forEach(item => {
        item.setAttribute('style', `--polygon:${polygon};-webkit-mask:conic-gradient(from 0turn at 0 100%, #000, #000 ${maskR}, transparent ${maskR}, transparent)`);
    });
});


这样,每次点击鼠标,我们都能得到不同的随机剪纸图案:

69c828ca010f428ebc9b167032bbdd61_tplv-k3u1fbpfcp-zoom-1.png


看看这个简单录制的 GIF:


image.png

完整的代码,你可以猛击这里 CodePen Demo -- Pure CSS Art Page Cutting


目录
相关文章
|
前端开发
css创意罗盘表盘
css创意罗盘表盘
|
2月前
|
前端开发
2s 利用 HTML+css动画实现企业官网效果
2s 利用 HTML+css动画实现企业官网效果
HTML+CSS 实现通用的企业官网页面(记得收藏)
HTML+CSS 实现通用的企业官网页面(记得收藏)
|
5天前
|
前端开发 数据安全/隐私保护 容器
HTML+CSS 水滴登录页
该代码实现了一个创意的水滴登录页面,包含一个水滴形状的登录框与两个按钮(忘记密码和注册)。登录框包括用户名、密码输入框及登录按钮。页面设计独特,采用渐变色与动态效果,增强了交互性和视觉美感。以下为关键实现步骤: - 重置默认样式。 - 设置页面背景颜色和尺寸。 - 定义登录表单容器的布局、位置和尺寸。 - 设置登录表单内容样式,包括3D效果和过渡动画。 - 创建伪元素增强水滴效果。 - 设定输入框容器和输入框样式。 - 为提交按钮、忘记密码和注册按钮设定特定样式,并添加悬停效果。
|
23天前
|
Web App开发 前端开发 JavaScript
HTML/CSS/JS学习笔记 Day3(HTML--网页标签 下)
HTML/CSS/JS学习笔记 Day3(HTML--网页标签 下)
|
2月前
|
前端开发 JavaScript
HTML+CSS如何打造撒花动画效果?3分钟学会,炫酷到爆!
HTML+CSS如何打造撒花动画效果?3分钟学会,炫酷到爆!
|
2月前
|
前端开发 JavaScript
HTML+CSS助你轻松打造惊艳登录页,零基础也能学会!
HTML+CSS助你轻松打造惊艳登录页,零基础也能学会!
HTML+CSS 实现带轮播图的企业官网页面(记得收藏)
HTML+CSS 实现带轮播图的企业官网页面(记得收藏)
HTML+CSS 星空闪烁登录页面(记得收藏)
HTML+CSS 星空闪烁登录页面(记得收藏)
|
2月前
|
前端开发
html+css 实现产品小米产品展示效果(记得收藏)
html+css 实现产品小米产品展示效果(记得收藏)