難度係數:平常
關鍵詞:GSAP SVG
媒介
看到species網站做的很炫,想要自創,發明主如果用css3的clip-path
完成的,兼容不好,因而想着用js完成下。下面作簡樸引見,須要細緻代碼見github庫。
基礎知識
- SVG基礎知識,重點viewBox,polygon;
- GSAP動畫平台,重點TimelineMax,TweenMax;
- parcel構建東西的基礎運用,parcel。
完成思緒
依據參考網站的代碼,動物圖案是用clip-path: polygon()
完成的,第一時間想到了SVG的polygon;別的關於轉場動畫,過渡動畫,找個本身熟習的動畫庫完成就好了。須要迥殊申明的是:
- css的clip-path用的用的百分比數值,svg的polygon的points值不能用百分比數值,曉得viewBox觀點的應當清晰,實在points的值也不是平常以為的相對像素值,因而寫了個東西函數
parsePolygonStr
。 - 由於圖案是分動物和場景(樹枝,石優等)兩部分,而且願望先繪製動物再繪製場景,因而HTML部分用g標籤分紅
extra
和anis
。
重要的代碼以下:
進口文件HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>species</title>
</head>
<body>
<div id="wrap">
<svg class="stage" viewBox="0 0 1000 700" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="extra">
<polygon points="793.50,476.88,949.50,500.88,805.50,518.88" fill="hsla(0, 0%, 100%, 0)"></polygon>
<polygon points="793.50,476.88,949.50,500.88,805.50,518.88" fill="hsla(0, 0%, 100%, 0)"></polygon>
<polygon points="793.50,476.88,949.50,500.88,805.50,518.88" fill="hsla(0, 0%, 100%, 0)"></polygon>
</g>
<g id="anis">
</g>
</svg>
<button id="go_btn">GO!</button>
<h1 class="name"></h1>
<h2 class="desc"></h2>
</div>
<script src="./index.js"></script>
</body>
</html>
重要js劇本
// 導入一個 SCSS module
import '../css/main.scss';
import { data, preData } from './data'
import { TweenMax, TweenLite, TimelineMax } from 'gsap'
// NodeList轉換Array
function NodeList2Array(nodelist) {
let arr = [];
if (nodelist.length) {
arr = Array.prototype.slice.call(nodelist, 0);
}
return arr;
}
// 把'clip-path'值轉成svg polygon可用的值
function parsePolygonStr(polygonStr, width, height) {
let pointsArr = polygonStr.split(/\s+|,\s/);
let newPointArr = pointsArr.map(function(currentVal, index, arr) {
if (index % 2 === 0) {
return (parseFloat(currentVal) * width / 100).toFixed(2);
} else {
return (parseFloat(currentVal) * height / 100).toFixed(2);
}
});
return newPointArr;
}
let body = document.querySelector('body'),
wrap = document.querySelector('#wrap'),
name = wrap.querySelector('.name'),
desc = wrap.querySelector('.desc'),
stage = wrap.querySelector('.stage'),
anis = document.querySelector('#anis'),
extra = document.querySelector('#extra'),
goBtn = document.querySelector('#go_btn'),
anisPolygons = null,
extraPolygons = null;
let currentSpeciesIndex = 0,
width = 1000,
height = 700;
function init() {
let initSpecies = preData.preload;
name.innerHTML = initSpecies.name;
desc.innerHTML = initSpecies.desc;
body.style.background = initSpecies.background;
let polygonArr = initSpecies.polygon;
if (Object.prototype.toString.call(polygonArr) === '[object Array]') {
let polygonHtml = '';
polygonArr.forEach(function(element, index) {
let pointsVal = parsePolygonStr(element[0], width, height);
polygonHtml += '<polygon points="' + pointsVal + '" fill="' + element[1] + '"/>';
});
anis.innerHTML = polygonHtml;
}
}
init();
anisPolygons = anis.querySelectorAll('polygon');
extraPolygons = extra.querySelectorAll('polygon');
let tl = new TimelineMax({ delay: 0.2 });
// 初始的loading動畫
NodeList2Array(anisPolygons).forEach(function(target, index) {
let tm = TweenMax.fromTo(target, 0.9, { attr: { fill: 'rgba(0, 0, 0, .7)' } }, { attr: { fill: 'rgba(200, 20, 20, .45)' }, ease: Power0.easeNone, repeat: -1, yoyo: true });
tl.add(tm, 0.9 - 0.03 * index);
})
// 模仿加載完成
setTimeout(function() {
// 消滅tl
tl.clear();
// loading完以後的一系列動畫
// 1,變色,放大,爆炸碎片
tl.add(
[
TweenMax.to('#anis polygon', .6, {
attr: {
fill: function(index) {
let fillVal = '#111';
if (index % 5 === 0) {
fillVal = '#28282a';
} else if (index % 5 === 1) {
fillVal = '#111';
} else if (index % 5 === 2) {
fillVal = '#333';
} else if (index % 5 === 3) {
fillVal = '#222';
} else if (index % 5 === 4) {
fillVal = '#121212';
}
return fillVal;
}
}
}),
TweenMax.to('#wrap .stage', .6, {
scale: 1,
ease: Back.easeOut.config(1.7)
}),
TweenMax.to('#anis polygon', .6, {
attr: {
points: function(index, target) {
let nextSpeciesPolygon = preData.ready.polygon;
// debugger
return parsePolygonStr(nextSpeciesPolygon[index][0], width, height)
},
fill: function(index, target) {
let nextSpeciesPolygon = preData.ready.polygon;
return nextSpeciesPolygon[index][1];
},
}
// ease: Power2.easeInOut,
})
]
)
// 2,海豚
.add(
TweenLite.to('#anis polygon', .6, {
attr: {
points: function(index, target) {
let nextSpeciesPolygon = preData.preAni.polygon;
return parsePolygonStr(nextSpeciesPolygon[index][0], width, height)
},
fill: function(index, target) {
let nextSpeciesPolygon = preData.preAni.polygon;
return nextSpeciesPolygon[index][1];
},
},
// ease: Power2.easeInOut,
})
)
// 3,爆炸碎片
.add(
TweenMax.to('#anis polygon', .6, {
attr: {
points: function(index, target) {
let nextSpeciesPolygon = preData.ready.polygon;
// debugger
return parsePolygonStr(nextSpeciesPolygon[index][0], width, height)
},
fill: function(index, target) {
let nextSpeciesPolygon = preData.ready.polygon;
return nextSpeciesPolygon[index][1];
},
}
}),
'+=0.4'
)
// 4,“piece”logo
.add(
TweenMax.to('#anis polygon', .6, {
attr: {
points: function(index, target) {
let nextSpeciesPolygon = preData.title.polygon;
// debugger
return parsePolygonStr(nextSpeciesPolygon[index][0], width, height)
},
fill: function(index, target) {
let nextSpeciesPolygon = preData.title.polygon;
return nextSpeciesPolygon[index][1];
},
}
}),
'+=0.4'
);
}, 3000);
// 動物圖案切換
function playHandler() {
let nextSpecies = data[currentSpeciesIndex++];
if (!nextSpecies) {
return false;
}
name.innerHTML = nextSpecies.name;
desc.innerHTML = nextSpecies.desc;
body.style.background = nextSpecies.background;
let nextSpeciesPolygon = nextSpecies.polygon;
let subTl = new TimelineMax({ pause: true });
let arr1 = NodeList2Array(anisPolygons);
let arr2 = NodeList2Array(extraPolygons);
// 之所以沒用TweenMax.staggerTo是由於屬性對象中沒法用取得index,以下完成不了
// attr: {
// points: pointVal.join(' '),
// fill: function(index){return nextSpeciesPolygon[index][1];}
// }
arr1.concat(arr2).forEach(function(target, index) {
let pointVal = parsePolygonStr(nextSpeciesPolygon[index][0], width, height),
fillVal = nextSpeciesPolygon[index][1];
subTl.add(
TweenMax.to(target, 0.5, {
attr: {
points: pointVal.join(' '),
fill: fillVal
},
ease: Back.easeOut.config(1.7)
}),
'-=0.47'
)
});
subTl.play();
}
goBtn.addEventListener('click', playHandler, false);
export default () => {
};