vue3+ts 实现d3思维导图代码 并且自定义无线嵌套
时间: 2023-08-21 10:07:49 浏览: 188
以下是一个使用 Vue3 和 TypeScript 实现的 D3 思维导图的示例代码,同时自定义了无限嵌套的功能:
```vue
<template>
<div ref="svgContainer"></div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref } from 'vue';
import * as d3 from 'd3';
export default defineComponent({
setup() {
const svgContainer = ref<HTMLElement | null>(null);
let svg: d3.Selection<SVGSVGElement, unknown, null, undefined> | undefined;
let zoom: d3.ZoomBehavior<Element, unknown> | undefined;
let g: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
let root: d3.HierarchyNode<unknown> | undefined;
const data = {
name: 'root',
children: [
{ name: 'child1' },
{ name: 'child2', children: [{ name: 'grandchild1' }, { name: 'grandchild2' }] },
],
};
const treeLayout = d3.tree().size([400, 400]);
// 递归遍历树结构,为每个节点添加 id 和 parent 属性
const addIdAndParent = (node: d3.HierarchyNode<unknown>, parentId?: string) => {
node.id = `${node.data.name}_${Math.random().toString(16).slice(2)}`;
node.parentId = parentId;
if (node.children) {
node.children.forEach((child) => addIdAndParent(child, node.id));
}
};
// 自定义的无限嵌套函数,每次嵌套时在当前节点下添加两个子节点
const nest = (node: d3.HierarchyNode<unknown>) => {
const id1 = `${node.id}_1`;
const id2 = `${node.id}_2`;
node.children = [
{ name: `child1_${id1}` },
{ name: `child2_${id2}`, children: [{ name: `grandchild1_${id1}` }, { name: `grandchild2_${id2}` }] },
];
node.children.forEach((child) => {
child.id = `${child.name}_${Math.random().toString(16).slice(2)}`;
child.parentId = node.id;
});
return node;
};
const update = (source: d3.HierarchyNode<unknown>) => {
// 更新树结构
const nodes = treeLayout(root!).descendants();
const links = treeLayout(root!).links();
// 更新链接
const link = g!.selectAll('.link').data(links, (d) => (d as any).target.id);
link.enter().append('path').attr('class', 'link').attr('d', d3.linkHorizontal().x((d: any) => d.y).y((d: any) => d.x));
link.transition().attr('d', d3.linkHorizontal().x((d: any) => d.y).y((d: any) => d.x));
link.exit().remove();
// 更新节点
const node = g!.selectAll('.node').data(nodes, (d) => (d as any).id);
const nodeEnter = node.enter().append('g').attr('class', 'node').attr('transform', () => `translate(${source.y0},${source.x0})`);
nodeEnter.append('circle').attr('r', 10).style('fill', '#fff').style('stroke', '#000');
nodeEnter.append('text').attr('dy', '0.35em').attr('x', (d) => (d.children ? -13 : 13)).style('text-anchor', (d) => (d.children ? 'end' : 'start')).text((d) => d.data.name);
const nodeUpdate = nodeEnter.merge(node);
nodeUpdate.transition().attr('transform', (d) => `translate(${d.y},${d.x})`);
node.exit().transition().attr('transform', () => `translate(${source.y},${source.x})`).remove();
nodes.forEach((d) => {
d.x0 = d.x;
d.y0 = d.y;
});
};
const init = () => {
svg = d3.select(svgContainer.value).append('svg').attr('width', '100%').attr('height', '100%');
zoom = d3.zoom().scaleExtent([0.1, 10]).on('zoom', () => {
g!.attr('transform', d3.event.transform);
});
svg.call(zoom);
g = svg.append('g').attr('transform', 'translate(50, 50)');
root = d3.hierarchy(data, (d) => d.children);
addIdAndParent(root!);
update(root!);
// 点击节点时无限嵌套
svg.selectAll('.node').on('click', (d: d3.HierarchyNode<unknown>) => {
nest(d);
update(d);
zoom?.transform(svg!, d3.zoomIdentity.translate(50, 50).scale(1));
});
};
onMounted(() => {
init();
});
return {
svgContainer,
};
},
});
</script>
<style>
.node {
cursor: pointer;
}
</style>
```
在这个示例中,我们使用了 D3 的树形布局来排列思维导图的节点,使用递归函数遍历树结构为每个节点添加了 id 和 parentId 属性,并且使用了 D3 的选择集和过渡来更新节点和链接的位置。同时,在点击节点时使用自定义的无限嵌套函数实现了无限嵌套的功能。
需要注意的是,这个示例代码只是一个比较简单的实现,具体的实现方式和界面样式还需要根据具体的需求进行调整。
阅读全文