nuxt、vue樹形圖d3.js

在這里插入圖片描述

直接上代碼

//安裝
npm i d3 --save
<template><div class="d3"><div :id="id" class="d3-content"></div></div>
</template>
<script>
import * as d3 from "d3";export default {props: {data: Object,nodeWidth: {type: Number,default: 340,},nodeHeight: {type: Number,default: 40,},active: {type: String,default: "",},},data() {return {id: "TreeMap" + randomString(4),deep: 0,treeData: null,show: true,demoData: {label: "中國",// link: "demo",url: "https://baike.baidu.com/item/%E4%B8%AD%E5%9B%BD/1122445?fr=aladdin",children: [{label: "浙江45468761321",// link: "isClick",disabled: true,children: [{ label: "杭州999999999" },{ label: "寧波" },{ label: "溫州" },{ label: "紹興" },],},{label: "廣西",children: [{label: "桂林56465465465464",children: [{ label: "秀峰區" },{ label: "疊彩區" },{ label: "象山區" },{ label: "七星區" },],},{ label: "南寧" },{ label: "柳州" },{ label: "防城港" },],},],},};},mounted() {this.$nextTick(() => {this.drawMap();window.handleCustom=this.handleCustom;});},methods: {drawMap() {let that = this;// 源數據let data = {};// 判斷data是否為空對象if (this.data && JSON.stringify(this.data) !== "{}") {data = this.data;} else {data = this.demoData;}if (!this.treeData) {this.treeData = data;} else {// 清空畫布d3.select("#" + this.id).selectAll("svg").remove();}let leafList = [];getTreeLeaf(data, leafList);let leafNum = leafList.length;let TreeDeep = getDepth(data);// 左右內邊距let mapPaddingLR = 10;// 上下內邊距let mapPaddingTB = 0;let mapWidth = this.nodeWidth * TreeDeep + mapPaddingLR * 2;let mapHeight = (this.nodeHeight - 4) * leafNum + mapPaddingTB * 2;// 定義畫布—— 外邊距 10pxlet svgMap = d3.select("#" + this.id).append("svg").attr("width", mapWidth).attr("height", mapHeight).style("margin", "0px");// 定義樹狀圖畫布let treeMap = svgMap.append("g").attr("transform","translate(" +mapPaddingLR +"," +(mapHeight / 2 - mapPaddingTB) +")");// 將源數據轉換為可以生成樹狀圖的數據(有節點 nodes 和連線 links )let treeData = d3.tree()// 設置每個節點的尺寸.nodeSize(// 節點包含后方的連接線 [節點高度,節點寬度][this.nodeHeight, this.nodeWidth])// 設置樹狀圖節點之間的垂直間隔.separation(function (a, b) {// 樣式一:節點間等間距// return (a.parent == b.parent ? 1: 2) / a.depth;// 樣式二:根據節點子節點的數量,動態調整節點間的間距let rate =(a.parent == b.parent? b.children? b.children.length / 2: 1: 2) / a.depth;// 間距比例不能小于0.7,避免間距太小而重疊if (rate < 0.7) {rate = 0.7;}return rate;})(// 創建層級布局,對源數據進行數據轉換d3.hierarchy(data).sum(function (node) {// 函數執行的次數,為樹節點的總數,node為每個節點return node.value;}));// 貝塞爾曲線生成器let Bézier_curve_generator = d3.linkHorizontal().x(function (d) {return d.y;}).y(function (d) {return d.x;});//繪制邊treeMap.selectAll("path")// 節點的關系 links.data(treeData.links()).enter().append("path").attr("d", function (d) {// 根據name值的長度調整連線的起點var start = {x: d.source.x,// 連線起點的x坐標// 第1個10為與紅圓圈的間距,第2個10為link內文字與邊框的間距,第3個10為標簽文字與連線起點的間距,60為自定義htmly:d.source.y +10 +(d.source.data.link ? getPXwidth(d.source.data.link) + 10 : 0) +getPXwidth(d.source.data.label) +(!d.source.data.children?82:0) +20,};var end = { x: d.target.x, y: d.target.y };return Bézier_curve_generator({ source: start, target: end });}).attr("fill", "none").attr("stroke", "#00AB6B")// 虛線// .attr("stroke-dasharray", "8").attr("stroke-width", 1);// 創建分組——節點+文字let groups = treeMap.selectAll("g")// 節點 nodes.data(treeData.descendants()).enter().append("g").attr("transform", function (d) {var cx = d.x;var cy = d.y;return "translate(" + cy + "," + cx + ")";});//繪制節點(節點前的圓圈)groups.append("circle")// 樹的展開折疊.on("click", function (event, node) {let data = node.data;if (data.children) {data.childrenTemp = data.children;data.children = null;} else {data.children = data.childrenTemp;data.childrenTemp = null;}that.drawMap();}).attr("cursor", "pointer").attr("r", 4).attr("fill", function (d) {if (d.data.childrenTemp) {return "#00AB6B";} else {return "white";}}).attr("stroke", "#00AB6B").attr("stroke-width", 1);//繪制標注(節點前的矩形)groups.append("rect").attr("x", 8).attr("y", -10).attr("width", function (d) {return d.data.link ? getPXwidth(d.data.link) + 10 : 0;}).attr("height", 22).attr("fill", "red").attr("border", "blue")// 添加圓角.attr("rx", 4);//繪制鏈接方式groups.append("text").attr("x", 12).attr("y", -5).attr("dy", 10).attr("fill", "white").attr("font-size", 12).text(function (d) {return d.data.link;});//繪制文字groups.append("text").on("click", function (event, node) {let data = node.data;// 被禁用的節點,點擊無效if (data.disabled) {return;}// 有外鏈的節點,打開新窗口后恢復到思維導圖頁面if (data.url) {window.open(data.url);that.$emit("activeChange", "map");return;}// 標準節點—— 傳出 propif (data.dicType) {that.$emit("dicTypeChange", data.dicType);}// 標準節點—— 傳出 propif (data.prop) {that.$emit("activeChange", data.prop);}}).attr("x", function (d) {return 12 + (d.data.link ? getPXwidth(d.data.link) + 10 : 0);}).attr("fill", function (d) {if (d.data.prop === that.active) {return "#409EFF";}}).attr("font-weight", function (d) {if (d.data.prop === that.active) {return "bold";}}).attr("font-size", 14).attr("cursor", function (d) {if (d.data.disabled) {return "not-allowed";} else {return "pointer";}}).attr("y", -5).attr("dy", 10).attr("slot", function (d) {return d.data.prop;});// .text(function (d) {//   return d.data.label;// });groups.append("foreignObject").attr("width", (d) => {return getPXwidth(d.data.label) + 22 + (!d.data.children?82:0);}).attr("height", 100).attr("x", function (d) {return 12 + (d.data.link ? getPXwidth(d.data.link) + 10 : 0);}).on("click", function (event, node) {}).attr("y", -10).append("xhtml:div").style("font", '14px "Helvetica Neue"').html((d) => {let _html = `<div class="custom-html"><div>${d.data.label}</div></div>`;if(!d.data.children){_html = `<div class="custom-html"><div>${d.data.label}</div><div οnclick="handleCustom(${1})"><i class="iconfont">&#xe648;</i>視頻課</div></div>`;}return _html});},handleCustom(data){debugger}},
};// 獲取樹的深度
function getDepth(json) {var arr = [];arr.push(json);var depth = 0;while (arr.length > 0) {var temp = [];for (var i = 0; i < arr.length; i++) {temp.push(arr[i]);}arr = [];for (var i = 0; i < temp.length; i++) {if (temp[i].children && temp[i].children.length > 0) {for (var j = 0; j < temp[i].children.length; j++) {arr.push(temp[i].children[j]);}}}if (arr.length >= 0) {depth++;}}return depth;
}// 提取樹的子節點,最終所有樹的子節點都會存入傳入的leafList數組中
function getTreeLeaf(treeData, leafList) {// 判斷是否為數組if (Array.isArray(treeData)) {treeData.forEach((item) => {if (item.children && item.children.length > 0) {getTreeLeaf(item.children, leafList);} else {leafList.push(item);}});} else {if (treeData.children && treeData.children.length > 0) {getTreeLeaf(treeData.children, leafList);} else {leafList.push(treeData);}}
}// 獲取包含漢字的字符串的長度
function getStringSizeLength(string) {//先把中文替換成兩個字節的英文,再計算長度return string.replace(/[\u0391-\uFFE5]/g, "aa").length;
}// 生成隨機的字符串
function randomString(strLength) {strLength = strLength || 32;let strLib = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz";let n = "";for (let i = 0; i < strLength; i++) {n += strLib.charAt(Math.floor(Math.random() * strLib.length));}return n;
}// 獲取字符串的像素寬度
function getPXwidth(str, fontSize = "12px", fontFamily = "Microsoft YaHei") {var span = document.createElement("span");var result = {};result.width = span.offsetWidth;result.height = span.offsetHeight;span.style.visibility = "hidden";span.style.fontSize = fontSize;span.style.fontFamily = fontFamily;span.style.display = "inline-block";document.body.appendChild(span);if (typeof span.textContent != "undefined") {span.textContent = str;} else {span.innerText = str;}result.width = parseFloat(window.getComputedStyle(span).width) - result.width;// 字符串的顯示高度// result.height = parseFloat(window.getComputedStyle(span).height) - result.height;return result.width;
}
</script>
<style lang="scss" scoped>
.d3 {position: relative;overflow: hidden;width: calc(100%);min-height: 500px;overflow-x: scroll;.d3-content {position: absolute;width: max-content;::v-deep .custom-html {display: flex;div {i {font-size: 12px;margin-right: 4px;}&:nth-child(2) {margin-left: 10px;background: #f2faf7;border: 0.5px solid #c3e7da;border-radius: 4px;color: #00ab6b;font-size: 12px;padding: 0 4px;height: 20px;cursor: pointer;}}}}
}
</style>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/43914.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/43914.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/43914.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Github Actions 構建Vue3 + Vite項目

本篇文章以自己創建的項目為例&#xff0c;用Github Actions構建。 Github地址&#xff1a;https://github.com/ling08140814/myCarousel 訪問地址&#xff1a;https://ling08140814.github.io/myCarousel/ 具體步驟&#xff1a; 1、創建一個Vue3的項目&#xff0c;并完成代…

接口基礎知識1:認識接口

課程大綱 一、定義 接口&#xff1a;外部與系統之間、內部各子系統之間的交互點。 比如日常使用的電腦&#xff0c;有電源接口、usb接口、耳機接口、顯示器接口等&#xff0c;分別可以實現&#xff1a;與外部的充電、文件數據傳輸、聲音輸入輸出、圖像輸入輸出等功能。 接口的本…

262個地級市-市場潛力指數(do文件+原始文件)

全國262個地級市-市場潛力指數&#xff08;市場潛力計算方法代碼數據&#xff09;_市場潛力數據分析資源-CSDN文庫 市場潛力指數&#xff1a;洞察未來發展的指南針 市場潛力指數是一個綜合性的評估工具&#xff0c;它通過深入分析市場需求、競爭環境、政策支持和技術創新等多個…

面向字節流傳輸數據

當提到“傳輸數據面向字節流”&#xff0c;這是指在網絡通信中&#xff0c;數據被視作一連串的無結構字節&#xff0c;而不是按照特定的數據塊或記錄進行傳輸。這種傳輸方式是面向傳輸層協議&#xff08;如TCP&#xff09;的一個特性&#xff0c;它允許數據以連續的字節流形式在…

phpstudy框架,window平臺,如何開端口給局域網訪問?

Windows平臺上使用phpstudy框架開端口給同事訪問&#xff0c;主要涉及到幾個步驟&#xff1a;查看并確認本機IP地址、配置phpstudy及網站項目、開放防火墻端口以及確保同事能夠通過局域網訪問。以下是詳細的步驟說明&#xff1a; 1. 查看并確認本機IP地址 首先&#xff0c;需…

SQLAlchemy pool_pre_ping

pool_pre_ping 是 SQLAlchemy 中 create_engine 函數的一個參數&#xff0c;它用于配置連接池的行為。當設置為 True 時&#xff0c;pool_pre_ping 啟用了連接池在每次從池中取出&#xff08;即“簽出”或“checkout”&#xff09;連接之前&#xff0c;先測試該連接是否仍然活躍…

(2)滑動窗口算法練習:無重復字符的最長子串

無重復字符的最長子串 題目鏈接&#xff1a;3. 無重復字符的最長子串 - 力扣&#xff08;LeetCode&#xff09; 給定一個字符串 s &#xff0c;請你找出其中不含有重復字符的最長子串的長度。 輸入: s "abcabcbb" 輸出: 3 解釋: 因為無重復字符的最長子串是"a…

mov視頻怎么改成mp4?把mov改成MP4的四個方法

mov視頻怎么改成mp4&#xff1f;選擇合適的視頻格式對于確保內容質量和流通性至關重要。盡管蘋果公司的mov格式因其出色的視頻表現備受贊譽&#xff0c;但在某些情況下&#xff0c;它并非最佳選擇&#xff0c;因為使用mov格式可能面臨一些挑戰。MP4格式在各種設備&#xff08;如…

構造二進制字符串

目錄 LeetCode3221 生成不含相鄰零的二進制字符串 #include <iostream> #include <vector> using namespace std;void dfs(string s,int n,vector<string>& res){if(s.size()n){res.push_back(s);return;}dfs(s"0",n,res);dfs(s"1"…

使用redis進行短信登錄驗證(驗證碼打印在控制臺)

使用redis進行短信登錄驗證 一、流程1. 總體流程圖2. 流程文字講解&#xff1a;3.代碼3.1 UserServiceImpl&#xff1a;&#xff08;難點&#xff09;3.2 攔截器LoginInterceptor&#xff1a;3.3 攔截器配置類&#xff1a; 4 功能實現&#xff0c;成功存入redis &#xff08;黑…

搜維爾科技為空氣分離、氫氣、石化和天然氣工廠的現場操作員提供虛擬現實(VR)培訓

搜維爾科技為空氣分離、氫氣、石化和天然氣工廠的現場操作員提供虛擬現實(VR)培訓 搜維爾科技為空氣分離、氫氣、石化和天然氣工廠的現場操作員提供虛擬現實(VR)培訓

python 中關于append和extend的區別用法

#方法1 d[1,2,[3,4]] c[] for i in d:if type(i) int:c.append(i)else:c.extend(i)# append方法用于將單個元素添加到列表的末尾&#xff0c;這意味著無論元素是什么類型# &#xff08;如整數、字符串等&#xff09;&#xff0c;它都將作為一個獨立的元素添加到列表中。# exten…

UE5.2 AI實時摳像(無需綠幕) + OBS推流直播 全流程

最近通過2個UE5.2插件實現了從AI實時摳像到OBS推流的直播流程搭建&#xff0c;也為了水一篇博客&#xff0c;就在這里記錄一下了&#xff0c;覺得沒有意思的朋友&#xff0c;這里先說為敬了。 具體教程參考&#xff1a;【UE5 AI摳像OBS推流全流程&#xff08;簡單免費&#xf…

華為機考真題 -- 尋找身高相近的小朋友

題目描述: 小明今年升學到z小學—年級,來到新班級后發現其他小朋友們身高參差不齊,然后就想基于各4朋友和自己的身高差q對他們進行排序,請幫他實現排序。 輸入描述: 有一行為正整數h和n,0<h<200,為小明的身高,0<n<50,為新班級其他小朋友個數。 第二行為…

java中 使用數組實現需求小案例

Date: 2024.04.08 18:32:57 author: lijianzhan 需求實現&#xff1a; 設計一個java類&#xff0c;java方法&#xff0c;根據用戶手動輸入的績點&#xff0c;從而獲取到績點最高的成績。 實現業務邏輯的代碼塊 import java.util.Scanner;public class PointDemo {/*** 需求&…

Spring相關面試題(四)

49 JavaConfig方式如何啟用AOP?如何強制使用cglib&#xff1f; 在JavaConfig類&#xff0c;加上EnableAspectJAutoProxy 如果要強制使用CGLIB動態代理 &#xff0c;加上(proxyTargetClass true) 加上(exposeProxy true) 就是將對象暴露到線程池中。 50 介紹AOP在Spring中…

【3】遷移學習模型

【3】遷移學習模型 文章目錄 前言一、安裝相關模塊二、訓練代碼2.1. 管理預訓練模型2.2. 模型訓練代碼2.3. 可視化結果2.4. 類別函數 總結 前言 主要簡述一下訓練代碼 三葉青圖像識別研究簡概 一、安裝相關模塊 #xingyun的筆記本 print(xingyun的筆記本) %pip install d2l %…

詳解TCP和UDP通信協議

目錄 OSI的七層模型的主要功能 tcp是什么 TCP三次握手 為什么需要三次握手&#xff0c;兩次握手不行嗎 TCP四次揮手 揮手會什么需要四次 什么是TCP粘包問題&#xff1f;發生的原因 原因 解決方案 UDP是什么 TCP和UDP的區別 網絡層常見協議 利用socket進行tcp傳輸代…

【js面試題】深入理解DOM操作:創建、查詢、更新、添加和刪除節點

面試題&#xff1a;DOM常見的操作有哪些 引言&#xff1a; 在前端開發中&#xff0c;DOM&#xff08;文檔對象模型&#xff09;操作是日常工作中不可或缺的一部分。DOM提供了一種以編程方式訪問和更新文檔內容、結構和樣式的接口。 任何html或 xml 文檔都可以用dom表示為一個由…

KIVY Button?

Button — Kivy 2.3.0 documentation Button Jump to API ? Module: kivy.uix.button Added in 1.0.0 The Button is a Label with associated actions that are triggered when the button is pressed (or released after a click/touch). To configure the button, the s…