效果图:
1.使用canvas画边框,文本内容居中
getCtx(){
let cs = document.getElementById('canvas');
let cxt = cs.getContext('2d');
return cxt;
},
drawTextItem({
startX,
startY,
width,
height,
title,
}) {
let ctx = this.getCtx();
// 重新开辟一个线路
ctx.beginPath();
// 画一个外边框
ctx.rect(startX, startY, width, height);
ctx.strokeStyle = "red";
ctx.fillStyle = "pink";
ctx.stroke();
ctx.fill();
// 写字
ctx.fillStyle = "red";
ctx.textBaseline = "middle";
let { width:textWidth } = ctx.measureText(title);
// fillText(text, x, y, maxWidth),maxWidth最大宽度
ctx.fillText(title, startX+(width-textWidth)/2, startY+height/2, textWidth);
},
2.思路:
1.判断是否有子节点,有则继续执行画边框操作
2.如何画第二层子节点?难点在于获取子节点的起始节点
如果不考虑最下层节点的话,画到最后结果会发现有节点存在覆盖情况,所以需要轮询查询第二层节点下最多有多少个节点
这里以左边为例,查看得出有4个,以一个边框固定宽100,高50,高度间距12为例,计算设置杭州市居中的位置,杭州市下面的节点执行相同操作
//计算节点下占据空间多少个节点,没有子节点则标记为1
getAllCount(node){
let count = 0;
node.children.reduce((pre,cue)=>{
count = pre+(cue.children && cue.children.length>0?this.getAllCount(cue):1)
return count;
},0)
return count;
},
如何计算节点起点:
比如左右子节点共用8个节点空间,左边4个,右边4个的话,
先计算最左侧起点: let start = startX - (nodeWidth*count+(nodeMargin*(count-1)))/2;//起点
然后占5个节点空间: childrenStartX = start+(nodeWidth*4+(nodeMargin*(4-1)))/2;考虑间距
if(node.children && node.children.length>0){
let count = 0;
count = this.getAllCount(node);//计算最底层节点个数
console.log('count',count);
let start = startX - (nodeWidth*count+(nodeMargin*(count-1)))/2;//起点
node.children.forEach((item,index) => {
let childrenStartX = 0;
let childrenStartY = startY+nodeMargin+nodeHeight;
let nodeLenth = 0;
if(item.children && item.children.length>0){
nodeLenth= this.getAllCount(item)
}else{
nodeLenth = 1;
}
childrenStartX = start+(nodeWidth*nodeLenth+(nodeMargin*(nodeLenth-1)))/2;
start = start+(nodeWidth*nodeLenth+(nodeMargin*(nodeLenth-1)))+nodeMargin;
this.drawLine(startX,startY,childrenStartX,childrenStartY);//划线
this.render(childrenStartX,childrenStartY,item);//划子级
});
}
完整代码:
<div class="canvas">
<canvas height="1000" width="1000" id="canvas"></canvas>
</div>
let node = {
title: "浙江省",
children: [
{
title: "杭州市",
children: [
{
title: "拱墅区",
children: [
{
title: "北部软件园",
},
],
},
{
title: "余杭区",
children: [
{
title: "梦想小镇",
},
{
title: "欧美金融城",
},
{
title: "华容科技城",
},
],
},
],
},
{
title: "宁波市",
children: [
{
title: "海曙区",
children: [
{
title: "疆良小镇",
},
{
title: "欧美金融城",
},
],
},
{
title: "江北区",
children: [
{
title: "华兴小镇",
},
{
title: "感电东方城",
},
],
},
],
},
],
};
this.render(500,10,node);
getCtx(){
let cs = document.getElementById('canvas');
let cxt = cs.getContext('2d');
return cxt;
},
drawTextItem({
startX,
startY,
width,
height,
title,
}) {
let ctx = this.getCtx();
// 重新开辟一个线路
ctx.beginPath();
// 画一个外边框
ctx.rect(startX, startY, width, height);
ctx.strokeStyle = "red";
ctx.fillStyle = "pink";
ctx.stroke();
ctx.fill();
// 写字
ctx.fillStyle = "red";
ctx.textBaseline = "middle";
let { width:textWidth } = ctx.measureText(title);
// fillText(text, x, y, maxWidth),maxWidth最大宽度
ctx.fillText(title, startX+(width-textWidth)/2, startY+height/2, textWidth);
},
drawLine(nodeX,nodeY,childX,childY){
let ctx = this.getCtx();
let nodeWidth = 100;
let nodeHeight = 50;
let nodeMargin = 12;
// 重新开辟一个线路
ctx.beginPath();
ctx.moveTo(nodeX+(nodeWidth/2),nodeY+nodeHeight);
ctx.lineTo(nodeX+(nodeWidth/2),nodeY+nodeHeight+(nodeMargin/2));
ctx.lineTo(childX+(nodeWidth/2),childY-(nodeMargin/2));
ctx.lineTo(childX+(nodeWidth/2),childY);
ctx.stroke() // 将起点和终点连接起来
},
render(startX,startY,node){
let nodeWidth = 100;
let nodeHeight = 50;
let nodeMargin = 12;
this.drawTextItem({startX,startY,width:nodeWidth,height:nodeHeight,title:node.title});
if(node.children && node.children.length>0){
let count = 0;
count = this.getAllCount(node);//计算最底层节点个数
console.log('count',count);
let start = startX - (nodeWidth*count+(nodeMargin*(count-1)))/2;//起点
node.children.forEach((item,index) => {
let childrenStartX = 0;
let childrenStartY = startY+nodeMargin+nodeHeight;
let nodeLenth = 0;
if(item.children && item.children.length>0){
nodeLenth= this.getAllCount(item)
}else{
nodeLenth = 1;
}
childrenStartX = start+(nodeWidth*nodeLenth+(nodeMargin*(nodeLenth-1)))/2;
start = start+(nodeWidth*nodeLenth+(nodeMargin*(nodeLenth-1)))+nodeMargin;
this.drawLine(startX,startY,childrenStartX,childrenStartY);//划线
this.render(childrenStartX,childrenStartY,item);//划子级
});
}
},
getAllCount(node){
let count = 0;
node.children.reduce((pre,cue)=>{
count = pre+(cue.children && cue.children.length>0?this.getAllCount(cue):1)
return count;
},0)
return count;
},