我至今没想到,我也能在 CSS 中实现 SVG 动画了

动画是网络中不可或缺的一部分。与互联网早期使用 GIF 图像不同,现在的动画更加细腻和高雅。设计师和前端开发者利用动画使网站看起来更加精致,不仅提升用户体验,还吸引用户关注重要的元素,以传达信息。

本篇文章我们就来一起学习学习如何在 CSS 中实现 SVG 动画。

开篇:CSS 与 SVG 相关核心概念

在实践动画之前,你需要了解 svg 的内部工作原理。SVG 与 HTML 类似,我们可以使用 XML语法定义 SVG 元素,并使用 CSS 对它们进行样式上的设置,你把它们当做是 HTML 一样就行。

不过,与 HTML 不同的是,SVG 元素专门用于绘制图形。例如,我们可以使用 来绘制矩形,使用 来绘制圆等等。svg 还定义了 用于绘制图形的元素。

SVG 元素的完整列表甚至包括 ,它允许你使用同步多媒体集成语言(SMIL)创建动画。然而,它的未来是不确定的,因为 Chromium 团队建议尽可能使用基于CSS 或javascript 的方法来创建 svg 动画。

而元素可用的属性取决于元素本身。例如 具有宽度和高度属性,而 元素具有定义其半径的 r 属性。

同时需要注意一点:虽然大多数HTML元素可以有子元素,但大多数 SVG 元素不能有子元素group 元素 是一个例外,因为可以使用它来同时对多个元素应用 CSS 样式。

元素及其属性

HTML 和 SVG 之间的另一个重要区别是我们如何定位元素,特别是通过给定的外部 < SVG > 元素的 viewBox 属性。

这个属性取值由四个数字组成,分别是:min-x、min-y、widthheight,中间用空格或逗号分隔。它们一起指定了我们希望浏览器呈现多少 SVG 图形。同时该区域将根据 元素的宽度和高度属性进行缩放,以适应视口的边界。

不过, 视口 viewport 的宽度和高度属性的比例可能确实不同于 viewBox 属性的宽度和高度部分的比例。

默认情况下,SVG 画布的长宽比将被保留,代价是 viewBox 比指定的要大,从而导致viewport 内呈现的字体更小。但是你可以通过 preserveAspectRatio 属性指定不同的行为。它能使我们能够独立绘制图像,并且无论上下文或渲染大小如何,所有元素都将正确定位。

下面我们一起来感受一下。

基础示例

CSS 的 transition 属性允许我们定义属性变化的速率和持续时间。

transition: margin-right 4s ease-in-out 1s; /* property name | duration | easing function | delay */ 

例如,下面这个例子,当你用鼠标悬停在 SVG 圆圈上时,它的颜色会发生变化,而不是立即从起始值跳到结束值。

jcode

<svg viewBox="0 0 300 200">
 <circle cx="150" cy="100" r="60" class="spot" />
svg>
html {
  height: 100%;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
}

svg {
  max-width: 50vw;
  max-height: 80vh;
}

.spot {
  fill: #204ecf;
  transition: fill 0.5s;
}

.spot:hover {
  fill: #03cc83;
}

我们可以为多个CSS属性定义过渡,每个属性都可以有单独的过渡值。然而,这种方法有两个明显的限制。

第一个限制是,当属性值发生变化时,会自动触发转换。这在某些场景下是不方便的。例如,我们不能有一个无限循环的动画。

第二个限制是转换总是有两个步骤:初始状态和最终状态。我们可以延长动画的持续时间,但不能添加不同的关键帧。

于是,这就催生了一个更强大的概念: CSS animation。使用 CSS animation,我们可以有多个关键帧和一个无限循环。例如下面这个例子:

jcode

<svg viewBox="0 0 300 200">
  <rect width="100%" height="100%" class="background" />
  <g class="cross">
    <line x1="130" y1="80" x2="170" y2="120" />
    <line x1="130" y1="120" x2="170" y2="80" />
  g>
svg>
@keyframes move-around {
  0% {
    transform: translate(-40%, -35%);
  }

  25% {
    transform: translate(40%, -35%);
  }

  50% {
    transform: translate(40%, 35%);
  }

  75% {
    transform: translate(-40%, 35%);
  }

  100% {
    transform: translate(-40%, -35%);
  }
}

html {
  height: 100%;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
}

svg {
  max-width: 50vw;
  max-height: 80vh;
}

.background {
  fill: #03cc83;
}

.cross {
  animation: move-around 5s infinite;
  stroke: #262d3d;
  stroke-width: 10px;
}

要在多个关键帧上使用 animation 属性,我们需要使用 @keyframes 规则来定义关键帧。关键帧的时间是用相对单位(百分比)来定义的。每个关键帧描述一个或多个 CSS 属性在那个时间点的值。CSS animation 将确保关键帧之间的平滑过渡。

我们使用 animation 属性将具有描述的关键帧的动画应用到所需的元素上。与 transition属性类似,它接受一个持续时间、一个缓和函数和一个延迟。

唯一的区别是第一个参数是我们的 @keyframes 称,而不是属性名称:

/* @keyframes name | duration | easing-function | delay */ animation: my-sliding-animation 3s linear 1s;

示例:为汉堡菜单添加切换动画

现在我们对svg动画的工作原理有了基本的了解。我们可以开始构建一个菜单切换的动画:

jcode

我们发现这个菜单能够巧妙地吸引了用户的注意力,告诉用户可以使用图标关闭菜单。

接下来我们来一起解析具体的代码。

首先我们创建一个 svg 元素,用于创建“汉堡”菜单图形:

<svg class="hamburger"> 
    <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--top" /> 
    <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--mid" /> 
    <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--bot" /> 
svg>

代码中,每行有两组属性。其中,x1y1 代表直线的起点坐标,而 x2y2 代表直线的终点坐标。你会发现我使用相对单位 % 来设置位置,这是一种确保图像内容调整大小以适应包含 SVG 元素的简单方法。虽然这种方法在这种情况下有效,但有一个很大的缺点:

我们无法维护以这种方式定位的元素的长宽比。为此,我们必须使用元素的 viewBox 属性。

注意,我们对 SVG 元素应用了 CSS 类,应用了一些基本样式。

在这个样式中,我们设置了 元素的大小,并更改光标类型以表明它是可单击的。但是要设置线条的颜色和粗细,我们将使用 stroke和stroke-width 属性。

.hamburger {
  width: 62px;
  height: 62px;
  cursor: pointer;
}
.hamburger__bar {
  stroke: white;
  stroke-width: 10%;
}

如果我们现在渲染,我们会看到所有三条线都有相同的大小和位置,彼此完全重叠。不幸的是,我们不能通过 CSS 独立地改变开始和结束的位置。但是我们可以使用 CSS transform 属性移动整个元素的顶部和底部的条:

.hamburger__bar--top {
  transform: translateY(-40%);
}
.hamburger__bar--bot {
  transform: translateY(40%);
}

通过移动 Y 轴上的条,我们最终得到了一个看起来不错的汉堡菜单图形。

现在继续编写菜单的第二个状态: 关闭按钮。

我们将依赖于应用于SVG元素的 .is-opened 类来在这两种状态之间切换。为了使结果更易于访问,让我们将SVG包装在

你可能感兴趣的:(CSS,css,前端)