canvas绘图树形构造可视性图型的完成

日期:2021-01-19 类型:科技新闻 

关键词:在线网页制作,建网页,个人简介网页制作,简单网页,建立网页

以下图,近期新项目中必须这么个树形构造可视性化数据信息图型,找了许多可视性化软件,沒有寻找能用的,因此就自身画了1个,编码以下。

  • 树形支系是后端开发插口回到数据信息3D渲染,可展现好几条;
  • 编码可扩展,可封裝;
  • 点一下连接点可查询备注;

<canvas id="canvas" width="750" height="800"></canvas>
const canvas_options={
    canvasWidth: 750,
    canvasHeight: 800,
    chartZone: [70,70,750,570], //座标绘图地区
    yAxisLabel: ['0%','10%','20%','30%','40%','50%','60%','70%','80%','90%','100%'],//y轴座标text
    yAxisLabelWidth: 70,//y轴最大宽度
    yAxisLabelMax: 100,//y轴最大值
    middleLine: 410, //正中间线
    pillarWidth: 10,//柱子宽度
    distanceBetween: 50,//柱状图绘图地区间距两侧空隙
    pillar: [120,70,700,750],//柱状图绘图地区
    mainTrunkHeight: 90,//底部刚开始主杆高宽比
    dialogWidth: 300,//弹窗宽度
    dialogLineHeight: 30,//弹窗高宽比
    dialogDrawLineMax: 4,
}
const nodeClick = [];
var chooseNode = null;
const datalist={
    showDataInfo: {
        city:[
            { 
                name: '新项目1', 
                status: 1, //情况:0已进行 1开展中
                node: [
                    { value: 10, date: '2020-03⑴2 15:50:02', content: '用于机构信息内容和实际操作,一般也做为详尽信息内容的通道。' },
                    { value: 20, date: '2020-03⑴2 15:50:02', content: '用于机构信息内容和实际操作,一般也做为详尽信息内容的通道。' },
                ] 
            },
            { 
                name: '新项目2', 
                status: 0, //情况:0已进行 1开展中
                node: [
                    { value: 10, date: '2020-03⑴2 15:50:02', content: '用于机构信息内容和实际操作,一般也做为详尽信息内容的通道。' },
                    { value: 50, date: '2020-03⑴2 15:50:02', content: '用于机构信息内容和实际操作,一般也做为详尽信息内容的通道。' },
                    { value: 100, date: '2020-03⑴2 15:50:02', content: '用于机构信息内容和实际操作,一般也做为详尽信息内容的通道。' },
                ] 
            },
            { 
                name: '新项目3', 
                status: 1, //情况:0已进行 1开展中
                node: [
                    { value: 20, date: '2020-03⑴2 15:50:02', content: '用于机构信息内容和实际操作,一般也做为详尽信息内容的通道。' },
                    { value: 30, date: '2020-03⑴2 15:50:02', content: '用于机构信息内容和实际操作,一般也做为详尽信息内容的通道。' },
                    { value: 40, date: '2020-03⑴2 15:50:02', content: '用于机构信息内容和实际操作,一般也做为详尽信息内容的通道。' },
                ] 
            },
            { 
                name: '新项目4', 
                status: 1, //情况:0已进行 1开展中
                node: [
                    { value: 20, date: '2020-03⑴2 15:50:02', content: '用于机构信息内容和实际操作,一般也做为详尽信息内容的通道。' },
                    { value: 30, date: '2020-03⑴2 15:50:02', content: '用于机构信息内容和实际操作,一般也做为详尽信息内容的通道。' },
                ] 
            },
        ]
    }
}

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext('2d');
ctx.save();

drawYLabel(canvas_options,ctx); //绘图y轴座标
drawStartButton(ctx,canvas_options);
drawData(ctx,datalist.showDataInfo,canvas_options);

canvas.addEventListener("click",event=>{
    //消除以前的弹窗
    if(chooseNode!=null){
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.save();
         drawYLabel(canvas_options,ctx); //绘图y轴座标
         drawStartButton(ctx,canvas_options);
         drawData(ctx,datalist.showDataInfo,canvas_options);
        chooseNode = null
    }
    //分辨点一下连接点
    let rect = canvas.getBoundingClientRect();
    let zoom = rect.width/canvas_options.canvasWidth;
    let x = (event.clientX/zoom - rect.left/zoom).toFixed(2);
    let y = (event.clientY/zoom - rect.top/zoom).toFixed(2);

    for(var t=0;t<nodeClick.length;t++){
        ctx.beginPath();
        ctx.arc(nodeClick[t].x,nodeClick[t].y,15,0,Math.PI*2,true);
        if(ctx.isPointInPath(x, y)){
            textPrewrap(ctx,`备注叙述:${nodeClick[t].date}`,nodeClick[t].x+20,nodeClick[t].y+20,canvas_options.dialogWidth⑷0);
            ctx.restore();
            chooseNode=t
            break;
        }else{
            chooseNode=null
        }
    }
});

//content:必须绘图的文字內容; drawX:绘图文字的x座标; drawY:绘图文字的y座标;
//lineMaxWidth:每写作本的最大宽度
function textPrewrap(ctx,content,drawX, drawY, lineMaxWidth){
    var drawTxt=''; //当今绘图的內容
    var drawLine  = 1;//第几行刚开始绘图
    var drawIndex=0;//当今绘图內容的数据库索引
    //分辨內容是够能够1行绘图结束
    if(ctx.measureText(content).width<=lineMaxWidth){
        drawDialog(ctx,canvas_options.dialogWidth,canvas_options.dialogLineHeight,drawX,drawY);
        ctx.fillText(content.substring(drawIndex,i),drawX.drawY);
    }else{
        for(var i=0;i<content.length;i++){
            drawTxt += content[i];
            if(ctx.measureText(drawTxt).width>=lineMaxWidth){
                drawDialog(ctx,canvas_options.dialogWidth,canvas_options.dialogLineHeight,drawX,drawY);
                ctx.fillText(content.substring(drawIndex,i+1),drawX,drawY);
                drawIndex = i+1;
                drawLine+=1;
                //drawY+=lineHeight;
                drawTxt='';
            }else{
                //內容绘图结束,可是剩余的內容宽度不到lineMaxWidth
                if(i===content.length⑴){
                    drawDialog(ctx,canvas_options.dialogWidth,canvas_options.dialogLineHeight,drawX,drawY);
                    ctx.fillText(content.substring(drawIndex,i+1),drawX,drawY)
                }
            }
        }
    }
}

function drawDialog(ctx,width,height,x,y){
    ctx.beginPath();
    ctx.fillStyle="rgba(0,0,0,0.8)";
    ctx.fillRect(x,y,width,height);
    ctx.font="22px ''";
    ctx.fillStyle="#fff";
    ctx.textAlign = 'left';
    ctx.textBaseline="top";
}

//绘图y轴座标
function drawYLabel(options,ctx){
    let labels = options.yAxisLabel;
    let yLength = (options.chartZone[3]-options.chartZone[1])*0.98;
    let gap = yLength/(labels.length⑴);

    labels.forEach((item,index)=>{
        //绘图圆角情况
        //this.radiusButton(ctx,0,options.chartZone[3]-index*gap⑴3,50,24,8,"#313947");

        //绘图座标文本
        ctx.beginPath();
        ctx.fillStyle="#878787";
        ctx.font="18px ''";
        ctx.textAlign="center";
        ctx.fillText(item,25,options.chartZone[3]-index*gap+5);
        //绘图輔助线
        ctx.beginPath();
        ctx.strokeStyle="#eaeaea";
        ctx.strokeWidth=2;
        ctx.moveTo(options.chartZone[0],options.chartZone[3]-index*gap);
        ctx.lineTo(options.chartZone[2],options.chartZone[3]-index*gap);
        ctx.stroke();
    })

}
//绘图刚开始按钮
function drawStartButton(ctx,options){
    //绘图按钮图型
    this.radiusButton(ctx,options.middleLine-(160/2),options.canvasHeight⑸0,160,50,8,'#F4C6三d');
    ctx.fillStyle="#fff";
    ctx.font="24px ''";
    ctx.textAlign="center";
    ctx.fillText('刚开始',options.middleLine,options.canvasHeight⑴5);

    //绘图情况
    ctx.beginPath();
    ctx.fillStyle="#333";
    ctx.font="24px ''";
    ctx.textAlign = "left";
    ctx.fillText("已进行",0,options.canvasHeight⑴00);
    ctx.fillText("开展中",0,options.canvasHeight⑸0);
    //绘图鲜红色按钮
    ctx.beginPath();
    ctx.fillStyle="#d35453";
    ctx.arc(options.chartZone[0]+30,options.canvasHeight⑴00⑺,8,0,2 * Math.PI,true);
    ctx.fill();
    ctx.beginPath();
    ctx.strokeStyle="#d35453";
    ctx.arc(options.chartZone[0]+30,options.canvasHeight⑴00⑺,14,0,2 * Math.PI,true);
    ctx.stroke();
    //绘图蓝色按钮
    ctx.beginPath();
    ctx.fillStyle="#24b99a";
    ctx.arc(options.chartZone[0]+30,options.canvasHeight⑸0⑻,8,0,2 * Math.PI,true);
    ctx.fill();
    ctx.beginPath();
    ctx.strokeStyle="#24b99a";
    ctx.arc(options.chartZone[0]+30,options.canvasHeight⑸0⑻,14,0,2 * Math.PI,true);
    ctx.stroke();

}
//封裝绘图圆角矩形框涵数
function radiusButton(ctx,x,y,width,height,radius,color_back){
    ctx.beginPath();
    ctx.fillStyle= color_back
    ctx.moveTo(x,y+radius);
    ctx.lineTo(x,y+height-radius);
    ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
    ctx.lineTo(x+width-radius,y+height);
    ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
    ctx.lineTo(x+width,y+radius);
    ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
    ctx.lineTo(x+radius,y);
    ctx.quadraticCurveTo(x,y,x,y+radius);
    ctx.fill()
}
//绘图数据信息
function drawData(ctx,data,options){

    //const paths=[];
    let number = data.city.length;
    //绘图矩形框
    data.city.forEach((item,index)=>{
        let indexVal = number==1?1:index;
        let numberVal = number==1?2:number⑴
        let x0 = options.chartZone[0]+options.distanceBetween+(options.chartZone[2]-options.chartZone[0]-options.distanceBetween*2)/numberVal*indexVal;
        let value = item.node[item.node.length⑴].value;
        let height = (value/options.yAxisLabelMax*(options.chartZone[3]-options.chartZone[0])*0.98).toFixed(2);
        let y0=options.chartZone[3] - height;

        //柱状图底部
        ctx.beginPath();
        ctx.fillStyle= '#eee';
        ctx.fillRect(x0⑸,80,options.pillarWidth,options.chartZone[3]⑻0);

        //贝塞尔曲线图
        ctx.beginPath();

        ctx.strokeStyle = item.status==0?"#d35453":'#24b99a';
        ctx.lineWidth=options.pillarWidth;
        ctx.moveTo(options.middleLine,options.pillar[3]); //贝塞尔曲线图起止点
        ctx.lineTo(options.middleLine,options.canvasHeight⑸0-options.mainTrunkHeight); //贝塞尔曲线图正中间竖线
        ctx.quadraticCurveTo(x0,options.canvasHeight⑸0-options.mainTrunkHeight,x0,options.chartZone[3]);
        //绘图柱状图进度
        ctx.lineTo(x0,y0);
        ctx.stroke();

        //绘图文本
        ctx.font="28px ''";
        ctx.textAlign='center';
        ctx.fillStyle="#333";
        ctx.fillText(item.name,x0,options.chartZone[1]⑵0);

        //绘图连接点
        item.node.forEach((node_item,node_index)=>{
            let y1= options.chartZone[3] - (node_item.value/options.yAxisLabelMax*(options.chartZone[3]-options.chartZone[0])*0.98).toFixed(2);
            ctx.beginPath();
            ctx.arc(x0,y1,15,0,Math.PI*2,true);
            ctx.fillStyle="rgba(108,212,148,1)";
            ctx.fill();
            ctx.beginPath();
            ctx.arc(x0,y1,9,0,Math.PI*2,true);
            ctx.fillStyle="rgba(255,255,255,0.8)";
            ctx.fill();
            const pointInfo={
                x:x0,
                y:y1,
                date: node_item.data,
                content: node_item.content,
                value: node_item.value
            };
            nodeClick.push(pointInfo);
        })
    })
}

到此这篇有关canvas绘图树形构造可视性图型的完成的文章内容就详细介绍到这了,更多有关canvas树形构造內容请检索脚本制作之家之前的文章内容或再次访问下面的有关文章内容,期待大伙儿之后多多适用脚本制作之家!