diff --git a/src/hook/render/wire.js b/src/hook/render/wire.js index 68b3339..5201802 100644 --- a/src/hook/render/wire.js +++ b/src/hook/render/wire.js @@ -129,126 +129,16 @@ export class WireRender { if (data.active) { const pathSelection = d3.select(this); - // 如果当前激活,显示数据流向 - // const pathLength = pathSelection.node().getTotalLength(); + const pulse = new PulseLine(); + id2PluseLine.set(data.id, pulse); - const g = _this.selection.append("g") - .attr("mask", "url(#m1)"); - - // 创建 mask - const mask = g.append("mask") - .attr('id', 'm1') - - mask.append("use") - .attr("id", "u1") - .attr("href", "#p1") - .attr("stroke", "white") - .attr("stroke-width", 7) - .attr("fill", "none") - .attr('stroke-dasharray', '200 300') - .attr("stroke-linecap", "round"); - - // 创建 radialGradient - const radialGradient = g.append("radialGradient") - .attr("id", "g1"); - - 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 - g.append("path") - .attr("id", "p1") - .attr('stroke-width', 10) - .attr("fill", "none") - .attr("d", data.svg); - - // 创建 circle 和 animateMotion - g.append("circle") - .attr("r", 100) - .attr("fill", "url(#g1)") - .append("animateMotion") - .attr("dur", "3000ms") - .attr("repeatCount", "indefinite") - .append("mpath") - .attr("href", "#p1"); - - let p1 = document.getElementById('p1'); - let u1 = document.getElementById('u1'); - - const pathLength = p1.getTotalLength(); - - var u1Keyframes = new KeyframeEffect( - u1, [{ - strokeDasharray: `200 ${pathLength - 200}`, - strokeDashoffset: "200" - }, - { - strokeDasharray: `200 ${pathLength}`, - strokeDashoffset: `-${pathLength - 200}` - } - ], { - duration: 3000, - iterations: Infinity - } - ); - - const pulsationAnimation = new Animation(u1Keyframes, document.timeline); - - requestAnimationFrame(() => { - const motion = g.selectAll('animateMotion').node(); - console.log(motion); - motion.setAttribute('begin', '0s'); - motion.beginElement(); - - pulsationAnimation.play(); - }); - - // const pivot = _this.selection - // .append('circle') - // .attr('r', 6) - // .attr('fill', 'var(--wire-ball-color)'); - - // // 进行一次动画 - // function renderOneAnimation() { - // console.log(pathLength); - // // 1400 的长度差不多配 3500 ? - // const duration = pathLength / 14 * 35; - // let transition = pivot.transition().duration(3500); - // transition.attrTween('transform', () => { - // return t => { - // const x = cubicBezierAnimation(t, 0, pathLength); - // const point = pathSelection.node().getPointAtLength(x); - // return `translate(${point.x},${point.y})`; - // } - // }); - // transition = transition.on('end', function() { - // if (id2animation.has(data.id)) { - // renderOneAnimation(); - // } - // }); - // // 动画结束后重新启动动画 - // id2animation.set(data.id, { - // pivot, - // transition - // }); - // } - - // renderOneAnimation(); + pulse.loadToSelection(_this.selection, data); + pulse.startAnimation(); } else { - if (id2animation.has(data.id)) { - const { pivot, transition } = id2animation.get(data.id); - pivot.remove(); - id2animation.delete(data.id); + if (id2PluseLine.has(data.id)) { + const pulse = id2PluseLine.get(data.id); + pulse.destory(); + id2PluseLine.delete(data.id); } } }); diff --git a/src/hook/skin/plusation.js b/src/hook/skin/plusation.js index 97efb03..a95ace7 100644 --- a/src/hook/skin/plusation.js +++ b/src/hook/skin/plusation.js @@ -4,98 +4,133 @@ let PluseIDCount = 0; export class PulseLine { constructor() { + this.animation = undefined; + this.pluseId = undefined; } - loadData(pathString, id) { - this.path = pathString; - this.pluseId = id; - } /** * * @param {d3.Selection} parentSelection */ - loadToSelection(parentSelection, config) { - config = config || {}; - const pluse = parentSelection.append('g'); + loadToSelection(parentSelection, data) { + this.pluseId = data.id; - const gId = 'pluse-g' + this.pluseId; - const pId = 'pluse-p' + this.pluseId; - const mId = 'pluse-m' + this.pluseId; - const uId = 'pluse-u' + this.pluseId; + const gId = 'pluse-g' + data.id; + const pId = 'pluse-p' + data.id; + const mId = 'pluse-m' + data.id; + const uId = 'pluse-u' + data.id; - pluse.append('radialGradient') - .attr("id", gId) - .selectAll("stop") - .data([ - { offset: "0%", color: "white" }, - { offset: "25%", color: "#CB81DA" }, - { offset: "95%", color: "rgb(70, 70, 222)", opacity: 0 } - ]) - .enter() - .append("stop") - .attr("offset", d => d.offset) - .attr("stop-color", d => d.color) - .attr("stop-opacity", d => d.opacity || 1); - - pluse.append("path") - .attr("id", pId) - .attr('fill', 'none') - .attr("d", "M133,613 L470,613 L470,515.5 L661,515.5"); - - const mask = pluse.append("mask") - .attr("id", mId); - - mask.append("use") - .attr("id", uId) - .attr("href", pId) - .attr("stroke", "white") - .attr("stroke-width", 5) - .attr("fill", "none") - .attr("stroke-dasharray", "100 200") - .attr("stroke-linecap", "round"); - - // 创建使用 mask 和 radialGradient 的 - const maskedG = pluse.append("g") + 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 - maskedG.append("circle") - .attr("r", 50) - .attr("fill", `url(#${pId})`) + g.append("circle") + .attr("r", 100) + .attr("fill", `url(#${gId})`) .append("animateMotion") - .attr("dur", "4500ms") + .attr("dur", "3000ms") .attr("repeatCount", "indefinite") .append("mpath") - .attr("href", `#${pId}`); + .attr("href", "#" + pId); } - animation() { - const pId = 'pluse-p' + this.pluseId; - const uId = 'pluse-u' + this.pluseId; - - const pathElement = document.getElementById(pId); - const pluseElement = document.getElementById(uId); - - const pathLength = pathElement.getTotalLength(); - - const keyframes = new KeyframeEffect( - pluseElement, [{ - strokeDasharray: `100 ${pathLength}`, - strokeDashoffset: "100" - }, - { - strokeDasharray: `100 ${pathLength}`, - strokeDashoffset: `-${pathLength - 100}` + 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 } - ], { - duration: this.duration, - iterations: Infinity - } - ); + ); + + const pulsationAnimation = new Animation(keyframes, document.timeline); + this.animation = pulsationAnimation; - const pulsationAnimation = new Animation(keyframes, document.timeline); - return 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(); + } } }