I am learning this v3 example code and it works fine, but after modify it for v6, I got it running but the second time move the same circle coordinate will not match the mouse position any more!
console.clear()
console.log("======")
//connect_arrow()
drag_move()
function drag_move() {
var w = 600,
h = 400,
r = 25;
var data = [{x: 50, y: 50}, {x: 150, y: 150} ];
var drag = d3.drag();
//drag.subject(Object)
drag.on('start', dragstart)
drag.on('end', dragend)
drag.on('drag', dragmove)
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h)
.style('border','2px solid');
d3.select("body")
.append("div")
.attr("id", "circle_id")
.text("Circle ID: ");
d3.select("body")
.append("div")
.attr("id", "x_var")
.text("X: ");
d3.select("body")
.append("div")
.attr("id", "y_var")
.text("Y: ");
d3.select("#circle_id")
.append("span")
.attr("id", "circle_id_text");
d3.select("#x_var")
.append("span")
.attr("id", "x_coord");
d3.select("#y_var")
.append("span")
.attr("id", "y_coord");
svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('r', r)
.attr('cx', (d)=> d.x)
.attr('cy', (d)=> d.y)
.attr('id', (d,i)=> ("circle_" + i))
.call(drag);
// Functions
var circle;
var circleID;
function dragstart(d){
circle = d3.select(this);
if (circle.attr('id') === 'circle_0'){
circle.style('fill','red');
} else {
circle.style('fill', 'blue');
}
}
function dragend(d){
d3.select('#circle_id_text')
.text(null);
d3.select('#x_coord')
.text(null);
d3.select('#y_coord')
.text(null);
d3.select(this)
.style('fill','black');
}
function dragmove(d,event){
circle = d3.select(this);
circleID = circle.attr('id');
d.x = Math.max(r, Math.min(w-r, event.x))
d.y = Math.max(r, Math.min(h-r, event.y))
d3.select('#circle_id_text').text(circleID);
d3.select('#x_coord').text(d.x);
d3.select('#y_coord').text(d.y);
circle.attr('cx',d.x).attr('cy',d.y)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
dragmove parameter should be (event,d), not (d,event)!
console.clear()
console.log("======")
//connect_arrow()
drag_move()
function drag_move() {
var w = 600,
h = 400,
r = 25;
var data = [{x: 50, y: 50}, {x: 150, y: 150} ];
var drag = d3.drag();
//drag.subject(Object)
drag.on('start', dragstart)
drag.on('end', dragend)
drag.on('drag', dragmove)
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h)
.style('border','2px solid');
d3.select("body")
.append("div")
.attr("id", "circle_id")
.text("Circle ID: ");
d3.select("body")
.append("div")
.attr("id", "x_var")
.text("X: ");
d3.select("body")
.append("div")
.attr("id", "y_var")
.text("Y: ");
d3.select("#circle_id")
.append("span")
.attr("id", "circle_id_text");
d3.select("#x_var")
.append("span")
.attr("id", "x_coord");
d3.select("#y_var")
.append("span")
.attr("id", "y_coord");
svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('r', r)
.attr('cx', (d)=> d.x)
.attr('cy', (d)=> d.y)
.attr('id', (d,i)=> ("circle_" + i))
.call(drag);
// Functions
var circle;
var circleID;
function dragstart(d){
circle = d3.select(this);
if (circle.attr('id') === 'circle_0'){
circle.style('fill','red');
} else {
circle.style('fill', 'blue');
}
}
function dragend(d){
d3.select('#circle_id_text')
.text(null);
d3.select('#x_coord')
.text(null);
d3.select('#y_coord')
.text(null);
d3.select(this)
.style('fill','black');
}
function dragmove(event,d){
circle = d3.select(this);
circleID = circle.attr('id');
d.x = Math.max(r, Math.min(w-r, event.x))
d.y = Math.max(r, Math.min(h-r, event.y))
d3.select('#circle_id_text').text(circleID);
d3.select('#x_coord').text(d.x);
d3.select('#y_coord').text(d.y);
circle.attr('cx',d.x).attr('cy',d.y)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
Related
When I try to brush & zoom a portion of the line chart, some parts of the selected area render outside the chart.
Code and behavior reproduction can be found at this jsbin.
Click & drag to select a portion and zoom in, double click to zoom out.
var svg = d3
.select('body')
.append('svg')
.attr('class', 'chart')
.attr('width', 960)
.attr('height', 500);
var margin = {
top: 40,
right: 40,
bottom: 40,
left: 40
};
var width = +svg.attr('width') - margin.left - margin.right;
var height = +svg.attr('height') - margin.top - margin.bottom;
var g = svg
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var timeParser = d3.timeParse('%Y-%m-%d');
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var brush = d3.brush().on('end', brushended);
var idleTimeout;
var idleDelay = 350;
var x0;
var y0;
var xAxis;
var yAxis;
var line = d3
.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.price);
})
.curve(d3.curveNatural);
var start = new Date();
var end = new Date(start.toDateString());
start.setFullYear(end.getFullYear() - 1);
var startStr = start.toISOString().slice(0, 10);
var endStr = end.toISOString().slice(0, 10);
var url = "https://api.coindesk.com/v1/bpi/historical/close.json?start=" + startStr + "&end=" + endStr;
d3.json(url, function(error, response) {
var data = Object.keys(response.bpi).map(function(date) {
return {
date: timeParser(date),
price: response.bpi[date]
};
});
x0 = d3.extent(data, function(d) {
return d.date;
});
y0 = d3.extent(data, function(d) {
return d.price;
});
x.domain(x0);
y.domain(y0);
xAxis = d3.axisBottom(x);
yAxis = d3.axisLeft(y);
g
.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
g
.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
g
.append('path')
.attr('class', 'line')
.datum(data)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('d', line);
svg
.append('g')
.attr('class', 'brush')
.call(brush);
});
function brushended() {
var s = d3.event.selection;
if (!s) {
if (!idleTimeout) {
return (idleTimeout = setTimeout(idled, idleDelay));
}
x.domain(x0);
y.domain(y0);
} else {
x.domain([s[0][0] - 40, s[1][0] - 40].map(x.invert, x));
y.domain([s[1][1] - 40, s[0][1] - 40].map(y.invert, y));
svg.select('.brush').call(brush.move, null);
}
zoom();
}
function idled() {
idleTimeout = null;
}
function zoom() {
var t = svg.transition().duration(750);
svg
.select('.axis--x')
.transition(t)
.call(xAxis);
svg
.select('.axis--y')
.transition(t)
.call(yAxis);
svg
.select('.line')
.transition(t)
.attr('d', line);
}
.chart {
border: 1px solid #bdbdbd;
box-sizing: border-box;
}
<script src="https://unpkg.com/d3#4.12.2/build/d3.min.js"></script>
That's the expected behaviour. The most common way to deal with that is using a <clipPath>.
For instance, in your case:
var clipPath = g.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
Then, in your path:
g.append('path')
//etc...
.attr("clip-path", "url(#clip)");
Here is the updated JSBin: https://jsbin.com/tatuhipevi/1/edit?js,output
And here the updated S.O. snippet:
var svg = d3
.select('body')
.append('svg')
.attr('class', 'chart')
.attr('width', 960)
.attr('height', 500);
var margin = {
top: 40,
right: 40,
bottom: 40,
left: 40
};
var width = +svg.attr('width') - margin.left - margin.right;
var height = +svg.attr('height') - margin.top - margin.bottom;
var g = svg
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var clipPath = g.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var timeParser = d3.timeParse('%Y-%m-%d');
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var brush = d3.brush().on('end', brushended);
var idleTimeout;
var idleDelay = 350;
var x0;
var y0;
var xAxis;
var yAxis;
var line = d3
.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.price);
})
.curve(d3.curveNatural);
var start = new Date();
var end = new Date(start.toDateString());
start.setFullYear(end.getFullYear() - 1);
var startStr = start.toISOString().slice(0, 10);
var endStr = end.toISOString().slice(0, 10);
var url = "https://api.coindesk.com/v1/bpi/historical/close.json?start=" + startStr + "&end=" + endStr;
d3.json(url, function(error, response) {
var data = Object.keys(response.bpi).map(function(date) {
return {
date: timeParser(date),
price: response.bpi[date]
};
});
x0 = d3.extent(data, function(d) {
return d.date;
});
y0 = d3.extent(data, function(d) {
return d.price;
});
x.domain(x0);
y.domain(y0);
xAxis = d3.axisBottom(x);
yAxis = d3.axisLeft(y);
g
.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
g
.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
g
.append('path')
.attr('class', 'line')
.datum(data)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('d', line)
.attr("clip-path", "url(#clip)");
svg
.append('g')
.attr('class', 'brush')
.call(brush);
});
function brushended() {
var s = d3.event.selection;
if (!s) {
if (!idleTimeout) {
return (idleTimeout = setTimeout(idled, idleDelay));
}
x.domain(x0);
y.domain(y0);
} else {
x.domain([s[0][0] - 40, s[1][0] - 40].map(x.invert, x));
y.domain([s[1][1] - 40, s[0][1] - 40].map(y.invert, y));
svg.select('.brush').call(brush.move, null);
}
zoom();
}
function idled() {
idleTimeout = null;
}
function zoom() {
var t = svg.transition().duration(750);
svg
.select('.axis--x')
.transition(t)
.call(xAxis);
svg
.select('.axis--y')
.transition(t)
.call(yAxis);
svg
.select('.line')
.transition(t)
.attr('d', line);
}
.chart {
border: 1px solid #bdbdbd;
box-sizing: border-box;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
Also, it's a good idea using a <clipPath> in the axes as well.
I have the following segment in one of my d3 graph.
What I want is to display some text in a rectangle.
var values = $('#<%=hdnDtArray.ClientID%>').val();
var data = JSON.parse(values);
margin = {
top: 20,
right: 60,
bottom: 20,
left: 100
};
var width = 900,
height = 350;
var vis = d3.select("#line_chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var parseTime = d3.time.format("%Y.%m.%d").parse;
max_y = 0;
min_y = data[0].close;
var extent = d3.extent(data.map(function(d) {
return d.date
}));
max_x = extent[1];
min = extent[0];
for (i = 0; i < data.length; i++) {
max_y = Math.max(max_y, data[i].close);
min_y = Math.min(min_y, data[i].close);
}
var x = d3.time.scale()
.rangeRound([margin.left, width]);
xScale = x.domain(d3.extent(data, function(d) {
return parseTime(d.date);
}));
yScale = d3.scale.linear().range([height - margin.top, margin.bottom]).domain([0, max_y]),
xAxis = d3.svg.axis()
.scale(xScale),
yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.innerTickSize(-width + margin.left)
.outerTickSize(0)
.tickPadding(10);
vis.append("svg:g")
.attr("class", "x axis")
.style({
'stroke': 'Black',
'fill': 'none',
"stroke-width": 1,
"font-size": "13px"
})
.attr("transform", "translate(0," + (height - margin.bottom) + ")")
.call(xAxis)
.selectAll("text")
.attr("transform", "translate(-10,0) rotate(-40)")
.style("text-anchor", "end");
vis.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width + 120)
.attr("y", height - 10)
.attr("font-weight", "bold");
vis.append("svg:g")
.attr("class", "y axis")
.style({
'stroke': 'Black',
'fill': 'none',
'stroke-width': 1,
"font-size": "13px"
})
.attr("transform", "translate(" + (margin.left) + ",0)")
.call(yAxis);
vis.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("x", margin.left + 5)
.attr("y", margin.top - 2)
.attr("font-weight", "bold");
var line = d3.svg.line()
.x(function(d) {
return xScale(parseTime(d.date));
})
.y(function(d) {
return yScale(d.close);
})
.interpolate("basis");
vis.append('svg:path')
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
var hoverLineGroup = vis.append("g")
.attr("class", "hover-line");
var hoverLine = hoverLineGroup.append("line")
.attr("stroke", "#000000")
.attr("stroke-width", "1px")
.attr("x1", 10).attr("x2", 10)
.attr("y1", 20).attr("y2", height - 20);
var hoverTT = hoverLineGroup.append('text')
.attr("class", "hover-tex capo")
.attr('dy', "0.35em");
var cle = hoverLineGroup.append("circle")
.attr("r", 4.5);
var hoverTT2 = hoverLineGroup.append('text')
.attr("class", "hover-text capo")
.attr('dy', "0.55em");
hoverLineGroup.style("opacity", 1e-6);
var rectHover = vis.append("rect")
.data(data)
.attr("fill", "none")
.attr("width", width)
.attr("height", height);
var hoverCircle = hoverLineGroup.append("circle");
var hoverRect = hoverLineGroup
.append("rect");
vis.on("mouseout", hoverMouseOff)
.on("mousemove", hoverMouseOn);
var bisectDate = d3.bisector(function(d) {
return parseTime(d.date);
}).left;
function hoverMouseOn() {
var mouse_x = d3.mouse(this)[0];
var mouse_y = d3.mouse(this)[1];
var graph_y = yScale.invert(mouse_y);
var graph_x = xScale.invert(mouse_x);
var mouseDate = xScale.invert(mouse_x);
var i = bisectDate(data, mouseDate);
var d0 = data[i - 1]
var d1 = data[i];
var d = mouseDate - d0[0] > d1[0] - mouseDate ? d1 : d0;
hoverRect.attr("class", "y")
.style("fill", "none")
.style("stroke", "black")
.attr('x', mouse_x + 8)
.attr('y', yScale(d.close) - 20)
.attr("width", 200)
.attr("height", 50);
hoverTT.text("Test Text")
.attr("opacity", "1");
hoverTT.attr('x', mouse_x + 23);
hoverTT.attr('y', yScale(d.close));
/*
hoverTT.text("Date: " + d.date);
hoverTT.attr('x', mouse_x + 23);
hoverTT.attr('y', yScale(d.close));
hoverTT2.text("Portfolio Value: " + Math.round(d.close * 100) / 100)
.attr('x', mouse_x + 23)
.attr('y', yScale(d.close) + 10);
*/
hoverLine.attr("x1", mouse_x).attr("x2", mouse_x)
hoverLineGroup.style({
'font-weight': 'bold',
'opacity': 1
});
hoverCircle.attr("class", "y")
.style("fill", "blue")
.style("stroke", "blue")
.attr("r", 4)
.attr('cx', mouse_x)
.attr('cy', yScale(d.close));
}
function hoverMouseOff() {
hoverLineGroup.style("opacity", 1e-6);
}
The text is not visible now. But if I set the "fill" property to "none", then the text becomes visible.
What I want is the background to be non transparent, that's why I made it white.
Still I want the text to be visible.
The problem with your code is the order of the selections.
In an SVG, just like a real painter using ink in a real canvas, what is painted later remains on top. So, if you want the text to be on top of the rectangle (with any fill you want), set the text's selection after the rectangle's selection.
Therefore, in your case, this...
var hoverTT = hoverLineGroup.append('text')
.attr("class", "hover-tex capo")
.attr('dy', "0.35em");
... has to be after this:
var hoverRect = hoverLineGroup
.append("rect");
Here is a demo, the rectangle has a solid white fill. Have a look at the order of the selections:
var svg = d3.select("svg");
var hoverRect = svg.append("rect")
.attr("fill", "white")
.attr("stroke", "firebrick")
.attr("width", 40)
.attr("height", 30)
.attr("opacity", 0);
var hoverText = svg.append("text")
.text("foo")
svg.on("mousemove", function() {
var coords = d3.mouse(this);
hoverRect.attr("x", coords[0] + 15)
.attr("y", coords[1])
.attr("opacity", 1)
hoverText.attr("x", coords[0] + 25)
.attr("y", coords[1] + 20)
})
svg {
border: 1px solid gray;
background-color: gainsboro;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
I am trying to make my tooltip read my data. But it won't. How do i make it read the data?
I do not understand why I can apply text labels in my chart by writing
.text(function(d) { return d; });
while the tooltip won't read it.
var data = {
labels: [
'Trøndelag', 'Innlandet', 'Oslo','Nordland','Sør-Øst', 'Alle distr.',
'Øst', 'Sør-Vest', 'Møre og R.',
'Troms', 'Vest', 'Finnmark',
],
series: [
{
label: 'Svært stor tillit',
values: [32, 29, 29, 22, 27, 27,31,25,24,26,26,20,24]
},
{
label: 'Ganske stor tillit',
values: [55,54,53,58,53,53,49,53,54,51,48,53,48]
},
{
label: 'Verken stor eller liten tillit',
values: [7,12,13,14,14,16,14,15,16,19,19,15]
},
{
label: 'Ganske liten tillit',
values: [4,4,3,2,3,3,3,3,4,5,4,4,7]
},
{
label: 'Svært liten tillit',
values: [1,1,2,3,3,2,1,3,3,1,2,4,6]
},
{
label: 'Vet ikke',
values: [0,0,1,0,0,0,1,1,0,0,0,0,1]
},
{
label: 'Ubesvart',
values: [0,0,0,0,0,0,0,0,0,0,0,0,0]
}
]
};
var margin = {top: 20, right: 5, bottom: 20, left: 5},
width = parseInt(d3.select('.chart').style('width'), 10),
width = width - margin.left - margin.right,
chartHeight = 1310,
groupHeight = barHeight * data.series.length,
gapBetweenGroups = 0,
spaceForLabels =62,
spaceForLegend = 64,
barHeight=14;
var zippedData = [];
for (var i=0; i<data.labels.length; i++) {
for (var j=0; j<data.series.length; j++) {
zippedData.push(data.series[j].values[i]);
}
}
// Color scale
var color = d3.scale.category20c();
var x = d3.scale.linear()
.domain([0, d3.max(zippedData)])
.range([0, width]);
var y = d3.scale.linear()
.range([chartHeight + gapBetweenGroups, 0]);
d3.select(window).on('resize', resize);
function resize (){
width = parseInt(d3.select('.chart').style('width'),10);
width= width - margin.left - margin.right;
x.range([0,width]);
}
var yAxis = d3.svg.axis()
.scale(y)
.tickFormat('')
.tickSize(0)
.orient("left");
// Specify the chart area and dimensions
var chart = d3.select(".chart")
.attr("width", spaceForLabels + width + spaceForLegend)
.attr("height", chartHeight);
// Create bars
var bar = chart.selectAll("g")
.data(zippedData)
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(" + spaceForLabels + "," + (i * barHeight + gapBetweenGroups * (0.5 + Math.floor(i/data.series.length))) + ")";
})
;
var legendPlass = 150;
var tooltip = d3.select("body")
.append("div")
.attr("class", "d3-tip")
.style("position", "absolute")
.style("opacity", 0);
// Create rectangles of the correct width
bar.append("rect")
.attr("fill", function(d,i) { return color(i % data.series.length); })
.attr("class", "bar")
.attr("width", x)
.attr('y', legendPlass )
.attr("height", barHeight - 1)
;
// Add text label in bar
bar.append("text")
.attr("x", function(d) { return x(d) - 3; })
.attr("y", legendPlass + barHeight / 2)
.attr("fill", "red")
.attr("dy", ".35em")
.text(function(d) { return d; });
// Draw labels
bar.append("text")
.attr("class", "label")
.attr("x", function(d) { return - 5; })
.attr("y", legendPlass)
.attr("dy", "1em")
.text(function(d,i) {
if (i % data.series.length === 0)
return data.labels[Math.floor(i/data.series.length)];
else
return ""});
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + spaceForLabels + ", " + -gapBetweenGroups/2 + ")")
.call(yAxis);
//CREATING THE TOOLTIP
chart.selectAll(".bar")
.on("click", function() {
tooltip.style("opacity", 0); })
.on("click", function(d) {
var pos = d3.mouse(this);
tooltip
.transition()
.duration(500)
.style("opacity", 1)
.style("left", d3.event.x + "px")
.style("top", d3.event.y + "px")
.text(function(d) { return d; });
});
// Draw legend
var legendRectSize = 16,
legendSpacing = 4;
var legend = chart.selectAll('.legend')
.data(data.series)
.enter()
.append('g')
.attr('transform', function (d, i) {
var height = legendRectSize + legendSpacing;
var offset = -gapBetweenGroups/2;
var horz = spaceForLegend;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', function (d, i) { return color(i); })
.style('stroke', function (d, i) { return color(i); });
legend.append('text')
.attr('class', 'legend')
.attr('x', legendRectSize + legendSpacing )
.attr('y', legendRectSize - legendSpacing)
.text(function (d) { return d.label; });
You need to append data to it to be able to read. You have this :
var tooltip = d3.select("body")
.append("div")
.attr("class", "d3-tip")
.style("position", "absolute")
.style("opacity", 0);
Needs to be like this :
var tooltip = d3.select("body")
.append("div")
.attr("class", "d3-tip")
.style("position", "absolute")
.style("opacity", 0);
var tooltipWithData = tooltip.data(data).enter();
Then use this later :
tooltipWithData
.transition()
.duration(500)
.style("opacity", 1)
.style("left", d3.event.x + "px")
.style("top", d3.event.y + "px")
.text(function(d) { return d; });
I need help adding a second series of data to my bar chart. Currently I'm populating the bars from the glob key in my dataset. This will be the first series. The second series I would like to be is local.
How do I go about adding that?
Play with my JSFiddle here.
var w = 300;
var h = 200;
var colors = ["#377EB8", "#4DAF4A"];
var dataset = [
{"keyword": "payday loans", "glob": 1500000, "local": 673000, "cpc": "14.11"},
{"keyword": "title loans", "glob": 165000, "local": 165000, "cpc": "12.53" },
{"keyword": "personal loans", "glob": 550000, "local": 301000, "cpc": "6.14"}
];
var data = [[1500000, 165000, 550000],
[673000, 165000, 301000]];
var tdata = d3.transpose(dataset.glob, dataset.local);
var series = 2; // Global & Local
var x0Scale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var x1Scale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {return d.glob;})])
.range([0, h]);
var glob = function(d) {
return d.glob;
};
var cpc = function(d) {
return d.cpc;
};
var commaFormat = d3.format(',');
//SVG element
var svg = d3.select("#searchVolume")
.append("svg")
.attr("width", w)
.attr("height", h);
// Graph Bars
svg.selectAll("rect")
.data(dataset, cpc, glob)
.enter()
.append("rect")
.attr("x", function(d, i){
return x0Scale(i);
})
.attr("y", function(d) {
return h - yScale(d.glob);
})
.attr("width", x0Scale.rangeBand())
.attr("height", function(d) {
return yScale(d.glob);
})
.attr("fill", colors[1])
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("x")) + x0Scale.rangeBand() / 3;
var yPosition = parseFloat(d3.select(this).attr("y")) + yScale;
//Update Tooltip Position & value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#cpcVal")
.text(d.cpc);
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Remove the tooltip
d3.select("#tooltip").classed("hidden", true);
});
//Create labels
svg.selectAll("text")
.data(dataset, glob)
.enter()
.append("text")
.text(function(d) {
return commaFormat(d.glob);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return x0Scale(i) + x0Scale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d.glob) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
It's easiest to create an svg group (<g>) element for each set of data, and then add the individual parts to each group. For example, roughly:
http://jsfiddle.net/WXMwv/1/
var sets = svg.selectAll(".set")
.data(dataset)
.enter().append("g")
.attr("class","set")
.attr("transform",function(d,i){
return "translate(" + x0Scale(i) + ",0)"
})
.on("mouseover", function(d,i) {
//Create x value from data index
var xPosition = parseFloat(x0Scale(i) + x0Scale.rangeBand() / 6);
var yPosition = 0;
//Update Tooltip Position & value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#cpcVal")
.text(d.cpc);
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Remove the tooltip
d3.select("#tooltip").classed("hidden", true);
});
sets.append("rect")
.attr("class","glob")
.attr("width", x0Scale.rangeBand()/2)
.attr("y", function(d) {
return yScale(d.glob);
})
.attr("height", function(d){
return h - yScale(d.glob);
})
.attr("fill", colors[1])
sets.append("rect")
.attr("class","local")
.attr("width", x0Scale.rangeBand()/2)
.attr("y", function(d) {
return yScale(d.local);
})
.attr("x", x0Scale.rangeBand()/2)
.attr("height", function(d){
return h - yScale(d.local);
})
.attr("fill", colors[0])
The text elements are left as an exercise for the reader :)
I'm generating voronoi paths based on some points in forced layout. I'd like to randomly assign these paths 1 of 10 classes and then wrap some of these classes with a clipPath that I can then apply to another element.
Is it possible to wrap svg tags around elements using d3 as opposed to appending?
Or is it even possible to use multiple paths generated by d3 in a clipPath?
Thank you for your help,
w = $(window).width();
h = $(window).height();
function ranNum(){
return Math.floor((Math.random()*10)+1);
}
$('#grid').css('height', h);
var vertices = d3.range(50).map(function(d) { return {x: d.x, y: d.y}; });
//console.log(vertices);
links = [];
voronoiVertices = [];
var force = d3.layout.force()
.nodes(vertices)
.size([w, h])
.linkDistance(60)
.charge(-900)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
//path gradient
var defs = svg.append('defs')
var radialGradient = defs.append('radialGradient')
.attr('id', 'pathGrad')
.attr('cx', '50%')
.attr('cy', '50%')
.attr('r', '50%')
.attr('fx', '50%')
.attr('fy', '50%');
var stop1 = radialGradient.append('stop')
.attr('offset', '.2')
.attr('stop-color', '#a8a8a8');
var stop2 = radialGradient.append('stop')
.attr('offset', '1')
.attr('stop-color', '#0000000');
//path dropShadow
var filterShadow = defs.append('filter')
.attr('id', 'pathShadow')
.attr('height', '130%');
var gCir = svg.append('g')
.attr("class", "gCircle");
var gPath = svg.append('g')
.attr("class", "gPath");
var circle = svg.selectAll("circle");
var path = gPath.selectAll("path")
.data(d3.geom.voronoi(vertices))
.enter().append("path")
.attr("fill", "url(#pathGrad)");
//wraps path with random class after generation
$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
var clip = defs.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", '900px')
.attr("height", '900px');
var gClip = svg.append("svg:g")
.attr('clip-path', 'url(#clip)');
/*
var clip = gClip.append("svg:image")
.attr("class", "circle")
.attr("xlink:href", "clip.jpg")
.attr("x", "0px")
.attr("y", "0px")
.attr("width", w)
.attr("height", h);
*/
var selectPath = d3.selectAll('.path-10');
console.log(selectPath);
function tick() {
voronoiVertices = vertices.map(function(t){return [t.x, t.y]})
path = path.data(d3.geom.voronoi(voronoiVertices))
path.enter().append("path")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
path.attr("fill", "url(#pathGrad)")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
circle = circle.data(vertices)
circle.enter().append("circle")
.call(force.drag)
.attr("r", 0)
.attr('class', function(d) { return d.index; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.transition().duration(5000).attr("r", 5);
circle.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
});
There is a strange mix of jQuery and D3 in this. Try to do the things in D3 when you work with it. For example I'd rather do this:
.attr("class", function(d){return 'path-'+Math.floor((Math.random()*10)+1))});
than this:
$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
d3 has an exotic but smart way of doing things, it's better to learn this update pattern well before doing something serious.
And here is the working code:
w = 1200;
h = 500;
function ranNum(){
return Math.floor((Math.random()*10)+1);
}
var vertices = d3.range(50).map(function(d) { return {x: d.x, y: d.y}; });
//console.log(vertices);
links = [];
voronoiVertices = [];
var force = d3.layout.force()
.nodes(vertices)
.size([w, h])
.linkDistance(60)
.charge(-900)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
//path gradient
var defs = svg.append('defs')
var radialGradient = defs.append('radialGradient')
.attr('id', 'pathGrad')
.attr('cx', '50%')
.attr('cy', '50%')
.attr('r', '50%')
.attr('fx', '50%')
.attr('fy', '50%');
var stop1 = radialGradient.append('stop')
.attr('offset', '.2')
.attr('stop-color', '#a8a8a8');
var stop2 = radialGradient.append('stop')
.attr('offset', '1')
.attr('stop-color', '#0000000');
//path dropShadow
var filterShadow = defs.append('filter')
.attr('id', 'pathShadow')
.attr('height', '130%');
var gCir = svg.append('g')
.attr("class", "gCircle");
var gPath = svg.append('g')
.attr("class", "gPath");
var circle = svg.selectAll("circle");
var path = gPath.selectAll("path")
.data(d3.geom.voronoi(vertices))
.enter().append("path")
.attr("fill", "url(#pathGrad)");
//wraps path with random class after generation
//$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
var clip = defs.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", '900px')
.attr("height", '900px');
var gClip = svg.append("svg:g")
.attr('clip-path', 'url(#clip)');
function tick() {
voronoiVertices = vertices.map(function(t){return [t.x, t.y]})
path = path.data(d3.geom.voronoi(voronoiVertices))
path.enter().append("path")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
path.attr("fill", "url(#pathGrad)")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
circle = circle.data(vertices)
circle.enter().append("circle")
.call(force.drag)
.attr("r", 0)
.attr('class', function(d) { return d.index; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.transition().duration(5000).attr("r", 5);
circle.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
Good luck!