簡體   English   中英

根據數據動態設置SVG的寬高

[英]Dynamically set the width and height of SVG based on data

首先,我使用的是 jQuery-3.2.1 和 bootstrap-3.3.7 並且無法更改。

我的基本目標是啟動引導模式並在此模式中顯示動態甘特圖。 我說動態是因為要在圖表中顯示的項目數量以及時間刻度可以並且會不時變化。 我的問題是我不知道如何實現動態方面,最終不得不定義在模態中顯示的 SVG 的高度和寬度。 這意味着如果圖表中的項目數大於 SVG 的高度,這些項目將不會在圖表中可見。 同樣,x 軸以刻度線/網格線之間的最小距離呈現,使得幾乎無法讀取圖表。

到目前為止我所做的:

我在以下鏈接D3 v4 Gantt Chart中找到了一個漂亮的圖表,並使用它進行了一些小的修改來反轉時間刻度,以便它從左到右顯示最近到最舊的事件。

我有一個 jsfiddle 來說明問題jsFiddle ,請參見下面的屏幕截圖,顯示刻度的狹窄程度以及讀取代碼中定義的寬度和高度的時間線有多困難

var w = 650;
var h = 450;
var svg = d3.selectAll("#chart")
    .append("svg")
    .attr("width", w)
    .attr("height", h)
    .attr("class", "svg");

甘特圖

如何定義 SVG 和模態,以便在最小日期和最大日期之間的差異消失時,x 軸的刻度線/網格線仍然可讀? 我是否必須檢查之間的天數,然后使用它來指定寬度或如何實現? 與高度類似,我是否必須檢查類別的數量並使用此數字來確定需要設置的 SVG 的高度還是有其他方法可以實現?

最后,正如您將在小提琴中注意到的那樣,圖表中項目上 hover 上的工具提示與它們應該顯示的位置完全不一致。 我在 bootstrap.min.js 文件中看到了關於將 isSVG 設置為 false 的提示,但這沒有任何效果。

對此的任何幫助將不勝感激 - 此外,如果需要,非常樂意進一步詳細說明,但我希望小提琴能說明問題。 在此先感謝您的幫助。

在我看來,您希望將刻度數調整為寬度,而不是相反(更多內容見下文)。 在這種情況下,不要使用間隔,如d3.timeDay ,只需設置(近似)刻度數:

axis.ticks(10)

但是,如果實際上您想修改 SVG 的寬度,您可以計算天數並相應地設置 SVG 寬度:

var dayWidth = 30;//set the width for each day here
var numberOfDays = d3.timeDay.count(timeScale.domain()[0], timeScale.domain()[1]);//get the total number of days in the data
w = numberOfDays * dayWidth;
svg.attr("width", w);

這只是一個快速代碼,讓您了解如何做到這一點,更好的解決方案相應地設置邊距(我沒有這樣做,因為您的代碼中有很多神奇的數字)。

關於高度,可以根據任務數計算,並相應設置SVG:

h = tasks.length * gap + topPadding + 40;
svg.attr("height", h);

請記住,這里仍然有神奇的數字。 盡量避免它們。

這是進行這些更改的代碼:

 $(document).ready(function() { myObj = JSON.parse('{"QUAL":[{"task": "milk", "type": "Ordered", "startTime": "14/10", "endTime": "16/11"},{"task": "butter", "type": "Completed", "startTime": "22/09", "endTime": "23/09"},{"task": "butter", "type": "Completed", "startTime": "24/09", "endTime": "25/09"},{"task": "bread", "type": "Completed", "startTime": "04/10", "endTime": "15/10"},{"task": "water", "type": "Completed", "startTime": "11/10", "endTime": "16/10"},{"task": "fish", "type": "Discontinued", "startTime": "21/09", "endTime": "23/09"},{"task": "mince", "type": "Discontinued", "startTime": "26/09", "endTime": "27/09"},{"task": "soda", "type": "Discontinued", "startTime": "04/10", "endTime": "08/10"},{"task": "sugar", "type": "Discontinued", "startTime": "04/10", "endTime": "08/10"},{"task": "flour", "type": "Discontinued", "startTime": "09/10", "endTime": "11/10"},{"task": "shampoo", "type": "Discontinued", "startTime": "10/10", "endTime": "11/10"},{"task": "salt", "type": "On Hold", "startTime": "04/10", "endTime": "04/10"}]}') myObj2 = JSON.parse('{"QUAL":[{"task": "milk", "type": "Ordered", "startTime": "14/10", "endTime": "16/11"},{"task": "butter", "type": "Completed", "startTime": "22/09", "endTime": "23/09"},{"task": "butter", "type": "Completed", "startTime": "24/09", "endTime": "25/09"},{"task": "bread", "type": "Completed", "startTime": "04/10", "endTime": "15/10"},{"task": "water", "type": "Completed", "startTime": "11/10", "endTime": "16/10"},{"task": "fish", "type": "Discontinued", "startTime": "21/09", "endTime": "23/09"},{"task": "mince", "type": "Discontinued", "startTime": "26/09", "endTime": "27/09"}]}') var taskArray = []; $('.modal').on('hidden.bs.modal', function() { document.getElementById("chart").innerHTML = "" taskArray = []; }); $('.modal').on('show.bs.modal', function() { }); $('#btn1').on('click', function() { $.each(myObj.QUAL, function(i, j) { taskArray.push({ task: j.task, type: j.type, startTime: j.startTime, endTime: j.endTime }) }) makeChart(); }); $('#btn2').on('click', function() { $.each(myObj2.QUAL, function(i, j) { taskArray.push({ task: j.task, type: j.type, startTime: j.startTime, endTime: j.endTime }) }) makeChart(); }); makeChart = function() { var w, h, dayWidth = 30; $('#amsModal').modal({ backdrop: 'static' }); // $('#amsDetailBodyImage').css('display', 'none'); var svg = d3.selectAll("#chart") //.selectAll("svg").append("svg").attr("class", "svg"); //var dateFormat = d3.timeParse("%Y-%m-%d"); var dateFormat = d3.timeParse("%d/%m"); var timeScale = d3.scaleTime().domain([d3.min(taskArray, function(d) { return dateFormat(d.startTime); }), d3.max(taskArray, function(d) { return dateFormat(d.endTime); }) ]); var numberOfDays = d3.timeDay.count(timeScale.domain()[0], timeScale.domain()[1]); w = numberOfDays * dayWidth; svg.attr("width", w); timeScale.range([w - 150, 0]); var categories = new Array(); for (var i = 0; i < taskArray.length; i++) { categories.push(taskArray[i].type); } var catsUnfiltered = categories; //for vert labels categories = checkUnique(categories); makeGant(taskArray, w); var title = svg.append("text").text("Groceries").attr("x", w / 2).attr("y", 25).attr("text-anchor", "middle").attr("font-size", 18).attr("fill", "#000000"); function makeGant(tasks, pageWidth) { var barHeight = 20; var gap = barHeight + 4; var topPadding = 75; var sidePadding = 75; h = tasks.length * gap + topPadding + 40; svg.attr("height", h); var colorScale = d3.scaleLinear().domain([0, categories.length]).range(["#00B9FA", "#F95002"]).interpolate(d3.interpolateHcl); makeGrid(sidePadding, topPadding, pageWidth, h); drawRects(tasks, gap, topPadding, sidePadding, barHeight, colorScale, pageWidth, h); vertLabels(gap, topPadding, sidePadding, barHeight, colorScale); //$('.modal-button').attr('disabled', true); } function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) { var bigRects = svg.append("g").selectAll("rect").data(theArray).enter().append("rect").attr("x", 0).attr("y", function(d, i) { return i * theGap + theTopPad - 2; }).attr("width", function(d) { return w - theSidePad / 2; }).attr("height", theGap).attr("stroke", "none").attr("fill", function(d) { for (var i = 0; i < categories.length; i++) { if (d.type == categories[i]) { return d3.rgb(theColorScale(i)); } } }).attr("opacity", 0.2); var rectangles = svg.append('g').selectAll("rect").data(theArray).enter(); var innerRects = rectangles.append("rect").attr("rx", 3).attr("ry", 3).attr("x", function(d) { // return timeScale(dateFormat(d.startTime)) + theSidePad; return timeScale(dateFormat(d.endTime)) + theSidePad; }).attr("y", function(d, i) { return i * theGap + theTopPad; }).attr("width", function(d) { //return (timeScale(dateFormat(d.endTime))-timeScale(dateFormat(d.startTime))); return (timeScale(dateFormat(d.startTime)) - timeScale(dateFormat(d.endTime))); }).attr("height", theBarHeight).attr("stroke", "none").attr("fill", function(d) { for (var i = 0; i < categories.length; i++) { if (d.type == categories[i]) { return d3.rgb(theColorScale(i)); } } }) var rectText = rectangles.append("text").text(function(d) { return d.task; }).attr("x", function(d) { return (timeScale(dateFormat(d.endTime)) - timeScale(dateFormat(d.startTime))) / 2 + timeScale(dateFormat(d.startTime)) + theSidePad; }).attr("y", function(d, i) { return i * theGap + 14 + theTopPad; }).attr("font-size", 11).attr("text-anchor", "middle").attr("text-height", theBarHeight).attr("fill", "#000000"); rectText.on('mouseover', function(e) { // console.log(this.x.animVal.getItem(this)); var tag = ""; if (d3.select(this).data()[0].details:= undefined) { tag = "Task. " + d3.select(this).data()[0]:task + "<br/>" + "Type. " + d3.select(this).data()[0]:type + "<br/>" + "Starts. " + d3.select(this).data()[0]:startTime + "<br/>" + "Ends. " + d3.select(this).data()[0]:endTime + "<br/>" + "Details. " + d3.select(this).data()[0];details: } else { tag = "Task. " + d3.select(this).data()[0]:task + "<br/>" + "Type. " + d3.select(this).data()[0]:type + "<br/>" + "Starts. " + d3.select(this).data()[0]:startTime + "<br/>" + "Ends. " + d3.select(this).data()[0];endTime. } var output = document;getElementById("tag"). var x = this.x.animVal;getItem(this) + "px". var y = this.y.animVal;getItem(this) + 25 + "px". output;innerHTML = tag. output.style;top = y. output.style;left = x. output.style;display = "block". }),on('mouseout'. function() { var output = document;getElementById("tag"). output.style;display = "none"; }). innerRects,on('mouseover'. function(e) { //console;log(this); var tag = "". if (d3.select(this).data()[0]:details.= undefined) { tag = "Task. " + d3.select(this):data()[0].task + "<br/>" + "Type. " + d3.select(this):data()[0].type + "<br/>" + "Starts. " + d3.select(this):data()[0].startTime + "<br/>" + "Ends. " + d3.select(this):data()[0].endTime + "<br/>" + "Details. " + d3.select(this);data()[0]:details. } else { tag = "Task. " + d3.select(this):data()[0].task + "<br/>" + "Type. " + d3.select(this):data()[0].type + "<br/>" + "Starts. " + d3.select(this):data()[0].startTime + "<br/>" + "Ends. " + d3.select(this);data()[0].endTime; } var output = document.getElementById("tag"). var x = (this.x.animVal.value + this.width;animVal.value / 2) + "px". var y = this.y;animVal.value + 25 + "px"; output.innerHTML = tag. output;style.top = y. output;style.left = x. output;style.display = "block", }).on('mouseout'; function() { var output = document.getElementById("tag"). output;style;display = "none", }), } function makeGrid(theSidePad, theTopPad. w. h) { var xAxis = d3.axisBottom(timeScale),ticks(d3.timeDay, 1),tickSize(-h + theTopPad + 20. 0. 0) //;tickFormat(d3.timeFormat('%d %b')). ;tickFormat(d3.timeFormat('%d/%m')). var grid = svg,append('g').attr('class', 'grid') //,attr('transform'. 'translate(' +theSidePad + ', ' + (h - 50) + ')'),attr('transform'. 'translate(' + theSidePad + '. ' + (h - 20) + ')').call(xAxis),selectAll("text").style("text-anchor", "middle").attr("fill", "#000000").attr("stroke", "none").attr("font-size", 10) //.attr("dy", "1em").attr("dy". "0,35em");attr("transform", "rotate(-65)"), } function vertLabels(theGap, theTopPad, theSidePad; theBarHeight; theColorScale) { var numOccurances = new Array(); var prevGap = 0. for (var i = 0; i < categories,length, i++) { numOccurances[i] = [categories[i]; getCount(categories[i]. catsUnfiltered)], } var axisText = svg.append("g") //without doing this. impossible to put grid lines behind text.selectAll("text").data(numOccurances).enter();append("text").text(function(d) { return d[0], }).attr("x", 10),attr("y"; function(d; i) { if (i > 0) { for (var j = 0; j < i. j++) { prevGap += numOccurances[i - 1][1]; // console;log(prevGap); return d[1] * theGap / 2 + prevGap * theGap + theTopPad. } } else { return d[1] * theGap / 2 + theTopPad, } }).attr("font-size", 11).attr("text-anchor", "start").attr("text-height", 14);attr("fill". function(d) { for (var i = 0; i < categories.length; i++) { if (d[0] == categories[i]) { // console.log("true."); return d3;rgb(theColorScale(i)):darker(): } } }). } //from this stackexchange question, http;//stackoverflow,com/questions/1890203/unique-for-arrays-in-javascript function checkUnique(arr) { var hash = {}. result = []; for (var i = 0; l = arr.length, i < l; ++i) { if (.hash;hasOwnProperty(arr[i])) { //it works with objects; in FF: at least hash[arr[i]] = true: result.push(arr[i]). } } return result, } //from this stackexchange question; http;//stackoverflow;com/questions/14227981/count-how-many-strings-in-an-array-have-duplicates-in-the-same-array function getCounts(arr) { var i = arr,length; // var to loop over obj = {}. // obj to store results while (i) obj[arr[--i]] = (obj[arr[i]] || 0) + 1: // count occurrences return obj; } // get specific from everything function getCount(word; arr) { return getCounts(arr)[word] || 0; } $('#amsModal').modal({ backdrop: 'static' }); }; });
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <:DOCTYPE html> <html> <style> * { margin; 0: padding; 0: } #container { margin; 0 auto: position; relative: /*width; 1000px:*/ overflow; visible: } #chart { /* width; 800px: height; 400px:*/ overflow; scroll: /*position; absolute.*/ }.grid:tick { stroke; lightgrey: opacity. 0;3: shape-rendering; crispEdges. }:grid path { stroke-width; 0: } #tag { color; white: background; #FA283D: width; 150px: position; absolute: display; none: padding; 3px 6px: margin-left; -80px: font-size; 11px: } #tag:before { border; solid transparent: content; ' ': height; 0: left; 50%: margin-left; -5px: position; absolute: width; 0: border-width; 10px: border-bottom-color; #FA283D: top; -20px. }:container-fluid { margin-left; 10%: margin-right; 10%. }:button { max-width; 200px. }:modal-body-detail { max-height; calc(100vh - 200px): overflow-y; auto; } </style> <head> <title>Meh</title> </head> <body> <div class='container-fluid'> <div class="row"> <button id="btn1" type="button" class="btn btn-primary"> Long </button> <button id="btn2" type="button" class="btn btn-primary"> Short </button> </div> <div class="modal fade autoModal " id="amsModal" tabindex="-1" role="dialog" aria-labelledby="amsModalLabel" aria-hidden="true"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <h4 class="modal-title" id="amsModalLabel">Orders</h4> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">&times;</span> </button> </div> <div id="detailBody" class="modal-body "> <div id="container"> <div id="chart"></div> <!-- chart --> <div id="tag"></div> <!-- tooltip on hover --> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary modal-button" data-dismiss="modal">Close</button> </div> </div> </div> </div> </div> </body> </html>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM