效果圖:
目前創新互聯建站已為1000+的企業提供了網站建設、域名、虛擬空間、網站托管、服務器托管、企業網站設計、建昌網站維護等服務,公司將堅持客戶導向、應用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協力一起成長,共同發展。

基于d3-v5, 依賴dagre-d3, 直接上代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
svg {
border: 1px solid darkcyan;
}
/* 拓撲圖--start */
/* 節點狀態顏色 */
g.type-current>circle {
fill: #FFAC27;
}
g.type-success>circle {
fill: #9270CA;
}
g.type-fail>circle {
fill: #67C23A;
}
g.type-done>circle {
fill: #E8684A;
}
/* 拓撲圖--end */
/* 坐標軸-start */
.axis path,
.axis line {
fill: none;
stroke: #DCDCDC;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 12px;
fill: #999999;
}
.axis .x2-axis text {
font-size: 14px;
font-weight: 400;
fill: #333;
}
.axis .x2-axis .tick {
stroke-width: 2px;
}
/* 坐標軸-end */
</style>
</head>
<script src=" http://d3js.org/d3.v5.min.js "></script>
<script src="https://cdn.bootcss.com/dagre-d3/0.6.3/dagre-d3.js"></script>
<body>
</body>
<script>
let nodeInfo = [{
id: 0,
label: "",
status: 'success',
date: 1575129600000
}, {
id: 1,
label: "",
status: 'fail',
date: 1578376890000
}, {
id: 2,
label: '',
status: 'success',
date: 1578376890000
}, {
id: 3,
label: '',
status: 'fail',
date: 1578895290000
}, {
id: 4,
label: '',
status: 'current',
date: 1578895290000
}, {
id: 5,
label: '',
status: 'done',
date: 1579327290000
}, {
id: 6,
label: '',
status: 'done',
date: 1579932090000
}, {
id: 7,
label: '',
status: 'done',
date: 1581487290000
}, {
id: 8,
label: '',
status: 'success',
date: 1583461994000
}]
let lineInfo = [
{ from: 0, to: 1 },
{ from: 0, to: 2 },
{ from: 0, to: 3 },
{ from: 2, to: 4 },
{ from: 2, to: 5 },
{ from: 3, to: 6 },
{ from: 6, to: 7 },
{ from: 6, to: 8 },
]
let nodeMap = new Map() //節點信息map
let nodeDomMap = new Map() //節點dom--map
let timeArr = [] //存儲時間
const width = 1200
const height = 400
const padding = { top: 0, bottom: 40, left: 40, right: 40 }
// 節點信息轉化為map
nodeInfo.forEach(item => {
nodeMap.set(item.id, item);
timeArr.push(item.date)
})
let max = new Date(d3.max(timeArr))
let min = new Date(d3.min(timeArr))
maxY = max.getFullYear()
maxM = max.getMonth()
minY = min.getFullYear()
minM = min.getMonth()
// 創建畫布 svg
let svg = d3.select("body").append("svg")
.attr("id", "svg-canvas")
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("viewBox", `0 0 ${width} ${height}`)
// 初始化元素
let background = svg.append("rect").attr("class", "bg")
let view = svg.append("g").attr("class", "view")
let grid = svg.append("g").attr("class", "grid")
let axis = svg.append("g").attr("class", "axis")
let separateLine = svg.append("line").attr("class", "separate-line")
// 繪制箭頭以供引用
d3.select("#svg-canvas").append("defs").append("marker")
.attr("id", "triangle").attr("viewBox", "0 0 10 10")
.attr("refX", "17").attr("refY", "5")
.attr("markerWidth", "6").attr("markerHeight", "6")
.attr("orient", "auto").append("path")
.attr("d", "M 0 0 L 10 5 L 0 10 z").style("fill", "#bbbbbb")
// 添加背景板 rect
background.attr("fill", "#FAFAFA")
.attr("x", 0).attr("y", 0)
.attr("width", width).attr("height", height - padding.bottom)
const monthNum = d3.timeMonth.count(min, max) // 區間月份數量
// 確定比例尺
let xScale = d3.scaleTime()
.domain([new Date(minY, minM, 1), new Date(maxY, ++maxM, 1)])
.range([0, width - padding.left - padding.right])
// 坐標軸文本格式化
let formatDay = d3.axisBottom(xScale).tickFormat((d, i) => {
const date = new Date(d)
const day = date.getDate()
return `${day === 1 ? "" : day}` // 如果是1號, 不顯示刻度,直接由xAxis2顯示年月
})
let formatMonth = d3.axisBottom(xScale).ticks(d3.timeMonth.every(1)).tickPadding(6).tickSizeInner(20).tickFormat((d, i) => {
const date = new Date(d)
const mon = date.getMonth() + 1
const year = date.getFullYear()
return `${year} - ${mon > 9 ? mon : "0" + mon}`
})
axis.attr('transform', `translate(${padding.left},${height - padding.bottom})`)
let xAxisDay = axis.append("g")
.attr("class", "x-axis").call(formatDay)
let xAxisMonth = axis.append("g")
.attr("class", "x2-axis").call(formatMonth)
// 繪制x網格
const lineGroup = grid.attr("transform", `translate(${padding.left},0)`)
.selectAll("g")
.data(xScale.ticks(monthNum))
.enter().append("g")
lineGroup.append("line")
.attr("x1", d => { return xScale(new Date(d)) })
.attr("x2", d => { return xScale(new Date(d)) })
.attr("y1", padding.top)
.attr("y2", height - padding.bottom)
.attr("class", "grid-line")
.style("stroke", "#DCDCDC")
.style("stroke-dasharray", 6)
// 添加坐標軸與拓撲圖分隔線
separateLine.style("stroke", "#DCDCDC")
.style("stroke-width", 2)
.attr("x1", 0)
.attr("x2", width)
.attr("y1", height - padding.bottom)
.attr("y2", height - padding.bottom)
// 繪制流程圖 節點--箭頭
let g = new dagreD3.graphlib.Graph()
.setGraph({})
.setDefaultEdgeLabel(function () { return {}; });
g.graph().rankdir = "LR"; // 控制水平顯示
g.graph().marginx = 0;
g.graph().marginy = 50;
nodeInfo && nodeInfo.map((item, i) => {
g.setNode(item.id, {
label: item.label,
class: "type-" + item.status,
style: "stroke-width: 2px; stroke: #fff",
shape: "circle",
id: item.id
});
})
lineInfo && lineInfo.map((item, i) => {
g.setEdge(item.from, item.to,
{
arrowheadStyle: "stroke:none; fill: none", // 箭頭頭部樣式
style: "stroke:none; fill: none" //線條樣式
})
})
let render = new dagreD3.render();
render(view.attr("transform", `translate(${padding.left},0)`), g);
// 重新定位節點x坐標
const nodesArr = d3.select(".nodes").selectAll(".node")._groups[0]
nodesArr.forEach((item) => {
let dom = d3.select(item)._groups[0][0]
let id = Number(dom.id)
let date = nodeMap.get(id).date
const x = xScale(new Date(date));
const y = dom.transform.animVal[0].matrix.f
d3.select(item).attr("transform", `translate(${x},${y})`)
nodeDomMap.set(Number(item.id), item)
})
// 重新繪制箭頭
lineInfo && lineInfo.map((item, i) => {
let fromDom = nodeDomMap.get(Number(item.from))
let toDom = nodeDomMap.get(Number(item.to))
const [x1, y1, x2, y2] = [
fromDom.transform.animVal[0].matrix.e,
fromDom.transform.animVal[0].matrix.f,
toDom.transform.animVal[0].matrix.e,
toDom.transform.animVal[0].matrix.f,
]
d3.select(".edgePaths").append("g")
.append("line")
.attr("class", `to-${item.to}`) // 設置唯一的class方便修改路徑
.attr("stroke-width", "2")
.attr("stroke", "#bbbbbb")
.style("stroke-dasharray", 8)
.attr("marker-end", "url(#triangle)")
.attr("x1", x1).attr("y1", y1)
.attr("x2", x2).attr("y2", y2)
})
// 設置zoom參數
let zoom = d3.zoom()
.scaleExtent([1, 10])
.translateExtent([[0, 0], [width, height]]) //移動的范圍
.extent([[0, 0], [width, height]])//視窗 (左上方,右下方)
svg.call(zoom.on("zoom", reRender.bind(this)));
// 每次縮放重定位渲染拓撲圖
function reRender() {
const t = d3.event.transform.rescaleX(xScale) //獲得縮放后的比例尺
xAxisDay.call(formatDay.scale(t)) //重新設置x坐標軸的scale
xAxisMonth.call(formatMonth.scale(t)) //重新設置x坐標軸的scale
const view = d3.select(".output")
const axis = d3.select(".axis-month")
const grid = d3.selectAll(".grid-line")
// 重新繪制節點
nodesArr.forEach((item) => {
let dom = d3.select(item)._groups[0][0]
let id = Number(dom.id)
let date = nodeMap.get(id).date
const x = t(new Date(date));
const y = dom.transform.animVal[0].matrix.f
d3.select(item).attr("transform", `translate(${x},${y})`)
nodeDomMap.set(Number(item.id), item)
})
// 重新繪制箭頭
lineInfo && lineInfo.map((item, i) => {
let fromDom = nodeDomMap.get(Number(item.from))
let toDom = nodeDomMap.get(Number(item.to))
const [x1, y1, x2, y2] = [
fromDom.transform.animVal[0].matrix.e,
fromDom.transform.animVal[0].matrix.f,
toDom.transform.animVal[0].matrix.e,
toDom.transform.animVal[0].matrix.f,
]
d3.select(`.to-${item.to}`)
.attr("x1", x1).attr("y1", y1)
.attr("x2", x2).attr("y2", y2)
})
//重新繪制x網格
svg.selectAll(".grid-line")
.attr("x1", d => { return t(new Date(d)) })
.attr("x2", d => { return t(new Date(d)) })
}
</script>
</html>以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持創新互聯。
當前文章:D3.js實現帶伸縮時間軸拓撲圖的示例代碼
網址分享:http://www.yijiale78.com/article40/pcssho.html
成都網站建設公司_創新互聯,為您提供網站設計公司、關鍵詞優化、網站設計、手機網站建設、網站改版、搜索引擎優化
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯