项目中需要做个家谱图,网上查了好多资料没找到合适的,就自己写个简单的,方便以后查看,附上效果图

首先展示父亲、配偶、子女,三代人信息,然后选择其他人可以展开他的三代关系。如下图

下面是代码,这个关系图还只是个初稿,里有些逻辑不全,其中母亲这个通过父亲展开就不合适。以后有机会再完善吧。

height='250'

highlight-current-row

@current-change="handleCurrentChange"

:data='nodes'>

prop='id'

label='id'

width='50'>

prop='name'

label='姓名'>

height='250'

:data='links'>

prop='srcId'

label='源id'

width='50'>

prop='toId'

label='目标id'>

label='关系'>

{{ scope.row.type | toCn }}

import * as d3 from 'd3';

let width_ = 60;

export default {

data () {

return {

srcNode: null,

svg: null,

nodes: [

{id: '0', name: '张三'},

{id: '1', name: '张父'},

{id: '2', name: '张母'},

{id: '3', name: '张三妻'},

{id: '4', name: '张大'},

{id: '5', name: '张小'},

{id: '6', name: '张小妻'},

{id: '7', name: '张小小'},

{id: '8', name: '张三妻父'},

{id: '9', name: '张三妻母'},

{id: '10', name: '张三妻弟'}

],

links: [

{srcId: '0', toId: '1', type: 0}, // 0 父子

// {srcId: '0', toId: '2', type: 1}, // 1 母子

{srcId: '1', toId: '2', type: 2}, // 2 配偶

{srcId: '0', toId: '3', type: 2}, // 2 配偶

{srcId: '0', toId: '4', type: 3}, // 子女

{srcId: '0', toId: '5', type: 3}, // 子女

{srcId: '5', toId: '6', type: 2}, // 配偶

{srcId: '5', toId: '7', type: 3}, // 子女

{srcId: '3', toId: '8', type: 0}, // 父

{srcId: '8', toId: '9', type: 2}, // 配偶

{srcId: '8', toId: '10', type: 3} // 子女

],

drag: false

};

},

filters: {

toCn (src) {

let res = ['父子', '母子', '配偶', '子女'];

return res[src];

}

},

methods: {

handleCurrentChange (row) {

this.srcNode = row;

// 查找中心点连线关系 srcId = 0

let srcId = row.id;

let srcNode = this.nodes.filter(n => n.id === srcId)[0];

// 获取与此节点关系点

let links = this.links.filter(l => l.srcId === srcId);

let otherlinks = this.links.filter(l => l.toId === srcId).map(l => {

let link = {};

if (l.type === 3) { // 子女 -> 父子

link.type = 0;

} else if (l.type === 0) { // 父女 -> 子女

link.type = 3;

} else {

link.type = l.type;

}

link.srcId = l.toId;

link.toId = l.srcId;

return link;

});

links.push(...otherlinks);

// 计算节点坐标

let nodes_ = this.convert(srcNode, links);

let that = this;

// 设置画布

let width = 1000;

let height = 720;

// console.log(d3.select('.testd3')[0].innerHTML);

d3.select('.testd3').selectAll('*').remove();

// 画中心点

let svg = d3.select('.testd3').append('svg')

.attr('width', width)

.attr('height', height)

.append('g')

.attr('transform', 'translate(40,0)');

var drag = d3.behavior.drag()

.on('drag', function(d) {

d3.select(this)

.attr('transform', 'translate(' + (d3.event.x - 30) + ',' + (d3.event.y - 30) + ')')

.append('rect')

.attr('x', d.x = d3.event.x - 30)

.attr('y', d.y = d3.event.y - 30);

// 线条

svg.selectAll(`.link-${d.id}`).attr('d', function(dd) {

return that.getPath(d, dd);

});

});

that.drag = drag;

// 画线

this.drawLinks(svg, links);

// 画点

console.log('nodes_', nodes_);

this.drawNodes(svg, nodes_);

},

// 转换

convert (srcNode, links) {

let nodes = [];

let map = new Map();

// 子女个数

let childsize = links.filter(l => l.type === 3).length;

// 子女开始位置

let start = -(childsize - 1) * width_;

if (srcNode.x === undefined) { // 默认坐标

srcNode.x = 150;

srcNode.y = 150;

}

map.set(srcNode.id, srcNode);

let {x, y} = srcNode;

links.forEach(l => {

if (l.type === 0) { // 父子

map.set(l.toId, {x: x, y: y - width_ * 2, type: l.type});

}

if (l.type === 1) { // 母子

map.set(l.toId, {x: x + width_ * 2, y: y - width_ * 2, type: l.type});

}

if (l.type === 2) { // 配偶

map.set(l.toId, {x: x + width_ * 2, y: y, type: l.type});

}

if (l.type === 3) { // 子女

map.set(l.toId, {x: x + start, y: y + width_ * 2, type: l.type});

start = start + width_ * 2;

}

});

this.nodes.forEach(n => {

let m = map.get(n.id);

if (m) {

n['x'] = n.x || m.x;

n['y'] = n.y || m.y;

n['type'] = m.type;

nodes.push(n);

}

});

return nodes;

},

getPath (move, stas) {

// 获取对方坐标

let srcId = stas.srcId;

let flag = false;

if (move.id === stas.srcId) {

srcId = stas.toId;

flag = true;

}

let path;

// 源

let {x, y} = this.nodes.filter(n => n.id === srcId)[0];

if (stas.type === 0) { // 父子

if (flag) {

path = `M${x + width_ / 2} ${y}

L${x + width_ / 2} ${y + width_ * 1.5}

L${move.x + width_ / 2} ${move.y - width_ / 2}

L${move.x + width_ / 2} ${move.y}`;

} else {

path = `M${x + width_ / 2} ${y}

L${x + width_ / 2} ${y - width_ / 2}

L${move.x + width_ / 2} ${move.y + width_ * 1.5}

L${move.x + width_ / 2} ${move.y}`;

}

}

if (stas.type === 2 || stas.type === 1) { // 配偶

let w_ = move.x > x ? width_ : -width_;

let padding = move.x > x ? -width_ / 2 : width_ * 1.5;

if (flag) {

path = `M${x + width_ / 2 + w_ / 2} ${y + width_ / 2}

L${move.x + padding} ${y + width_ / 2}

L${move.x + width_ / 2 - w_ / 2} ${move.y + width_ / 2}`;

} else {

path = `M${x + width_ / 2 + w_ / 2} ${y + width_ / 2}

L${move.x + padding} ${y + width_ / 2}

L${move.x + width_ / 2} ${move.y + width_ / 2}`;

}

}

if (stas.type === 3) { // 子女

if (flag) {

path = `M${x + width_ / 2} ${y + width_}

L${x + width_ / 2} ${y - width_ / 2}

L${move.x + width_ / 2} ${move.y + width_ * 1.5}

L${move.x + width_ / 2} ${move.y + width_}`;

} else {

path = `M${x + width_ / 2} ${y + width_}

L${x + width_ / 2} ${y + width_ * 1.5}

L${move.x + width_ / 2} ${move.y - width_ / 2}

L${move.x + width_ / 2} ${move.y + width_}`;

}

}

return path;

},

drawNode (svg, node) {

let node_ = svg.selectAll(`.node-${node.id}`)

.data([node])

.enter()

.append('g')

.attr('class', `node-${node.id}`)

.attr('transform', function(d) {

return 'translate(' + (d.x) + ',' + (d.y) + ')';

})

.on('dblclick', (d) => {

let links = this.links.filter(l => l.srcId === d.id);

if (links.length === 0) {

this.$message('没有关联数据');

return;

}

let nodes2 = this.convert(d, links);

this.drawLinks(svg, links);

this.drawNodes(svg, nodes2);

})

.on('mouseover', function(d) {

d3.select(this).select('rect')

.attr('stroke', '#FFCC33')

.attr('stroke-width', 3); // 设置边框

})

.on('mouseout', function(d) {

d3.select(this).select('rect')

.attr('stroke-width', 0); // 取消边框

})

.call(this.drag);

node_.append('rect')

.attr('width', 60)

.attr('height', 60)

.attr('x', 0)

.attr('y', 0)

.attr('style', (d) => {

return (d.type === 1 || d.type === 2) ? 'fill:#FFAD5B;' : 'fill:#35AD5B;';

});

node_.append('text')

.attr('dx', function(d) {

return 30;

})

.attr('dy', 30)

.style('text-anchor', function(d) {

return 'middle';

})

.style('fill', '#fff')

.text(function(d) {

return d.name;

});

},

drawNodes (svg, nodes) {

nodes.forEach(n => {

this.drawNode(svg, n);

});

},

// 添加链接

drawLinks (svg, linksData) {

let that = this;

linksData.forEach(l => {

let classFlag = l.srcId > l.toId ? `${l.srcId}-${l.toId}` : `${l.toId}-${l.srcId}`;

svg.selectAll(`.link-${classFlag}`)

.data([l])

.enter()

.append('path')

.attr('class', d => {

return `link-${d.srcId} link-${d.toId} link-${classFlag}`;

})

.attr('d', function(d) {

let node = that.nodes.filter(n => n.id === d.toId)[0];

return that.getPath(node, d);

})

.attr('style', function() {

return 'stroke:#F7881F';

});

});

}

},

mounted() {

}

};

[class^=link] {

fill: none;

stroke: #ccc;

stroke-width: 1.5px;

}

.demo {

width: 100%;

height: 100%;

}

.left {

float: left;

width: 30%;

}

.testd3 {

float: left;

width: 70%;

}

vue族谱架构_vue.js中使用d3.js画家谱关系图相关推荐

  1. D3.js的v5版本入门教程(第一章)—— 如何在项目中使用D3.js

    D3.js的v5版本入门教程(第一章) 1.需要的一些工具 这个其实随便!最简单的就是建一个.txt文件就可以敲起代码来!作者本人用的是myeclipse(主要需要安装tomcat),因为写的是前端, ...

  2. 使用antV-G6在angualr中画树形关系图

     使用antV-G6在angualr中画树形关系图(流量追踪图) 公司有个需求就是既要呈现出每个节点之间的关系(图里面需要带箭头,为了表现出流向关系),又要排版呈现出树状结构,也就是说是具备层次关系的 ...

  3. 数学建模中如何用 matlab画漂亮的图(一)

    数学建模中如何用 matlab画漂亮的图(二维图形) 1 plot绘图命令*** 1.1 plot(x) 当x为实向量时,plot(x)绘制出的曲线,横坐标为该向量的下表,纵坐标为每一个下表位置所对应 ...

  4. vue族谱架构_从零开始做Vue前端架构(1)

    前言 想想也已经做过不少架构的项目了,有基于vue,基于react,基于thinkPHP,基于laravel的. 做多了,也就对现有的架构有各种想法,有好的,有坏的,总之,用起来还是不爽.vue-cl ...

  5. 在vue文件引入echarts_vue文件中使用echarts.js的两种方式

    最近工作中需要用到echarts,由于项目是用的vue-cli开发的.在网上搜到vue中合成了vue-echarts,但是不想使用vue中规定好的数据格式,于是就自己做了一个vue项目引用原生echa ...

  6. vue族谱架构_【Vue】谈Vue的依赖追踪系统 ——搞懂methods watch和compute的区别和联系...

    之前一直在博客园写作,最近几天才开的知乎专栏,才疏学浅, 谬误之处请不吝于评论区指教,谢谢大家.从作用机制和性质上看待methods,watch和computed的关系 watch和computed的 ...

  7. js:js中加载js文件

    这个问题,之前没怎么想过,因为现在大部分时间我们都在搞 react.vue + webpack 这种有模块化的,所以基本上用 es module 就可以. 一般最终形态都是 html 引入 scrip ...

  8. js中使用template.js插件

    $.ajax({url:'http://10.162.12.85:8080/api/info?code=bingbing2&type=1',type:'get',dataType: 'json ...

  9. JS中包含其它JS文件

    2008-01-06 12:15 把下面的代码保存为同一目录下a.htm, a.js, b.js三个文件,然后打开a.htm看效果. a.htm的内容 ------------------------ ...

最新文章

  1. 剑指offer(Java实现) 从上往下打印二叉树
  2. struts实战--登录功能实现
  3. (WPF, MVVM) Event 处理
  4. 在jsp中应如何避免,request.getContextPath();等get报错问题
  5. 如何给视频中插入视频,字幕,以及去掉前后广告
  6. java多功能钟_Java 11将包含更多功能
  7. 689 Maximum Sum of 3 Non-Overlapping Subarrays
  8. IO编程——转自廖雪峰博客
  9. Android ViewDragHelper的简单分析及应用(二)
  10. 偶遇 649453.sys / Adware.Cdn / Hacktool.Rootkit
  11. 帝国网站mysql 数据库开发_帝国cms操作数据库函数范例(二次开发)
  12. PHP二次元风格发卡系统源码荔枝发卡网
  13. 2019 ICPC 上海网络赛 K. Peekaboo
  14. The Shawshank Redemption-3
  15. 2014年10月30日 1、完成AHC合并部分,调试通过代码
  16. 芝麻信用网页api php,谈谈php对接芝麻信用踩的坑
  17. ios图片放大之后如何不模糊_如何放大图片而不模糊
  18. html倒计时10s,vue做30s倒计时,在最后10s倒数的时候有个放大的效果
  19. 计算机动画技术的应用领域,3D动画技术的应用领域
  20. 什么是桑基图,桑基图又有什么作用?

热门文章

  1. elasticSearch常见问题答疑
  2. (精华)2020年8月22日 ABP vNext Web应用ABP
  3. HDU6194 后缀数组的应用
  4. 吴恩达深度学习相关资源下载地址(蓝奏云)
  5. Rancher 2.2 GA:企业进入应用跨多K8S集群、混合云部署新时代
  6. RabbitMQ简介及简单使用
  7. iled to read key AndroidDebugKey from store “D:\路经\.android\debug.keystore“: Invalid keystore format
  8. 下载安装Java运行环境
  9. 绘图软件origin使用总结
  10. Oracle 11g 的下载与安装