I am working on an SVG, and came across the issue of trying to share different colors on separate sides of an arc. I have created this example to help go through this problem I'm having:
const svg = d3.select('#chart')
.attr("viewBox", "0, 0, " + 50 + ", " + 47 + "")
// clip to cut off circle
svg.append("defs").append("clipPath")
.attr("id", "cut-off")
.append("rect")
.attr("width", 44)
.attr("height", 23.75)
.attr("x", 25)
.attr("y", 42.25)
.attr("transform", "translate(" + -22 + "," + -28.5 + ")");
svg.append("circle")
.attr("cx", 25)
.attr("cy", 4.75)
.attr("r", 23.75)
.attr("fill", "orange")
.attr("opacity", 0.25)
.attr("stroke", 'black')
.attr("stroke-width", 0.25)
.attr("clip-path", "url(#cut-off)");
svg.append("rect")
.attr("x", 3)
.attr("y", 0)
.attr("width", 44)
.attr("height", 14)
.attr("fill", 'blue')
.attr("opacity", 0.2)
// here's the one
svg.append("rect")
.attr("x", 0)
.attr("y", 14)
.attr("width", 17)
.attr("height", 20)
.attr("fill", 'green')
.attr("opacity", 0.2)
#chart {
width: 500px;
height: 470px;
border: 2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg
version="1.1"
baseProfile="full"
xmlns="http://www.w3.org/2000/svg"
id="chart"></svg>
The rectangle that is currently shaded green I would like to instead shade two different colors. I would like the area inside of the arc (the area currently overlapping orange and green) to be set to one color, and the area outside of the arc (only green) to be set to another color. I think this probably requires using 2 rects, and cutting off the rects based on the circle, but I'm not sure how to do this.
Note: The way the arc was drawn was by drawing a circle, and then clipping a rectangle over the parts of the circle I don't want shown. Given that I'm trying to fill color differently based on what side of the circle's line the fill color is on, I'm not sure if this is the best way to draw the arc.
Thanks in advance for help with this!!
You can use cloneNode on your green rect and set an clip-path attribute on the clonedNode. Point the clip-path url to a circle with the same d attribute as your original circle defined under defs tag.
If you provide a fiddle, I can perhaps help.
I used this link here - How to calculate the SVG Path for an arc (of a circle) - to solve the problem. Anybody trying to draw circles (or parts of a circle) as a path using svg arc should check this link.
Related
I have a stacked bar chart. You can see the fiddle here.
I have drawn a line that is actually a horizontal line leveling the current stack of a bar. Below is the code.
.on('mouseenter', function (actual, i) {
const y = yScale(actual.y + actual.y0);
debugger;
line = svg.append('line')
.attr('id', 'limit')
.attr('x1', 0)
.attr('y1', y)
.attr('x2', width)
.attr('y2', y);
And the output is,
Here, you can see that, for the monthly data, the line is correct. But for the quarterly data, the line is a bit above the actual position. And for the yearly data, the line is not showing.
What is the problem here?
And how can I show a tooltip along with the line?
Looking at the fiddle, it seems that the scale you are using to render the rectangles is not yScale, but actually just y
Changing the following fragment:
const y = yScale(actual.y + actual.y0)
line = svg.append('line')
.attr('id', 'limit')
.attr('x1', 0)
.attr('y1', y)
.attr('x2', width)
.attr('y2', y);
To:
const limitY = y(actual.y + actual.y0);
line = svg.append('line')
.attr('id', 'limit')
.attr('x1', 0)
.attr('y1', limitY)
.attr('x2', width)
.attr('y2', limitY);
Adjusts the position of the line to match the rectangles, because it is now using the same scale that the bars and the axis are using.
Regarding the tooltip, I see there is a rectangle you want to append:
line.append("rect")
.attr("width", "10px")
.attr("height", "10px")
.style("fill", "red");
However, a <line> can not have a <rect> element inside. What you actually want is to add the <rect> to the <svg>:
svg.append("rect")
.attr('id', 'myId') // Also give it an Id for clean up
.attr("width", "10px")
.attr("height", "10px")
.attr("y", limitY) // The limitY is available to position the tooltip under the line
.style("fill", "red");
Don't forget to remove it in the mouseout event, as you are doing with <line#limit>:
.on("mouseout", function() {
svg.selectAll('#limit').remove();
// clean the rectangle on mouseout:
svg.selectAll('#myId').remove();
})
You can use the same premise of the above <rect> in a <g> element to create a full tooltip with text and background, but coding it is outside of the scope of this answer. I hope the above explanations can give you a direction.
Here is a fiddle with the changes.
Hi basically svg coordinate space growing from top to bottom.what should i change to growing from bottom to top as like Graph Coordinate Space. the below code which draws top to bottom in left side how i need change to bottom to top
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500);
//Create and append line
svg.append("line")
.attr("x1", 100)
.attr("x2", 500)
.attr("y1", 50)
.attr("y2", 250)
.attr("stroke", "black")
You can apply a simple transform to the main SVG element to flip the coordinates:
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500)
.attr( 'transform', 'scale( 1 -1 )' );
MDN # scale()
The attached fiddle shows that on zoom the blue rectangles resize with the scale as expected but the yellow rectangles don't! The main difference is that the yellow rectangles were added to a 'g' element with text included. Any ideas why?
https://jsfiddle.net/sjp700/u6rj20jc/1/
var group = svg.selectAll(".rectangle")
.data(data);
gEnter = group.enter()
.append("g")
.attr("class", "rectangle")
.attr("fill", "yellow")
.attr("transform", function (d) { return "translate(" + x(d.start) + "," + y(d.finish) + ")"; });
gEnter.append("rect")
.attr("class", "rectband")
.merge(gEnter)
.attr("width", 50)
.attr("height", 18)
//.attr("rx", 10)
//.attr("ry", 10)
.style("opacity", .5) // set the element opacity
.style("stroke", "black");
Your yellow rectangles and text is not contained in an element that the zoom is applied to. Simple fix is to append them to gMain (which is the element on which the zoom is applied):
var group = gMain
.selectAll(".rectangle")
.data(data);
Updated fiddle here.
I have a D3JS scatter plot, with circles, and a white background.
I want to add 2 horizontal rectangles, all over the graph on X-axis, BEHIND the circles, and between certain values on Y axis.
The 2 grey areas I need are the following (made w/ Photoshop) :
The first rectangle is between 10 and 20 on X axis, and the second one is between 30 and 35.
I found this thread : How to add a highlight mark/area to a chart?
But I don't know how to adapt it for my horizontal example as it is not as simple as a rotation of this vertical solution.
You could probably achieve the same effect by creating two SVG rectangles with some opacity. Of course you'd want to fill in the correct data or generate it from your scales.
var rectOne = svg.append("rect")
.attr("x", 10)
.attr("y", 10)
.attr("width", 50)
.attr("height", 100)
.style("fill-opacity", 0.5)
.style("fill", "grey")
var rectTwo = svg.append("rect")
.attr("x", 10)
.attr("y", 10)
.attr("width", 50)
.attr("height", 100)
.style("fill-opacity", 0.5)
.style("fill", "grey")
Is it possible to make a treemap in d3 with the background of each rectangle be an image? I am looking for something similar to what was done in Silverlight here, but for d3. If it is possible, are there any recommended tutorials that walk through the process of connecting the background to an image?
Yes, there are several ways of using images in SVGs. You probably want to define the image as a pattern and then use it to fill the rectangle. For more information, see e.g. this question (the procedure is the same regardless of the element you want to fill).
In D3 code, it would look something like this (simplified).
svg.append("defs")
.append("pattern")
.attr("id", "bg")
.append("image")
.attr("xlink:href", "image.jpg");
svg.append("rect")
.attr("fill", "url(#bg)");
Its important to note, that the image needs to have width, height attributes
chart.append("defs")
.append('pattern')
.attr('id', 'locked2')
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', 4)
.attr('height', 4)
.append("image")
.attr("xlink:href", "locked.png")
.attr('width', 4)
.attr('height', 4);
Using patterns to add an image in a rectangle can make your visualisation quite slow.
You can do something like that instead, this is the code I used for my rectangular nodes into a force layout, I wanted to put rectangles filled by an image as nodes:
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node");
node.append("rect")
.attr("width", 80)
.attr("height", 120)
.attr("fill", 'none')
.attr("stroke", function (d) {
return colors(d.importance);
});
node.append("image")
.attr("xlink:href", function (d) { return d.cover;})
.attr("x", 2)
.attr("width", 76)
.attr("height", 120)
.on('dblclick', showInfo);