【CodePen】SVG多边形动画教程(翻译)

分类:文档教程
原文:Animating SVG polygons - Nat
翻译:SVG多边形动画教程(翻译) - xh_loop
ps:如发现有翻译错误,欢迎在下方评论区指出,谢谢 :)

导语

我收到了很多关于 我是如何制作 "animating svg polygon points" 的疑问,所以我决定写一个小教程。我制作这个Pen是为了试着重新实现这个网站 facesofpower.net 的效果。

免责声明

我不知道我做的是否是最佳的或者最优的方式,所以如果有人有任何建议或批评,请让我知道!

内容清单

  1. Tools
  2. 逻辑概述
  3. HTML & SCSS
  4. Javascript
  5. 资源

Tools

  • 我使用了 primitive, 它能将图片转换为SVG。
    终端命令我使用的是 primitive -i input.jpg -o output.svg -n 250 -m 1。
    这个 -n 250 指定了 250 个多边形, -m 1 指定了三角形, 然后 -i input.jpg -o output.svg 是输入和输出。

为这个特定的例子指定三角形是很重要的,因为这将影响多边形的各个points属性如何展现。如果你指定了一个不同的形状,你必须更新getCoordinates function中的正则表达式,并相应地改变animatePolygons function中的setAttribute方法

  • 动画效果的制作使用了 TweenMax。

逻辑概述

我们将为每个多边形的points属性添加动画。我们需要2个数组来做这个动画:一个是我们变化目标的值集合,一个是我们变化之前的值集合。变化之前的数组从它展现到页面上开始,将一直拥有一个svg-holder的class。

每当一个链接被点击:

  1. 根据被点击的元素的href的值去寻找id为该值的svg。
  2. 获取该svg中多边形的每个points属性的值,将他们放入一个object,并将这个object加入to数组。
  3. Animate the from values to the to values.
  4. After animation, set the from array to the to array.

HTML & SCSS

在创建了SVG之后,将他们粘贴到HTML的body中。复制第一个SVG,并给它添加class svg-holder。这个 svg-holder 将成为唯一一个真正的可见区域,并且所有被移动的多边形将在这里进和出。

给除了可见区域 以外的SVG都添加一个class hidden 和一个唯一的id。这个id必须和链接的href值统一。这些 hidden 将用display: none;隐藏。

很重要的一点是,确保每个链接的href和他们各自对应的svg的id值统一吻合:

<a href="#nat">Nat</a>
<a href="#bwl">bwl</a>
<a href="#kevin">kevin</a>
<svg class="svg-holder">
  polygons for #nat go here
</svg>
<svg id="nat" class="hidden">
 polygons for #nat go here
</svg>
<svg id="bwl" class="hidden">
 polygons for #bwl go here
</svg>
<svg id="kevin" class="hidden">
 polygons for #kevin go here
</svg>

这里同样需要一些样式。这将规定当前这个点如何展现:

Javascript

Variables

首先,让我们声明我们的变量。

let toPolygonArray = [];
let fromPolygonArray = [];
const links = document.querySelectorAll("a");

这里的重点是开头的两个数组。 他们被使用 let 声明,因为这些数组不会拥有一个常量值。toPolygonArray会保存我们将要变化的目标多边形,fromPolygonArray会保存变化之前的多边形。

那些链接上将会有一个点击事件监听,以此来触发动画效果。

Functions

Click Event Listene(点击事件监听)

[].forEach.call(links, function(el, i, els) {
    el.addEventListener("click", function(event) {
        const idToAnimateTo = this.getAttribute("href").substring(1);

        [].forEach.call(els, function(el) {
            if (el !== this) {
                el.classList.remove("active");
            } else {
                this.classList.add("active");
            }
        }, this);

        event.preventDefault();
        this.classList.add("active");
        updatePolygonArrays(idToAnimateTo);
    });
});

每当一个链接被点击,这个函数就会取得其 href 并将它赋值给 idToAnimateTo 变量:

const idToAnimateTo = this.getAttribute("href").substring(1);

然后调用updatePolygonArrays函数,idToAnimateTo作为参数传入。

updatePolygonArrays(idToAnimateTo);

点击事件监听还会设置一个激活状态的class到当前链接上,以此来让这个激活的链接拥有不同的样式。

getCoordinates

const getCoordinates = (polygon) => {
  return polygon.getAttribute("points").match(/(-?[0-9][0-9\.]*),(-?[0-9][0-9\.]*)\ (-?[0-9][0-9\.]*),(-?[0-9][0-9\.]*)\ (-?[0-9][0-9\.]*),(-?[0-9][0-9\.]*)/);
};

这个函数传入一个 polygon 元素,返回一个包含points属性的所有数字的数组

这个函数根据points属性的设置可以不同。在我的示例中,每个points拥有相同的精确设置:6个被逗号或空格分割的数字。这6个数字是三角形的x,y坐标。

<polygon fill="#ffffff" points="-16,-16 32,69 271,7" />

这个函数使用了正则表达式来寻找每个数字。这个正则表达式能被修改,例如你想变化一个 path 的d属性。

在上面这个例子中,path 的内容如下:

<path d="M46,282L28,228L62,184Z" fill="rgb(7, 7, 7)" fill-opacity="0.66"/>

d属性相应的正则表达式应该如下:

path.getAttribute("d").match(/M(-?[0-9][0-9]*),(-?[0-9][0-9]*)L(-?[0-9][0-9]*),(-?[0-9][0-9]*)L(-?[0-9][0-9]*),(-?[0-9][0-9]*)Z/);

createPolygonPointsObject

const createPolygonPointsObject = (polygons) => {
  const polygonsArray = [];

  polygons.forEach((polygon, i) => {
    const coordinates = getCoordinates(polygon);

    polygonsArray.push({
      fill: polygon.getAttribute("fill"),
      one: coordinates[1],
      two: coordinates[2],
      three: coordinates[3],
      four: coordinates[4],
      five: coordinates[5],
      six: coordinates[6]
    });
  });

  return polygonsArray;
}

这个函数将 polygons 作为参数传入,返回包含每个多边形的fill和points属性的object数组

创建一个空的数组:

const polygonsArray = [];

获取参数(储存多边形的一个数组),并且为数组中的每个项 调用 getCoordinates 函数

polygons.forEach((polygon, i) => {
   const coordinates = getCoordinates(polygon);
});	

这设置coordinatespoints里的一个数字数组

在同一个forEach中,将points的值和每个多边形的fill属性push到polygonsArray里。这些值将被作为object存入,以此来简化发生动画时对他们的处理。

所以现在 polygonsArray 是一个object对象数组,每个object指向一个多边形并且拥有7个属性:fill属性和points属性的6个数字。所以举例来说,要获取第三个多边形的fill属性,你只需要写 polygonsArray[2].fill。要获取第三个多边形的points属性的第一个数字,你只需要写polygonsArray[2].one。

polygons.forEach((polygon, i) => {
  const coordinates = getCoordinates(polygon);

    polygonsArray.push({
      fill: polygon.getAttribute("fill"),
      one: coordinates[1],
      two: coordinates[2],
      three: coordinates[3],
      four: coordinates[4],
      five: coordinates[5],
      six: coordinates[6]
  });
});

返回数组。

return polygonsArray;

updatePolygonArrays

const updatePolygonArrays = (idToAnimateTo) => {
  toPolygonArray = createPolygonPointsObject(document.getElementById(idToAnimateTo).querySelectorAll("polygon"));

  animatePolygons();

  fromPolygonArray = toPolygonArray;
}

这是那个在点击监听事件中被调用的函数。

idToAnimateTo 是那个被点击的链接的href值。所以这个函数寻找一个id和href值吻合的svg。它获取该svg中的所有多边形,把他们传入createPolygonPointsObject运行,并设置到toPolygonArray。

所以现在,获取我们将要变化的第三个多边形的fill值,也就是toPolygonArray[2].fill。

然后,调用animatePolygons。

变化之后,fromPolygonArray会被更新,来获得上个周期的toPolygonArray的值。

animatePolygons

const animatePolygons = () => {
  const polygons = document.querySelector(".svg-holder").querySelectorAll("polygon");
  fromPolygonArray = createPolygonPointsObject(polygons);

  fromPolygonArray.forEach((obj, i) => {
    TweenMax.to(obj, 1, {
      one: toPolygonArray[i].one,
      two: toPolygonArray[i].two,
      three: toPolygonArray[i].three,
      four: toPolygonArray[i].four,
      five: toPolygonArray[i].five,
      six: toPolygonArray[i].six,
      ease: Power3.easeOut,
      onUpdate: () => {
        polygons[i].setAttribute("points", `${obj.one},${obj.two} ${obj.three},${obj.four} ${obj.five},${obj.six}`);
      }
    });
  });

  // animate color
  polygons.forEach((polygon, i) => {
    const toColor = toPolygonArray[i].fill;

    TweenLite.to(polygon, 1, {
      fill: toColor,
      ease: Power3.easeOut
    });
  });
}

获取当前svg-holder的多边形。这是当前可见的SVG,并且是我们将要变化的多边形。

const polygons = document.querySelector(".svg-holder").querySelectorAll("polygon");

将这些多边形传入createPolygonPointsObject运行,并且将他们放入from数组。

fromPolygonArray = createPolygonPointsObject(polygons);

变化多边形的位置/尺寸。

fromPolygonArray.forEach((obj, i) => {
   TweenMax.to(obj, 1, {
      one: toPolygonArray[i].one,
      two: toPolygonArray[i].two,
      three: toPolygonArray[i].three,
      four: toPolygonArray[i].four,
      five: toPolygonArray[i].five,
      six: toPolygonArray[i].six,
      })
   });

from数组中的值放入to中,替代原来的值。

onUpdate: () => {
 polygons[i].setAttribute("points", `${obj.one},${obj.two} ${obj.three},${obj.four} ${obj.five},${obj.six}`);
}

在每次动画中,设置新的值到当前可见的多边形(.svg-holder)的points属性。TweenMaX中的onUpdate方法在每一次动画更新时都被调用,所以在这执行obj.one, obj.two,obj.three等值的每次变化。所以我们正在设置obj.onetoPolygonArray[i].onepoints属性的每个属性。这就是从变化属性开始后,动画发生的最基本所在。

Animate Polygon fill

每个.svg-holder中的多边形,设置他的fill到toPolygonArray中的相同索引值的fill

polygons.forEach((polygon, i) => {
    const toColor = toPolygonArray[i].fill;

    TweenLite.to(polygon, 1, {
      fill: toColor,
      ease: Power3.easeOut
    });
  });

资源

  1. Regex
  2. SVG polygons
  3. TweenMax.to

2017-02-06 16:53 - xh_loop 3976

非特殊说明,本文版权归原作者所有,转载请注明出处

推荐阅读

? 时时彩专家杀号定胆 868| 463| 523| 964| 682| 568| 73| 91| 586| 850| 418| 814| 550| 493| 679| 100| 487| 85| 469| 685| 208| 7| 943| 631| 667| 568| 460| 766| 697| 37|