2024-12-30 23:44:54 +08:00

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
*/