I started going through d3.js a day ago. I have a small snippet of code that runs when a svg rectangle element is clicked on. In this code snippet the only second transition works not the first one.
var body = d3.select("body");
var colors = ["blue", "darkblue", "black", "red", "green"]
var svg = body.append("svg")
.attr("width", 500)
.attr("height", 400)
var rect = svg.append("rect")
.attr("x", 10)
.attr("y", 10)
.attr("width", 100)
.attr("height", 100);
rect.on("click", function () {
rect.transition()
.style("fill", colors[Math.floor((Math.random() * 10) / 2)])
.attr("x", 400)
.ease("elastic")
.duration(1500);
rect.transition()
.style("fill", colors[Math.floor((Math.random() * 10) / 2)])
.attr("y", 300)
.ease("elastic")
.duration(1500);
});
Why is it not running first transition? Here is the JSFIDDLE LINK.
By setting up transitions in two separate bits of code, you're overwriting the first with the second before it had a chance to run. To add another transition after one has finished, simply do a .transition() again in the same block of code:
rect.transition()
.style("fill", colors[Math.floor((Math.random() * 10) / 2)])
.attr("x", 400)
.ease("elastic")
.duration(1500)
.transition()
.style("fill", colors[Math.floor((Math.random() * 10) / 2)])
.attr("y", 300)
.ease("elastic")
.duration(1500);
Complete demo here.
Related
I have code to make a circle and I'd like to place text on top of it.
I'm using this for my example: https://bl.ocks.org/mbostock/raw/7341714/
infoHeight = 200
infoWidth = 200
var compareSVG = d3.select(".info-container")
.append("svg")
.attr("class","comparison-svg")
.attr("width", infoWidth)
.attr("height", infoHeight);
var circle = compareSVG.append("g")
circle.append("circle")
.attr("r", circleRadius(d.properties.contextvalue))
.attr("cy", infoHeight/2)
.attr("cx", infoWidth/2)
.style("fill","grey")
.style("stroke","black")
.style("stroke-width","3px")
circle.append("text")
.text(d.properties.contextvalue)
.style("display", "block")
.style("y", infoHeight/2)
.style("x", infoHeight/2)
.style("color","red")
.style("font-size","20px")
The circle works, but the text won't appear on top of it. Instead, it is in the top left corner of the SVG element. I've tried position: absolute along with top and left and it stays in the same corner.
In D3, the attr methods uses Element.setAttribute internally, while style uses CSSStyleDeclaration.setProperty().
In an SVG <text> element, x and y are attributes. Therefore, change those style() methods for attr(). Also, get rid of that .style("display", "block").
So, it should be:
circle.append("text")
.text(d.properties.contextvalue)
.attr("y", infoHeight/2)
.attr("x", infoHeight/2)
.style("color","red")
.style("font-size","20px")
Here is your code with that change:
infoHeight = 200
infoWidth = 200
var compareSVG = d3.select("body")
.append("svg")
.attr("width", infoWidth)
.attr("height", infoHeight);
var circle = compareSVG.append("g")
circle.append("circle")
.attr("r", 50)
.attr("cy", infoHeight / 2)
.attr("cx", infoWidth / 2)
.style("fill", "lightgrey")
.style("stroke", "black")
.style("stroke-width", "3px")
circle.append("text")
.text("Foo Bar Baz")
.attr("y", infoHeight / 2)
.attr("x", infoHeight / 2)
.style("color", "red")
.style("font-size", "20px")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Finally, pay attention to the position of the text: it's not entered (regarding the circle). If you want to center it, use text-anchor and dominant-baseline:
infoHeight = 200
infoWidth = 200
var compareSVG = d3.select("body")
.append("svg")
.attr("width", infoWidth)
.attr("height", infoHeight);
var circle = compareSVG.append("g")
circle.append("circle")
.attr("r", 50)
.attr("cy", infoHeight / 2)
.attr("cx", infoWidth / 2)
.style("fill", "lightgrey")
.style("stroke", "black")
.style("stroke-width", "3px")
circle.append("text")
.text("Foo Bar Baz")
.attr("y", infoHeight / 2)
.attr("x", infoHeight / 2)
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.style("color", "red")
.style("font-size", "20px")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
I have created a D3 bar chart, which takes in my data, applies a scale and then draws my rectangles. Here's my current output:
I would like to make it more visual. The values are temperatures, so I'd like to create something like the first bar here:
Basically I want to make a bar that is made up of many rectangles of different colors.
After considering several options, I decided to try to inject a pattern into my bars. I tried making my pattern from paths, and then I attempted rectangles, but no success.
I'm not tied to the 'pattern' approach, so an different approach, or how I can make this pattern, is what I'm after. Here's my code:
// Set variables.
var dataset = // an array of objects, each with two properties: location and temperature
w = 500,
h = 800,
xScale = d3.scale.linear()
.domain([-30, 40])
.range([5, 350]),
yScale = d3.scale.ordinal();
// Create SVG element.
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
/* svg.append("defs")
.append("pattern")
.attr("id", "heatHatch")
.attr("patternUnits", "userSpaceOnUse")
.attr("width", 350)
.attr("height", 25);
.append("path")
.attr("d", "M10 0 L10 20 L0 20 L0 0 Z M22 0 L22 20 L12 20 L12 0 Z")
.attr("fill", "pink")
.append("path")
.attr("d", "M22 0 L22 20 L12 20 L12 0 Z")
.attr("fill", "red");
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 10)
.attr("height", 20)
.attr("fill", "pink"); */
// Create bars.
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", 0)
.attr("y", function(d, i) {
return i * 25;
})
.attr("width", function(d) {
return xScale(d[1]);
})
.attr("height", 20);
/*.attr("fill", "url(#heatHatch)"); */
Thanks.
If you want pattern that scales to the bar size, use a gradient pattern:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<script>
var w = 500,
h = 500;
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
var color = d3.scale.category10();
var gradient = svg.append("svg:defs")
.append("svg:linearGradient")
.attr("id", "gradient")
.attr("x1", "0%")
.attr("y1", "50%")
.attr("x2", "100%")
.attr("y2", "50%")
.attr("spreadMethod", "pad");
d3.range(10).forEach(function(d, i) {
var c = color(i);
gradient.append("svg:stop")
.attr("offset", (i * 10) + "%")
.attr("stop-color", c)
.attr("stop-opacity", 1);
gradient.append("svg:stop")
.attr("offset", ((i + 1) * 10) + "%")
.attr("stop-color", c)
.attr("stop-opacity", 1);
});
svg.selectAll("bar")
.data(d3.range(10))
.enter()
.append("rect")
.attr("class", "bar")
.attr("width", function(d,i){
return (w / 10) * i;
})
.attr("height", (h / 10) - 10)
.attr("y", function(d,i){
return (h / 10) * i;
})
.style("fill", "url(#gradient)");
</script>
</body>
</html>
If you want a pattern with statically sized colors, create a pattern of rects:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<script>
var w = 500,
h = 500;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
var color = d3.scale.category10();
var pattern = svg.append("svg:defs")
.append("svg:pattern")
.attr("width", w)
.attr("height", 4)
.attr("id", "myPattern")
.attr("patternUnits","userSpaceOnUse");
d3.range(10).forEach(function(d, i) {
var c = color(i);
pattern.append("rect")
.attr("height", "4px")
.attr("width", w / 10)
.attr("x", (w / 10) * i)
.attr("fill", c);
});
svg.selectAll("bar")
.data(d3.range(10))
.enter()
.append("rect")
.attr("class","bar")
.attr("width", function(d,i){
return (w / 10) * i;
})
.attr("height", (h / 10) - 10)
.attr("y", function(d,i){
return (h / 10) * i;
})
.style("fill", "url(#myPattern)");
</script>
</body>
</html>
I'm trying to get an element with an appended image to transition using the d3.js library; I had successfully achieved this just using a plain circle that transitioned nicely around the screen but now that I've added a png the transition doesn't happen - the png does appear though when the page is refreshed, it just won't move like it did before! My code is below.. your help is appreciated!
<script>
var data = [60, 120, 40, 710, 560, 850];
var data1 = data[0];
var canvas = d3.select("body").append("svg")
.attr("width", 2000)
.attr("height", 2000);
var imgs = canvas.append("image")
.attr("xlink:href", "AWT-Bus.png")
.attr("x", "60")
.attr("y", "60")
.attr("width", "20")
.attr("height", "20")
.attr("cx", 50)
.attr("cy", 200)
.attr("r", 20)
;
imgs.transition()
.duration(data1*100)
.delay(2000)
.attr("cx", 200)
.transition()
.attr("cx", 50)
.attr("cy", 200)
.transition()
.attr("cx", 150)
.attr("cy", 300)
;
The attributes you are changing in your code (cx and cy) are applicable to circles which are described by the x and y co-ordinates of their center (cx and cy) plus the radius (r). This is why your circle example worked.
But images are described by their width, height and the x and y co-ordinates of the upper-left corner of the box (using x and y attributes as shown below).
Different svg elements have different attributes which describe their dimensions and their location on the page, so you need to be aware of the different attributes that each type of element has, perhaps using a reference such as https://developer.mozilla.org/en-US/docs/Web/SVG/Element. Then you can animate your svg element using transition as you have done in your code and changing the value of the appropriate attribute.
var canvas = d3.select("body").append("svg")
.attr("width", 2000)
.attr("height", 2000);
var imgs = canvas.append("image")
.attr("xlink:href", "AWT-Bus.png")
.attr("x", "60")
.attr("y", "60")
.attr("width", "20")
.attr("height", "20");
imgs.transition()
.duration(2000)
.delay(1000)
.attr("x", 200)
.transition()
.attr("x", 50)
.attr("y", 200)
.transition()
.attr("x", 150)
.attr("y", 300);
I'm trying to animate width of a rectangle in d3 like this:
svg.append("rect")
.attr("x", 10)
.attr("y", 20)
.attr("height", 20)
.style('fill',barColor)
.transition()
.attr("width", 200)
.duration(1000)
.delay(1000);
I'm trying to increase the width to 200 px over a period of 1 sec, but the animation is not happening.
Can someone please explain what I'm doing wrong ?
You need to set an initial value for the width in order for D3 to be able to interpolate and transition:
svg.append("rect")
.attr("x", 10)
.attr("y", 20)
.attr("height", 20)
.attr("width", 0)
.style('fill',barColor)
.transition()
.duration(1000)
.attr("width", 200);
I want to achieve followings,
1 - Create a given column matrix with rectangles with provided colours
Done
2 - Make this matrix zoom able
3 - Add list items to each rectangle which will only show numbers of list items in it if completely zoomed out and on zoom in, it will show the list items e.g. there Titles.
Now I want to achieve Number 2 here, this is what I am trying,
http://jsfiddle.net/nhe613kt/25/
When I add code for zooming it fails,
var svgContainer = d3.select("body").append("svg")
.attr("width", 300)
.attr("height", 300)
.style("background-color", "black");
var zoomed = function () {
svgContainer.attr("transform", "translate("+ d3.event.translate + ")
scale(" + d3.event.scale + ")");
};
var zoom = d3.behavior.zoom()
.scaleExtent([1, 8])
.on("zoom", zoomed);
.size([width, height]);
var rectangle1 = svgContainer.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red")
.call(zoom);;
var rectangle2 = svgContainer.append("rect")
.attr("x", 100)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "yellow");
var rectangle3 = svgContainer.append("rect")
.attr("x", 200)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red");
var rectangle4 = svgContainer.append("rect")
.attr("x", 0)
.attr("y", 100)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "yellow");
var rectangle5 = svgContainer.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red");
var rectangle6 = svgContainer.append("rect")
.attr("x", 200)
.attr("y", 100)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "yellow");
var rectangle7 = svgContainer.append("rect")
.attr("x", 0)
.attr("y", 200)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red");
var rectangle8 = svgContainer.append("rect")
.attr("x", 100)
.attr("y", 200)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "yellow");
var rectangle9 = svgContainer.append("rect")
.attr("x", 200)
.attr("y", 200)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red");
My desired result will be this,
http://bl.ocks.org/mbostock/3680957
The code you provided has several problems:
1. There is a syntax error in definition of zoom (.on("zoom", zoomed);)
2. You haven't defined width and height.
3. zoomed function possibly couldn't be parsed because of wrong line breaks (notice point where you define scale of transformation).
Here is JSFiddle, where zoom works correctly for first element of matrix. Main points is:
// don't forget about width and height
var width = 960,
height = 500;
// make sure that string defining transform attribute is correct. scale isn't a method, but part of string
var zoomed = function () {
svgContainer.attr("transform", "translate("+ d3.event.translate + ")scale(" + d3.event.scale + ")");
};
// don't place semicolon after on("zoom", zoomed)
var zoom = d3.behavior.zoom()
.scaleExtent([1, 8])
.on("zoom", zoomed)
.size([width, height]);
// add zoom behaviour to desired rectangle
var rectangle1 = svgContainer.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red")
.call(zoom);