preface

Portal: Jabber here

In fact, most of the techniques have been covered in the previous article, this is a supplementary article. 0.0

3 d cube

How do you create three-dimensional squares in CSS? Use the following SCSS mixin

The length, height, and depth of the block can be adjusted freely using CSS variables

@mixin cube($width.$height.$depth) {
  &__front {
    @include cube-front($width.$height.$depth);
  }
  &__back {
    @include cube-back($width.$height.$depth);
  }
  &__right {
    @include cube-right($width.$height.$depth);
  }
  &__left {
    @include cube-left($width.$height.$depth);
  }
  &__top {
    @include cube-top($width.$height.$depth);
  }
  &__bottom {
    @include cube-bottom($width.$height.$depth);
  }
  .face {
    position: absolute; }}@mixin cube-front($width.$height.$depth) {
  width: var($width);
  height: var($height);
  transform-origin: bottom left;
  transform: rotateX(-90deg) translateZ(calc(calc(var(#{$depth}) * 2) - var(#{$height})));
}

@mixin cube-back($width.$height.$depth) {
  width: var($width);
  height: var($height);
  transform-origin: top left;
  transform: rotateX(-90deg) rotateY(180deg) translateX(calc(var(#{$width* -})1)) translateY(
      calc(var(#{$height* -})1)); }@mixin cube-right($width.$height.$depth) {
  width: calc(var(#{$depth}) * 2);
  height: var($height);
  transform-origin: top left;
  transform: rotateY(90deg) rotateZ(-90deg) translateZ(var(#{$width})) translateX(calc(var(#{$depth* -})2)) translateY(calc(var(
            #{$height* -})1));
}

@mixin cube-left($width.$height.$depth) {
  width: calc(var(#{$depth}) * 2);
  height: var($height);
  transform-origin: top left;
  transform: rotateY(-90deg) rotateZ(90deg) translateY(calc(var(#{$height* -})1));
}

@mixin cube-top($width.$height.$depth) {
  width: var($width);
  height: calc(var(#{$depth}) * 2);
  transform-origin: top left;
  transform: translateZ(var($height));
}

@mixin cube-bottom($width.$height.$depth) {
  width: var($width);
  height: calc(var(#{$depth}) * 2);
  transform-origin: top left;
  transform: rotateY(180deg) translateX(calc(var(#{$width* -})1));
}

.cube {
  --cube-width: 3rem;
  --cube-height: 3rem;
  --cube-depth: 1.5 rem;

  @include cube(--cube-width, --cube-height, --cube-depth);
  width: 3rem;
  height: 3rem;
}
Copy the code

Alternating rotation

Applying staggered animation to multiple blocks produces the following effect

.spiral-tower {
  display: grid;
  grid-auto-flow: row;
  transform: rotateX(-30deg) rotateY(45deg);

  .cube {
    @for $i from 1 through 48{&:nth-child(#{$i}) {
        animation-delay: 0.015 s * ($i - 1); }}}}@keyframes spin {
  0%,
  15% {
    transform: rotateY(0); Were 85% and 100%} {transform: rotateY(1turn); }}Copy the code

This demo address: Spiral Tower

The length of the scale

In CSS animations, we can’t animate variables directly (we can animate them, but it’s hard)

In this case, CSS Houdini is used to declare variables as units of length, because units of length can be moved

CSS.registerProperty({
  name: "--cube-width".syntax: "<length>".initialValue: 0.inherits: true}); CSS.registerProperty({name: "--cube-height".syntax: "<length>".initialValue: 0.inherits: true}); CSS.registerProperty({name: "--cube-depth".syntax: "<length>".initialValue: 0.inherits: true});Copy the code

Demo address:3D Stair Loading

Text segmentation

In the last article we discussed how to use javascript to SplitText. In this article we will introduce a simpler way to do this — gsap’s SplitText plugin

<div class="staggered-land-in font-bold text-2xl">Fushigi no Monogatari</div>
Copy the code
const t1 = gsap.timeline();
const staggeredLandInText = new SplitText(".staggered-land-in", {
  type: "chars"}); t1.from(staggeredLandInText.chars, {duration: 0.8.opacity: 0.y: "20%".stagger: 0.05});Copy the code

Simplified demo address: SplitText Starter

Key frames

Simple animations can be achieved, but what about relatively complex animations? Again, rely on the powerful @Keyframes and CSS variables

Note: Although KEYframes are currently supported by GSAP, they cannot be combined with interlaced animation, so @Keyframes is used as an alternative

<div class="staggered-scale-in font-bold text-6xl">Never Never Give Up</div>
Copy the code
.scale-in-bounce {
  animation: scale-in-bounce 0.4 s both;
  animation-delay: calc(0.1 s * var(--i));
}

@keyframes scale-in-bounce {
  0% {
    opacity: 0;
    transform: scale(2);
  }

  40% {
    opacity: 1;
    transform: scale(0.8);
  }

  100% {
    opacity: 1;
    transform: scale(1); }}Copy the code
const t1 = gsap.timeline();
const staggeredScaleInText = new SplitText(".staggered-scale-in", {
  type: "chars"});const staggeredScaleInChars = staggeredScaleInText.chars;
staggeredScaleInChars.forEach((item, i) = > {
  item.style.setProperty("--i".`${i}`);
});
t1.to(staggeredScaleInChars, {
  className: "scale-in-bounce"});Copy the code

Staggered Scale In Text

SVG filter

CSS filters are actually encapsulated versions of SVG filters for our convenience

SVG filters are more flexible and powerful, and here are a few common filter scenarios

SVG Filters is a website for debugging SVG Filters online

Viscous effect

<svg width="0" height="0" class="absolute">
  <filter id="goo">
    <feGaussianBlur stdDeviation="10 10" in="SourceGraphic" result="blur" />
    <feColorMatrix
      type="matrix"
      values="1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 18-7"
      in="blur"
      result="colormatrix"
    />
    <feComposite in="SourceGraphic" in2="colormatrix" operator="over" result="composite" />
  </filter>
</svg>
Copy the code
.gooey {
  filter: url("#goo");
}
Copy the code

SVG Filter Gooey Menu

The fault effect

<svg width="0" height="0" class="absolute">
  <filter id="glitch">
    <feTurbulence type="fractalNoise" baseFrequency="0.00001 0.000001" numOctaves="1" result="turbulence1">
      <animate
        attributeName="baseFrequency"
        from="0.00001 0.000001"
        to="0.00001 0.4"
        dur="0.4 s"
        id="glitch1"
        fill="freeze"
        repeatCount="indefinite"
      ></animate>
      <animate
        attributeName="baseFrequency"
        from="0.00001 0.4"
        to="0.00001 0.2"
        dur="0.2 s"
        begin="glitch1.end"
        fill="freeze"
        repeatCount="indefinite"
      ></animate>
    </feTurbulence>
    <feDisplacementMap
      in="SourceGraphic"
      in2="turbulence1"
      scale="30"
      xChannelSelector="R"
      yChannelSelector="G"
      result="displacementMap"
    />
  </filter>
</svg>
Copy the code
.glitch {
  filter: url("#glitch");
}
Copy the code

SVG Filter Glitch Button

The dynamic fuzzy

Blur of CSS filter is omnidirectional blur, while blur of SVG filter can control blur in one direction

<svg width="0" height="0" class="absolute">
  <filter id="motion-blur" filterUnits="userSpaceOnUse">
    <feGaussianBlur stdDeviation="100" in="SourceGraphic" result="blur">
      <animate dur="0.6 s" attributeName="stdDeviation" from="100" to="0 0" fill="freeze"></animate>
    </feGaussianBlur>
  </filter>
</svg>
Copy the code
.motion-blur {
  filter: url("#motion-blur");
}
Copy the code

SVG Filter Motion Blur

Mask mask

Sometimes we want to create a transitional translucent effect, like the one below

This is where the mask property is used, because the overlap between the image and the gradient transparent generated by the mask becomes transparent

.divider-grad-mask {
  background: linear-gradient(90deg.var(--blue-color) 0 50%, transparent 0 100%) 0 0 / 2rem 1rem;
  mask: linear-gradient(-90deg, black, transparent);
}
Copy the code

Demo address: Gradient Mask Divider

It can also be quite interesting when combined with clip-path, as shown in the loading effects below

Demo address: Mask Loader

CSS variable

The mouse tracking

In the previous article, I mentioned using the Web Animations API to achieve mouse hover tracking, but CSS variables can also be used, and are much cleaner and more efficient

Define x and Y variables in CSS, then listen for mouse movement events in JS and get mouse coordinates, update the corresponding X and Y variables

:root {
  --mouse-x: 0;
  --mouse-y: 0;
}

.target {
  transform: translate(var(--mouse-x), var(--mouse-y));
}
Copy the code
let mouseX = 0;
let mouseY = 0;
let x = 0;
let y = 0;
let offset = 50; // center
let windowWidth = window.innerWidth;
let windowHeight = window.innerHeight;
const percentage = (value, total) = > (value / total) * 100;

window.addEventListener("mousemove".(e) = > {
  mouseX = e.clientX;
  mouseY = e.clientY;
  x = percentage(mouseX, windowWidth) - offset;
  y = percentage(mouseY, windowHeight) - offset;
  document.documentElement.style.setProperty("--mouse-x".`${x}% `);
  document.documentElement.style.setProperty("--mouse-y".`${y}% `);
});

window.addEventListener("resize".() = > {
  windowWidth = window.innerWidth;
  windowHeight = window.innerHeight;
});
Copy the code

Address: Mousemove Starter

The ghosting effect

If you combine mouse tracking with interleaved animation, and add a blur filter, you can create a handsome residual effect

The demo address is Motion table-delay

Image segmentation

To make an animation of the motion of the pieces of an image, or a jigsaw puzzle, we need to split an image and control the number of pieces, size, and so on. This is where CSS variables come in handy

.puzzle {
  --puzzle-width: 16rem;
  --puzzle-height: 24rem;
  --puzzle-row: 3;
  --puzzle-col: 4;
  --puzzle-gap: 1px;
  --puzzle-frag-width: calc(var(--puzzle-width) / var(--puzzle-col));
  --puzzle-frag-height: calc(var(--puzzle-height) / var(--puzzle-row));
  --puzzle-img: url(...) ;display: flex;
  flex-wrap: wrap;
  width: calc(var(--puzzle-width) + calc(var(--puzzle-col) * var(--puzzle-gap) * 2));
  height: calc(var(--puzzle-height) + calc(var(--puzzle-row) * var(--puzzle-gap) * 2));

  .fragment {
    --x-offset: calc(var(--x) * var(--puzzle-frag-width) * -1);
    --y-offset: calc(var(--y) * var(--puzzle-frag-height) * -1);
 
    width: var(--puzzle-frag-width);
    height: var(--puzzle-frag-height);
    margin: var(--puzzle-gap);
    background: var(--puzzle-img) var(--x-offset) var(--y-offset) / var(--puzzle-width) var(--puzzle-height) no-repeat; }}Copy the code
  1. Set a good division of the row, according to the row to dynamically calculate the size of the slice
  2. Jigsaw puzzle high total width | = | wide high + column | line number * 2 * clearance
  3. Slice display using the background position x, y axis offset, the offset calculation: x | y * sliced wide | high * 1

In JS, set variable values and dynamically generate xy coordinates of slices to complete image segmentation

class Puzzle {
  constructor(el, width = 16, height = 24, row = 3, col = 3, gap = 1) {
    this.el = el;
    this.fragments = el.children;
    this.width = width;
    this.height = height;
    this.row = row;
    this.col = col;
    this.gap = gap;
  }

  create() {
    this.ids = [...Array(this.row * this.col).keys()];
    const puzzle = this.el;
    const fragments = this.fragments;
    if (fragments.length) {
      Array.from(fragments).forEach((item) = > item.remove());
    }
    puzzle.style.setProperty("--puzzle-width".this.width + "rem");
    puzzle.style.setProperty("--puzzle-height".this.height + "rem");
    puzzle.style.setProperty("--puzzle-row".this.row);
    puzzle.style.setProperty("--puzzle-col".this.col);
    puzzle.style.setProperty("--puzzle-gap".this.gap + "px");
    for (let i = 0; i < this.row; i++) {
      for (let j = 0; j < this.col; j++) {
        const fragment = document.createElement("div");
        fragment.className = "fragment";
        fragment.style.setProperty("--x", j);
        fragment.style.setProperty("--y", i);
        fragment.style.setProperty("--i", j + i * this.col); puzzle.appendChild(fragment); }}}}const puzzle = new Puzzle(document.querySelector(".puzzle"));
Copy the code

Split Image With CSS Variable

Complex animation

Case 1

Demo address: Elastic Love

Case 2

This demo address: Infinite Line Animation

Case 3

This demo address: Orbit Reverse

Case 4

The demo address is Motion table-solid Rotation

Case 5

The demo address is Motion Table-Symmetric Move

summary

The above complex animations have more or less the following characteristics:

  1. divA lot, very high requirements for layout
  2. @keyframesA lot. It’s very demanding for animation
  3. Some animations have more 3D transformations

The tutorial for Case 5 was written in a previous post on “Drawing Objects – The Beauty of CSS Animation”, and the rest of the cases can be studied using the same methods described in this post

All the author’s CSS Animation works are in this Collection: CSS Animation Collection

eggs

Spiral Staircase animation (inspired by Grey Fruit OP)

Demo address: Spiral Stair Loading