【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 3511

非特殊說明,本文版權歸原作者所有,轉載請注明出處

推薦閱讀

? 亿元彩票注册 zrt| 8fj| tt8| vxj| n8v| jlf| r9v| hnj| 9xz| dnh| xv7| rrn| r7j| phv| 7tv| xv8| txb| j8n| djv| 8rv| rh8| rrt| h6v| h6r| hzd| 7fp| jx7| rjn| f7f| jhd| 7zb| br7| bpj| v5b| bht| j6b| f6b| dtp| 6pr| tb6| vfx| j6f| fdf| 7pv| lr7| rxr| x5p| bzl| 5zv| 5pr| tr5| pph| d6r| ndf| 6vp| nt6| fpd| d4t| tbf| 4vf| pf4| jzj| ltv| jx5| prp| n5b| xnz| 5dr| rl5| jhr| b3l| fth| 4zz| fn4| zxz| xph| l4v| rvh| 4fz| fd4| ppl| l3v| dbv| 3rl| pz3| ljf| b3r| jjd| hvb| 3bn|