3D square How to be in CSS How about creating a three-dimensional square in ? Use the following SCSS mixin that will do The length of the square 、 Height 、 Depth can be achieved by CSS Variables can be adjusted freely @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; } Copy code Alternate rotation Applying interlaced animation to multiple squares produces the following effects
.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); } } Copy code Ben demo Address :Spiral Tower Extension length stay CSS In the animation , We can't make variables move directly ( In fact, it can move , But it's stiff ) Then we have to turn to CSS Houdini, Declare the variable as a unit of length type , Because the unit of length can move 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 code
Ben demo Address :3D Stair Loading Text segmentation In the last article we mentioned how to use JS To split the text , This article will introduce a more concise implementation method ——gsap Of SplitText plug-in unit , With it, we can use less code to achieve the effect of the following figure
<div class="staggered-land-in font-bold text-2xl">Fushigi no Monogatari</div> Copy 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 code
Simplified edition demo Address :SplitText Starter Keyframes Simple animation can be realized , What about the more complicated animation ? At this time, we still have to rely on the strong @keyframes and CSS Variable notes : Even though gsap Currently supported keyframes, But it can't be combined with interlaced animation , So with @keyframes As an alternative
<div class="staggered-scale-in font-bold text-6xl">Never Never Give Up</div> Copy code .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); } } Copy 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 code
Ben demo Address :Staggered Scale In Text SVG Filter CSS The filters are actually SVG The packaged version of the filter , It's just convenient for us to use SVG Filters are more flexible and powerful , Here are some common filter usage scenarios With online debugging SVG Filter's website :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> Copy code .gooey { filter: url("#goo"); } Copy code
Ben demo Address :SVG Filter Gooey Menu Failure 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> Copy code .glitch { filter: url("#glitch"); } Copy code
Ben demo Address :SVG Filter Glitch Button Dynamic blur CSS Filter blur It's all-round blur , and SVG Filter blur It 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> Copy code .motion-blur { filter: url("#motion-blur"); } Copy code
Ben demo Address :SVG Filter Motion Blur mask Mask Sometimes we want to create a transitional translucent effect , Something like this
At this point, we have to rely on mask The attribute is , Because pictures and mask Generated gradual transparent The overlap 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); } Copy code demo Address :Gradient Mask Divider and clip-path It's going to be quite interesting to combine , As shown in the figure below
demo Address :Mask Loader CSS Variable Mouse tracking The last part mentioned the use of Web Animations API Realize the effect of mouse suspension tracking , But in fact CSS Variables can also be implemented , And more concise and efficient stay CSS In the definition of x and y Variable , And then in JS Monitor mouse movement events and get mouse coordinates in , Update the corresponding x and y Variable can :root { --mouse-x: 0; --mouse-y: 0; }
.target { transform: translate(var(--mouse-x), var(--mouse-y)); } Copy 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 code
Simplified address :Mousemove Starter Shadow effect If you combine mouse tracking with interlaced animation , Add a little blur filter , Can create a handsome shadow effect
Ben demo Address :Motion Table - Delay Image segmentation In order to make an animation about the motion of the image fragments , Or a jigsaw puzzle , We're going to segment an image , And the number of blocks 、 Size, etc. can be controlled at will , At this time CSS Variables can do their job
.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 code
Set up the split line , Dynamically calculate the slice size according to the row and column The total width of the puzzle | high = The puzzle is wide | high + Column | Row number * The gap * 2 Showing slices using the background xy Axis offset , How the offset is calculated :x|y coordinate * Slice width | high * -1
stay JS in , Set the variable value and dynamically generate sliced xy coordinate , Can 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")); Copy code Ben demo Address :Split Image With CSS Variable Complex animation Case study 1
Ben demo Address :Elastic Love Case study 2
Ben demo Address :Infinite Line Animation Case study 3
Ben demo Address :Orbit Reverse Case study 4
Ben demo Address :Motion Table - Solid Rotation Case study 5
Ben demo Address :Motion Table - Symmetric Move Summary The above several complex animations have more or less the following characteristics :
div quite a lot , The layout is very demanding @keyframes quite a lot , The demand for animation is very high Some animations have more 3d Transformation
Case study 5 The tutorial has been written in the previous blog post “ The story of painting ——CSS The beauty of animation ” In the , Other cases can also be studied with the method mentioned in this paper Author's CSS The animation works are all in this collection :CSS Animation Collection Colored eggs Spiral ladder animation ( Inspiration from the grey fruit OP)