校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃

主頁 > 知識庫 > canvas繪制樹形結構可視圖形的實現

canvas繪制樹形結構可視圖形的實現

熱門標簽:如何查看地圖標注 惡搞電話機器人 欣鼎電銷機器人 效果 電話機器人技術 ok電銷機器人 地圖標注軟件打印出來 黃石ai電銷機器人呼叫中心 高德地圖標注商戶怎么標 智能電銷機器人被禁用了么

如下圖,最近項目中需要這么個樹形結構可視化數據圖形,找了好多可視化插件,沒有找到可用的,所以就自己畫了一個,代碼如下。

  • 樹形分支是后端接口返回數據渲染,可展示多條;
  • 代碼可拓展,可封裝;
  • 點擊節點可查看備注;

<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-12 15:50:02', content: '用于組織信息和操作,通常也作為詳細信息的入口。' },
                    { value: 20, date: '2020-03-12 15:50:02', content: '用于組織信息和操作,通常也作為詳細信息的入口。' },
                ] 
            },
            { 
                name: '項目2', 
                status: 0, //狀態:0已完成 1進行中
                node: [
                    { value: 10, date: '2020-03-12 15:50:02', content: '用于組織信息和操作,通常也作為詳細信息的入口。' },
                    { value: 50, date: '2020-03-12 15:50:02', content: '用于組織信息和操作,通常也作為詳細信息的入口。' },
                    { value: 100, date: '2020-03-12 15:50:02', content: '用于組織信息和操作,通常也作為詳細信息的入口。' },
                ] 
            },
            { 
                name: '項目3', 
                status: 1, //狀態:0已完成 1進行中
                node: [
                    { value: 20, date: '2020-03-12 15:50:02', content: '用于組織信息和操作,通常也作為詳細信息的入口。' },
                    { value: 30, date: '2020-03-12 15:50:02', content: '用于組織信息和操作,通常也作為詳細信息的入口。' },
                    { value: 40, date: '2020-03-12 15:50:02', content: '用于組織信息和操作,通常也作為詳細信息的入口。' },
                ] 
            },
            { 
                name: '項目4', 
                status: 1, //狀態:0已完成 1進行中
                node: [
                    { value: 20, date: '2020-03-12 15:50:02', content: '用于組織信息和操作,通常也作為詳細信息的入口。' },
                    { value: 30, date: '2020-03-12 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-40);
            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;//當前繪制內容的索引
    //判斷內容是夠可以一行繪制完畢
    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-1){
                    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-1);

    labels.forEach((item,index)=>{
        //繪制圓角背景
        //this.radiusButton(ctx,0,options.chartZone[3]-index*gap-13,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-50,160,50,8,'#F4C63D');
    ctx.fillStyle="#fff";
    ctx.font="24px ''";
    ctx.textAlign="center";
    ctx.fillText('開始',options.middleLine,options.canvasHeight-15);

    //繪制狀態
    ctx.beginPath();
    ctx.fillStyle="#333";
    ctx.font="24px ''";
    ctx.textAlign = "left";
    ctx.fillText("已完成",0,options.canvasHeight-100);
    ctx.fillText("進行中",0,options.canvasHeight-50);
    //繪制紅色按鈕
    ctx.beginPath();
    ctx.fillStyle="#d35453";
    ctx.arc(options.chartZone[0]+30,options.canvasHeight-100-7,8,0,2 * Math.PI,true);
    ctx.fill();
    ctx.beginPath();
    ctx.strokeStyle="#d35453";
    ctx.arc(options.chartZone[0]+30,options.canvasHeight-100-7,14,0,2 * Math.PI,true);
    ctx.stroke();
    //繪制藍色按鈕
    ctx.beginPath();
    ctx.fillStyle="#24b99a";
    ctx.arc(options.chartZone[0]+30,options.canvasHeight-50-8,8,0,2 * Math.PI,true);
    ctx.fill();
    ctx.beginPath();
    ctx.strokeStyle="#24b99a";
    ctx.arc(options.chartZone[0]+30,options.canvasHeight-50-8,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-1
        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-1].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-5,80,options.pillarWidth,options.chartZone[3]-80);

        //貝塞爾曲線
        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-50-options.mainTrunkHeight); //貝塞爾曲線中間豎線
        ctx.quadraticCurveTo(x0,options.canvasHeight-50-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]-20);

        //繪制節點
        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樹形結構內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章,希望大家以后多多支持腳本之家!

標簽:赤峰 綏化 萍鄉 阿壩 金昌 中山 盤錦 聊城

巨人網絡通訊聲明:本文標題《canvas繪制樹形結構可視圖形的實現》,本文關鍵詞  canvas,繪制,樹形,結構,可,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《canvas繪制樹形結構可視圖形的實現》相關的同類信息!
  • 本頁收集關于canvas繪制樹形結構可視圖形的實現的相關信息資訊供網民參考!
  • 推薦文章
    校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃
    国产成人精品午夜视频免费| 久久成人免费网| 国产成人精品aa毛片| 国产视频在线观看一区二区三区 | 亚洲成人自拍偷拍| 日韩午夜精品视频| 在线免费精品视频| 18欧美亚洲精品| 欧美日韩一区在线观看| 男人的j进女人的j一区| 久久久天堂av| 欧美日韩国产一级| 丁香亚洲综合激情啪啪综合| 亚洲精品中文在线影院| 欧美一二区视频| 成人av一区二区三区| 久色婷婷小香蕉久久| 中文字幕不卡三区| 91精品国产综合久久久久久久久久 | 日韩一区二区三区观看| 国产丝袜在线精品| av一区二区三区四区| 亚洲综合一二区| 国产人成亚洲第一网站在线播放 | 麻豆精品一区二区综合av| 亚洲国产成人在线| 日韩欧美一区在线| 欧美色欧美亚洲另类二区| 国产成人免费视频网站| 日精品一区二区| 亚洲美女精品一区| 欧美一区二区三区在线视频| 亚洲免费av高清| 欧美成人r级一区二区三区| 欧美写真视频网站| 91免费版pro下载短视频| 成人免费观看男女羞羞视频| 日韩专区在线视频| 免费成人性网站| 麻豆91免费观看| 久久99国产精品成人| 亚洲色图在线视频| 久久精品综合网| 国产精品超碰97尤物18| 亚洲伦在线观看| 一区二区成人在线观看| 丝袜美腿亚洲色图| 日韩在线一区二区三区| 久久精品av麻豆的观看方式| 性做久久久久久免费观看| 亚洲国产精品一区二区www| 夜夜嗨av一区二区三区中文字幕 | 国产乱国产乱300精品| 国产成人免费视频一区| 一本色道久久综合亚洲91| 色婷婷激情综合| 欧美日韩综合不卡| 精品成人佐山爱一区二区| 中文字幕一区二区三区蜜月| 亚洲欧洲日韩综合一区二区| 午夜一区二区三区在线观看| 看国产成人h片视频| 99精品热视频| 欧美精品一区二区三区视频| 中文字幕高清一区| 石原莉奈在线亚洲三区| 国产91精品露脸国语对白| 国产传媒久久文化传媒| 日韩精品国产欧美| 色哟哟在线观看一区二区三区| 日韩欧美国产系列| 亚洲午夜激情网页| 波多野结衣中文一区| 久久久久久久久久久黄色| 久久成人18免费观看| 91 com成人网| 喷水一区二区三区| 7777女厕盗摄久久久| 青青草成人在线观看| 欧美猛男gaygay网站| 亚洲国产日日夜夜| 欧美日韩aaaaa| 亚洲成人tv网| 精品美女一区二区| 国产一区二区按摩在线观看| 精品国产91亚洲一区二区三区婷婷 | 欧美日韩一区二区三区四区| 婷婷激情综合网| 日韩欧美不卡在线观看视频| 免费在线观看一区| 国产亚洲va综合人人澡精品| av电影在线观看不卡| 亚洲日本乱码在线观看| 7777精品伊人久久久大香线蕉超级流畅| 天堂成人免费av电影一区| 精品乱码亚洲一区二区不卡| 狠狠v欧美v日韩v亚洲ⅴ| 国产精品麻豆欧美日韩ww| 色综合色综合色综合| 欧美a级一区二区| 亚洲国产精品v| 欧美美女网站色| 国产精品一区二区三区四区 | 91在线观看免费视频| 欧美变态口味重另类| gogogo免费视频观看亚洲一| 亚洲精品成人少妇| 337p日本欧洲亚洲大胆色噜噜| 国产91精品一区二区| 水蜜桃久久夜色精品一区的特点| 欧美性视频一区二区三区| 亚洲一区二区三区四区五区黄| 久久综合久久综合久久综合| 一本色道综合亚洲| 成人短视频下载| 国产乱色国产精品免费视频| 亚洲6080在线| 亚洲精品高清在线| 国产精品久久久久久亚洲毛片| 欧美成人vps| 日韩天堂在线观看| 欧美在线免费视屏| 色哟哟亚洲精品| 91在线观看下载| 一本色道久久综合精品竹菊 | 中文字幕亚洲区| 亚洲国产成人一区二区三区| 久久新电视剧免费观看| 日韩精品中文字幕一区二区三区| 91精品国产入口| 欧美性色aⅴ视频一区日韩精品| 91尤物视频在线观看| av高清不卡在线| 91视频在线看| 欧美私人免费视频| 欧美日韩一区二区在线视频| 在线观看亚洲精品| 欧美一区欧美二区| 337p日本欧洲亚洲大胆色噜噜| 国产视频不卡一区| 色偷偷久久人人79超碰人人澡| 国产成人av电影免费在线观看| 国产一区二区视频在线播放| 韩国精品主播一区二区在线观看| 国产在线播精品第三| eeuss鲁片一区二区三区在线观看| 91麻豆精品一区二区三区| 欧美性色欧美a在线播放| 日韩精品在线看片z| 国产精品美女一区二区三区| 亚洲国产精品久久久男人的天堂 | 日韩和欧美一区二区三区| 日本美女一区二区三区视频| 国产成人在线视频网站| 色www精品视频在线观看| 欧美一区午夜视频在线观看| 欧美日韩一区二区在线观看| 欧美久久久久久蜜桃| 久久麻豆一区二区| 一区二区三区四区不卡视频| 一区av在线播放| 国产馆精品极品| 欧美日韩高清影院| 国产午夜精品久久久久久免费视 | 亚洲免费成人av| 激情六月婷婷综合| 欧美精品777| 亚洲高清免费视频| 91小视频免费看| 国产精品视频yy9299一区| 免费在线观看日韩欧美| 欧美日韩免费一区二区三区视频| 2024国产精品视频| 激情久久五月天| 日韩一级片在线观看| 日韩精品91亚洲二区在线观看| 日产精品久久久久久久性色| 激情文学综合丁香| 日韩欧美亚洲一区二区| 视频在线观看一区| 欧美日韩久久不卡| 一区二区在线观看视频 | 国产丶欧美丶日本不卡视频| 日韩免费观看高清完整版 | 亚洲色大成网站www久久九九| 高清成人免费视频| 亚洲精品精品亚洲| 成人国产视频在线观看| 国产精品不卡在线观看| www.欧美色图| 亚洲综合在线视频| 欧美三级视频在线播放| 亚洲成人你懂的| 精品少妇一区二区三区在线播放| 青草av.久久免费一区| 欧美本精品男人aⅴ天堂| 粉嫩av亚洲一区二区图片| 亚洲素人一区二区| 91精品国产色综合久久ai换脸 | 2021久久国产精品不只是精品|