繁体   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