用CSS怎么圆满实现中国结且添加红包雨动效
发布时间:2023-08-29 14:54:21 所属栏目:语言 来源:
导读:这篇文章给大家分享的是“用CSS怎么实现中国结且添加红包雨动效”,对大家学习和理解有一定的参考价值和帮助,有这方面学习需要的朋友,接下来就跟随小编一起学习一下吧。
那么如何用CSS来实现一个中国
那么如何用CSS来实现一个中国
这篇文章给大家分享的是“用CSS怎么实现中国结且添加红包雨动效”,对大家学习和理解有一定的参考价值和帮助,有这方面学习需要的朋友,接下来就跟随小编一起学习一下吧。 那么如何用CSS来实现一个中国结呢? 先看最终效果。 在线预览Codepen地址: https://codepen.io/cliea/pen/LYOPbBr 如此 Amazing 的效果,你也可以做出来,下面让我们开始吧! 一、编码之前 1. 搜集素材,越简洁越好 先从网上搜一张中国结的图片,中国结的样式不止一种,我们选择一种最经典的中国结的编织样式。 图片的质量决定了最后成品的质量,下面就是一张比较整洁,结构清晰的中国结图片。供我们写CSS时参考使用。 2. 观察细节,构思实现的可能性 有了图片就可以开始写代码了吗?当然不是。 首先回想一下现在要做的事:用CSS画个中国结。 你真的想好了吗?这是一个可以实现的目标吗?想象一下,当你的领导给你布置一个任务:让手机壳根据APP的主题色而变色。你会直接开始写代码吗? 你会想两个问题: APP作为一个软件,是否有和手机壳互动的接口 手机壳如果接收到色值,如何变色 这是一个比较极端的例子,上面两条都无法实现。回到CSS和这张中国结的图片。我们首先要想的是,我们应该用哪些CSS技术,来实现这个图片。你现在回过头仔细观察一下上面的图片。 经过短暂的观察,我们发现这样一些要点: 中国结的绳子是由渐变色组成,深红,浅红,深红 中间的主体部分由22根相互交叉的绳子组合而成,而且每过一个交叉点就会交换一下层级顺序 有一些环状结构,颜色渐变的过程与直线相同 整体都是红色,以黄色点缀 然后就是预想一下实现原理: 直线的颜色渐变,使用 linear-gradient 或者 repeating-linear-gradient 环状渐变,使用 radial-gradient 网格的交叉,使用 mask 遮罩来达到交叉效果 四分之三环以及底部两根弯曲的绳子使用 clip-path 来裁剪 为了使编码更方便,采用 SCSS 许多地方可以使用 ::before ::after 实现,减少html代码 3. 结构拆分,化整为零 上面是从技术角度从整体观察,下面就是对整个图片进行拆分,先确定其 html 结构。 中间像棋盘一样的网格结构,可以作为一个 html 标签 四周16个小半圆,使用16个标签定位实现 两个四分之三圆,放在一组里,使用相同的样式,第二个基于第一个旋转180deg 两个十字结,样式一样,所以也放在一组里 顶部三个小结构,放在一组,外层命名为header 底部左右两部分高度相似,也放在一组,命名为footer 这样我们得到了 html 的结构 <div class="chinese-knot"> <div class="grid"></div> <div class="ring-small"> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> </div> <div class="ring-big"> <i><b></b></i> <i><b></b></i> </div> <div class="cross-node"> <div class="node"> <i></i> <i></i> <i></i> <i></i> </div> <div class="node"> <i></i> <i></i> <i></i> <i></i> </div> </div> <div class="header"> <i></i> <b></b> <span></span> </div> <div class="footer"> <b></b> <b></b> <div class="tassels"> <i></i> <i></i> </div> </div> </div> 实际编码当中,html 并不是一次写成,而是经过不断调整才成为上面这个样子。 二、CSS逐个实现中国结部件 1. 网格 网格最终效果是个菱形,也就是正方形旋转了45deg,我们先不旋转,看看它是什么样子 先设定一个变量,表示绳子的宽度,我们设为--width,这个尺寸很重要,后面所有尺寸都是基于这个宽度,这样后面我们调整整个图形的大小,只要改这一个--width就行了。 :root { --width: 1.7vh; } 垂直和水平都各有11根绳,绳子之间的间隙约为绳子宽度的 0.5 倍,所以可以得到网格的宽高都为 11 + 0.5 * 10 = 16 倍的绳子宽度,所以我们可以这样写: :root { --width: 1.7vh; --grid-width: calc(var(--width) * 16); } .grid { width: var(--grid-width); height: var(--grid-width); } 给 body 加上一些样式,让盒子居中,再加一个深色背景 body{ margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background: #1d1e22; overflow: hidden; } 再给 .grid 也加一个白色背景色,测试一下: 这样屏幕正中间就出现了一个白色方块,下面我们把白色背景改成11根线的样式: :root{ --width: 1.7vh; --red-1: #f40001; --red-2: #d40000; --red-3: #8c0703; --rope: var(--red-3), var(--red-2) calc(var(--width) * 0.25), var(--red-1) calc(var(--width) * 0.45), var(--red-1) calc(var(--width) * 0.55), var(--red-2) calc(var(--width) * 0.75), var(--red-3) var(--width); --grid-width: calc(var(--width) * 16); --bg-line: linear-gradient(90deg, var(--rope), transparent var(--width)) 0 0 / calc(var(--width) * 1.5) calc(var(--width) * 1.5); } .grid{ width: var(--grid-width); height: var(--grid-width); background: var(--bg-line); } 就得到了下面的效果: 可能你有点蒙圈。发生了什么事情? 还是让事情变得简单点,我们先画一根不带渐变的红线: .grid{ background: linear-gradient( 90deg, var(--red-1), var(--red-1) var(--width), transparent var(--width) ); } 先是一个线性渐变 linear-gradient,然后旋转角度设为90deg,让它从左到右渐变(默认是从下往上),然后起始值设为--red-1(你问我red-1到red-3哪来的?效果图上吸来的),在--width处也设置为--red-1,这样就得到了一根宽为 --width 的红线。但这还没完,得接着在--width 处加一个透明transpanrent,这样从--width直到图形的最右侧就都不填充颜色了。 但这不太像根绳子,让红线渐变起来: .grid{ background: linear-gradient( 90deg, var(--red-3), var(--red-2) calc(var(--width) * 0.25), var(--red-1) calc(var(--width) * 0.45), var(--red-1) calc(var(--width) * 0.55), var(--red-2) calc(var(--width) * 0.75), var(--red-3) var(--width), transparent var(--width) ); } 这样就得到了一根有一点点立体效果的绳子。可是怎么让它横向重复11次,并且间隔0.5倍的--width呢?看下面的代码: .grid{ background: linear-gradient( 90deg, var(--red-3), var(--red-2) calc(var(--width) * 0.25), var(--red-1) calc(var(--width) * 0.45), var(--red-1) calc(var(--width) * 0.55), var(--red-2) calc(var(--width) * 0.75), var(--red-3) var(--width), transparent var(--width) ) 0 0 / calc(var(--width) * 1.5) calc(var(--width) * 1.5); } 大家来找茬:这段代码和上一段有什么不同?眼尖的你可能已经看出来了,多了这一行: 0 0 / calc(var(--width) * 1.5) calc(var(--width) * 1.5) 以/为分界线,左边的含义是background-positoin,右边的含义是background-size。 0 0也就是左上角。calc(var(--width) * 1.5) calc(var(--width) * 1.5) 也就是一个正方形,宽度为1.5倍绳宽。 这样一个小方块,在垂直和水平方向上重复,就得了我们想要的结果: 可是我们想要的是网格,现在顶多也就算个栅格。 那就使用伪类复制一份,并且旋转90deg: :root{ --width: 1.7vh; --red-1: #f40001; --red-2: #d40000; --red-3: #8c0703; --rope: var(--red-3), var(--red-2) calc(var(--width) * 0.25), var(--red-1) calc(var(--width) * 0.45), var(--red-1) calc(var(--width) * 0.55), var(--red-2) calc(var(--width) * 0.75), var(--red-3) var(--width); --grid-width: calc(var(--width) * 16); --bg-line: linear-gradient(90deg, var(--rope), transparent var(--width)) 0 0 / calc(var(--width) * 1.5) calc(var(--width) * 1.5); } .grid { width: var(--grid-width); height: var(--grid-width); background: var(--bg-line); &:after { content: ""; display: block; width: var(--grid-width); height: var(--grid-width); background: var(--bg-line); transform: rotate(90deg); } } 对比一下参考图片: 不能说完全不相干,但是人家一看就经过了能工巧匠的编织,咱们这只能算简单的叠加,怎么才能让上面变成下面呢? 经过仔细的观察,发现只要把上面一层横着的线,稍加一些遮挡就能实现交叉编织的效果。用哪个css属性实现呢?那就只有mask 了。 下图蓝色框是需要遮挡的部分,绿色框是需要重复的部分。 仔细分析一下绿框的构成: 本质上是在一个3×3的正方形上挖两个1×1的小洞,位置分别是0 0 和 1.5 1.5。我们要如何画这样一张图?并把这张图应用到mask上呢? mask是通过传入的图片进行遮罩处理,而背景图除了传入一张png以外,CSS还内置了几个生成背景图的函数: linear-gradient:线性渐变 repeating-linear-gradient:重复线性渐变 radial-gradient:径向渐变 conic-gradient:圆锥渐变 这些函数都可以和mask配合。这里我们使用conic-gradient实现上面的图形。 用conic-gradient 实现上图,思路要反着来:不是在方形上挖孔,而是用多个矩形将要渲染的部分填充颜色,剩下的部分自然就是透明的: CSS实现如下: :root{ ... --conic: #000 0 90deg, transparent 0 100%; } .grid { ... &:after { ... -webkit-mask: conic-gradient(from 0deg at var(--width) calc(var(--width) * 1.5), var(--conic)) 0 0 / calc(var(--width) * 3) calc(var(--width) * 3), conic-gradient(from 90deg at calc(var(--width) * 2.5) 0, var(--conic)) 0 0 / calc(var(--width) * 3) calc(var(--width) * 3), conic-gradient(from 180deg at calc(var(--width) * 1.5) var(--width), var(--conic)) 0 0 / calc(var(--width) * 3) calc(var(--width) * 3), conic-gradient(from 90deg at 0 calc(var(--width) * 2.5), var(--conic)) 0 0 / calc(var(--width) * 3) calc(var(--width) * 3); } } 预览效果 目前为止完整代码 :root{ --width: 1.7vh; --red-1: #f40001; --red-2: #d40000; --red-3: #8c0703; --rope: var(--red-3), var(--red-2) calc(var(--width) * 0.25), var(--red-1) calc(var(--width) * 0.45), var(--red-1) calc(var(--width) * 0.55), var(--red-2) calc(var(--width) * 0.75), var(--red-3) var(--width); --grid-width: calc(var(--width) * 16); --bg-line: linear-gradient(90deg, var(--rope), transparent var(--width)) 0 0 / calc(var(--width) * 1.5) calc(var(--width) * 1.5); --conic: #000 0 90deg, transparent 0 100%; } body{ margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background: #1d1e22; overflow: hidden; } .grid { width: var(--grid-width); height: var(--grid-width); background: var(--bg-line); &:after { content: ""; display: block; width: var(--grid-width); height: var(--grid-width); background: var(--bg-line); transform: rotate(90deg); -webkit-mask: conic-gradient(from 0deg at var(--width) calc(var(--width) * 1.5), var(--conic)) 0 0 / calc(var(--width) * 3) calc(var(--width) * 3), conic-gradient(from 90deg at calc(var(--width) * 2.5) 0, var(--conic)) 0 0 / calc(var(--width) * 3) calc(var(--width) * 3), conic-gradient(from 180deg at calc(var(--width) * 1.5) var(--width), var(--conic)) 0 0 / calc(var(--width) * 3) calc(var(--width) * 3), conic-gradient(from 90deg at 0 calc(var(--width) * 2.5), var(--conic)) 0 0 / calc(var(--width) * 3) calc(var(--width) * 3); } } <div class="grid"></div> 没错,这个图形,只用了.grid这一个标签! 但是只有网格还不够,让我们继续。 2. 半圆环 回头看一下参考图: 嗯,环形渐变,那就是radial-gradient了: <div class="ring-small"> <i></i> </div> .ring-small { i { position: absolute; width: calc(var(--width) * 2.5); height: calc(var(--width) * 1.5); background: radial-gradient( circle at 50% 100%, transparent calc(var(--width) * 0.25), var(--red-3) calc(var(--width) * 0.25), var(--red-2) calc(var(--width) * (0.25 + 0.25)), var(--red-1) calc(var(--width) * (0.25 + 0.45)), var(--red-1) calc(var(--width) * (0.25 + 0.55)), var(--red-2) calc(var(--width) * (0.25 + 0.75)), var(--red-3) calc(var(--width) * (0.25 + 1)), transparent calc(var(--width) * (0.25 + 1)) ); } } 这样就得到了半个环形图,让我们使用定位把它和网格结合看看 /* 先给最外层加个相对定位,后面的绝对定位都相对这一层 */ .chinese-knot { width: var(--grid-width); height: var(--grid-width); position: relative; } .ring-small { i { position: absolute; top: calc(var(--width) * -1.5); left: calc(var(--width) * 3); } } 对比素材图,发现环形不是直接紧贴在网格上的,而是先延伸了一小段直线,再接的曲线。那我们就给它增个高吧: .ring-small { i { &:before, &:after { content: ""; position: absolute; bottom: calc(var(--width) * -0.5 + 1px); width: var(--width); height: calc(var(--width) * 0.5); background: var(--bg-line); } &:after { right: 0; } } } 上面使用两个伪类,为半圆环加了两截高度为 0.5 倍 --width的增高垫,效果如下图 接着复制16个这样的图形,分别定位到各自的位置上: <div class="chinese-knot"> <div class="grid"></div> <div class="ring-small"> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> </div> </div> .ring-small { i { position: absolute; width: calc(var(--width) * 2.5); height: calc(var(--width) * 1.5); background: radial-gradient( circle at 50% 100%, transparent calc(var(--width) * 0.25), var(--red-3) calc(var(--width) * 0.25), var(--red-2) calc(var(--width) * (0.25 + 0.25)), var(--red-1) calc(var(--width) * (0.25 + 0.45)), var(--red-1) calc(var(--width) * (0.25 + 0.55)), var(--red-2) calc(var(--width) * (0.25 + 0.75)), var(--red-3) calc(var(--width) * (0.25 + 1)), transparent calc(var(--width) * (0.25 + 1)) ); &:before, &:after { content: ""; position: absolute; bottom: calc(var(--width) * -0.5 + 1px); width: var(--width); height: calc(var(--width) * 0.5); background: var(--bg-line); } &:after { right: 0; } &:nth-child(-n + 4) { top: calc(var(--width) * -2 + 2px); } &:nth-child(1) { left: calc(var(--width) * 3); } &:nth-child(2) { left: calc(var(--width) * 6); } &:nth-child(3) { left: calc(var(--width) * 9); } &:nth-child(4) { left: calc(var(--width) * 12); } &:nth-child(-n + 8):nth-child(n + 5) { bottom: calc(var(--width) * -2 + 2px); transform: rotate(180deg); } &:nth-child(5) { left: calc(var(--width) * 1.5); } &:nth-child(6) { left: calc(var(--width) * 4.5); } &:nth-child(7) { left: calc(var(--width) * 7.5); } &:nth-child(8) { left: calc(var(--width) * 10.5); } &:nth-child(-n + 12):nth-child(n + 9) { left: calc(var(--width) * -2.5 + 2px); transform: rotate(-90deg); } &:nth-child(9) { top: calc(var(--width) * 3.5); } &:nth-child(10) { top: calc(var(--width) * 6.5); } &:nth-child(11) { top: calc(var(--width) * 9.5); } &:nth-child(12) { top: calc(var(--width) * 12.5); } &:nth-child(-n + 16):nth-child(n + 13) { right: calc(var(--width) * -2.5 + 2px); transform: rotate(90deg); } &:nth-child(13) { top: calc(var(--width) * 2); } &:nth-child(14) { top: calc(var(--width) * 5); } &:nth-child(15) { top: calc(var(--width) * 8); } &:nth-child(16) { top: calc(var(--width) * 11); } } } 就得到了这样的效果 哈哈,很像下水管道~ 3. 四分之三圆环 还是先看素材: 嗯,不得不怀疑网易云的 LOGO 的灵感是不是就是中国结。 单个环形已经实现了,两个环也不难吧: <div class="ring-big"> <i><b></b></i> </div> .ring-big { i { position: absolute; width: calc(var(--width) * 6); height: calc(var(--width) * 6); b { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: radial-gradient( circle at 50% 50%, transparent calc(var(--width) * 0.5), var(--red-3) calc(var(--width) * 0.5), var(--red-2) calc(var(--width) * (0.5 + 0.25)), var(--red-1) calc(var(--width) * (0.5 + 0.45)), var(--red-1) calc(var(--width) * (0.5 + 0.55)), var(--red-2) calc(var(--width) * (0.5 + 0.75)), var(--red-3) calc(var(--width) * (0.5 + 1)), transparent calc(var(--width) * (0.5 + 1)), transparent calc(var(--width) * 2), var(--red-3) calc(var(--width) * 2), var(--red-2) calc(var(--width) * (2 + 0.25)), var(--red-1) calc(var(--width) * (2 + 0.45)), var(--red-1) calc(var(--width) * (2 + 0.55)), var(--red-2) calc(var(--width) * (2 + 0.75)), var(--red-3) calc(var(--width) * (2 + 1)), transparent calc(var(--width) * (2 + 1)) ); } } } 为什么 <i> 标签里要再套一个标签呢,因为接下来我们要执行 clip-path,还要给圆环增高,而clip-path 会给增高的部分也裁剪掉,所以只能再套一层,让内层的 <b> 自己 clip,增高则使用 <i> 的伪类实现。下面就是将圆环右下角 1/4 裁剪掉并且加一个增高垫的代码: .ring-big { i { ... b { ... clip-path: polygon(0 0, 100% 0, 100% 50%, 50% 50%, 50% 100%, 0 100%); } &:before, &:after { content: ""; position: absolute; top: calc(var(--width) * 3 - 1px); left: calc(var(--width) * 3.5); width: calc(var(--width) * 2.5); height: calc(var(--width) * 0.5 + 2px); background: repeating-linear-gradient( 90deg, var(--red-3), var(--red-2) calc(var(--width) * 0.25), var(--red-1) calc(var(--width) * 0.45), var(--red-1) calc(var(--width) * 0.55), var(--red-2) calc(var(--width) * 0.75), var(--red-3) var(--width), transparent var(--width), transparent calc(var(--width) * 1.5) ); } &:after { transform: rotate(90deg); transform-origin: left top; top: calc(var(--width) * 3.5); left: calc(var(--width) * 3.5 + 1px); } } } 复制一份并定位: .ring-big { i { ... &:nth-child(1) { left: calc(var(--width) * -3.5); top: calc(var(--width) * -3.5); } &:nth-child(2) { left: auto; top: auto; right: calc(var(--width) * -3.5); bottom: calc(var(--width) * -3.5); transform: rotate(180deg); } } } 到这里,工作的一半就已经完成了~继续 4. 十字结 这个图形,相对于上面几个,已经没什么难度了,五个1×1 的正方形,中间的渐变方向和周围四个垂直。 中间的正方形,用父级本身实现,里面周围四个,用四个子<i>标签实现: <div class="cross-node"> <div class="node"> <i></i> <i></i> <i></i> <i></i> </div> <div class="node"> <i></i> <i></i> <i></i> <i></i> </div> </div> .cross-node { .node { position: absolute; z-index: 2; width: var(--width); height: var(--width); background: var(--bg-line); i { position: absolute; width: var(--width); height: var(--width); background: var(--bg-line); transform: rotate(90deg); &:nth-child(1) { left: calc(var(--width) * -1); } &:nth-child(2) { left: var(--width); } &:nth-child(3) { top: calc(var(--width) * -1); } &:nth-child(4) { top: var(--width); } } &:nth-child(1) { right: calc(var(--width) * -1); top: calc(var(--width) * -1); } &:nth-child(2) { left: calc(var(--width) * -1); bottom: calc(var(--width) * -1); } } } 5. 挂绳 前面我们都是让中国结处于一个斜躺的姿态,写头部和尾部之前,让我们先把它摆正: .chinese-knot { ... transform: rotate(-45deg) translate(calc(var(--width) * 4), calc(var(--width) * -4)); } 先确定一下html结构: <div class="header"> <i></i> <b></b> <span></span> </div> i 是上面的吊绳,b 是圆环,span 是衔接处的短绳,带点黄色装饰。为了方便调整定位,我们从下往上实现,先写短绳: :root { --yellow-1: #fced00; --yellow-2: #f28a00; --yellow-3: #da571b; --bg-yellow: linear-gradient( 90deg, var(--yellow-3), var(--yellow-2) 20%, var(--yellow-1) 40%, var(--yellow-1) 60%, var(--yellow-2) 80%, var(--yellow-3) 100% ); } .header { position: absolute; right: 0; top: 0; transform: rotate(45deg); i { position: absolute; bottom: calc(var(--width) * 1); left: calc(var(--width) * -0.5); width: calc(var(--width) * 1); height: calc(var(--width) * 2); background: var(--bg-line); &:before { content: ""; display: block; height: calc(var(--width) * 0.5); background: var(--bg-yellow); } } } 然后是圆环: .header { ... b { position: absolute; bottom: calc(var(--width) * 3); left: calc(var(--width) * -1.5); width: calc(var(--width) * 3); height: calc(var(--width) * 3); background: radial-gradient( circle at 50%, transparent calc(var(--width) * 0.75), var(--red-3) calc(var(--width) * 0.75), var(--red-2) calc(var(--width) * (0.75 + 0.15)), var(--red-1) calc(var(--width) * (0.75 + 0.3)), var(--red-1) calc(var(--width) * (0.75 + 0.45)), var(--red-2) calc(var(--width) * (0.75 + 0.6)), var(--red-3) calc(var(--width) * (0.75 + 0.75)), transparent calc(var(--width) * (0.75 + 0.75)) ); } } 最后是长的吊绳: .header { ... span { position: absolute; bottom: calc(var(--width) * 5); left: calc(var(--width) * -0.25); width: calc(var(--width) * 0.5); height: calc(var(--width) * 30); background: linear-gradient(90deg, var(--red-2), var(--red-1) 20%, var(--red-2) 70%, var(--red-3)); border-radius: calc(var(--width) * 0.25); } } 6. 流苏 确定html结构: <div class="footer"> <b></b> <b></b> <div class="tassels"> <i></i> <i></i> </div> </div> 可以看到,流苏部分,有两个弯曲的 1/8 环,我们用两个b 标签来表示。这个形状依然还是先画一个完整的环,然后裁剪来实现: .footer { position: absolute; left: 0; bottom: 0; b { position: absolute; width: calc(var(--width) * 15); height: calc(var(--width) * 15); background: radial-gradient( circle at 50%, transparent calc(var(--width) * 6.5), var(--red-3) calc(var(--width) * 6.5), var(--red-2) calc(var(--width) * (6.5 + 0.25)), var(--red-1) calc(var(--width) * (6.5 + 0.45)), var(--red-1) calc(var(--width) * (6.5 + 0.55)), var(--red-2) calc(var(--width) * (6.5 + 0.75)), var(--red-3) calc(var(--width) * (6.5 + 1)), transparent calc(var(--width) * (6.5 + 1)) ); } } 加上裁剪并定位: .footer { ... b { ... &:nth-child(1) { left: calc(var(--width) * -8.5); top: calc(var(--width) * 1); clip-path: polygon(50% 0, 50% 50%, 10% 0); } &:nth-child(2) { left: calc(var(--width) * -16); top: calc(var(--width) * -6.5); clip-path: polygon(100% 50%, 50% 50%, 100% 90%); } } } 两个小尾巴就实现了。 最后是流苏。先画一下背景上的垂直细线,这里我们用 repeating-linear-gradient 实现,每隔 2px 画一条 1px 宽的透明度为 0.2 的黑线: .footer { .tassels { i { position: absolute; width: calc(var(--width) * 2.5); height: calc(var(--width) * 14); background: var(--red-2) repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.2) 1px, transparent 1px, transparent 3px) 50% 50% / 3px 1px;} } } 再蒙上一层黄色的装饰: .footer { .tassels { i { ... &:before { content: ""; position: absolute; top: calc(var(--width) * 0.5); width: 100%; height: calc(var(--width) * 3.6); background: var(--bg-yellow); clip-path: polygon(0 0, 100% 0, 100% 10%, 0 10%, 0 15%, 100% 15%, 100% 85%, 0 85%, 0 90%, 100% 90%, 100% 100%, 0 100%, 0 0); } } } 上面代码中使用 clip-path 对黄色背景裁剪,露出两条红线,裁剪路径可以用下图表示: 最终效果: 三、加点动画 本来到这里就应该结束了。但是我想让这个中国结有点实际用途,比如加点交互什么的。 红包也是春节的习俗之一,那就加一个拉一下中国结掉落红包雨的特效吧~ 1. 拉一下 给中国结在:active状态下加个位移即可实现: .chinese-knot { width: var(--grid-width); height: var(--grid-width); position: relative; transform: rotate(-45deg) translate(calc(var(--width) * 4), calc(var(--width) * -4)); cursor: pointer; -webkit-tap-highlight-color: transparent; transition: all 0.5s; &:active { transform: rotate(-45deg) translate(calc(var(--width) * 2), calc(var(--width) * -2)); } } 2. 画个红包 先搜索一个红包素材: 观察一下红包结构,深红背景,浅红弧形开口,加一个黄色圆形封口,上面写着一个繁体的开字。 我们可以先确定 html 结构。.rain 作为外层,代表整个红包雨,一个i标签代表一个红包: <div class="rain"> <i></i> </div> 一个标签怎么实现上面提到的三种元素呢?看代码: .rain { position: absolute; top: 0; left: 0; right: 0; display: flex; justify-content: space-around; i { position: relative; display: block; width: calc(var(--width) * 5); height: calc(var(--width) * 8); background: var(--red-3); border-radius: calc(var(--width) * 0.4); overflow: hidden; box-shadow: 0 calc(var(--width) * 1) calc(var(--width) * 1) rgba(0, 0, 0, 0.3); &:before { content: ""; position: absolute; left: 50%; transform: translate(-50%, -50%); width: calc(var(--width) * 8); height: calc(var(--width) * 8); background: var(--red-1); opacity: 0.5; border-radius: 50%; } &:after { content: "開"; position: absolute; left: 50%; transform: translate(-50%, 140%); width: calc(var(--width) * 2); height: calc(var(--width) * 2); background: var(--yellow-2); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-style: normal; font-size: calc(var(--width) * 0.5); color: var(--yellow-1); text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.1); } } } 使用i标签自身实现红包主体,:before 伪类实现弧形的开口,:after 伪类实现黄色圆形封口,在content中写上開字。 一个红包完成了,再复制 9 个: <div class="rain"> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> <i></i> </div> 这样就得到了 10 个固定在顶部,并且整齐排列的红包了。 3. 红包雨动画 下雨嘛,从上往下运动就好了: .rain { ... i { ... animation: fall 3s ease-in infinite; } } @keyframes fall { 0% { transform: translate(0, 0); } 100% { transform: translate(0, 100vh); } } 聪明的你估计已经猜到了这样的结果:谁家的雨是这样齐刷刷的下来的? 那我们就红包的垂直位置错落一点,使用 sass 的 random 函数来实现随机: .rain { ... i { ... @for $i from 1 through 10 { &:nth-child(#{$i}) { top: random(60) + vh; } } } } 额,效果怎么和想象的不一样。依旧还是齐刷刷下落,只不过是"错落"的齐刷刷。 那我们让每个红包的开始时间也随机不就行了嘛: .rain { ... i { ... @for $i from 1 through 10 { &:nth-child(#{$i}) { top: random(60) + vh; animation-delay: random(30) * 0.1s; } } } } 嗯,好了一点点。但是有一个问题,屏幕上的雨点,有时候很多,有时候很少,不够均匀。那我们把动画的持续时间也随机会怎么样呢? .rain { ... i { ... @for $i from 1 through 10 { &:nth-child(#{$i}) { top: random(60) + vh; animation-delay: random(30) * 0.1s; animation-duration: random(10) * 0.1s + 2s; /* 2s ~ 3s 之间随机 */ } } } } 终于更像雨了~ 但是现在雨滴是凭空出现的,很生硬,我们只要把开始的位置挪到负一屏,然后让它下落到正二屏就行了: .rain { ... top: -100vh; } @keyframes fall { 0% { transform: translate(0, 0); } 100% { transform: translate(0, 200vh); } } 这样就有了源源不断下落的效果。 4. 拉一下触发红包雨 CSS 不是 JS ,怎么触发点击事件呢? 我们就要运用 CSS 本身的特性了,checkbox 复选框有个选中状态 :checked,而复选框可以用点击切换这个状态,再使用 CSS 的兄弟选择器 element ~ element 即可实现点击添加样式的效果。 样式可以触发了,那如何触发动画呢? animation 属性添加到元素上后,播放状态默认是 running ,我们需要先把初始播放状态改为 paused (暂停), 然后通过上面的方法,把元素的播放状态改回 running 来实现播放动画的效果: <input type="checkbox" id="switch"> <label class="chinese-knot" for="switch">...</label> <div class="rain">...</div> .rain { ... i { ... animation: fall 3s ease-in infinite; /* 默认不播放动画 */ animation-play-state: paused; } } #switch { visibility: hidden; pointer-events: none; } /* checkbox 选中时播放动画 */ #switch:checked ~ .rain i { animation-play-state: running; } /* 点击时重置动画,否则取消checkbox选中状态,动画会中止并停留在当前位置 */ .chinese-knot:active ~ .rain i { animation: none; } 上面的 html 中,我们让.chinese-knot 从 div 改为 label 来指向 checkbox,方法是 label 的 for 和 checkbox 的 id 设为相同的值。 效果很不错,我们再给红包雨下落时加个背景,以提醒用户当前的状态。并且下红包雨时,调低中国结的透明度,以突出红包的存在感。 <input type="checkbox" id="switch"> <div class="bg"></div> <label class="chinese-knot" for="switch">...</label> <div class="rain">...</div> .bg { position: absolute; left: 0; top: 0; height: 100vh; width: 100vw; background: linear-gradient(0deg, #171a4b, #96367f); opacity: 0; transition: all 0.5s; } #switch:checked ~ .bg { opacity: 1; } #switch:checked ~ .chinese-knot { opacity: 0.2; &:hover { opacity: 0.5; } } (编辑:聊城站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
推荐文章
站长推荐