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;
}
}
复制代码
- Set the split row and column, and dynamically calculate the size of the slice according to the row and column
- Total Width of Puzzle | Height = Puzzle Width | Height + Columns | Number of Rows * Gap * 2
- 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:
div
Many, high layout requirements@keyframes
Many, high requirements for animation- 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