I am trying to convert a bubble chart from d3v3 to v4. Running into x,y,d missing variables?
In this version -- a rect is applied to the svg - and then a circle is cut -- so its like an inverse bubble chart.
I am keen to work out a set radius for the chart as a maxium -- if it should act like a score between 0 and 100? What kind of math to apply that a max radius has been reached to signify that the value is very big?
I also tried to have the svg mask adapt - if the browser or its container changed size -- ideally would want it to response during the change - rather than resizeEnd
//version 3
https://jsfiddle.net/8ag1vf6e/1/
//current version 4
https://jsfiddle.net/d56g9r0y/
// filters go in defs element
var defs = innversebubble.append("defs");
var mask = defs.append("mask")
.attr("id", "myMask");
mask.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.style("fill", "white")
.style("opacity", 1);
var invisiblebubble = mask.append("circle")
.data(data);
//create a fixed bubble first
invisiblebubble
.attr("cx", "50%")
.attr("cy", "50%")
.attr("r", function(d) {
return d.value - 20;
});
//now mask the fixed circle
var masker = defs.append(function() {
return mask.node().cloneNode(true)
})
.attr("id", "myMaskForPointer")
.select("rect")
.style("fill", "white")
.style("opacity", 1);
invisiblebubble
.attr("r", 10);
//apply the rest of the chart elements
var rect = innversebubble
.attr("class", "series")
.append("g")
.attr("transform", "translate(0,0)")
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "100%")
.attr("height", "100%")
.attr("mask", "url(#myMask)")
.style("fill", backcolor)
.style("opacity", backopacity);
//animate this circle
invisiblebubble
.attr("cx", "50%")
.attr("cy", "50%")
.transition()
.duration(1800)
.attr("r", 10)
.transition()
.duration(900)
.attr("r", function(d) {
return d.value;
});
latest jsfiddle - 15th June -- needs fixing
https://jsfiddle.net/xmrtahns/
"I am keen to work out a set radius for the chart as a maxium -- if it should act like a score between 0 and 100? What kind of math to apply that a max radius has been reached to signify that the value is very big?
I also tried to have the svg mask adapt - if the browser or its container changed size -- ideally would want it to response during the change - rather than resizeEnd"
I've fixed the conversion and the data source - but still need issues to resolve.
var backcolor = $this.data("color");
var backopacity = $this.data("opacity");
var width = $this.data("width");
var height = $this.data("height");
var data = [{
"label": $this.data("label-name"),
"centralLabel": $this.data("central-label"),
"xPer": $this.data("displace-left"),
"yPer": $this.data("displace-top"),
"value": $this.data("bubble-value")
}];
http://jsfiddle.net/hLymw8et/2/
--I am keen to work out a set radius for the chart as a maximum -- if it should act like a score between 0 and 100?
--What kind of math to apply that a max radius has been reached to signify that the value is very big?
--I also tried to have the svg mask adapt - if the browser or its container changed size -- ideally would want it to response during the change - rather than resizeEnd –
Related
My d3 js chart brushed and zoomed is not working for background.
Inside my brushed function I have below code, while brushing and zooming it should reflect the orange background.
var orangeBack = svg
.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", margin.left)
// .attr("x", function (d) {
// return xScale(1568283720049);
// })
.attr("y", margin.top)
.attr("height", containerHeight - 120)
// .attr("width", 200)
.attr("width", function (d) {
return xScale(1568283720049) - xScale(1567851720049) + 10;
})
.style("stroke", bordercolor)
.attr("fill", "orange")
.attr("opacity", 0.05)
.style("stroke-width", border);
currently I have given the timestamp directly e.g. xScale(1568283720049) this can be calculated based on new scale dynamically.
code sandbox - https://codesandbox.io/s/quizzical-bhabha-4ullr?file=/src/TimelineChart.js
after brushing and zooming -
I have an svg graph using d3.js, but I'm having trouble with the viewbox. As I have it now, the distance under the graph (30px) is fine, but there is still too much space between the text above the graph (48 px).
I don't have much experience with viewbox yet, so I researched and the best answer I found was at Find svg viewbox that trim whitespace around where the solution was to use the bounding box. That answer involves creating a button to click to show the graph correctly presented, but I want it to be positioned without additional user input.
Here is the html code above the graph:
<h1 class="h1_info">First Entry</h1><br>
<div class="zebra_01">This is the general text to describe the issue.<br><br>
With no further improvements:<ul><li>One item was this tall<br>
</li><li>Another item was that tall</li><li>
<span class="span_00">The first is taller than the second</span></li></ul>
</div>
<svg viewBox="0 0 700 100" preserveAspectRatio="xMinYMid meet" class="d3svg"></svg>
<script src="JS\D3_BarChart.js"></script>
Other html code starts here, 30px below the graph.
Here is the javascript:
var dataArray = [23, 13];
var colors = [ "red", "green" ];
var names = [ "First Name", "Second Name" ];
var widths = [ "5", "700" ]
var dists = ["45", "40"]
var svg = d3.select("svg.d3svg")
.attr("height", "auto")
.attr("width", "100%")
var bar = svg.selectAll("g")
.data(dataArray)
.enter().append("g")
var gradient = svg
.append("linearGradient")
.attr("y1", "0%")
.attr("y2", "20%")
.attr("x1", "0%")
.attr("x2", "25%")
.attr("id", "gradient")
.attr("gradientUnits", "userSpaceOnUse")
gradient
.append("stop")
.attr('class', 'start')
.attr("offset", "0%")
.attr("stop-color", "red")
.attr("stop-opacity", 1);
gradient
.append("stop")
.attr('class', 'end')
.attr("offset", "100%")
.attr("stop-color", "green")
.attr("stop-opacity", 1);
var rect = bar.append('rect')
.attr("height", "7")
.attr("width", function(d, i) { return widths[i] })
.attr("y", function(d, i) { return (i * dists[i]) + 30 })
.attr("x", "0")
.attr("fill", "url(#gradient)")
var text = bar.append('text')
.attr("class", "text-svg")
.text (function(d, i) { return names[i] })
.attr("x", "0")
.attr("y", function(d, i) { return (i * dists[i]) + 55 });
So my question is whether using bounding box is the correct solution, and how can it be set without using a button?
Thanks for any help on this.
Looking at the chart, the first element starts at y =30. You can change the viewBox to be consistent with this fact:
viewBox="0 30 700 100"
Alternatively, if you are not sure about the y coordinates of the first element, you can use d3 to programmatically adjust the viewBox. Here is the relevant piece of your code:
var rect = bar.append('rect')
.attr("height", "7")
.attr("width", function(d, i) { return widths[i] })
.attr("y", function(d, i) { return (i * dists[i]) + 30 })
.attr("x", "0")
.attr("fill", "url(#gradient)")
svg.attr("viewBox", `0 ${rect.attr("y")} 700 100`) //add this. It uses es6 way to interpolate a string
The last line takes the y attribute of the rect (the topmost element in this case) and changes the viewBox accordingly.
I have a simple D3 donut diagram with a .mouseover() event that updates an SVG:text element at the center of the donut hole. It works great...
Until I encounter users with IE 9, 10 and 11. These browsers won't render the center label. Is there a way to accommodate IE and show the center label in both browsers?
The HTML page is based on HTML5BoilerPlate with the various shims to detect old browsers.
The D3 script seems pretty straight forward.
d3.json("data/census.php", function(error, dataset) {
var h = 220, w = 295;
var outerRadius = h / 2, innerRadius = w / 4;
var color = d3.scale.category20b();
var svg= d3.select("#dailycensus")
.append("svg")
.data([dataset])
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")");
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.layout.pie()
.value(function(d,i) { return +dataset[i].Census; });
var arcs = svg.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice");
var syssum = d3.sum(dataset, function(d,i) { return +dataset[i].Census; });
var tip = d3.tip()
.attr("class", "d3-tip")
.html(String);
var formatter = d3.format(".1%");
svg.append("text")
.attr("id", "hospital")
.attr("class", "label")
.attr("y", -10)
.attr("x", 0)
.html("Health System Census"); // Default label text
svg.append("text")
.attr("id", "census")
.attr("class", "census")
.attr("y", 40)
.attr("x", 0)
.html(syssum); // Default label value
arcs.append("svg:path")
.call(tip) // Initialize the tooltip in the arc context
.attr("fill", function(d,i) { return color(i); }) // Color the arc
.attr("d", arc)
.on("mouseover", function(d,i) {
tip.show( formatter(dataset[i].Census/syssum) );
// Update the doughnut hole label with slice meta data
svg.select("#hospital").remove();
svg.select("#census").remove();
svg.append("text")
.attr("id", "hospital")
.attr("class", "label")
.attr("y", -10)
.attr("x", 0)
.html(dataset[i].Facility);
svg.append("text")
.attr("id", "census")
.attr("class", "census")
.attr("y", 40)
.attr("x", 0)
.html(+dataset[i].Census);
})
.on("mouseout", function(d) {
tip.hide();
// Return the doughnut hole label to the default label
svg.select("#hospital").remove();
svg.select("#census").remove();
svg.append("text")
.attr("id", "hospital")
.attr("class", "label")
.attr("y", -10)
.attr("x", 0)
.html("Health System Census");
svg.append("text")
.attr("id", "census")
.attr("class", "census")
.attr("y", 40)
.attr("x", 0)
.html(syssum);
})
Replace all the .html calls with .text calls. Generally innerHTML is for HTML things although browsers are giving it SVG support as everybody keeps expecting it to work.
It's not immediately clear what is causing the issue, however setting the .text property instead resolves the issue after testing with Fiddler:
svg.append("text")
.attr("id", "hospital")
.attr("class", "label")
.attr("y", -10)
.attr("x", 0)
.text(dataset[i].Facility);
svg.append("text")
.attr("id", "census")
.attr("class", "census")
.attr("y", 40)
.attr("x", 0)
.text(+dataset[i].Census);
})
After investigating the <text /> elements directly in the Developer Tools you can see that setting the .innerHTML property doesn't render the results you'd expect, however .textContent does.
If this is working as expected in both Chrome and Firefox, I'll gladly open up an interop bug for the IE team to look into. We've been doing some SVG work lately, so I may find that this has already been discussed.
I had the same issue and innerSvg polyfill helps me. Now html() in SVG works in IE.
Following image shows how I want legend items in a d3 graph to be like. All items should be aligned with one line for all items.
How do I achieve the above using d3.js?
I tried multiple times not solve this problem. I can not put put all items in a single line.
Here you go.
Just changed the x and y attributes of the legend and it did the work.
var legend = svg.append("g")
.attr("class", "legend")
.attr("x", w)
.attr("height", 50)
.attr("width", 300)
.attr("transform", "translate(" + (w - 200) + ", -50)");
Removed y and used the transform and placed the legend on top right corner.
legend.selectAll('g').data(dataset)
.enter()
.append('g')
.each(function(d, i) {
var g = d3.select(this);
g.append("rect")
.attr("x", i * 60)
.attr("y", 65)
.attr("width", 10)
.attr("height", 10)
.style("fill", color_hash[String(i)][1]);
Here I changed the x and y attribute values to get the desired out.
g.append("text")
.attr("x", i * 60 + 15)
.attr("y", 73)
.attr("height",30)
.attr("width",100)
.style("fill", color_hash[String(i)][1])
.text(color_hash[String(i)][0]);
And finally amended the text with same logic to stay inline with the legend icons.
Hope this helps you.
http://jsfiddle.net/pPMqQ/146/
I'm developing a bubble chart application that is a derivative of a force chart to provide a little bit of movement.
Here is some of the code. As I create the svg - I also set up the viewBox - which I think could be used to adjust the size of the chart - but I've not been able to adjust the ratios properly to get the correct scaling.
I've also added here the code that adds the nodes - as the node size is calculated - its using a scale variable to affect the size of the orbs.
var svg = d3.select(selector)
.append("svg")
.attr("class", "bubblechart")
.attr("width", parseInt(w + margin.left + margin.right,10))
.attr("height", parseInt(h + margin.top + margin.bottom,10))
.attr('viewBox', "0 0 "+parseInt(w + margin.left + margin.right,10)+" "+parseInt(h + margin.top + margin.bottom,10))
.attr('perserveAspectRatio', "xMinYMid")
.append("g")
.attr("transform", "translate(0,0)");
methods.force = d3.layout.force()
.charge(1000)
.gravity(100)
.size([methods.width, methods.height])
var bubbleholder = svg.append("g")
.attr("class", "bubbleholder")
var bubbles = bubbleholder.append("g")
.attr("class", "bubbles")
var labelbubble = bubbleholder.append("g")
.attr("class", "labelbubble")
// Enter
nodes.enter()
.append("circle")
.attr("class", "node")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("r", 1)
.style("fill", function (d) { return methods.fill(d.label); })
.call(methods.force.drag);
// Update
nodes
.transition()
.delay(300)
.duration(1000)
.attr("r", function (d) { return d.radius*scale; })
// Exit
nodes.exit()
.transition()
.duration(250)
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("r", 1)
.remove();
I have the remaining issues
scaling is an issue
I've set the width/heights via data attributes - currently I have a scaling variable set to adjust the size of the orbs depending on the width of the chart. I would like to find a more scientific way of ensuring the chart is resized accordingly and also that the elements always remain central (don't become obscured).
ensuring the smaller elements are on top of the big elements
I've also noticed that small objects may randomly fall underneath larger orbs, is there a way to organize the rendering of the orbs dependant on size, so bigger elements always sit at the bottom layer.
I've solved the second problem by sorting the data via value.
http://jsfiddle.net/pPMqQ/149/
data = data.sort(function(a, b) {return b.value - a.value})
Currently I have a scale variable depending on the width of the chart - var scale = methods.width*.005;
but its not very scientific per say
If the chart is 150 width
http://jsfiddle.net/pPMqQ/150/
the chart renders - but the bubbles no longer fit in the space.
the chart at 250 px
http://jsfiddle.net/pPMqQ/151/