143 lines
4.1 KiB
JavaScript
143 lines
4.1 KiB
JavaScript
import * as d3 from 'd3';
|
|
|
|
let PluseIDCount = 0;
|
|
|
|
export class PulseLine {
|
|
constructor() {
|
|
this.animation = undefined;
|
|
this.pluseId = undefined;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {d3.Selection} parentSelection
|
|
*/
|
|
loadToSelection(parentSelection, data) {
|
|
this.pluseId = data.id;
|
|
|
|
const gId = 'pluse-g' + data.id;
|
|
const pId = 'pluse-p' + data.id;
|
|
const mId = 'pluse-m' + data.id;
|
|
const uId = 'pluse-u' + data.id;
|
|
|
|
const g = parentSelection.append("g")
|
|
.attr("mask", `url(#${mId})`);
|
|
|
|
this.g = g;
|
|
|
|
// 创建 mask
|
|
const mask = parentSelection.append("mask")
|
|
.attr('id', mId);
|
|
|
|
const maskPathSelection = mask.append("use")
|
|
.attr("id", uId)
|
|
.attr("href", "#" + pId)
|
|
.attr("stroke", "white")
|
|
.attr("stroke-width", 7)
|
|
.attr("fill", "none")
|
|
.attr('stroke-dasharray', '200 300')
|
|
.attr("stroke-linecap", "round");
|
|
|
|
this.maskPathSelection = maskPathSelection;
|
|
|
|
// 创建 radialGradient
|
|
const radialGradient = g.append("radialGradient")
|
|
.attr("id", gId);
|
|
|
|
radialGradient.append("stop")
|
|
.attr("offset", "0%")
|
|
.attr("stop-color", "white");
|
|
|
|
radialGradient.append("stop")
|
|
.attr("offset", "25%")
|
|
.attr("stop-color", "#CB81DA");
|
|
|
|
radialGradient.append("stop")
|
|
.attr("offset", "95%")
|
|
.attr("stop-opacity", 0)
|
|
.attr("stop-color", "rgb(70, 70, 222)");
|
|
|
|
// 创建 path
|
|
const pathSelection = g.append("path")
|
|
.attr("id", pId)
|
|
.attr('stroke-width', 10)
|
|
.attr("fill", "none")
|
|
.attr("d", data.svg);
|
|
|
|
this.pathSelection = pathSelection;
|
|
|
|
// 创建 circle 和 animateMotion
|
|
g.append("circle")
|
|
.attr("r", 100)
|
|
.attr("fill", `url(#${gId})`)
|
|
.append("animateMotion")
|
|
.attr("dur", "3000ms")
|
|
.attr("repeatCount", "indefinite")
|
|
.append("mpath")
|
|
.attr("href", "#" + pId);
|
|
}
|
|
|
|
startAnimation() {
|
|
if (this.animation) {
|
|
requestAnimationFrame(() => {
|
|
const motionElement = this.g.selectAll('animateMotion').node();
|
|
motionElement.setAttribute('begin', '0s');
|
|
motionElement.beginElement();
|
|
this.animation.play();
|
|
});
|
|
} else {
|
|
const pathElement = this.pathSelection.node();
|
|
const pluseElement = this.maskPathSelection.node();
|
|
|
|
const pathLength = pathElement.getTotalLength();
|
|
|
|
const keyframes = new KeyframeEffect(
|
|
pluseElement, [{
|
|
strokeDasharray: `200 ${pathLength - 200}`,
|
|
strokeDashoffset: "200"
|
|
},
|
|
{
|
|
strokeDasharray: `200 ${pathLength}`,
|
|
strokeDashoffset: `-${pathLength - 200}`
|
|
}
|
|
], {
|
|
duration: 3000,
|
|
iterations: Infinity
|
|
}
|
|
);
|
|
|
|
const pulsationAnimation = new Animation(keyframes, document.timeline);
|
|
this.animation = pulsationAnimation;
|
|
|
|
requestAnimationFrame(() => {
|
|
const motionElement = this.g.selectAll('animateMotion').node();
|
|
motionElement.setAttribute('begin', '0s');
|
|
motionElement.beginElement();
|
|
|
|
pulsationAnimation.play();
|
|
});
|
|
}
|
|
}
|
|
|
|
pauseAnimation() {
|
|
if (!this.animation) {
|
|
return;
|
|
}
|
|
this.animation.pause();
|
|
}
|
|
|
|
destory() {
|
|
if (this.g) {
|
|
this.g.selectAll('*').remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @typedef PulseLineSvgConfig
|
|
* @property {string} pluseStartColor
|
|
* @property {string} pluseMiddleColor
|
|
* @property {string} pluseEndColor
|
|
* @property {string} transform
|
|
* @property {number} duration
|
|
*/ |