关于前端:离谱的-CSS从表盘刻度到艺术剪纸

36次阅读

共计 6257 个字符,预计需要花费 16 分钟才能阅读完成。

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

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

单标签,应用 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 个刻度。成果大略是这样:

接下来,只须要将两头镂空即可。如果背景色是红色,间接叠加一个圆形即可,当然,更好的形式是通过 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)
}

这样,咱们就失去了一个表盘刻度:

这是应用一个标签就能实现的形式,当然,毛病也很显著:

  1. 锯齿感重大,突变的通病
  2. 因为是应用的角向突变,刻度存在头重脚轻的景象,越向外部,宽度越窄(刻度愈大,差别更加显著)

应用多个标签实现

如果不介意应用太多的标签,那么通常而言,更容易想到的办法就是利用 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 语法缩小反复的代码量,批量实现每个元素逐步绕一点旋转肯定的角度,也是能够实现一个表盘刻度的:

这个计划的益处是,每个刻度粗细统一,并且,不会产生锯齿。

借助 -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');
}

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

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

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

借助 -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});
    }
}

咱们能够失去这样一个图形:

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

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

成果如下:

大抵的成果就进去了,当然,0 点、3 点、6 点、9 点上左下右 4 个刻度有点问题。不过 -webkit-box-reflect 也提供间隔调整性能,再简略批改下代码:

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

这次,成果就是咱们想要的最终成果:

咱们就胜利地借助 -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%);
}

成果如下:

通过两次镜像后的成果如下:

是不是有那么点意思了?能够随机利用 clip-path 多尝试几次,能够失去不同的成果:

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%);
}

成果如下:

这个图可能它会特地的奇怪,没有问题,咱们持续。

如果,咱们将一个矩形,从左下角开始算,分为 4 份,那么每一个份就是 90° / 4 = 22.5°,咱们心愿切割失去其中一份:

咱们能够借助 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);
}

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

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);
}

成果如下:

咱们再通过 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);
}

就失去了一个斜向角度的镜像图像:

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

再关上 -webkit-box-reflect,整个的图形成果如下:

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

当然,为了失去不一样的成果,咱们能够借助 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)`);
    });
});

这样,每次点击鼠标,咱们都能失去不同的随机剪纸图案:

看看这个简略录制的 GIF:

残缺的代码,你能够猛击这里 CodePen Demo — Pure CSS Art Page Cutting

最初

本文到此结束,心愿对你有帮忙 :)

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 — iCSS 前端趣闻 😄

更多精彩 CSS 技术文章汇总在我的 Github — iCSS,继续更新,欢送点个 star 订阅珍藏。

如果还有什么疑难或者倡议,能够多多交换,原创文章,文笔无限,满腹经纶,文中若有不正之处,万望告知。

正文完
 0