能写好这些CSS特效代码真的不简单

6ab80b5cfdaa5ef48dd39f8caf704086.png

3D 方块

如何在 CSS 中创建立体的方块呢?用以下的 SCSS mixin 即可方块的长度、高度、深度都可以通过 CSS 变量自由调节

@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;
}

交错旋转

给多个方块应用交错动画会产生如下效果b11851df408abbce6d919b4f2286af47.png

.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);
  }
}

本 demo 地址:Spiral Tower[1]

伸缩长度

在 CSS 动画中,我们无法直接使变量动起来(其实能动,但很生硬)这时我们就得求助于 CSS Houdini,将变量声明为长度单位类型即可,因为长度单位是可以动起来的

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,
});

a527acbe7b86a9b00ad4f00595450223.png本 demo 地址:3D Stair Loading[2]

文本分割

在上一篇我们提到了如何用 JS 来分割文本,本篇将介绍一种更简洁的实现方法——gsap 的 SplitText 插件,利用它我们能用更少的代码来实现下图的效果

<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,
});

99ba5409b4e2828e4194890fff923451.png简化版 demo 地址:SplitText Starter[3]

关键帧

简单的动画固然可以实现,那么相对复杂一点的动画呢?这时候还是要依靠强大的@keyframes 和 CSS 变量注:尽管 gsap 目前也支持 keyframes,但是无法和交错动画结合起来,因此用@keyframes 作为替代方案

<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",
});

53f913b735430332c74255d80b8aea58.png本 demo 地址:Staggered Scale In Text[4]

SVG 滤镜

CSS 的滤镜其实都是 SVG 滤镜的封装版本,方便我们使用而已SVG 滤镜则更加灵活强大,以下是几个常见的滤镜使用场景附在线调试 SVG 滤镜的网站:SVG Filters[5]

粘滞效果

<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");
}

4f200bda3394f1c98e97c35a37ebd3c6.png本 demo 地址:SVG Filter Gooey Menu[6]

故障效果

<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");
}

449df4afeb3ea86409f3a788e06035c0.png本 demo 地址:SVG Filter Glitch Button[7]

动态模糊

CSS 滤镜的 blur 是全方位模糊,而 SVG 滤镜的 blur 可以控制单方向的模糊

<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");
}

13cd308f4129ccdecfd1dad346992367.png本 demo 地址:SVG Filter Motion Blur[8]

mask 遮罩

有时候我们想做出一种过渡式的半透明效果,类似下图这样的d47f55ed6a50959c7a3cd59b7db61ab7.png这时候就得借助 mask 属性了,因为图片与 mask 生成的渐变的 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 地址:Gradient Mask Divider[9]和 clip-path 结合也会相当有意思,如下图所示的加载特效b34f994a04d80217c6ca9a30273d186a.pngdemo 地址:Mask Loader[10]

CSS 变量

鼠标跟踪

上篇提到了利用 Web Animations API 实现鼠标悬浮跟踪的效果,但其实 CSS 变量也能实现,而且更加简洁高效在 CSS 中定义 x 和 y 变量,然后在 JS 中监听鼠标移动事件并获取鼠标坐标,更新对应的 x 和 y 变量即可

: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;
});

85c693c1ebc23b81cf424a1fe0a9b205.png简化版地址:Mousemove Starter[11]

残影效果

如果将鼠标跟踪和交错动画结合起来,再加点模糊滤镜,就能创作出帅气的残影效果8d08eb6e0ea74478e86488be2351ead7.png本 demo 地址:Motion Table - Delay[12]

图片分割

为了做出一个图片碎片运动相关的动画,或者是一个拼图游戏,我们就要对一张图片进行分割,且块数、大小等都能随意控制,这时CSS变量就能发挥它的用场了7e5c27de1658942cd063c198108563f4.png

.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. 设定好分割的行列,根据行列来动态计算切片的大小

  2. 拼图的总宽|高=拼图宽|高+列|行数 * 间隙 * 2

  3. 切片的显示利用背景定位的xy轴偏移,偏移量的计算方式:x|y坐标 * 切片宽|高 * -1

在JS中,设定好变量值并动态生成切片的xy坐标,即可完成图片的分割

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"));

本demo地址:Split Image With CSS Variable[13]

复杂动画

案例1

15e6e0803b0f458f3039c3ea96a0eb2f.png本demo地址:Elastic Love[14]

案例2

27d961ab71e48fa533669d714903a0e2.png本demo地址:Infinite Line Animation[15]

案例3

2e4285e4414fbc52ad9063a008343a8c.png本demo地址:Orbit Reverse[16]

案例4

76672fd1614061c168243b102804cb2a.png本demo地址:Motion Table - Solid Rotation[17]

案例5

a00c51ceff31d4ee7d005f7bd39af58e.png本demo地址:Motion Table - Symmetric Move[18]

小结

以上几个复杂的动画或多或少都有以下的特征:

  1. div很多,对布局的要求很高

  2. @keyframes很多,对动画的要求很高

  3. 有的动画有较多的3d变换

案例5的教程已经写在之前的博文“画物语——CSS动画之美[19]”里了,其余案例亦可以用此文提到的方法进行研究笔者的CSS动画作品全放在这个集合里了:CSS Animation Collection[20]

彩蛋

螺旋阶梯动画(灵感来自灰色的果实OP)38eabbba9ac96ddae03ad6ac3b57bbc9.png本demo地址:Spiral Stair Loading[21]

作者:alphardex
链接:https://juejin.cn/post/6881546676188741645

参考资料(更多链接请阅读原文)

[1]

https://codepen.io/alphardex/pen/mdPjLGm

[2]

https://codepen.io/alphardex/pen/YzqrGXb

[3]

https://codepen.io/alphardex/pen/ZEWRBJp

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 我可以帮助你一个炸开的 CSS 特效。这是一个例子: HTML: ``` <div class="exploding-box">点击爆炸</div> ``` CSS: ```css .exploding-box { display: inline-block; width: 200px; height: 200px; background-color: red; border-radius: 50%; text-align: center; line-height: 200px; color: white; font-size: 24px; cursor: pointer; transition: transform 0.5s; } .exploding-box:active { transform: scale(1.5); } .exploding-box::after { content: ""; display: block; position: absolute; top: 0; left: 0; width: 200px; height: 200px; background-color: red; border-radius: 50%; transform: scale(0); transition: transform 0.5s; } .exploding-box:active::after { transform: scale(1); } ``` 你可以使用这个代码作为起点,并调整样式来达到你想要的爆炸效果。 ### 回答2: 为了回答你的问题,我给你设计了一个基于CSS的爆炸特效。 首先,你需要一个有背景的HTML元素,可以是一个`div`或者其他。例如: ```html <div class="explosion"></div> ``` 然后,你需要在CSS中为这个元素设置样式,并且使用关键帧动画来实现爆炸特效。例如: ```css .explosion { width: 100px; height: 100px; background: red; position: relative; animation: explode 1s ease infinite; } @keyframes explode { 0% { width: 100px; height: 100px; opacity: 1; transform: scale(1); } 100% { width: 300px; height: 300px; opacity: 0; transform: scale(2); } } ``` 在上面的CSS代码中,我们定义了一个名为`.explosion`的类,设置了元素的宽度、高度、背景颜色,并且指定了一个名为`explode`的动画,持续时间为1秒,以缓慢的速度进行无限次播放。 在`@keyframes`规则内,我们定义了动画的关键帧。在0%位置,元素的大小是初始的100x100像素,完全不透明,并且没有任何缩放。在100%位置,元素的大小变成了300x300像素,完全透明,并且放大了2倍。 如果你将上面的HTML和CSS代码放入一个HTML文件,并且将该文件在浏览器中打开,你将会看到一个有所述爆炸特效的红色方块。 这只是一个简单的爆炸特效示例,你可以根据你的需要进行更多的定制和改进。希望这能帮到你! ### 回答3: 当然可以帮你实现一个爆炸的CSS特效!以下是一个简单的示例: 首先,我们需要一个HTML元素,例如一个div,作为爆炸特效的容器。在CSS中,我们给这个容器设置一个背景颜色,宽度和高度,并将其位置设置为相对定位。 ```html <div class="explosion"></div> ``` ```css .explosion { position: relative; width: 100px; height: 100px; background-color: red; } ``` 接下来,我们使用CSS动画来创建爆炸的特效。我们将使用关键帧动画(@keyframes)来定义特效的起始状态和结束状态。 ```css @keyframes explode { 0% { transform: scale(1); opacity: 1; } 100% { transform: scale(10); opacity: 0; } } ``` 在该动画中,我们将元素从原始大小(1倍)放大到10倍,并将不透明度从完全可见(1)变为完全透明(0)。 为了将这个动画应用到我们的容器中,我们可以使用animation属性。我们可以设置动画的名称(explode),持续时间(2秒)和动画的播放方式(ease-in-out)。 ```css .explosion { position: relative; width: 100px; height: 100px; background-color: red; animation: explode 2s ease-in-out; } ``` 现在,当我们加载这个页面时,我们将看到爆炸特效。这个容器会从原始大小爆炸到10倍大小,并逐渐消失。 请注意,这只是一个简单的示例,你可以根据自己的需要进行修改和扩展。你可以尝试改变颜色、形状、动画时间等,以创造出更多丰富多样的爆炸特效!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值