PK creative Spring Festival, I am participating in the “Spring Festival creative submission contest”, please see: Spring Festival creative submission Contest

preface

Preview Codepen Github online

The Spring Festival is the most important festival for Chinese people. There are many customs during the Spring Festival, which are different from east, west, north and south. In order to add to the flavor of the New Year, every family will buy a variety of New Year goods and decorations, to build a prosperous home, red lanterns, red couplets, red fu character, and red Chinese knot.

The raw material of Chinese knot is simple red rope, which is woven into a diamond grid after the ingenious conception of ancient people. The lines on the grid are tightly connected, symbolizing family unity, harmony and happiness.

So how to use CSS to implement a Chinese knot? Let’s look at the end result.

So Amazing effect, you can also make, let’s start!

First, before coding

1. Gather material, as succinctly as possible

First search a picture of Chinese knot from the Internet, there is more than one style of Chinese knot, we choose one of the most classic Chinese knot weaving style. The quality of the picture determines the quality of the final product. Here is a picture of a neat Chinese knot with a clear structure. For reference when we write CSS.

2. Look at the details and visualize the possibilities

Can I start writing code once I have an image? Of course not.

First think back to what you’re doing now: draw a Chinese knot with CSS.

Have you really thought this through? Is that an achievable goal? Imagine your boss giving you the task of making your phone case change color according to the theme color of your APP. Would you just start coding?

There are two questions you think about:

  1. As a software, does APP have an interface to interact with the phone case
  2. How does the phone case change color if it receives color value

This is an extreme case where neither of the above is possible. Back to CSS and this Chinese knot image. The first thing we need to think about is what CSS techniques we should use to implement this image. Now go back and look at the picture above.

After a brief observation, we found the following points:

  1. The rope of Chinese knot is made up of gradient color, deep red, light red, deep red
  2. The main body in the middle is made up of 22 crossed ropes, and the hierarchy changes at each crossing point
  3. There are some rings, and the color gradient is the same as a straight line
  4. It’s all red, with yellow accents

Then imagine how it works:

  1. Line color gradient, uselinear-gradientorrepeating-linear-gradient
  2. Ring gradient, useradial-gradient
  3. Mesh crossover, usedmaskMask to achieve the crossover effect
  4. Three quarters of the ring and two curved cords at the bottom are usedclip-pathTo cut
  5. In order to make coding more convenient, adoptSCSS
  6. It can be used in many places::before ::afterAchieve, reducehtmlcode

3. The structure is broken down and divided into parts

Above is a technical view from the whole, the following is to split the entire image, first determine its HTML structure.

  • The checkerboard-like grid structure in the middle can be used as ahtmlThe label

  • Around 16 small semicircles, using 16 label positioning to achieve

  • Two three-quarters circles, placed in a group, using the same pattern, the second based on the first rotation180deg

  • Two cross knots, the same pattern, so they’re in the same group

  • The top three small structures are placed in a group, and the outer layer is namedheader

  • The bottom left and right parts are highly similar and also placed in a group namedfooter

So we have the structure of the 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>
Copy the code

In actual coding, HTML is not written all at once, but has been tweaked to look like this.

2. CSS implements Chinese knot components one by one

1. The grid

The mesh ends up being a rhombus, a square rotated 45deg, let’s not rotate it, let’s see what it looks like

Let’s say we have a variable that represents the width of the rope, so let’s call it –width, this is important, all the dimensions are based on this width, and then we can resize the whole shape, just change this –width.

:root{-width: 1.7 the vh;
}
Copy the code

There are 11 ropes vertically and horizontally, and the gap between the ropes is about 0.5 times of the rope width, so the width and height of the grid are 11 + 0.5 * 10 = 16 times of the rope width, so we can write as follows:

:root{-width: 1.7 the vh;
  --grid-width: calc(var(--width) * 16);
}
.grid {
  width: var(--grid-width);
  height: var(--grid-width);
}
Copy the code

Add some styling to the body, center the box, and add a dark background

body{
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: #1d1e22;
  overflow: hidden;
}
Copy the code

Test it by adding a white background color to the.grid as well:

Now a white square appears in the middle of the screen. Let’s change the white background to 11 lines:

:root{-width: 1.7 the vh;
  --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);
}
Copy the code

We get the following effect:

Maybe you’re a little confused. What’s going on?

To make things easier, let’s draw a red line without gradient:

.grid{
  background: linear-gradient(
    90deg.var(--red-1), 
    var(--red-1) var(--width), 
    transparent var(--width)
  );
}
Copy the code

Start with a linear gradient linear-gradient, then rotate it to 90deg and make it gradient from left to right (default is bottom to top), then set the starting value to red-1. –width is also set to –red-1, so we have a red line with –width. But that’s not the end of it. We need to add a transparent Transpanrent at –width so that no color is filled from –width to the far right of the shape.

But it’s not very much like a rope, where the red line gradually changes:

.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)
  );
}
Copy the code

So you get a little bit of three-dimensional string. How to make it repeat horizontally 11 times, 0.5 times width apart? Look at the following code:

.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);
}
Copy the code

Guys: What’s the difference between this code and the last one? Sharp-eyed you may already have noticed the addition of this line:

0 0 / calc(var(--width) * 1.5) calc(var(--width) * 1.5)
Copy the code

With/as the dividing line, the meaning on the left is background-positoin, and the meaning on the right is background-size.

0, 0 is the top left corner. Calc (var(–width) * 1.5) calc(var(–width) * 1.5)

This little square, repeated vertically and horizontally, gives us the result we want:

But what we want is a grid, and now it’s a grid at best.

Make a copy using the pseudo-class and rotate it 90deg:

:root{-width: 1.7 the vh;
  --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); }}Copy the code

Compare the reference image:

Cannot say completely irrelevant, but somebody else looked to have passed skillful crafter’s braid, we this can calculate simple superposition only, how can let above become below?

Through careful observation, it is found that as long as the above horizontal line, a little bit of shelter can achieve the effect of cross braid. Which CSS property does it use? That leaves mask.

The blue box below is the part that needs to be covered, and the green box is the part that needs to be repeated.

Analyze the composition of the green box carefully:

It’s essentially digging two small 1 by 1 holes in a 3 by 3 square at positions 0, 0 and 1.5, 1.5. How do we draw a picture like this? And apply it to the mask?

A mask is masked by an incoming image, while a background image is passed in as a PNG. CSS also has several built-in functions to generate a background image:

  • linear-gradient: Linear gradient
  • repeating-linear-gradient: Repeat the linear gradient
  • radial-gradient: Radial gradient
  • conic-gradientTaper gradient

These functions can be used with masks. Here we use Conic-gradient to achieve the above graph.

To achieve the above image with Conic-gradient, the idea is to do the opposite: Instead of digging holes in the square, fill in the parts of multiple rectangles that will be rendered, and the rest will naturally be transparent:

The CSS implementation is as follows:

: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); }}Copy the code

preview

Complete code so far

:root{-width: 1.7 the vh;
  --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); }}Copy the code
<div class="grid"></div>
Copy the code

That’s right, this graph, it only uses the. Grid tag!

But the grid alone is not enough, so let’s move on.

2. A half circle

Look back at the reference image:

A radial gradient is called a radial gradient:

  <div class="ring-small">
    <i></i>
  </div>
Copy the code
.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))); }}Copy the code

So we have a half circle graph, and let’s use positioning to combine it with the grid

/* Add a relative position to the outermost layer */
.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); }}Copy the code

Compared with the material picture, it is found that the ring is not directly attached to the grid, but first extended a short line, then connected with the curve. Let’s make it taller:

.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; }}}Copy the code

The above two pseudo-classes are used to add two heightening pads with a height of 0.5 times –width for the half ring, the effect is shown below

Then copy 16 of these shapes and place them in their respective positions:

<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>
Copy the code
.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); }}}Copy the code

And you get something like this

Ha ha, it looks like a sewer

3. Three quarters of a circle

First look at the material:

Well, I have to wonder if the inspiration of the LOGO of NetEase Cloud is Chinese knot.

A single ring is already implemented, but two rings are not difficult:

<div class="ring-big">
    <i><b></b></i>
</div>
Copy the code
.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))); }}}Copy the code

Why < I > tag should then set a label, because we want to perform next clip – path, to ring up, and the clip – path to heighten part is cut off, so can only set a layer again, let the inner < b > your clip, increased the use of < I > pseudo class implements. Here is the code that cuts out the bottom right quarter of the ring and adds a lift pad:

.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); }}}Copy the code

Make a copy and locate:

.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); }}}Copy the code

At this point, half the work is done ~ continue

4. Cross junction

This figure, compared to the previous ones, is not too difficult, five 1 by 1 squares, with the gradient in the middle perpendicular to the four around them.

The middle square is implemented with the parent itself, with four inner circles, and is implemented with four child < I > tags:

<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>
Copy the code
.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); }}}Copy the code

5. Hang on the rope

Before writing the head and tail, let’s straighten the Chinese knot:

.chinese-knot{...transform: rotate(-45deg) translate(calc(var(--width) * 4), calc(var(--width) * -4));
}
Copy the code

Go back to the footage:

Let’s define the HTML structure:

<div class="header">
    <i></i>
    <b></b>
    <span></span>
</div>
Copy the code

I is the upper rope, B is the ring, and SPAN is the short rope at the junction, with a little yellow decoration. In order to facilitate the adjustment of positioning, we implement from the bottom up, first write a short rope:

: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); }}}Copy the code

Then the circle:

.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))); }}Copy the code

Finally, the long sling:

.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); }}Copy the code

Separate effects

The overall effect

6. Tassel

Determine the HTML structure:

<div class="footer">
    <b></b>
    <b></b>
    <div class="tassels">
      <i></i>
      <i></i>
    </div>
</div>
Copy the code

And you can see, in the tassel part, there are two curved 1/8 rings, which we denote by two B tags. This shape is still drawn as a complete loop and then clipped to achieve:

.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))); }}Copy the code

Add clipping and positioning:

.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%); }}}Copy the code

Two little tails.

And finally, tassels. First draw a vertical thin line on the background. Here we use repeating-Linear-gradient and draw a black line every 2px with a opacity of 0.2 1px wide:

.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; }}}Copy the code

With a yellow ornament:

.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); }}}Copy the code

In the code above, clip-path is used to clip the yellow background to reveal two red lines. The clipping path can be shown as the following figure:

End result:

Three, add animation

It was supposed to end here. But I want this Chinese knot to have some practical use, like some interaction.

Red envelopes are also one of the customs of the Spring Festival, so let’s add a special effect of Chinese knot falling red envelopes

1. A pull

Add a displacement to the Chinese knot in :active state to achieve:

.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.5 s;
  &:active {
    transform: rotate(-45deg) translate(calc(var(--width) * 2), calc(var(--width) * -2)); }}Copy the code

2. Draw a red envelope

First search for a red envelope material:

Take a look at the structure of the red envelope. It has a dark red background, a light red curved opening, and a yellow circular seal with a traditional opening character written on it.

We can determine the HTML structure first. .rain, as the outer layer, represents the whole red envelope rain, and an I tag represents a red envelope:

<div class="rain">
  <i></i>
</div>
Copy the code

How does a tag implement all three elements mentioned above? Look at the code:

.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: "Open";
      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); }}}Copy the code

Use I tag itself to realize the main body of the red envelope, :before pseudo-class to realize the curved opening, :after pseudo-class to realize the yellow round seal, and write the opening character in the content.

One red envelope completed, 9 more copies:

<div class="rain">
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
</div>
Copy the code

So you have 10 red packets fixed at the top and arranged neatly.

3. Rain animation of red envelopes

When it rains, it moves from the top down:

.rain{...i{...animation: fall 3sease-in infinite; }}@keyframes fall {
  0% {
    transform: translate(0.0);
  }
  100% {
    transform: translate(0.100vh); }}Copy the code

Smart you estimate have guessed such a result: whose home rain is so together brush down?

Then we randomly place the vertical position of the red envelope and use the random function of Sass to achieve randomness:

.rain{...i{...@for $i from 1 through 10{&:nth-child(# {$i{})top: random(60) + vh; }}}}Copy the code

Well, the effect is not as expected. It’s still all going down, but it’s all going down.

Let’s make the start time of each red envelope random:

.rain{...i{...@for $i from 1 through 10{&:nth-child(# {$i{})top: random(60) + vh;
        animation-delay: random(30) * 0.1 s; }}}}Copy the code

Well, a little better. But there is a problem, the rain on the screen, sometimes a lot, sometimes a little, not uniform. What if we made the duration of the animation contingent?

.rain{...i{...@for $i from 1 through 10{&:nth-child(# {$i{})top: random(60) + vh;
        animation-delay: random(30) * 0.1 s;
        animation-duration: random(10) * 0.1 s + 2s; /* random between 2s and 3s */}}}}Copy the code

It’s finally more like rain

But now raindrops appear out of thin air, so we just have to move the starting position to negative one screen and let it fall to positive two screen:

.rain{...top: -100vh;
}
@keyframes fall {
  0% {
    transform: translate(0.0);
  }
  100% {
    transform: translate(0.200vh); }}Copy the code

So you have the effect of a continuous falling.

4. Pull to trigger the rain of red envelopes

CSS is not JS, how do you trigger click events?

The checkbox has a checked state :checked, and the checkbox can be toggle with a click. Then use the sibling selector element ~ element of CSS to add styles.

Styles can now trigger, so how do you trigger animations?

After the animation property is added to the element, the playback state is running by default. We need to change the initial playback state to paused first, and then change the playback state of the element back to RUNNING by using the above method to realize the effect of playing the animation:

<input type="checkbox" id="switch">
<label class="chinese-knot" for="switch">.</label>
<div class="rain">.</div>
Copy the code
.rain{...i{...animation: fall 3s ease-in infinite;
    /* Default does not play animation */
    animation-play-state: paused; }}#switch {
  visibility: hidden;
  pointer-events: none;
}
/* Play animation when checkbox is checked */
#switch:checked ~ .rain i {
  animation-play-state: running;
}
/* Reset the animation when clicked, otherwise uncheck the checkbox status and the animation will stop and stay in the current position */
.chinese-knot:active ~ .rain i {
  animation: none;
}
Copy the code

In the HTML above, we changed.chinese-knot from div to label to point to the checkbox by setting the for of label to the same value as the id of the checkbox.

The effect is very good, we add a background when the red envelope rain falls, to remind the user of the current state. And under the red envelope rain, lower the transparency of the Chinese knot, in order to highlight the presence of the red envelope.

<input type="checkbox" id="switch">
<div class="bg"></div>
<label class="chinese-knot" for="switch">.</label>
<div class="rain">.</div>
Copy the code
.bg {
  position: absolute;
  left: 0;
  top: 0;
  height: 100vh;
  width: 100vw;
  background: linear-gradient(0deg.#171a4b.#96367f);
  opacity: 0;
  transition: all 0.5 s;
}
#switch:checked ~ .bg {
  opacity: 1;
}
#switch:checked ~ .chinese-knot {
  opacity: 0.2;
  &:hover {
    opacity: 0.5; }}Copy the code

End scatter flower ~~~🎉🎉

conclusion

This article organized the whole process of creating a work from the beginning of collecting materials. It took me a day to write the code and half a day to write this article. Hope to let CSS beginners to CSS ignited interest, also hope to let contact with a period of time CSS friends get some inspiration and help.

If you want to watch me create more interesting projects, please like and bookmark them ❤! (Writing this is really tiring ~)