I'm attempting to add a drop-shadow to an svg image with D3. My code is below and an example block is posted here:
http://blockbuilder.org/anonymous/0751a819af7570b767ff
var imgurl = 'http://www.logo-designer.co/wp-content/uploads/2015/08/2015-Penn-State-University-logo-design-4.png';
var margin = {
top: 20,
right: 10,
bottom: 20,
left: 10
};
var width = 960 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var svg = d3.select('body').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', "translate(" + margin.left + "," + margin.top + ")");
var defs = svg.append('defs');
var clipPath = defs.append('clipPath')
.attr('id', 'clip-circle')
.append('circle')
.attr('r', 140)
.attr('cy', height / 2 - 10)
.attr('cx', width / 2 - 10);
var filter = defs.append('filter')
.attr('id', 'drop-shadow')
.attr('height', '130%');
filter.append('feGaussianBlur')
.attr('in', 'SourceAlpha')
.attr('stdDeviation', 5)
.attr('result', 'blur');
filter.append('feOffset')
.attr('in', 'blur')
.attr('dx', 5)
.attr('dy', 5)
.attr('result', 'offsetBlur');
var feMerge = filter.append('feMerge');
feMerge.append('feMergeNode')
.attr('in', 'offsetBlur')
feMerge.append('feMergeNode')
.attr('in', 'SourceGraphic');
svg.append('image')
.attr('x', width / 2 - 260)
.attr('y', height / 2 - 204)
.attr('height', 408)
.attr('width', 520)
.attr('xlink:href', imgurl)
.attr('filter', 'url(#drop-shadow)')
.attr('clip-path', 'url(#clip-circle)');
I have successfully created a circular image with the above code, but cannot seem to get the drop-shadow to appear. Ideally I would like this drop-shadow to completely surround the circular image, but first I need to get it to appear at all.
UPDATE: If I remove the below line of code I see the drop-shadow, but it appears around the original image. My aim is to make this drop-shadow appear around the circular image that results from clip-path:
.attr('clip-path', 'url(#clip-circle)')
Apply the shadow to a parent element so it doesn't get clipped.
var imgurl = 'http://www.logo-designer.co/wp-content/uploads/2015/08/2015-Penn-State-University-logo-design-4.png';
var margin = {
top: 20,
right: 10,
bottom: 20,
left: 10
};
var width = 960 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var svg = d3.select('body').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', "translate(" + margin.left + "," + margin.top + ")");
var defs = svg.append('defs');
var clipPath = defs.append('clipPath')
.attr('id', 'clip-circle')
.append('circle')
.attr('r', 140)
.attr('cy', height / 2 - 10)
.attr('cx', width / 2 - 10);
var filter = defs.append('filter')
.attr('id', 'drop-shadow')
.attr('height', '130%');
filter.append('feGaussianBlur')
.attr('in', 'SourceAlpha')
.attr('stdDeviation', 5)
.attr('result', 'blur');
filter.append('feOffset')
.attr('in', 'blur')
.attr('dx', 5)
.attr('dy', 5)
.attr('result', 'offsetBlur');
var feMerge = filter.append('feMerge');
feMerge.append('feMergeNode')
.attr('in', 'offsetBlur')
feMerge.append('feMergeNode')
.attr('in', 'SourceGraphic');
var g = svg.append('g')
.attr('filter', 'url(#drop-shadow)');
g.append('image')
.attr('x', width / 2 - 260)
.attr('y', height / 2 - 204)
.attr('height', 408)
.attr('width', 520)
.attr('xlink:href', imgurl)
.attr('clip-path', 'url(#clip-circle)');
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body></body>
Related
I am currently trying to draw a map of the US with the counties-albers-10m.json file found on the topojson repo. I initially got a solid rectangle and, after changing fill to none, I am getting specks here and there. Going through stack, I found that the winding order may be wrong so I incorporated turf.js, but nothing is really changing. Here is the code:
var margin = {top: 0, left: 0, right: 0, bottom: 0},
height = 600 - margin.top - margin.bottom,
width = 1200 - margin.left - margin.right;
var svg = d3.select("#map")
.append("svg")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.left + margin.right)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top +")");
d3.json("counties-albers-10m.json").then(function(data) {
console.log(data);
var projection = d3.geoAlbersUsa();
var path = d3.geoPath()
.projection(projection);
var counties = topojson.feature(data, data.objects.counties).features
console.log(counties)
counties.forEach(function(feature) {
feature.geometry = turf.rewind(feature.geometry, {reverse:true});
})
svg.selectAll(".county")
.data(counties)
.enter().append("path")
.attr("class", "county")
.attr("fill", "none")
.attr("stroke", "black")
.attr("d", path);
})
The dreaded black box
I basically have a barchart, and I want to put 2 texts. I want to put a title at a distance from my graph (top 30px for example). and I want to put a subtitle at a distance from the graph, so that it is located in the bottom right (bottom 30px for example)
No matter what method I use, my texts are always above my graphic, I wish I had separation margins between graphic and texts.
Desired effect:
what am I doing wrong?
this is my live code:
https://plnkr.co/edit/mRjyYN9nWiMWYRzj7Gtf?p=preview
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
//set title
svg.append("text")
.attr("x", (width / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "18px")
.text("title");
//set bottom-right text
svg.append("text")
.attr("x", 960 - 80)
.attr("y", 500 - 30)
.attr("text-anchor", "middle")
.style("font-size", "14px")
.text("subtitle");
current problem using your code (user lemming):
Normally you subtract the margins from the dimension of the div you are rendering to, like this, assuming the div is 800 x 600
// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 30, left: 50},
width = 800 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
Does that make sense?
I'm new to D3. I want to display a sentence on screen, I have the words in an array, when i run the code all words are overlapping, I want them displayed in a natural way like normal text.
var margin = {top: 20, right: 20, bottom: 30, left: 20},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var svgContainer = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var cars=["this","is","a","normal","sentence"];
var text = svgContainer.selectAll("text")
.data(cars)
.enter()
.append("text");
//Add SVG Text Element Attributes
var textLabels = text
.text( function (d) { return d; })
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("fill", "red");
One simple way is to use tspan elements within a single text element.
var margin = {top: 20, right: 20, bottom: 30, left: 20},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var svgContainer = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var text = svgContainer.append("text");
var cars=["this","is","a","normal","sentence"];
var text = text.selectAll("tspan")
.data(cars)
.enter()
.append("tspan");
//Add SVG Text Element Attributes
var textLabels = text
.text( function (d) { return d + ' '; })
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("fill", "red");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Lost on this especially since I've done it successfully before. The code below is within a function called on a click. This first example works just fine:
y.domain([1, get_max(data)]);
svg.select('.y.axis')
.call(yAxis);
svg.selectAll('.bars')
.attr('y', function (d) { return y(d); })
.attr('height', function (d) { return height - y(d); });
This second example doesn't do anything:
y.domain([1, get_max(data)]);
svg.select('.y.axis')
.transition()
.duration(80)
.call(yAxis);
svg.selectAll('.bars')
.transition()
.duration(80)
.attr('y', function (d) { return y(d); })
.attr('height', function (d) { return height - y(d); });
No javascript errors are produced. It simply doesn't do anything.
Any help would be greatly appreciated. Thank you!
Note: get_max(data) is a special function to get the max of some very oddly formatted data. When I replace it with a hard coded value of 10,000 the problem persists. Again it works fine until I add the transition.
EDIT:
function render(parent, data, brands){
var time_format = parent.attr('data-chart-size') > 3 ? '%b %e' : '%e',
margin = { top: 30, right: 20, bottom: 21, left: 45 },
width = parent.width() - margin.left - margin.right - 110; // -110 for the legends on the right side
height = 205 - margin.top - margin.bottom;
var x = d3.time.scale().domain([ML._dates.start(), ML._dates.end()]).range([0, width]);
y = d3.scale.log().clamp(true).range([height, 1]);
var xAxis = d3.svg.axis().scale(x).orient('bottom')
.tickFormat(d3.time.format(time_format)).tickSize(0).tickPadding(8);
yAxis = d3.svg.axis().scale(y).orient('left').ticks(5, 's').tickSize(0);
svg = d3.select(parent.get(0)).append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var brands_length = brands.length,
values_length = data.posts[brands[0]].values.length,
bar_width_adjustment = 60 / values_length,
bar_width = ((width / values_length) / brands_length) - bar_width_adjustment;
range_band = width / values_length;
y.domain([1, get_max(data)]);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(' + (bar_width * 2) + ',' + height + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(0, 0)')
.call(yAxis);
// BUILD THE BARS AND TOOLTIP HIGHLIGHT AREA
for(var brands_loop = 0; brands_loop < brands_length; ++brands_loop){
svg.selectAll('.chart-hover')
.data(data.posts[brands[brands_loop]].values)
.enter().append('rect')
.attr('width', width / values_length)
.attr('x', function (d, i) { return i * range_band - ((range_band - bar_width * 3) / 2); })
.attr('y', 1)
.attr('height', height - y(y.domain()[1]))
.attr('transform', 'translate(' + ((bar_width * (brands_loop + 1) - (bar_width / 2)) + ', 0)'))
.attr('data-index', function (d, i) { return i; })
.attr('class', 'chart-hover')
.style('opacity', 0.01);
svg.selectAll('.bar-' + brands_loop)
.data(data.posts[brands[brands_loop]].values)
.enter().append('rect')
.attr('data-legend-listener-brand', brands[brands_loop])
.attr('data-legend-listener-metric', 'posts')
.attr('data-hover-dispatcher-index', function (d, i) { return i; })
.attr('width', bar_width)
.attr('x', function (d, i) { return i * range_band; })
.attr('y', function (d) { return y(d); })
.attr('height', function (d) { return height - y(d); })
.attr('transform', 'translate(' + ((bar_width * (brands_loop + 1) - (bar_width / 2)) + ', 0)'))
.attr('class', 'posts bars bar-' + brands_loop);
// POPULATE LEGEND TEXTS FOR POSTS, EXPOSURE AND ENGAGEMENT
$('.brand-' + (brands_loop + 1))
.text(data.posts[brands[brands_loop]].label)
.attr('data-legend-brand', brands[brands_loop])
.attr('data-legend-listener-brand', brands[brands_loop])
.prev('i')
.attr('data-legend-brand', brands[brands_loop])
.attr('data-legend-listener-brand', brands[brands_loop]);
}
etc. etc.
I have this d3 code, but I'm having trouble to align the bars to the x-axis.
I want the number, which represents an hour (range 0h-23h) to appear in the middle of the number of hours, which is the y-value.
The variable times gets instantiated with 24 values (indexes 0 to 23 h).
Any response or ideas are welcome.
What it looks like now;
http://i.imgur.com/PLu7Uv2.png?1
var times = buildTimeTable(data);
var margin = {top: 20, right: 10, bottom: 20, left: 10};
var width = 500- margin.left - margin.right, height = 200- margin.top - margin.bottom;
var x = d3.scale.linear().domain([0, 23]).range([0, width]);
var y = d3.scale.linear().domain([0, Math.max.apply(Math, times)]).range([height, 0]);
var xAxis = d3.svg.axis().orient("bottom").scale(x).ticks(24);
var yAxis = d3.svg.axis().orient("left").scale(y);
d3.select("#statistics-hours > svg").remove();
var svg = d3.select("#statistics-hours").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");;
var bar = svg.selectAll(".bar").data(times).enter().append("rect")
.attr("class", "bar")
.attr("width", (width)/times.length - 5)
.attr("height", function(d){return height - y(d);})
.attr("x", function(d, i){return i * (width/times.length);})
.attr("y", function(d){return y(d);})
.attr("fill", "steelblue");
svg.append("g").attr("class", "axis").attr("transform", "translate("+ margin.left +"," + (height) + ")").call(xAxis);
svg.append("g").attr("class", "axis").attr("transform", "translate(" + (margin.left) + ",0)").call(yAxis);
You probably want to consider using an ordinal scale.
var x = d3.scale.ordinal().domain(d3.range[0,24]).rangeRoundBands([0,width],.1)
Now, x is just
.attr('x',function(d){return x(d);})
And the width is just...
.attr('width',function(d){return x.rangeBand();})