preface

Hi, here is CSS and WebGL magic – Alphardex.

I’ve been playing three.js, and I’ve been exposed to a lot of mathematical functions and created a lot of special effects with them. So I thought: could I use these mathematical functions in CSS, but FOUND that CSS is not yet, it is said that a new specification will be included in the future, I guess it will take a long time to wait.

However, there are a few tricks you can use to create your own CSS math functions to achieve some interesting animations.

Let’s get started!

CSS mathematical functions

Note: the following functions can be implemented with native CSS, SCSS function is used here just for easy encapsulation, encapsulation is more convenient to call

The absolute value

Absolute value is positive or positive, negative becomes positive

You can create two numbers, one of which is the opposite of the other, and compare their maximum value to get the absolute value of that number

@function abs($v) {
  @return max(#{$v}, calc(-1 * #{$v}));
}
Copy the code

The median

Subtract 1 and multiply by half

@function middle($v) {
  @return calc(0.5* (# {$v} - 1));
}
Copy the code

The distance between two points on the number line

The distance between two points on the number line is the absolute value of the difference between the numbers represented by the two points

@function dist-1d($v1.$v2) {
  $v-delta: calc(#{$v1# {} -$v2});
  @return #{abs($v-delta)};
}
Copy the code

Trigonometric functions

Chokcoco wrote an article about how to implement trigonometry in CSS. Thank you

@function fact($number) {
  $value: 1;
  @if $number>0 {
    @for $i from 1 through $number {
      $value: $value * $i; }}@return $value;
}

@function pow($number.$exp) {
  $value: 1;
  @if $exp>0 {
    @for $i from 1 through $exp {
      $value: $value * $number; }}@else if $exp < 0 {
    @for $i from 1 through -$exp {
      $value: $value / $number; }}@return $value;
}

@function rad($angle) {
  $unit: unit($angle);
  $unitless: $angle / ($angle * 0 + 1);
  @if $unit==deg {
    $unitless: $unitless / 180 * pi();
  }
  @return $unitless;
}

@function pi() {
  @return 3.14159265359;
}

@function sin($angle) {
  $sin: 0;
  $angle: rad($angle);
  // Iterate a bunch of times.
  @for $i from 0 through 20 {
    $sin: $sin + pow(-1.$i) * pow($angle, (2 * $i + 1)) / fact(2 * $i + 1);
  }
  @return $sin;
}

@function cos($angle) {
  $cos: 0;
  $angle: rad($angle);
  // Iterate a bunch of times.
  @for $i from 0 through 20 {
    $cos: $cos + pow(-1.$i) * pow($angle.2 * $i) / fact(2 * $i);
  }
  @return $cos;
}

@function tan($angle) {
  @return sin($angle) / cos($angle);
}
Copy the code

example

The following animation effects demonstrate the above mathematical functions in action

One-dimensional staggered animation

The initial state

Create a row of elements, fill them with inner shadows, and prepare our math function

<div class="list">
  <div class="list-item"></div>. (14 list-items omitted here)<div class="list-item"></div>
</div>
Copy the code
body {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  margin: 0;
  background: # 222;
}

:root {
  --blue-color1:#6ee1f5; } (Copy and paste all the mathematical formulas above here).list {
  --n: 16;

  display: flex;
  flex-wrap: wrap;
  justify-content: space-evenly;

  &-item {
    --p: 2vw;
    --gap: 1vw;
    --bg: var(--blue-color-1);

    @for $i from 1 through 16{&:nth-child(#{$i}) {
        --i: # {$i}; }}padding: var(--p);
    margin: var(--gap);
    box-shadow: inset 0 0 0var(--p) var(--bg); }}Copy the code

Application of animation

Two animations are used here: Grow is responsible for scaling elements out; Melt is responsible for “melting” elements (that is, eliminating the radius of shadow spread)

<div class="list grow-melt">
  <div class="list-item"></div>. (14 list-items omitted here)<div class="list-item"></div>
</div>
Copy the code
.list{&.grow-melt {
    .list-item {
      --t: 2s;

      animation-name: grow, melt;
      animation-duration: var(--t);
      animation-iteration-count: infinite; }}}@keyframes grow {
  0% {
    transform: scale(0); Were 50% and 100%} {transform: scale(1); }}@keyframes melt {
  0%,
  50% {
    box-shadow: inset 0 0 0var(--p) var(--bg); 100%} {box-shadow: inset 0 0 0 0var(--bg); }}Copy the code

Staggered animation

  1. Calculates the median of the element’s subscripts
  2. Calculates the distance from each element ID to the median
  3. Calculate the scale based on the distance
  4. Calculate the delay according to the scale
<div class="list grow-melt middle-stagger">
  <div class="list-item"></div>. (14 list-items omitted here)<div class="list-item"></div>
</div>
Copy the code
.list{&.middle-stagger {
    .list-item {
      --m: #{middle(var(--n))}; // The median is 7.5
      --i-m-dist: #{dist-1d(var(--i), var(--m))}; // Calculate the distance between each ID and the median
      --ratio: calc(var(--i-m-dist) / var(--m)); // Calculate the scale according to the distance
      --delay: calc(var(--ratio) * var(--t)); // Calculate the delay according to the scale
      --n-delay: calc((var(--ratio) - 2) * var(--t)); // Negative delay means animation starts early

      animation-delay: var(--n-delay); }}}Copy the code

Address: Symmetric Line Animation

Two dimensional staggered animation

The initial state

How do I get from one dimension to two? Apply the grid system

<div class="grid">
  <div class="grid-item"></div>. (62 grid-items omitted here)<div class="grid-item"></div>
</div>
Copy the code
.grid {
  $row: 8;
  $col: 8;
  --row: #{$row};
  --col: #{$col};
  --gap: 0.25 vw;

  display: grid;
  gap: var(--gap);
  grid-template-rows: repeat(var(--row), 1fr);
  grid-template-columns: repeat(var(--col), 1fr);

  &-item {
    --p: 2vw;
    --bg: var(--blue-color-1);

    @for $y from 1 through $row {
      @for $x from 1 through $col {
        $k: $col * ($y - 1) + $x;
        &:nth-child(#{$k}) {
          --x: #{$x};
          --y: #{$y}; }}}padding: var(--p);
    box-shadow: inset 0 0 0var(--p) var(--bg); }}Copy the code

Application of animation

It’s exactly like the previous animation

<div class="grid grow-melt">
  <div class="grid-item"></div>. (62 grid-items omitted here)<div class="grid-item"></div>
</div>
Copy the code
.grid{&.grow-melt {
    .grid-item {
      --t: 2s;

      animation-name: grow, melt;
      animation-duration: var(--t);
      animation-iteration-count: infinite; }}}Copy the code

Staggered animation

  1. Calculates the median of the grid rows and columns
  2. Compute the distance from the grid xy coordinates to the median and sum
  3. Calculate the scale based on the distance
  4. Calculate the delay according to the scale
<div class="grid grow-melt middle-stagger">
  <div class="grid-item"></div>. (62 grid-items omitted here)<div class="grid-item"></div>
</div>
Copy the code
.grid{&.middle-stagger {
    .grid-item {
      --m: #{middle(var(--col))}; // The median is 7.5
      --x-m-dist: #{dist-1d(var(--x), var(--m))}; // Calculate the distance from the x coordinate to the median
      --y-m-dist: #{dist-1d(var(--y), var(--m))}; // Calculate the distance from y to the median
      --dist-sum: calc(var(--x-m-dist) + var(--y-m-dist)); // The sum of distances
      --ratio: calc(var(--dist-sum) / var(--m)); // Calculate the scale based on distance and distance
      --delay: calc(var(--ratio) * var(--t) * 0.5); // Calculate the delay according to the scale
      --n-delay: calc(
        (var(--ratio) - 2) * var(--t) * 0.5
      ); // Negative delay means animation starts early

      animation-delay: var(--n-delay); }}}Copy the code

Address: Symmetric Grid Animation

Another kind of animation

Shuffle, an alternate animation, produces another peculiar effect

<div class="grid shuffle middle-stagger">
  <div class="grid-item"></div>. (254 grid-items omitted here)<div class="grid-item"></div>
</div>
Copy the code
.grid {
  $row: 16;
  $col: 16;
  --row: #{$row};
  --col: #{$col};
  --gap: 0.25 vw;

  &-item {
    --p: 1vw;

    transform-origin: bottom;
    transform: scaleY(0.1);
  }

  &.shuffle {
    .grid-item {
      --t: 2s;

      animation: shuffle var(--t) infinite ease-in-out alternate; }}}@keyframes shuffle {
  0% {
    transform: scaleY(0.1); 50%} {transform: scaleY(1);
    transform-origin: bottom;
  }

  50.01% {
    transform-origin: top; 100%} {transform-origin: top;
    transform: scaleY(0.1); }}Copy the code

Address: Shuffle Grid Animation

Cosine wave animation

The initial state

Create 7 different colored lists, each with 40 child elements, and each child element is a dot

Arrange the 7 lists in a line with staggered distances on the Z-axis, and set the basic delay

<div class="lists">
  <div class="list">
    <div class="list-item"></div>. (39 list-items omitted here)</div>. (6 lists omitted here)</div>
Copy the code
.lists {
  $list-count: 7;
  $colors: red, orange, yellow, green, cyan, blue, purple;

  position: relative;
  width: 34vw;
  height: 2vw;
  transform-style: preserve-3d;
  perspective: 800px;

  .list {
    position: absolute;
    top: 0;
    left: 0;
    display: flex;
    transform: translateZ(var(--z));

    @for $i from 1 through $list-count{&:nth-child(#{$i}) {
        --bg: #{nth($colors.$i)};
        --z: #{$i * -1vw};
        --basic-delay-ratio: #{$i / $list-count};
      }
    }

    &-item {
      --w: 0.6 vw;
      --gap: 0.15 vw;

      width: var(--w);
      height: var(--w);
      margin: var(--gap);
      background: var(--bg);
      border-radius: 50%; }}}Copy the code

Cosine arrangement

Using the trigonometric formula above, arrange the dots as part of the cosine

.lists {
  .list {
    &-item {
      $item-count: 40;
      $offset: pi() * 0.5;
      --wave-length: 21vw;

      @for $i from 1 through $item-count{&:nth-child(#{$i}) {
          --i: # {$i};
          $ratio: ($i - 1)/($item-count - 1);
          $angle-unit: pi() * $ratio;
          $wave: cos($angle-unit + $offset);
          --single-wave-length: calc(#{$wave} * var(--wave-length));
          --n-single-wave-length: calc(var(--single-wave-length) * -1); }}transform: translateY(var(--n-single-wave-length)); }}}Copy the code

Fluctuations in the animation

Apply an up and down translation animation to each dot. The translation distance is the fluctuation distance of the cosine

.lists {
  .list {
    &-item {
      --t: 2s;

      animation: wave var(--t) infinite ease-in-out alternate; }}}@keyframes wave {
  from {
    transform: translateY(var(--n-single-wave-length));
  }

  to {
    transform: translateY(var(--single-wave-length)); }}Copy the code

Staggered animation

Follow the above routine, calculate the delay from the middle, and then apply it to the animation

.lists {
  .list {
    &-item {
      --n: #{$item-count + 1};
      --m: #{middle(var(--n))};
      --i-m-dist: #{dist-1d(var(--i), var(--m))};
      --ratio: calc(var(--i-m-dist) / var(--m));
      --square: calc(var(--ratio) * var(--ratio));
      --delay: calc(
        calc(var(--square) + var(--basic-delay-ratio) + 1) * var(--t)
      );
      --n-delay: calc(var(--delay) * -1);

      animation-delay: var(--n-delay); }}}Copy the code

Address: Rainbow Sine

The last

CSS mathematical functions can achieve much more than this, I hope this article can inspire you to create special effects ~