捕捉动画状态

有许多方法可以为网站创建图标。 内联SVG具有可伸缩性,易于使用CSS进行修改,甚至可以进行动画处理。 如果您想了解更多有关使用内联SVG优点的信息,建议阅读内联SVG vs图标字体 。 随着浏览器支持的不断增加,再没有比现在更好的时间开始使用SVG了。 Snap Animation States是基于Snap.svg构建JavaScript插件,可帮助创建和扩展具有可缩放,可编辑SVG图标的图标库。 捕捉动画状态使您可以使用简单的架构轻松加载和设置这些SVG动画。

入门

让我们从基本的SVG汉堡菜单开始。 这是用 Affinity Designer ,但是还有许多其他免费的( Inkscape )和付费的( Adobe Illustrator )选项可用于制作矢量图像。

<svg width="100%" height="100%" viewBox="0 0 65 60" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:square;stroke-miterlimit:1.5;"  fill="none" stroke="#000" stroke-width="10">
   <g>
      <path class="hamburger-top" d="m 5,10 55,0" />
      <path class="hamburger-middle" d="m 5,30 55,0" />
   </g>
   <path class="hamburger-bottom" d="m 5,50 55,0" />
</svg>

尽管这是一个非常基本的SVG,但它仍然占据HTML文档中的多行。 如果要跨多个不同的网页在多个位置使用SVG,可能会很痛苦。 而且,如果您必须修改SVG,该怎么办? 然后,您要记住所有使用SVG的地方,以便您可以对其进行更新。 它不是很干净或可重复使用。 这就是“捕捉动画状态”所要解决的全部内容。

让我们继续使用相同的SVG,但是这次我们将使用插件将其加载到DOM。 插件的模式至少需要两个属性: selector: " .some-css-selector "svg: " svg string " 。 查看以下演示:

见笔Lydgoo由布里昂迪尔( @bkdiehl上) CodePen

您会在上方的笔中注意到,我将其称为icon-hamburger ,就像我将其称为字体图标一样。 只要记住, selector属性需要一个CSS选择器作为其值。

由于此插件是Snap.svg的扩展, 因此我们可以做更多的事情, Snap.svg是一个JavaScript库,用于创建和设置SVG动画。 因此,让我们看看为这个汉堡包图标提供一些基本动画需要些什么。

当我创建SVG时,我向知道要设置动画的元素添加了类。

<g>
   <path class="hamburger-top" d="m 5,10 55,0" />
   <path class="hamburger-middle" d="m 5,30 55,0" />
</g>
<path class="hamburger-bottom" d="m 5,50 55,0" />

在我的模式中,我可以开始包含动画所需的属性,并从开始设置transitionTime: 250 。 过渡时间应用于变换链中的每个步骤,以后可以由单个变换覆盖。

现在是时候包括我的动画状态了。 我首先设置属性states:{} 。 该对象的属性名称应与动画将导致的状态相关。 在这种情况下,我将命名我的属性为openclosed 。 该对象的属性值是变换对象的数组。 到目前为止,对架构的添加应如下所示:

transitionTime: 250,
states: {
  open:[],
  closed: []
}

接下来,我们需要包含定义SVG元素将如何转换的转换对象。

open:[
  { id: "top-lower", element: ".hamburger-top", y:20 },
  { id: "bottom-raise", element: ".hamburger-bottom", y:-20 },
  { waitFor: "top-lower", element: "g", r:45 },
  { waitFor: "bottom-raise", element: ".hamburger-bottom", r:-45},
]

每个转换对象都有一个id ,一个waitFor或两者的组合。 每个id必须是唯一的。 具有id对象表示动画链中的链接。 waitFor始终需要引用其前面的链接id 。 在这种情况下,有一个id: " top-lower "的对象和一个waitFor: " top-lower " 。 动画开始时, id:top-lower将是链中的第一个链接,它将持续250ms。 完成后, waitFor: " top-lower "将运行250ms。

每个变换对象都必须引用一个元素。 元素值可以是css选择器或直接元素引用。 例如,一个元素属性的值" g "引用SVG中的<g>元素,而另一个元素属性的值" .hamburger-bottom "引用我添加到<path>元素中的类。

现在我们知道了动画的顺序和需要变换的元素,我们只需要定义变换对象即可。 如果您不熟悉SVG转换的工作原理,则可以从SVG Elements上的转换开始。 否则,简单地说,假设您要操纵的SVG元素从x / y轴上的点[0,0]开始。 还需要记住的是,x从左到右,而y从上到下。 在上面的示例中,我们看到:

{ id: "top-lower", element: ".hamburger-top", y:20 },

此转换对象是指汉堡菜单的顶行。 y: 20告诉插件,从元素的原点[0,0]开始,我想将顶行向下移动20px 。 相反的情况适用于:

{ id: "bottom-raise", element: ".hamburger-bottom", y:-20 },

在这里,我告诉插件将元素向上移动20px 。 相同的原则适用于轮换:

{ waitFor: "top-lower", element: "g", r:45 },
{ waitFor: "bottom-raise", element: ".hamburger-bottom", r:-45}

旋转的元素以0度的旋转开始。 r: 45告诉插件将r: -45从0度旋转到45度,反之亦然。

我们在状态对象中的第二个状态如下所示:

closed: [
  { id: "top-angle", element: "g", r: 0 },
  { id: "bottom-angle", element: ".hamburger-bottom", r: 0 },                   
  { waitFor: "top-angle", element: ".hamburger-top", y: 0 },
  { waitFor: "bottom-angle", element: ".hamburger-bottom", y: 0 }
]

您会注意到,对于所有要转换的元素,其yr值均设置为0。这是因为此状态的目的是使SVG元素返回其原始状态。 由于0是原点,因此我们只是对每个元素执行转换,以将其返回原点。

我们快完成了。 现在已经定义了动画状态,我必须决定要启动这些动画的方式。 这需要架构上的另一个属性: eventsevents需要一组对象,因为您可能希望通过多个事件来启动动画。 对于汉堡图标,它将如下所示:

events: [
  { event: "click", state: ["open", "closed"] }
]

数组中的对象可能包含以下属性:

  1. event :旨在监听javascript事件。 汉堡包图标侦听<i class="icon-hamburger"</i>上的“点击”事件,因为这是架构中的选择器所引用的。
  2. state :采用字符串或数组。 如果此处的state" open " ,则单击<i class="icon-hamburger"></i> ,只有“ open”动画会在click事件上运行。 由于state的值是一个数组,因此click事件实际上将在“打开”和“关闭”动画之间切换。 该数组仅设计为采用两个值并启用切换。
  3. 最后一个属性是可选的: selector 。 默认情况下,此值为您的模式selector +“动画”。 在这种情况下,它将是icon-hamburger-animate 。 您可以根据需要在此处进行声明来更改选择器。 目的是使javascript事件绑定到SVG的父元素或同级元素。 例如,如果我有一个要在单击按钮时在其内部设置动画的SVG,则需要执行以下操作:
<button class="icon-hamburger-animate">
  <i class="icon-hamburger"></i>
</button>

哇,我们做到了。 现在是时候查看最终产品了。

见笔bWwQJZ由布里昂迪尔( @bkdiehl上) CodePen

它值得吗? 您可能在想,仅拥有一个图标需要进行很多工作。 我会同意你的。 这就是为什么我创建了一个Gulp插件来帮助您完成繁重工作的原因。

Gulp动画状态

到目前为止,我们只有一个图标,可以在包含模式的任何地方使用。 理想情况下,将icon-hamburger的架构保存到一个js文件中,该文件会捆绑在一起并包含在整个网站范围内,这意味着我可以在任何需要的地方调用icon-hamburger 。 如果此js文件是自动生成的,并且包含您有权访问的SVG图标的架构和插件调用,该怎么办? 您可以轻松访问SVG图标库! 这就是Gulp动画国家的目的。 确保在此处查看文档。

让我们从文件结构开始。 假设我去了IcoMoon,并生成了新项目所需的所有SVG文件。 我想将所有这些新生成的文件拖放到项目中的文件夹中。 我们将该文件夹称为“ svg”。 我的文件结构如下所示:

svg
|-- icon-folder.svg
|-- icon-hamburger.svg
|-- icon-mic.svg
|-- icon-wall.svg
|-- icon-wrench.svg

使用Gulp动画状态,我可以将svg文件夹中的所有SVG文件合并为一个js文件,并根据SVG的文件名为每个图标设置selector 。 文件内容如下所示:

var iconFolder = {"selector": ".icon-folder","svg": "<svg>Content</svg>"};
SnapStates(iconFolder);
var iconHamburger= {"selector": ".icon-hamburger","svg": "<svg>Content</svg>"};
SnapStates(iconHamburger);
var iconMic= {"selector": ".icon-mic","svg": "<svg>Content</svg>"};
SnapStates(iconMic);
var iconWall= {"selector": ".icon-wall","svg": "<svg>Content</svg>"};
SnapStates(iconWall);
var iconWrench= {"selector": ".icon-wrench","svg": "<svg>Content</svg>"};
SnapStates(iconWrench);

该文件可以与网站其他关键JavaScript捆绑在一起,从而在需要的地方使用SVG图标。 但是动画呢? 它们如何包含在此JavaScript文件中?

我们已经有汉堡图标的动画,因此我们将使用它。 在“ svg”文件夹中,我们需要创建一个名为“ icon-hamburger.js”的新文件。 请注意,其名称与对应的SVG文件相同。 这是新的文件结构:

svg
|-- icon-folder.svg
|-- icon-hamburger.svg
|-- icon-hamburger.js
|-- icon-mic.svg
|-- icon-wall.svg
|-- icon-wrench.svg

并且icon-hamburger.js的内容将是:

{
  transitionTime: 250,
  states: {
    open:[
      { id: "top-lower", element: ".hamburger-top", y:20 },
      { id: "bottom-raise", element: ".hamburger-bottom", y:-20 },
      { waitFor: "top-lower", element: "g", r:45 },
      { waitFor: "top-lower", element: ".hamburger-bottom", r:-45},
    ],
    closed: [
      { id: "top-angle", element: "g", r: 0 },
      { id: "bottom-angle", element: ".hamburger-bottom", r: 0 },                   
      { waitFor: "top-angle", element: ".hamburger-top", y: 0 },
      { waitFor: "bottom-angle", element: ".hamburger-bottom", y: 0 },
    ]
  },
  events: [
    { event: "click", state: ["open", "closed"] }
  ]
}

Gulp插件将查找与为其创建架构的S​​VG文件同名的js文件。 再次以动画状态演示输出:

var iconFolder = {"selector": ".icon-folder","svg": "<svg>Content</svg>"};
SnapStates(iconFolder);
var iconHamburger= {"selector": ".icon-hamburger","svg": "<svg>Content</svg>", "transitionTime":250,"states":{"open":[{"id":"top-lower","element":".hamburger-top","y":20},{"id":"bottom-raise","element":".hamburger-bottom","y":-20},{"waitFor":"top-lower","element":"g","r":45},{"waitFor":"top-lower","element":".hamburger-bottom","r":-45}],"closed":[{"id":"top-angle","element":"g","r":0},{"id":"bottom-angle","element":".hamburger-bottom","r":0},{"waitFor":"top-angle","element":".hamburger-top","y":0},{"waitFor":"bottom-angle","element":".hamburger-bottom","y":0}]},"events":[{"event":"click","state":["open","closed"]}};
SnapStates(iconHamburger);
var iconMic= {"selector": ".icon-mic","svg": "<svg>Content</svg>"};
SnapStates(iconMic);
var iconWall= {"selector": ".icon-wall","svg": "<svg>Content</svg>"};
SnapStates(iconWall);
var iconWrench= {"selector": ".icon-wrench","svg": "<svg>Content</svg>"};
SnapStates(iconWrench);

使用Gulp动画状态,您可以保留较小的,一口大小的文件,可以在需要更改某些内容时轻松地对其进行编辑。 这些小巧的片段可以很好地编译为一个文件,该文件可以与站点的其他关键组件捆绑在一起,从而允许快速轻松地调用以在HTML文档中包含SVG。

进一步的例子

汉堡包图标非常简单,因此让我们看一些更复杂的图标。 我们将从扬声器图标开始。

CodePen上查看Briant Diehl( @bkdiehl )的Pen WjoOoy

您会注意到,总体而言,该架构几乎是相同的。 您会注意到房地产easing是新的。 easing具有easeinout的默认值。 除此之外,唯一值得注意的更改是在我的转换对象中。

{ id: "waveline1", element: ".wave-line-1", x:-10, s:0.1, attr:{ opacity:.8 }, transitionTime: 250 },
{ id: "waveline2", element: ".wave-line-2", x:-16, s:0.1, attr:{ opacity:.8 }, transitionTime: 300 },
{ id: "waveline3", element: ".wave-line-3", x:-22, s:0.1, attr:{ opacity:.8 }, transitionTime: 350 }

s用于缩放,就像在CSS中一样,对象的缩放始终始于attr属性允许您修改SVG元素上的任何属性,在这种情况下为不透明度。 最后,还记得本文开头提到的如何用单个转换覆盖transitionTime方法吗? 好吧,这是完成的过程。 我什至没有在主模式中声明transitionTime 。 那是因为我希望每个变换都具有唯一的过渡时间。

接下来,让我们看一下线条画动画。

见笔OmbxVV由布里昂迪尔( @bkdiehl上) CodePen

我希望您看到的第一个主要区别是,我没有在架构中声明svg 。 SVG位于<i class="icon-new-document"></i> 。 这主要是出于演示目的,以免夸大我希望您查看的架构。 但是,插件确实允许此功能。 该用例适用于那些仅在文档中只需要几个SVG图标且不希望使用gulp插件的用户。

转换对象是我真正要重点关注的对象。 这里有很多新东西。

{ id: 'line1-init', element: ".new-document-line1", drawPath: { min: 25, max: 75 }, transitionTime: { min: 500, max: 1000 }, repeat: {times:1} },        
{ id: 'line2-init', element: ".new-document-line2", drawPath: { min: 25, max: 75 }, transitionTime: { min: 500, max: 1000 }, repeat: {times:1} },
{ id: 'line3-init', element: ".new-document-line3", drawPath: { min: 25, max: 75 }, transitionTime: { min: 500, max: 1000 }, repeat: {times:1} },
{ id: 'line4-init', element: ".new-document-line4", drawPath: { min: 25, max: 75 }, transitionTime: { min: 500, max: 1000 }, repeat: {times:1} },
{ id: 'line5-init', element: ".new-document-line5", drawPath: { min: 25, max: 75 }, transitionTime: { min: 500, max: 1000 }, repeat: {times:1} },
{ waitFor: 'line1-init', element: ".new-document-line1", drawPath: 100, transitionTime: { min: 500, max: 1000 } },
{ waitFor: 'line2-init', element: ".new-document-line2", drawPath: 100, transitionTime: { min: 500, max: 1000 } },
{ waitFor: 'line3-init', element: ".new-document-line3", drawPath: 100, transitionTime: { min: 500, max: 1000 } },
{ waitFor: 'line4-init', element: ".new-document-line4", drawPath: 100, transitionTime: { min: 500, max: 1000 } },
{ waitFor: 'line5-init', element: ".new-document-line5", drawPath: 100, transitionTime: { min: 500, max: 1000 } },

如果您看过钢笔,您会注意到,将鼠标悬停在新文档图标上会导致线条缩小和增长。 每条线都是一个path ,可以绘制一条path 。 上面的第一个转换对象包括drawPathdrawpath可以使用数字或具有minmax属性的对象。 该数字代表百分比。 假设转换对象具有drawPath: 0 。 这意味着我希望将当前path绘制为其长度的0%。 转换对象确实具有drawPath: { min: 25, max: 75 } 。 当drawpath的值是一个对象时,我告诉插件我希望将路径绘制为minmax之间的随机百分比。 在这种情况下,它将是25到75之间的随机数。如果再次将鼠标悬停在图标上,则可以看到每次动画发生时,行长都会发生变化。 使用minmax设置随机数的相同原理适用于transitionTime

repeat此动画模式的最后一个新手。 repeat接受具有四个有效属性的对象。

  1. loop :采用布尔值。 如果设置为true,则动画和整个链的所有变换将重复执行,直到另行通知为止。 为了打破循环,您必须设置loopDuration或更改为另一个动画状态。
  2. loopDuration :取一个整数。 如果将loop设置为true并将loopDuration为5000,则动画链将重复自身5000ms。 如果动画循环的持续时间不完全是5000ms,则循环将在设置的时间后继续其最终动画。
  3. times :取一个整数。 如果将times设置为2,则动画将总共运行3次。 因为动画总是至少运行一次然后再运行2次,所以选择一次。
  4. delay :取整数。 表示从动画结束到重复循环开始之间所需的时间。

接下来,我想说明更长的动画链。

见笔KmNXdW由布里昂迪尔( @bkdiehl上) CodePen

看一下shake状态。 第一个和最后一个转换对象具有idwaitFor属性。 每个其他转换对象都具有idwaitFor属性。

shake: [
  { id: "shake-right", element: '.wrench', r: 10 },
  { id: "shake-left", waitFor: 'shake-right', element: '.wrench', r: -10 },
  { id: "back-to-right", waitFor: 'shake-left', element: '.wrench', r: 10 },
  { id: "back-to-left", waitFor: 'back-to-right', element: '.wrench', r: -10 },
  { waitFor: 'back-to-left', element: '.wrench', r: 0 }
]

三个中间变换对象中的每一个都使用其waitFor引用前一个变换对象的id 。 第一个动画开始一个链,该链最终导致复位值r:0

最后,我想演示如何通过设置stroke-dashoffsetstroke-dasharray来画线。

见笔rmWzyW由布里昂迪尔( @bkdiehl上) CodePen

首先,我希望您注意到在我的许多path元素上都包含stroke-dashoffset:1000; stroke-dasharray:1000, 1000; stroke-dashoffset:1000; stroke-dasharray:1000, 1000;

<path class="right-upper-branch" d="M45.998,21.196C43.207,23.292 44.195,27.857 47.629,28.59C48.006,28.671 48.399,28.699 48.784,28.672C49.659,28.611 50.276,28.34 50.994,27.849C51.413,27.563 51.839,27.05 52.092,26.616C53.906,23.507 50.981,19.611 47.489,20.486C46.946,20.622 46.446,20.86 45.998,21.196L41.015,14.571" style="fill:none;stroke:#fff;stroke-width:1.7px;stroke-dashoffset:1000; stroke-dasharray:1000, 1000;"/>

有关stroke-dasharray详细说明,请查看stroke-dasharray 。 就我的目的而言,我们说一下stroke-dasharray基本上是设置我不想显示的路径的长度。 现在,我的路径肯定不是1000px长。 有点夸张,但这是夸张,可以确保我的路径的任何部分都不会过早显示。 在下面的转换中,路径绘制完成。

{ id:["right-upper-branch", 600], element: ".right-upper-branch", drawPath:100  },

当我设置drawPath回0将调整stroke-dasharraystroke-dashoffset相应。 关于这一行,我想指出的最后一件事是id 。 它是一个数组而不是字符串。 数组中的第一个值是id的名称。 第二个值将始终是代表超时的整数。 如果仅在动画中使用此变换对象,则将仅在mouseover事件之后600ms看到一条绘制的路径。

有关更多示例和更多文档,您可以查看我的演示页面

结论

关于将图标系统切换到新的东西是否是一件好事,可能还有很多人仍在讨论中。 当前可用的不同图标系统各有利弊。 我试图为您创建一种简单的方法,使您可以转到SVG图标。 希望对你有帮助。

翻译自: https://css-tricks.com/snap-animation-states/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值