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
- Set a good division of the row, according to the row to dynamically calculate the size of the slice
- Jigsaw puzzle high total width | = | wide high + column | line number * 2 * clearance
- 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:
div
A lot, very high requirements for layout@keyframes
A lot. It’s very demanding for animation- 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