From f52589a65132a2cd350f311044c8c3c3c6efbccd Mon Sep 17 00:00:00 2001 From: "qumeng039@126.com" <86925389+qumen@users.noreply.github.com> Date: Tue, 12 Aug 2025 08:56:18 +0800 Subject: [PATCH 1/2] Update customePaint.js --- src/utils/customePaint.js | 98 ++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 54 deletions(-) diff --git a/src/utils/customePaint.js b/src/utils/customePaint.js index e04b6f6..17ce9ab 100644 --- a/src/utils/customePaint.js +++ b/src/utils/customePaint.js @@ -30,63 +30,53 @@ export const paintNodeFunction = function (ctx, onlyShape) { this.drawShape(ctx, onlyShape) } - //按节点类型绘制节点边框 - if (this.properties.type != "normal") { - // ctx.beginPath() - // ctx.arc(0, 0, this.radius + 6, 0, Math.PI * 2) - // ctx.lineWidth = 8 - // ctx.strokeStyle = `rgba(${this.fillColor},${this.alpha * 0.4})` - // ctx.stroke() - // 半径 & 样式参数(按需调整) - // ===== 三层:实心圆 + 半透明边框 + 外圈虚线 ===== - const rFill = this.radius; // 实心圆半径 - const ringWidth = 15; // 半透明边框的宽度 - const ringGap = 0; // 实心与半透明边框之间的间隙(需要留缝就调大) - const outerGap = 0; // 半透明边框与虚线之间的间隙 - const dashWidth = 2; // 虚线线宽 - const dash = 2, gap = 4; // 虚线样式 - const ringAlphaFactor = 0.5; // 半透明边框的相对透明度(相对于 this.alpha) + // ctx.beginPath() + // ctx.arc(0, 0, this.radius + 6, 0, Math.PI * 2) + // ctx.lineWidth = 8 + // ctx.strokeStyle = `rgba(${this.fillColor},${this.alpha * 0.4})` + // ctx.stroke() + // 半径 & 样式参数(按需调整) + // ===== 三层:实心圆 + 半透明边框 + 外圈虚线 ===== + const rFill = this.radius // 实心圆半径 + const ringWidth = 15 // 半透明边框的宽度 + const ringGap = 0 // 实心与半透明边框之间的间隙(需要留缝就调大) + const outerGap = 0 // 半透明边框与虚线之间的间隙 + const dashWidth = 2 // 虚线线宽 + const dash = 2, + gap = 4 // 虚线样式 + const ringAlphaFactor = 0.5 // 半透明边框的相对透明度(相对于 this.alpha) - // 1) 中间实心圆(填充) - ctx.save(); - ctx.beginPath(); - ctx.arc(0, 0, rFill, 0, Math.PI * 2); - ctx.fillStyle = `rgba(${this.fillColor}, ${this.alpha})`; - ctx.fill(); - ctx.restore(); + // 1) 中间实心圆(填充) + ctx.save() + ctx.beginPath() + ctx.arc(0, 0, rFill, 0, Math.PI * 2) + ctx.fillStyle = `rgba(${this.fillColor}, ${this.alpha})` + ctx.fill() + ctx.restore() - // 2) 同色半透明边框(描边,不是挖空) - ctx.save(); - ctx.beginPath(); - const rRing = rFill + ringGap + ringWidth / 2; - ctx.arc(0, 0, rRing, 0, Math.PI * 2); - ctx.lineWidth = ringWidth; - ctx.setLineDash([]); // 确保这里是实线 - ctx.strokeStyle = `rgba(${this.fillColor}, ${this.alpha * ringAlphaFactor})`; - ctx.stroke(); - ctx.restore(); + // 2) 同色半透明边框(描边,不是挖空) + ctx.save() + ctx.beginPath() + const rRing = rFill + ringGap + ringWidth / 2 + ctx.arc(0, 0, rRing, 0, Math.PI * 2) + ctx.lineWidth = ringWidth + ctx.setLineDash([]) // 确保这里是实线 + ctx.strokeStyle = `rgba(${this.fillColor}, ${this.alpha * ringAlphaFactor})` + ctx.stroke() + ctx.restore() - // 3) 外圈虚线(同色) - ctx.save(); - ctx.beginPath(); - const rDash = rRing + ringWidth / 2 + outerGap + dashWidth / 2; - ctx.arc(0, 0, rDash, 0, Math.PI * 2); - ctx.lineWidth = dashWidth; - ctx.setLineDash([dash, gap]); - ctx.lineCap = 'round'; - ctx.strokeStyle = `rgba(${this.fillColor}, ${this.alpha})`; - ctx.stroke(); - ctx.setLineDash([]); // 清理,避免影响后续绘制 - ctx.restore(); - - } else { - ctx.beginPath() - ctx.arc(0, 0, this.radius + 8, 0, Math.PI * 2) - ctx.lineWidth = 12 - ctx.setLineDash([4, 4]) - ctx.strokeStyle = `rgba(${this.fillColor},${this.alpha * 0.6})` - ctx.stroke() - } + // 3) 外圈虚线(同色) + ctx.save() + ctx.beginPath() + const rDash = rRing + ringWidth / 2 + outerGap + dashWidth / 2 + ctx.arc(0, 0, rDash, 0, Math.PI * 2) + ctx.lineWidth = dashWidth + ctx.setLineDash([dash, gap]) + ctx.lineCap = "round" + ctx.strokeStyle = `rgba(${this.fillColor}, ${this.alpha})` + ctx.stroke() + ctx.setLineDash([]) // 清理,避免影响后续绘制 + ctx.restore() this.paintText(ctx) //调用内部方法绘制文字 } From bc45d925fd62ec23ee519cc1f3f69210dd014d73 Mon Sep 17 00:00:00 2001 From: "qumeng039@126.com" <86925389+qumen@users.noreply.github.com> Date: Tue, 12 Aug 2025 09:44:30 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=AC=AC=E5=9B=9B?= =?UTF-8?q?=E4=B8=AA=E9=A1=B5=E9=9D=A2=E7=9A=84=E8=8A=82=E7=82=B9=E5=92=8C?= =?UTF-8?q?=E5=89=8D=E9=9D=A2=E8=8A=82=E7=82=B9=E7=9A=84=E5=8C=BA=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/groupEvolution/index.js | 60 ++++--- src/utils/customePaint.js | 148 +++++++++--------- .../GroupEvolution/component/groupGraph.vue | 13 +- 3 files changed, 123 insertions(+), 98 deletions(-) diff --git a/src/store/groupEvolution/index.js b/src/store/groupEvolution/index.js index bd67fd3..6338036 100644 --- a/src/store/groupEvolution/index.js +++ b/src/store/groupEvolution/index.js @@ -463,9 +463,13 @@ export const useGroupStructureStore = defineStore("groupStructure", { chart: { xAxisData: item.chart.xaxisData.map((item) => utcStringToHHMMSS(item)), yAxisRange: { - min: item.name === '内部密度指数演化' ? 20.0 : 0, - max: item.name === '内部密度指数演化' ? 20.5 : Math.ceil(item.chart.yaxisRange.max / 5) * 5, - interval: item.name === '内部密度指数演化' ? 0.1 : Math.ceil(item.chart.yaxisRange.max / 5) + min: item.name === "内部密度指数演化" ? 20.0 : 0, + max: + item.name === "内部密度指数演化" + ? 20.5 + : Math.ceil(item.chart.yaxisRange.max / 5) * 5, + interval: + item.name === "内部密度指数演化" ? 0.1 : Math.ceil(item.chart.yaxisRange.max / 5) }, seriesList: item.chart.seriesList.map((item) => ({ data: item.data, @@ -480,9 +484,13 @@ export const useGroupStructureStore = defineStore("groupStructure", { chart: { xAxisData: item.chart.xaxisData.map((item) => utcStringToHHMMSS(item)), yAxisRange: { - min: item.name === '内部密度指数演化' ? 20.0 : 0, - max: item.name === '内部密度指数演化' ? 20.5 : Math.ceil(item.chart.yaxisRange.max / 5) * 5, - interval: item.name === '内部密度指数演化' ? 0.1 : Math.ceil(item.chart.yaxisRange.max / 5) + min: item.name === "内部密度指数演化" ? 20.0 : 0, + max: + item.name === "内部密度指数演化" + ? 20.5 + : Math.ceil(item.chart.yaxisRange.max / 5) * 5, + interval: + item.name === "内部密度指数演化" ? 0.1 : Math.ceil(item.chart.yaxisRange.max / 5) }, seriesList: item.chart.seriesList.map((item) => ({ data: item.data, @@ -497,9 +505,13 @@ export const useGroupStructureStore = defineStore("groupStructure", { chart: { xAxisData: item.chart.xaxisData.map((item) => utcStringToHHMMSS(item)), yAxisRange: { - min: item.name === '内部密度指数演化' ? 20.0 : 0, - max: item.name === '内部密度指数演化' ? 20.5 : Math.ceil(item.chart.yaxisRange.max / 5) * 5, - interval: item.name === '内部密度指数演化' ? 0.1 : Math.ceil(item.chart.yaxisRange.max / 5) + min: item.name === "内部密度指数演化" ? 20.0 : 0, + max: + item.name === "内部密度指数演化" + ? 20.5 + : Math.ceil(item.chart.yaxisRange.max / 5) * 5, + interval: + item.name === "内部密度指数演化" ? 0.1 : Math.ceil(item.chart.yaxisRange.max / 5) }, seriesList: item.chart.seriesList.map((item) => ({ data: item.data, @@ -626,9 +638,15 @@ export const useGroupMemberStore = defineStore("groupMember", { chart: { xAxisData: item.chart.xaxisData.map((item) => utcStringToHHMMSS(item)), yAxisRange: { - min: item.name ==='合并演化' || item.name ==='扩展演化' ? 20.00 : 0, - max: item.name ==='合并演化' || item.name ==='扩展演化' ? 20.03 : Math.ceil(item.chart.yaxisRange.max / 5) * 5, - interval: item.name ==='合并演化' || item.name ==='扩展演化' ? 0.006 : Math.ceil(item.chart.yaxisRange.max / 5) + min: item.name === "合并演化" || item.name === "扩展演化" ? 20.0 : 0, + max: + item.name === "合并演化" || item.name === "扩展演化" + ? 20.03 + : Math.ceil(item.chart.yaxisRange.max / 5) * 5, + interval: + item.name === "合并演化" || item.name === "扩展演化" + ? 0.006 + : Math.ceil(item.chart.yaxisRange.max / 5) }, seriesList: item.chart.seriesList.map((item) => ({ data: item.data.map((item) => item.toFixed(2)), @@ -644,9 +662,15 @@ export const useGroupMemberStore = defineStore("groupMember", { chart: { xAxisData: item.chart.xaxisData.map((item) => utcStringToHHMMSS(item)), yAxisRange: { - min: item.name ==='合并演化' || item.name ==='扩展演化' ? 20.00 : 0, - max: item.name ==='合并演化' || item.name ==='扩展演化' ? 20.03 : Math.ceil(item.chart.yaxisRange.max / 5) * 5, - interval: item.name ==='合并演化' || item.name ==='扩展演化' ? 0.006 : Math.ceil(item.chart.yaxisRange.max / 5) + min: item.name === "合并演化" || item.name === "扩展演化" ? 20.0 : 0, + max: + item.name === "合并演化" || item.name === "扩展演化" + ? 20.03 + : Math.ceil(item.chart.yaxisRange.max / 5) * 5, + interval: + item.name === "合并演化" || item.name === "扩展演化" + ? 0.006 + : Math.ceil(item.chart.yaxisRange.max / 5) }, seriesList: item.chart.seriesList.map((item) => ({ data: item.data.map((item) => item.toFixed(2)), @@ -662,9 +686,9 @@ export const useGroupMemberStore = defineStore("groupMember", { chart: { xAxisData: item.chart.xaxisData.map((item) => utcStringToHHMMSS(item)), yAxisRange: { - min: item.name ==='合并演化' ? 20.00 : 0, - max: item.name ==='合并演化' ? 20.03 : Math.ceil(item.chart.yaxisRange.max / 5) * 5, - interval: item.name ==='合并演化' ? 0.006 : Math.ceil(item.chart.yaxisRange.max / 5) + min: item.name === "合并演化" ? 20.0 : 0, + max: item.name === "合并演化" ? 20.03 : Math.ceil(item.chart.yaxisRange.max / 5) * 5, + interval: item.name === "合并演化" ? 0.006 : Math.ceil(item.chart.yaxisRange.max / 5) }, seriesList: item.chart.seriesList.map((item) => ({ data: item.data.map((item) => item.toFixed(2)), diff --git a/src/utils/customePaint.js b/src/utils/customePaint.js index 17ce9ab..f056019 100644 --- a/src/utils/customePaint.js +++ b/src/utils/customePaint.js @@ -1,84 +1,88 @@ //自定义绘制节点的方法 -export const paintNodeFunction = function (ctx, onlyShape) { - //默认绘制数据类型图片 - if (this.properties.typeIcon) { - if (this.selected || this.showSelected) { +export const paintNodeFunction = (storeId) => { + return function (ctx, onlyShape) { + //默认绘制数据类型图片 + if (this.properties.typeIcon) { + if (this.selected || this.showSelected) { + this.drawShape(ctx, onlyShape) + } + + if (this.alpha < 1) { + ctx.save() + ctx.globalAlpha = this.alpha + ctx.drawImage( + this.properties.typeIcon, + -this.width / 2, + -this.height / 2, + this.width, + this.height + ) + ctx.restore() + } else { + ctx.drawImage( + this.properties.typeIcon, + -this.width / 2, + -this.height / 2, + this.width, + this.height + ) + } + } else { this.drawShape(ctx, onlyShape) } + if (storeId == "anomalousGroup") { + // 半径 & 样式参数(按需调整) + // ===== 三层:实心圆 + 半透明边框 + 外圈虚线 ===== + const rFill = this.radius // 实心圆半径 + const ringWidth = 15 // 半透明边框的宽度 + const ringGap = 0 // 实心与半透明边框之间的间隙(需要留缝就调大) + const outerGap = 0 // 半透明边框与虚线之间的间隙 + const dashWidth = 2 // 虚线线宽 + const dash = 2, + gap = 4 // 虚线样式 + const ringAlphaFactor = 0.5 // 半透明边框的相对透明度(相对于 this.alpha) - if (this.alpha < 1) { + // 1) 中间实心圆(填充) ctx.save() - ctx.globalAlpha = this.alpha - ctx.drawImage( - this.properties.typeIcon, - -this.width / 2, - -this.height / 2, - this.width, - this.height - ) + ctx.beginPath() + ctx.arc(0, 0, rFill, 0, Math.PI * 2) + ctx.fillStyle = `rgba(${this.fillColor}, ${this.alpha})` + ctx.fill() + ctx.restore() + + // 2) 同色半透明边框(描边,不是挖空) + ctx.save() + ctx.beginPath() + const rRing = rFill + ringGap + ringWidth / 2 + ctx.arc(0, 0, rRing, 0, Math.PI * 2) + ctx.lineWidth = ringWidth + ctx.setLineDash([]) // 确保这里是实线 + ctx.strokeStyle = `rgba(${this.fillColor}, ${this.alpha * ringAlphaFactor})` + ctx.stroke() + ctx.restore() + + // 3) 外圈虚线(同色) + ctx.save() + ctx.beginPath() + const rDash = rRing + ringWidth / 2 + outerGap + dashWidth / 2 + ctx.arc(0, 0, rDash, 0, Math.PI * 2) + ctx.lineWidth = dashWidth + ctx.setLineDash([dash, gap]) + ctx.lineCap = "round" + ctx.strokeStyle = `rgba(${this.fillColor}, ${this.alpha})` + ctx.stroke() + ctx.setLineDash([]) // 清理,避免影响后续绘制 ctx.restore() } else { - ctx.drawImage( - this.properties.typeIcon, - -this.width / 2, - -this.height / 2, - this.width, - this.height - ) + ctx.beginPath() + ctx.arc(0, 0, this.radius + 6, 0, Math.PI * 2) + ctx.lineWidth = 5 + ctx.strokeStyle = `rgba(${this.fillColor},${this.alpha * 0.4})` + ctx.stroke() } - } else { - this.drawShape(ctx, onlyShape) + + this.paintText(ctx) //调用内部方法绘制文字 } - - // ctx.beginPath() - // ctx.arc(0, 0, this.radius + 6, 0, Math.PI * 2) - // ctx.lineWidth = 8 - // ctx.strokeStyle = `rgba(${this.fillColor},${this.alpha * 0.4})` - // ctx.stroke() - // 半径 & 样式参数(按需调整) - // ===== 三层:实心圆 + 半透明边框 + 外圈虚线 ===== - const rFill = this.radius // 实心圆半径 - const ringWidth = 15 // 半透明边框的宽度 - const ringGap = 0 // 实心与半透明边框之间的间隙(需要留缝就调大) - const outerGap = 0 // 半透明边框与虚线之间的间隙 - const dashWidth = 2 // 虚线线宽 - const dash = 2, - gap = 4 // 虚线样式 - const ringAlphaFactor = 0.5 // 半透明边框的相对透明度(相对于 this.alpha) - - // 1) 中间实心圆(填充) - ctx.save() - ctx.beginPath() - ctx.arc(0, 0, rFill, 0, Math.PI * 2) - ctx.fillStyle = `rgba(${this.fillColor}, ${this.alpha})` - ctx.fill() - ctx.restore() - - // 2) 同色半透明边框(描边,不是挖空) - ctx.save() - ctx.beginPath() - const rRing = rFill + ringGap + ringWidth / 2 - ctx.arc(0, 0, rRing, 0, Math.PI * 2) - ctx.lineWidth = ringWidth - ctx.setLineDash([]) // 确保这里是实线 - ctx.strokeStyle = `rgba(${this.fillColor}, ${this.alpha * ringAlphaFactor})` - ctx.stroke() - ctx.restore() - - // 3) 外圈虚线(同色) - ctx.save() - ctx.beginPath() - const rDash = rRing + ringWidth / 2 + outerGap + dashWidth / 2 - ctx.arc(0, 0, rDash, 0, Math.PI * 2) - ctx.lineWidth = dashWidth - ctx.setLineDash([dash, gap]) - ctx.lineCap = "round" - ctx.strokeStyle = `rgba(${this.fillColor}, ${this.alpha})` - ctx.stroke() - ctx.setLineDash([]) // 清理,避免影响后续绘制 - ctx.restore() - - this.paintText(ctx) //调用内部方法绘制文字 } //自定义连线的方法 diff --git a/src/views/GroupEvolution/component/groupGraph.vue b/src/views/GroupEvolution/component/groupGraph.vue index 2d662e9..b19d69b 100644 --- a/src/views/GroupEvolution/component/groupGraph.vue +++ b/src/views/GroupEvolution/component/groupGraph.vue @@ -46,10 +46,9 @@ const defaultConfig = { textOffsetY: 10 }, shape: "circle", - size: 15, + size: storeId == "anomalousGroup" ? 15 : 40, borderColor: "200,50,50", borderWidth: 0, - selected: { borderWidth: 5, borderColor: "100,250,100", @@ -77,8 +76,8 @@ const defaultConfig = { }, highLightNeiber: true // 相邻节点高亮开关 } -const registCustomePaintFunc = () => { - graphVis.definedNodePaintFunc(paintNodeFunction) //自定义节点绘图方法 +const registCustomePaintFunc = (curStoreId) => { + graphVis.definedNodePaintFunc(paintNodeFunction(curStoreId)) //自定义节点绘图方法 graphVis.definedLinkPaintFunc(paintLineFunction) //自定义关系绘图方法 } // 处理时间轴点击事件和拉动 @@ -128,7 +127,6 @@ const registEvents = () => { currentSelectNode.value.fx = null currentSelectNode.value.fy = null currentSelectNode.value = null - // highLightAboutNodesOrLinks(typeMap[storeId]) } }) } @@ -136,9 +134,9 @@ const registEvents = () => { //公用连线或节点高亮函数 const highLightAboutNodesOrLinks = (type) => { graphVis.cancelAllSelected() + const { newNodes, newLinks } = graph.value if (type == "nodes") { //实现高亮节点逻辑 - const { newNodes } = graph.value graphVis.nodes.forEach((node) => newNodes.forEach((newNode) => { if (node.id === newNode.name) { @@ -148,7 +146,6 @@ const highLightAboutNodesOrLinks = (type) => { ) } else if (type == "links") { //实现连线高亮逻辑 - const { newLinks } = graph.value graphVis.links.forEach((link) => newLinks.forEach((newLink) => { if (link.source.id === newLink.source && link.target.id === newLink.target) { @@ -311,7 +308,7 @@ const createGraph = () => { graphVis.setDragHideLine(false) //拖拽时隐藏连线 graphVis.setShowDetailScale(0.1) //展示细节的比例 graphVis.setZoomRange(0.1, 5) //缩放区间 - registCustomePaintFunc() //注册自定义绘图方法 + registCustomePaintFunc(storeId) //注册自定义绘图方法 registEvents() }