Maison > Article > interface Web > Je vais vous expliquer étape par étape comment dessiner un nœud chinois en utilisant du CSS pur et ajouter des effets d'animation !
Cet article vous apprend étape par étape à dessiner un nœud chinois en utilisant du CSS pur et à ajouter un effet d'animation de pluie d'enveloppe rouge à ce nœud chinois. J'espère que cela sera utile à tout le monde !
La Fête du Printemps est la fête la plus importante pour les Chinois. Il existe de nombreuses coutumes pendant la Fête du Printemps, qui diffèrent d'est en ouest, du nord et du sud. Afin d'ajouter à la saveur du Nouvel An, chaque foyer achètera divers articles et décorations du Nouvel An pendant le Nouvel An pour rendre la maison prospère, notamment des lanternes rouges, des distiques rouges, des caractères de bénédiction rouges et des nœuds chinois rouges.
La matière première du nœud chinois est une simple corde rouge, qui est tissée dans une grille de diamant grâce à la conception ingénieuse des anciens. Les cordes sur la grille sont étroitement liées, symbolisant l'unité, l'harmonie et le bonheur de la famille.
Alors comment utiliser CSS pour réaliser un noeud chinois ? Voyons d’abord l’effet final.
Aperçu en ligne Adresse Codepen :
https://codepen.io/cliea/pen/LYOPbBr
Vous pouvez également créer des effets aussi étonnants, commençons !
Recherchez d'abord une image de nœuds chinois sur Internet. Il existe plusieurs styles de nœuds chinois. Nous choisissons le style de tissage de nœuds chinois le plus classique. La qualité de l’image détermine la qualité du produit final. Ci-dessous, une image d’un nœud chinois relativement soigné et clair. Pour notre référence lors de l’écriture de CSS.
Pouvez-vous commencer à écrire du code maintenant que vous avez des images ? Bien sûr que non.
Tout d'abord, réfléchissez à ce que vous devez faire maintenant : dessiner un nœud chinois avec CSS.
Y avez-vous vraiment réfléchi ? Est-ce un objectif réalisable ? Imaginez lorsque votre leader vous confie une tâche : faire changer la couleur de la coque du téléphone en fonction de la couleur du thème de l'APP. Allez-vous simplement commencer à écrire du code ?
Vous réfléchirez à deux questions :
En tant que logiciel, l'application dispose-t-elle d'une interface pour interagir avec la coque du téléphone portable
Comment la coque du téléphone portable change-t-elle de couleur si elle reçoit la valeur de couleur ?
Quelles techniques CSS devrions-nous utiliser pour obtenir cette image. Revenez maintenant en arrière et regardez de plus près l'image ci-dessus.
Après une courteobservation, nous avons constaté les points clés suivants :
principe de mise en œuvre
:linear-gradient
ou repeating-linear-gradient<.></.>
Dégradé en anneau, utilisez radial-gradient
masque
pour obtenir l'effet d'intersectionlinear-gradient
或者 repeating-linear-gradient
环状渐变,使用 radial-gradient
网格的交叉,使用 mask
遮罩来达到交叉效果
四分之三环以及底部两根弯曲的绳子使用 clip-path
来裁剪
为了使编码更方便,采用 SCSS
许多地方可以使用 ::before
::after
实现,减少html
代码
上面是从技术角度从整体观察,下面就是对整个图片进行拆分,先确定其 html
结构。
html
标签180deg
trois- les quarts de l'anneau et le bas. Deux cordes courbes sont coupées à l'aide de clip-path
Afin de rendre le codage plus pratique, utilisez SCSS
:: à de nombreux endroits avant
::après
la mise en œuvre, réduisant ainsi le html
code🎜🎜🎜html
. 🎜html
🎜🎜🎜🎜180deg
🎜🎜🎜🎜🎜en-tête
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
并不是一次写成,而是经过不断调整才成为上面这个样子。
网格最终效果是个菱形,也就是正方形旋转了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
En bas à gauche et à droite Les deux parties sont très similaires et sont également regroupées, nommées footer
html
🎜: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); } }🎜Dans l'encodage réel, html Il n'a pas été écrit d'un seul coup, mais il est devenu ce qu'il est après des ajustements constants. 🎜
45deg
. Voyons à quoi il ressemble sans le faire pivoter pour l'instant🎜🎜🎜🎜Définissez d'abord une variable pour représenter la largeur de la corde, définissons-le sur --width
, cette taille est très importante
, toutes les tailles suivantes sont basées sur cette largeur, donc plus tard nous ajustons la taille de l'ensemble du graphique, changez simplement celui-ci --width
code> fera l'affaire. 🎜: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); } }🎜Il y a
11
cordes chacune verticalement et horizontalement. L'espace entre les cordes est d'environ 0,5
fois la largeur des cordes, donc la largeur et la hauteur de la grille peuvent être ajustées. être obtenu comme 11 + 0.5 * 10 = 16
fois la largeur de la corde, on peut donc écrire : 🎜: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); } }🎜Ajoutez quelques styles au
body
pour centrer la boîte et ajoutez un fond de couleur de profondeur🎜<div class="grid"></div>🎜Ajoutez une couleur de fond blanc à
.grid
et testez-le :🎜🎜🎜🎜Un carré blanc apparaîtra au milieu de l'écran. Maintenant, nous changeons le fond blanc en 11Le style de la ligne racine : 🎜<pre class="brush:html;toolbar:false;"> <div class="ring-small">
<i></i>
</div></pre>🎜 obtiendra l'effet suivant : 🎜🎜<img src="https://img.php.cn/upload/image/379/730/637/164325083675438Je%20vais%20vous%20expliquer%20%C3%A9tape%20par%20%C3%A9tape%20comment%20dessiner%20un%20n%C5%93ud%20chinois%20en%20utilisant%20du%20CSS%20pur%20et%20ajouter%20des%20effets%20danimation%C2%A0!" title=" 164325083675438Je vais vous expliquer étape par étape comment dessiner un nœud chinois en utilisant du CSS pur et ajouter des effets danimation !" alt="Je vais vous expliquer étape par étape comment dessiner un nœud chinois en utilisant du CSS pur et ajouter des effets danimation !">🎜🎜Vous êtes peut-être un peu confus. Ce qui s'est passé? 🎜🎜Rendons les choses plus simples, traçons d'abord une ligne rouge sans dégradé : 🎜<pre class="brush:css;toolbar:false;">.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))
);
}
}</pre>🎜<img src="https://img.php.cn/upload/image/924/565/363/1643250840811960%20.png" title="1643250840811960.png" alt="1Je vais vous expliquer étape par étape comment dessiner un nœud chinois en utilisant du CSS pur et ajouter des effets danimation !">🎜🎜D'abord est un dégradé linéaire <code>linear-gradient
, puis l'angle de rotation est défini sur 90deg
, Make il est dégradé de gauche à droite (la valeur par défaut est de bas en haut), puis définissez la valeur de départ sur --red-1
(vous me demandez red-1
sur red-3 ? (du rendu), et définissez-le sur --red-1
à --width
, comme ceci Vous obtenez une ligne rouge d'une largeur de --width
. Mais ce n'est pas fini, il faut ajouter un transpanrent
transparent à --width
, pour que de --width
à l'extrême droite côté du graphique Plus de remplissage de couleur. 🎜🎜Mais ça ne ressemble pas à une corde, laissez la ligne rouge se dégrader : 🎜/* 先给最外层加个相对定位,后面的绝对定位都相对这一层 */ .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); } }🎜🎜🎜De cette façon, vous obtenez une corde avec un petit effet tridimensionnel. Mais comment le faire répéter horizontalement
11
fois avec un intervalle de 0,5
fois --width
? Regardez le code ci-dessous : 🎜.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; } } }🎜 Trouvons les erreurs : Quelle est la différence entre ce code et le précédent ? Ceux d'entre vous qui ont les yeux perçants auront peut-être remarqué qu'il y a cette ligne supplémentaire : 🎜
🎜Avec<div class="grid"></div><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>
/
comme ligne de démarcation, la signification à gauche est background-positoin
, et la signification à droite est background-size. 🎜🎜0 0
est le coin supérieur gauche. calc(var(--width) * 1.5) calc(var(--width) * 1.5)
est un carré d'une largeur de 1,5
fois la largeur de la corde. 🎜🎜🎜🎜Ceci façon Un petit carré, répété verticalement et horizontalement, nous donnera le résultat souhaité : 🎜🎜🎜🎜Mais ce que nous voulons, c'est une grille, et maintenant ce n'est qu'une grille tout au plus. 🎜🎜Ensuite, utilisez la pseudo-classe pour faire une copie et faire pivoter 90deg
: 🎜.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); } } }🎜🎜🎜🎜Comparez l'image de référence : 🎜🎜🎜🎜
不能说完全不相干,但是人家一看就经过了能工巧匠的编织,咱们这只能算简单的叠加,怎么才能让上面变成下面呢?
经过仔细的观察,发现只要把上面一层横着的线,稍加一些遮挡就能实现交叉编织的效果。用哪个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
这一个标签!
但是只有网格还不够,让我们继续。
回头看一下参考图:
嗯,环形渐变,那就是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="grid"></div><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>
.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); } } }
就得到了这样的效果
哈哈,很像下水管道~
还是先看素材:
嗯,不得不怀疑网易云的 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></i>
标签里要再套一个标签呢,因为接下来我们要执行 clip-path
,还要给圆环增高,而clip-path
会给增高的部分也裁剪掉,所以只能再套一层,让内层的 <b></b>
自己 clip
,增高则使用 <i></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); } } }
到这里,工作的一半就已经完成了~继续
这个图形,相对于上面几个,已经没什么难度了,五个1×1
的正方形,中间的渐变方向和周围四个垂直。
中间的正方形,用父级本身实现,里面周围四个,用四个子<i></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); } } }
前面我们都是让中国结处于一个斜躺的姿态,写头部和尾部之前,让我们先把它摆正:
.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); } }
单独效果
整体效果
确定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
对黄色背景裁剪,露出两条红线,裁剪路径可以用下图表示:
最终效果:
本来到这里就应该结束了。但是我想让这个中国结有点实际用途,比如加点交互什么的。
红包也是春节的习俗之一,那就加一个拉一下中国结掉落红包雨的特效吧~
给中国结在: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)); } }
先搜索一个红包素材:
观察一下红包结构,深红背景,浅红弧形开口,加一个黄色圆形封口,上面写着一个繁体的开字。
我们可以先确定 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 个固定在顶部,并且整齐排列的红包了。
下雨嘛,从上往下运动就好了:
.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); } }
这样就有了源源不断下落的效果。
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; } }
完结撒花~~~
这篇文章整理了我从搜集素材开始,创作一个作品的全部过程,代码写了一天,这篇文章写了半天。希望能让 CSS 初学者对 CSS 燃起兴趣,也希望让接触了一段时间 CSS 的朋友获得一些灵感和帮助。
(学习视频分享:css视频教程)
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!