Loading src/components/NetworkGraph.vue +19 −51 Original line number Diff line number Diff line Loading @@ -45,35 +45,15 @@ <rect id="zoom-pan-interaction-layer" width="100%" height="100%" fill-opacity="0" /> <g id="g-elements"> <g class="g-links" <g id="g-elements" :transform=" `translate(${graph.transform.x}, ${graph.transform.y}) scale(${graph.transform.k})` " > <!-- when running force simulation, show a dumbed down version of links due to performance --> <transition name="fade-quick"> <g v-if="graph.showTemporaryLinks && data.links.length < 100"> <line v-for="link in data.links" :key="`temp-link-${link.id}`" :x1="link.source.x" :y1="link.source.y" :x2="link.target.x" :y2="link.target.y" class="temporary-link" /> </g> </transition> <transition name="fade"> <g v-if="!isSimulationRunning"> <GraphLink <GraphLinkFunctional v-for="link in data.links" :key="`link-${link.id}`" :dataId="link.id" :source="{ id: link.source.id, x: link.source.x, Loading @@ -84,30 +64,14 @@ x: link.target.x, y: link.target.y }" :opacity="link.opacity" :visuals="visuals" /> </g> </transition> </g> <g class="g-nodes" :transform=" `translate(${graph.transform.x}, ${graph.transform.y}) scale(${graph.transform.k})` " > <GraphNode <GraphNodeFunctional v-for="node in data.nodes" :key="`node-${node.id}`" :dataId="node.id" :node="{ type: 'regular', dataClass: 'person', id: 0, label: 'abc'}" :visuals="visuals" :graphX="node.x" :graphY="node.y" :opacity="node.opacity" :moveCallback="moveNode" @node-mouseout="saveNodePosition(node.id, node.x, node.y)" /> <!-- Moving a node shall render it on top, ideally reordering the DOM Loading @@ -116,26 +80,25 @@ moving another node, but it is acceptable.--> <use v-if="graph.lastMovedNodeId" :xlink:href="`#node-${graph.lastMovedNodeId}`" /> </g> </g> </svg> </div> </template> <script> import Vue from 'vue' import { mapActions, mapState } from 'vuex' import { mapActions, mapState, mapGetters } from 'vuex' import * as d3 from 'd3' // TODO: import only needed modules import VueScreenSize from 'vue-screen-size' import { sleep, getLastElementByIndex } from '@/mixins/helpers.js' import GraphNode from '@/components/NetworkGraph/GraphNode.vue' import GraphLink from '@/components/NetworkGraph/GraphLink.vue' import GraphNodeFunctional from '@/components/NetworkGraph/GraphNodeFunctional.vue' import GraphLinkFunctional from '@/components/NetworkGraph/GraphLinkFunctional.vue' import GraphLinkMarker from '@/components/NetworkGraph/GraphLinkMarker.vue' export default { components: { GraphNode, GraphLink, GraphNodeFunctional, GraphLinkFunctional, GraphLinkMarker }, Loading Loading @@ -184,7 +147,7 @@ export default { width: null, height: null, transform: { x: 0, y: 0, k: 1 }, // set by d3-zoom zoomMin: 0.2, zoomMin: 0.05, zoomMax: 5, isPanningOrZooming: false, lastMovedNodeId: NaN, // to maintain moving node on top in DOM Loading @@ -201,7 +164,8 @@ export default { }, computed: { ...mapState('interaction', ['inDebug']) ...mapState('interaction', ['inDebug']), ...mapState('data', ['nodes']) }, watch: { Loading Loading @@ -270,6 +234,10 @@ export default { ]), ...mapActions('data', ['updateNodes']), getNodeById(id) { return this.nodes.find(n => n.id === id) }, respondAddNode(payload) { console.log('NetworkGraph: data/ADD_NODE with payload:' + JSON.stringify(payload)) this.data.nodes.push({ Loading Loading @@ -452,9 +420,9 @@ export default { 'charge', d3 .forceManyBody() .strength(-2000) .distanceMin(50) .distanceMax(400) .strength(-20) .distanceMin(5) .distanceMax(40) ) // electrostatic force between all nodes .force('center', d3.forceCenter(this.graph.width / 2, this.graph.height / 2)) // translate to svg center .force( Loading src/components/NetworkGraph/GraphLinkFunctional.vue 0 → 100644 +86 −0 Original line number Diff line number Diff line <template functional> <g class="link"> <line :x1="props.source.x" :y1="props.source.y" :x2="props.target.x" :y2="props.target.y" class="link-line" ></line> <line :x1="props.source.x" :y1="props.source.y" :x2="props.target.x" :y2="props.target.y" class="support-link" ></line> </g> </template> <style lang="sass" scoped> .link line cursor: pointer stroke: #bbb &.with-arrows marker-end: url(#single-link-arrow) &.multilink stroke-width: 3px stroke: #ccc &.source-to-target marker-end: url(#multi-link-arrow) &.non-directional marker-end: url(#multi-link-arrow-bar) &.target-to-source marker-start: url(#multi-link-arrow-opposite) &.non-directional marker-start: url(#multi-link-arrow-bar-opposite) &.user-defined stroke-dasharray: 7,7 stroke-linecap: round &.transferred stroke-dasharray: 0,6 stroke-width: 2px stroke-linecap: round &.multilink stroke-width: 4px &.highlight.highlightable stroke: #444 line.hovered stroke: #757575 .link.detailed line.link-line stroke: #757575 .link.highlighted line.link-line stroke: #757575 .support-link stroke-opacity: 0 stroke-width: 16px .link-label-box display: inline fill: rgba(220, 220, 220, 0.7) text.label-id font-size: 11pt font-weight: 600 fill: #4903fc user-select: none text.multilink-label font-size: 0.8em font-weight: 600 fill: #999 .link-paws circle fill: #bbb .detailed fill: #444 .hovered fill: #757575 .highlighted fill: #757575 </style> src/components/NetworkGraph/GraphNodeFunctional.vue 0 → 100644 +112 −0 Original line number Diff line number Diff line <template functional> <g class="node" :transform="`translate(${props.graphX}, ${props.graphY})`"> <g class="node-label"> <rect class="node-label-box" :width="150" height="22" rx="5" ry="5" y="-12" :x="props.node.type == 'group' ? props.visuals.groupNodeRadiusDifference / 2.0 : 0" /> <text class="node-label-text" dx="24" dy="4" :x="props.node.type == 'group' ? props.visuals.groupNodeRadiusDifference : 0" > {{ props.node.label.length >= 18 ? props.node.label.substring(0, 15) + '...' : props.node.label }} </text> </g> <g class="node-icon"> <circle v-if="props.node.type === 'group'" class="node-circle" :r="20 + props.visuals.groupNodeRadiusDifference" /> <!-- alias --> <circle v-if="props.node.type === 'alias'" class="node-circle" :cx="-20 / 2.0" :cy="-20 / 2.0" :r="20" /> <circle class="node-circle" :r="20" :fill="`#fff`" /> <BaseIcon use-svg :name="props.node.type === 'group' ? 'group' : props.node.dataClass" :radius="20" /> </g> </g> </template> <style lang="sass" scoped> .node-icon cursor: pointer .hovered .node-circle stroke: #979797 .node-circle stroke-width: 1.5px stroke: #d8d8d8 fill: #fff .node.removed .node-circle stroke-dasharray: 4 image fill: red .node-label-box display: inline fill: rgba(220, 220, 220, 0.65) // #f2f2f2 .node-label-text font-size: 0.85em fill: #666 display: inline .node-label-id font-size: 1.3em font-weight: 600 fill: #4903fc user-select: none .detailed .node-icon .node-circle stroke: #757575 .highlighted .node-icon .node-circle stroke: #979797 fill: #979797 .selected .node-icon .node-circle stroke: #1e2dff stroke-width: 1.5px .node-circle-detailed-and-selected stroke: #979797 stroke-width: 5px .node-circle.connected-link-hovered stroke: #757575 .bundle .bundle-bubble display: inline .bundle-circle stroke-width: 1.5px stroke: #D8D8D8 fill: #fff .bundle-text font-size: 0.65em font-weight: 600 fill: #666 </style> src/datasets/generator.js +2 −2 Original line number Diff line number Diff line Loading @@ -27,8 +27,8 @@ export function generateDataset(nodeCount, linkCount) { } if (insertLinkWithMultilinkUpdate(link, links, n + 1)) n++ if (random(0, 100) < 40) sourceId++ if (random(0, 100) < 40) targetId-- if (random(0, 100) < 50) sourceId++ if (random(0, 100) < 50) targetId-- if (sourceId === targetId) sourceId++ if (sourceId > lastNodeId || targetId > lastNodeId || targetId < 0) break } Loading Loading
src/components/NetworkGraph.vue +19 −51 Original line number Diff line number Diff line Loading @@ -45,35 +45,15 @@ <rect id="zoom-pan-interaction-layer" width="100%" height="100%" fill-opacity="0" /> <g id="g-elements"> <g class="g-links" <g id="g-elements" :transform=" `translate(${graph.transform.x}, ${graph.transform.y}) scale(${graph.transform.k})` " > <!-- when running force simulation, show a dumbed down version of links due to performance --> <transition name="fade-quick"> <g v-if="graph.showTemporaryLinks && data.links.length < 100"> <line v-for="link in data.links" :key="`temp-link-${link.id}`" :x1="link.source.x" :y1="link.source.y" :x2="link.target.x" :y2="link.target.y" class="temporary-link" /> </g> </transition> <transition name="fade"> <g v-if="!isSimulationRunning"> <GraphLink <GraphLinkFunctional v-for="link in data.links" :key="`link-${link.id}`" :dataId="link.id" :source="{ id: link.source.id, x: link.source.x, Loading @@ -84,30 +64,14 @@ x: link.target.x, y: link.target.y }" :opacity="link.opacity" :visuals="visuals" /> </g> </transition> </g> <g class="g-nodes" :transform=" `translate(${graph.transform.x}, ${graph.transform.y}) scale(${graph.transform.k})` " > <GraphNode <GraphNodeFunctional v-for="node in data.nodes" :key="`node-${node.id}`" :dataId="node.id" :node="{ type: 'regular', dataClass: 'person', id: 0, label: 'abc'}" :visuals="visuals" :graphX="node.x" :graphY="node.y" :opacity="node.opacity" :moveCallback="moveNode" @node-mouseout="saveNodePosition(node.id, node.x, node.y)" /> <!-- Moving a node shall render it on top, ideally reordering the DOM Loading @@ -116,26 +80,25 @@ moving another node, but it is acceptable.--> <use v-if="graph.lastMovedNodeId" :xlink:href="`#node-${graph.lastMovedNodeId}`" /> </g> </g> </svg> </div> </template> <script> import Vue from 'vue' import { mapActions, mapState } from 'vuex' import { mapActions, mapState, mapGetters } from 'vuex' import * as d3 from 'd3' // TODO: import only needed modules import VueScreenSize from 'vue-screen-size' import { sleep, getLastElementByIndex } from '@/mixins/helpers.js' import GraphNode from '@/components/NetworkGraph/GraphNode.vue' import GraphLink from '@/components/NetworkGraph/GraphLink.vue' import GraphNodeFunctional from '@/components/NetworkGraph/GraphNodeFunctional.vue' import GraphLinkFunctional from '@/components/NetworkGraph/GraphLinkFunctional.vue' import GraphLinkMarker from '@/components/NetworkGraph/GraphLinkMarker.vue' export default { components: { GraphNode, GraphLink, GraphNodeFunctional, GraphLinkFunctional, GraphLinkMarker }, Loading Loading @@ -184,7 +147,7 @@ export default { width: null, height: null, transform: { x: 0, y: 0, k: 1 }, // set by d3-zoom zoomMin: 0.2, zoomMin: 0.05, zoomMax: 5, isPanningOrZooming: false, lastMovedNodeId: NaN, // to maintain moving node on top in DOM Loading @@ -201,7 +164,8 @@ export default { }, computed: { ...mapState('interaction', ['inDebug']) ...mapState('interaction', ['inDebug']), ...mapState('data', ['nodes']) }, watch: { Loading Loading @@ -270,6 +234,10 @@ export default { ]), ...mapActions('data', ['updateNodes']), getNodeById(id) { return this.nodes.find(n => n.id === id) }, respondAddNode(payload) { console.log('NetworkGraph: data/ADD_NODE with payload:' + JSON.stringify(payload)) this.data.nodes.push({ Loading Loading @@ -452,9 +420,9 @@ export default { 'charge', d3 .forceManyBody() .strength(-2000) .distanceMin(50) .distanceMax(400) .strength(-20) .distanceMin(5) .distanceMax(40) ) // electrostatic force between all nodes .force('center', d3.forceCenter(this.graph.width / 2, this.graph.height / 2)) // translate to svg center .force( Loading
src/components/NetworkGraph/GraphLinkFunctional.vue 0 → 100644 +86 −0 Original line number Diff line number Diff line <template functional> <g class="link"> <line :x1="props.source.x" :y1="props.source.y" :x2="props.target.x" :y2="props.target.y" class="link-line" ></line> <line :x1="props.source.x" :y1="props.source.y" :x2="props.target.x" :y2="props.target.y" class="support-link" ></line> </g> </template> <style lang="sass" scoped> .link line cursor: pointer stroke: #bbb &.with-arrows marker-end: url(#single-link-arrow) &.multilink stroke-width: 3px stroke: #ccc &.source-to-target marker-end: url(#multi-link-arrow) &.non-directional marker-end: url(#multi-link-arrow-bar) &.target-to-source marker-start: url(#multi-link-arrow-opposite) &.non-directional marker-start: url(#multi-link-arrow-bar-opposite) &.user-defined stroke-dasharray: 7,7 stroke-linecap: round &.transferred stroke-dasharray: 0,6 stroke-width: 2px stroke-linecap: round &.multilink stroke-width: 4px &.highlight.highlightable stroke: #444 line.hovered stroke: #757575 .link.detailed line.link-line stroke: #757575 .link.highlighted line.link-line stroke: #757575 .support-link stroke-opacity: 0 stroke-width: 16px .link-label-box display: inline fill: rgba(220, 220, 220, 0.7) text.label-id font-size: 11pt font-weight: 600 fill: #4903fc user-select: none text.multilink-label font-size: 0.8em font-weight: 600 fill: #999 .link-paws circle fill: #bbb .detailed fill: #444 .hovered fill: #757575 .highlighted fill: #757575 </style>
src/components/NetworkGraph/GraphNodeFunctional.vue 0 → 100644 +112 −0 Original line number Diff line number Diff line <template functional> <g class="node" :transform="`translate(${props.graphX}, ${props.graphY})`"> <g class="node-label"> <rect class="node-label-box" :width="150" height="22" rx="5" ry="5" y="-12" :x="props.node.type == 'group' ? props.visuals.groupNodeRadiusDifference / 2.0 : 0" /> <text class="node-label-text" dx="24" dy="4" :x="props.node.type == 'group' ? props.visuals.groupNodeRadiusDifference : 0" > {{ props.node.label.length >= 18 ? props.node.label.substring(0, 15) + '...' : props.node.label }} </text> </g> <g class="node-icon"> <circle v-if="props.node.type === 'group'" class="node-circle" :r="20 + props.visuals.groupNodeRadiusDifference" /> <!-- alias --> <circle v-if="props.node.type === 'alias'" class="node-circle" :cx="-20 / 2.0" :cy="-20 / 2.0" :r="20" /> <circle class="node-circle" :r="20" :fill="`#fff`" /> <BaseIcon use-svg :name="props.node.type === 'group' ? 'group' : props.node.dataClass" :radius="20" /> </g> </g> </template> <style lang="sass" scoped> .node-icon cursor: pointer .hovered .node-circle stroke: #979797 .node-circle stroke-width: 1.5px stroke: #d8d8d8 fill: #fff .node.removed .node-circle stroke-dasharray: 4 image fill: red .node-label-box display: inline fill: rgba(220, 220, 220, 0.65) // #f2f2f2 .node-label-text font-size: 0.85em fill: #666 display: inline .node-label-id font-size: 1.3em font-weight: 600 fill: #4903fc user-select: none .detailed .node-icon .node-circle stroke: #757575 .highlighted .node-icon .node-circle stroke: #979797 fill: #979797 .selected .node-icon .node-circle stroke: #1e2dff stroke-width: 1.5px .node-circle-detailed-and-selected stroke: #979797 stroke-width: 5px .node-circle.connected-link-hovered stroke: #757575 .bundle .bundle-bubble display: inline .bundle-circle stroke-width: 1.5px stroke: #D8D8D8 fill: #fff .bundle-text font-size: 0.65em font-weight: 600 fill: #666 </style>
src/datasets/generator.js +2 −2 Original line number Diff line number Diff line Loading @@ -27,8 +27,8 @@ export function generateDataset(nodeCount, linkCount) { } if (insertLinkWithMultilinkUpdate(link, links, n + 1)) n++ if (random(0, 100) < 40) sourceId++ if (random(0, 100) < 40) targetId-- if (random(0, 100) < 50) sourceId++ if (random(0, 100) < 50) targetId-- if (sourceId === targetId) sourceId++ if (sourceId > lastNodeId || targetId > lastNodeId || targetId < 0) break } Loading