The Beauty of CSS 1

3D cube

How to create a three-dimensional box in CSS? Use the following SCSS mixin

The length, height and depth of the block can be adjusted freely through 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.5rem;

  @include cube(--cube-width, --cube-height, --cube-depth);
  width: 3rem;
  height: 3rem;
}
复制代码

staggered rotation

Applying a 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.015s * ($i - 1);
      }
    }
  }
}

@keyframes spin {
  0%,
  15% {
    transform: rotateY(0);
  }

  85%,
  100% {
    transform: rotateY(1turn);
  }
}
复制代码

Address of this demo: Spiral Tower

Telescopic length

In CSS animation, we can't animate variables directly (actually, but very bluntly)

At this time, we have to resort to CSS Houdini, just declare the variable as the length unit type, because the length unit 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,
});
复制代码

Address of this demo: 3D Stair Loading

text segmentation

In the previous article, we mentioned how to use JS to split text. This article will introduce a more concise implementation method - the SplitText plug-in of gsap. With it, we can use less code to achieve the effect shown in the figure below

<div class="staggered-land-in font-bold text-2xl">Fushigi no Monogatari</div>
复制代码
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,
});
复制代码

Simplified demo address: SplitText Starter

Keyframe

Simple animations can be achieved, but what about more complex animations? At this time, we still have to rely on the powerful @keyframes and CSS variables

Note: Although gsap currently supports keyframes, it cannot be combined with staggered animations, so use @keyframes as an alternative

<div class="staggered-scale-in font-bold text-6xl">Never Never Give Up</div>
复制代码
.scale-in-bounce {
  animation: scale-in-bounce 0.4s both;
  animation-delay: calc(0.1s * 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);
  }
}
复制代码
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",
});
复制代码

Address of this demo: Staggered Scale In Text

SVG filter

CSS filters are actually packaged versions of SVG filters, which are convenient for us to use

SVG filters are more flexible and powerful. Here are a few common filter usage scenarios

A website with online debugging SVG filters: SVG Filters

sticky 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 1 0 0 0
    0 0 1 0 0
    0 0 0 18 -7"
      in="blur"
      result="colormatrix"
    />
    <feComposite in="SourceGraphic" in2="colormatrix" operator="over" result="composite" />
  </filter>
</svg>
复制代码
.gooey {
  filter: url("#goo");
}
复制代码

This demo address: SVG Filter Gooey Menu

glitch 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.4s"
        id="glitch1"
        fill="freeze"
        repeatCount="indefinite"
      ></animate>
      <animate
        attributeName="baseFrequency"
        from="0.00001 0.4"
        to="0.00001 0.2"
        dur="0.2s"
        begin="glitch1.end"
        fill="freeze"
        repeatCount="indefinite"
      ></animate>
    </feTurbulence>
    <feDisplacementMap
      in="SourceGraphic"
      in2="turbulence1"
      scale="30"
      xChannelSelector="R"
      yChannelSelector="G"
      result="displacementMap"
    />
  </filter>
</svg>
复制代码
.glitch {
  filter: url("#glitch");
}
复制代码

This demo address: SVG Filter Glitch Button

motion blur

The blur of the CSS filter is all-round blur, while the blur of the SVG filter can control the blur in one direction

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

This demo address: SVG Filter Motion Blur

mask mask

Sometimes we want to make a transitional translucent effect, like the picture below

At this time, you have to use the mask attribute, because the overlapping part of the image and the gradient transparent generated by the mask will become 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);
}
复制代码

demo address: Gradient Mask Divider

Combining with clip-path will also be quite interesting, as shown in the loading effect shown in the figure below

demo address: Mask Loader

CSS variables

mouse tracking

The previous article mentioned the effect of using the Web Animations API to achieve mouse hover tracking, but in fact CSS variables can also be achieved, and it is more concise and efficient

Define x and y variables in CSS, then listen to mouse movement events in JS and get mouse coordinates, and update the corresponding x and y variables

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

.target {
  transform: translate(var(--mouse-x), var(--mouse-y));
}
复制代码
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;
});
复制代码

Simplified version address: Mousemove Starter

afterimage effect

If you combine mouse tracking and staggered animation, and add a little blur filter, you can create a handsome afterimage effect

Address of this demo: Motion Table - Delay

image segmentation

In order to make an animation related to the movement of picture fragments, or a jigsaw puzzle, we need to divide a picture, and the number and size of the pieces can be controlled at will. At this time, CSS variables can be used.

.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;
  }
}
复制代码
  1. Set the split row and column, and dynamically calculate the size of the slice according to the row and column
  2. Total Width of Puzzle | Height = Puzzle Width | Height + Columns | Number of Rows * Gap * 2
  3. The display of the slice uses the xy axis offset of the background positioning, and the calculation method of the offset is: x|y coordinate* slice width|height* -1

In JS, set the variable value and dynamically generate the xy coordinates of the slice to complete the 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"));
复制代码

This demo address: Split Image With CSS Variable

complex animation

Case 1

Address of this demo: Elastic Love

Case 2

Address of this demo: Infinite Line Animation

Case 3

Address of this demo: Orbit Reverse

Case 4

This demo address: Motion Table - Solid Rotation

Case 5

This demo address: Motion Table - Symmetric Move

summary

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

  1. divMany, high layout requirements
  2. @keyframesMany, high requirements for animation
  3. Some animations have more 3d transformations

The tutorial of Case 5 has been written in the previous blog post " Painting Story - The Beauty of CSS Animation ", and other cases can also be studied with the methods mentioned in this article

The author's CSS animation works are all placed in this collection: CSS Animation Collection

eggs

Spiral staircase animation (inspired by the gray fruit OP)

Address of this demo: Spiral Stair Loading

Reprinted from: Author: alphardex
 

Guess you like

Origin blog.csdn.net/Jensen_Yao/article/details/109531669