SVG 实现飞线功能
svg 实现如图飞功能具体代码如下vue 中使用template div span iddv-1测试/span span iddv-2 stylemargin:100px;测试/span /div fly-line2 :fly-listflyLineList/fly-line2 /template script langts import Flyline2 from /components/FlylineChartEnhanced/FlyLine2.vue import {type FlyLineItem } from /api/types/RespTypes export default { name: home, components: { Flyline2 }, setup() { const flyLineList refFlyLineItem[]([]) function getCenter(el: HTMLElement) { const rect el.getBoundingClientRect() return { x: rect.left rect.width / 2, y: rect.top rect.height / 2 } } function test(){ nextTick((){ const el document.getElementById(dv-1) const el2 document.getElementById(dv-2) const form getCenter(el) const to getCenter(el2) flyLineList.value.push({ id: line_1, from: [form.x, form.y], to: [to.x, to.y] }) }) } onMounted(() { test() }) return { flyLineList } }, } /script style langscss scoped /style组件 FlyLine2.vuetemplate svg classsvg-fly-line width100% height100% xmlnshttp://www.w3.org/2000/svg defs radialGradient idlineGradient cx50% cy50% r50% stop offset0% stop-color#fff stop-opacity1/stop stop offset100% stop-color#fff stop-opacity0/stop /radialGradient linearGradient idlineGradient1 x191.93% y177.23% x28.07% y222.77% !-- 12.37% 处为透明蓝色 -- stop offset12.37% stop-color#00A0E9 stop-opacity0 / !-- 86.15% 处为不透明蓝色 -- stop offset86.15% stop-color#00A0E9 stop-opacity1 / /linearGradient /defs !-- 遍历多条飞线路径 -- g v-for(item, index) in lineLst :keyindex :data-indexindex !-- 动态路径 -- path :idflyPath${index} :dgetFlyPath(item) filltransparent/path !-- 遮罩 -- mask :idflyMask${index} !-- 拖尾 r 长度 -- circle cx0 cy0 r60 fillurl(#lineGradient) animateMotion :dur${item.speed || 5000}ms :pathgetFlyPath(item) rotateauto repeatCountindefinite/animateMotion /circle /mask !-- 底层轨迹线 -- use :xlink:href#flyPath${index} stroke-width8 strokergba(255, 255, 255, .1)/use !-- 渐变流光拖尾 → from/to 自动计算 -- use :xlink:href#flyPath${index} stroke-width6 stroke#00A0E9 :maskurl(#flyMask${index}) animate attributeNamestroke-dasharray :from0, ${item.pathLength} :to${item.pathLength}, 0 :dur${item.speed || 5000}ms repeatCountindefinite /animate /use !-- 箭头 -- path dM0,-4 L8,0 L0,4 Z fill#00A0E9 animateMotion :dur${item.speed || 5000}ms :pathgetFlyPath(item) rotateauto repeatCountindefinite / /path /g /svg /template script setup langts import { defineProps ,getCurrentInstance} from vue // 单条飞线类型 export interface FlyLineItem { from: number[] // 起点 x,y坐标 to: number[] // 终点 x curve?: number // 弧线偏移 正负控制上下弯曲 lineColor?: string // 轨迹颜色 lineWidth?: number // 轨迹宽度 arrowFill?: string // 箭头填充色 arrowScale?: number // 箭头缩放大小 speed?: number // 飞行时长 ms pathLength?:number } //ts-ignore const { ctx: that } getCurrentInstance() const props defineProps{ flyList: FlyLineItem[] }() const lineLst refFlyLineItem[]([]) watch(()props.flyList,async (nVal,oVal){ await nextTick() lineLst.value props.flyList lineLst.value.forEach((item, index) { const path document.getElementById(flyPath${index}) if (path) { //ts-ignore const length path.getTotalLength() console.log([FlyLine2#getPathLength:72]:, ${length}) item.pathLength length } }) },{deep:true,immediate:true}) // 生成贝塞尔曲线路径 const getFlyPath (item: FlyLineItem) { const { from, to, curve -70 } item const centerX (from[0] to[0]) / 2 const centerY (from[1] to[1]) / 2 curve //dM 起点X 起点Y Q 控制点X 控制点Y 终点X 终点Y return M${from[0]},${from[1]} Q${centerX},${centerY} ${to[0]},${to[1]} } async function getPathLength(idx: number) { await nextTick() //ts-ignore const path:SVGPathElement|null document.getElementById(flyPath${idx}) if (path) { const length path.getTotalLength() console.log([FlyLine2#getPathLength:108]:, ${length}) return length } return 0 } // 生成贝塞尔路径 // function getPathD(p1: { x: number, y: number }, p2: { x: number, y: number }, curveOffset -80) { // const mx (p1.x p2.x) / 2 // const my (p1.y p2.y) / 2 curveOffset // return M ${p1.x} ${p1.y} Q ${mx} ${my} ${p2.x} ${p2.y} // } // 获取元素中心点 function getCenter(el: HTMLElement) { const rect el.getBoundingClientRect() return { x: rect.left rect.width / 2, y: rect.top rect.height / 2, } } function handleResize() { lineLst.value.forEach((item, index) { //ts-ignore const path:SVGPathElement|null document.getElementById(flyPath${index}) if (path) { const length path.getTotalLength() item.pathLength length } }) that.$forceUpdate() } // 挂载 onMounted(() { window.addEventListener(resize, handleResize) }) // 销毁 onUnmounted(() { window.removeEventListener(resize, handleResize) }) /script style scoped langscss .svg-fly-line { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 1; } /style