关键帧动画效果
基本的vue3项目配置,需要安装d3和animejs的包。
export const keyframesList = [
{
anim:[
{
style: 'opacity',
from: "0",
to: "50",
start: "0",
end: "30",
},
{
style: 'opacity',
from: "50",
to: "100",
start: "30",
end: "40",
},
{
style: 'opacity',
from: "100",
to: "0",
start: "40",
end: "80",
},
{
style: 'opacity',
from: "0",
to: "100",
start: "80",
end: "100",
},
{
style: 'translate',
from: "0",
to: "50",
start: "0",
end: "30",
},
{
style: 'translate',
from: "50",
to: "100",
start: "30",
end: "40",
},
{
style: 'translate',
from: "100",
to: "0",
start: "40",
end: "80",
},
{
style: 'translate',
from: "0",
to: "100",
start: "80",
end: "100",
}
]
}
]
export function initData(obj){
// eslint-disable-next-line no-useless-escape
let reg = /[-\d\.]+/g;
let numArrFrom = obj.from.match(reg);
let numArrTo = obj.to.match(reg);
if (!numArrFrom || !numArrTo || numArrFrom.length == 0 || numArrTo.length == 0 || numArrFrom.length != numArrTo.length) {
console.warn('数据输入错误');
}
for (let i = 0; i < numArrFrom.length; i++) {
numArrFrom[i] = parseFloat(numArrFrom[i]);
numArrTo[i] = parseFloat(numArrTo[i]);
}
let strArr = obj.from.split(reg);
if (numArrFrom.length <= 0) {
return;
}
return {
strArr: strArr,
numArrFrom: numArrFrom,
numArrTo: numArrTo,
totalLength: strArr.length + numArrFrom.length,
isEnd: false,
}
}
keyframesAnime脚本
export function keyframesAnime({pathDom,keyframesList, duration = 5000}){
const pathLength = pathDom.getTotalLength();
let keyframes = [];
let list = []
const frameLength = 1;
// 计算全部路线
for(let i = 0; i<= pathLength; i++){
const framePosition = frameLength * i;
const framePoint = pathDom.getPointAtLength(framePosition);
const angle = getRotationAtPoint(pathDom, framePosition)
// let keyframe;
let keyframe = {
duration: duration / pathLength,
easing: 'linear',
translateY:`${framePoint.y}`,
translateX:`${framePoint.x}`,
rotate:`${angle}`,
};
keyframes.push(keyframe)
}
// 计算关键帧路线及透明度
for(let i = 0; i < keyframesList[0].anim.length; i++){
let target = keyframesList[0].anim[i]
if((Number(target.end) > Number(target.start) ) && !target.initData.isEnd){
let percent = (target.end - target.start) / 100
if(percent >= 1){
target.initData.isEnd = true
}
if(target.style === "translate"){
let index = i - keyframesList[0].anim.length/2
if(target.initData.numArrFrom[0] > target.initData.numArrTo[0]){
let num = ((keyframesList[0].anim[index].initData.numArrFrom[0] - keyframesList[0].anim[index].initData.numArrTo[0]) / 100) /(Math.round(keyframes.length * (target.initData.numArrFrom[0]/100)) - Math.round(keyframes.length * (target.initData.numArrTo[0]/100)))
let opacityNum = 0
for(let j = Math.round(keyframes.length * (target.initData.numArrFrom[0]/100))-1; j > Math.round(keyframes.length * (target.initData.numArrTo[0]/100)); j--){
list.push({
...keyframes[j],
duration:(duration * percent )/ (Math.round(keyframes.length * (target.initData.numArrFrom[0]/100)) - Math.round(keyframes.length * (target.initData.numArrTo[0]/100))),
opacity: opacityNum
})
opacityNum += num
}
}else{
let num = ((keyframesList[0].anim[index].initData.numArrTo[0] - keyframesList[0].anim[index].initData.numArrFrom[0]) / 100) /(Math.round(keyframes.length * (target.initData.numArrTo[0]/100)) - Math.round(keyframes.length * (target.initData.numArrFrom[0]/100)))
let opacityNum = 0
for(let j= Math.round(keyframes.length * (target.initData.numArrFrom[0]/100)); j< Math.round(keyframes.length * (target.initData.numArrTo[0]/100)); j++){
list.push({
...keyframes[j],
duration:(duration * percent )/ (Math.round(keyframes.length * (target.initData.numArrTo[0]/100)) - Math.round(keyframes.length * (target.initData.numArrFrom[0]/100))),
opacity:opacityNum
})
opacityNum += num
}
}
}
}
}
return list;
}
function getRotationAtPoint(pathEl, length) {
const startPoint = pathEl.getPointAtLength(length - 0.01);
const endPoint = pathEl.getPointAtLength(length + 0.01);
const angle = Math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x) * 180 / Math.PI;
return angle;
}
git地址vue-keyframes: js关键帧动画