How to setup svg to change position when resizing the window? - javascript

In a browser window I have an svg containing an image.
I also put some circles in this page.
When I resize the window, the image resizes correct but the circles just stay on their absolute position.
What is the best way to set this up?
If possible, the circles should not resize but change their position.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="robots" content="noindex, nofollow">
<meta name="googlebot" content="noindex, nofollow">
<style>
html,body{padding:0px; margin:0px; height:100%; width:100%;}
</style>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<title>Test</title>
<script type='text/javascript'>//<![CDATA[
window.onload=function()
{
function click()
{
// Ignore the click event if it was suppressed
if (d3.event.defaultPrevented) return;
// Extract the click location
var point = d3.mouse(this)
, p = {x: point[0], y: point[1] };
//Append the group
var newGroup = d3.select("svg").append("g")
.attr("transform", "translate(" + p.x + "," + p.y + ")")
.attr("drgg", "")
.style("cursor", "pointer")
.on("mouseup", selremove)
.call(drag);
//Append the circle
var newCircle = newGroup.append("circle")
.attr("r", "25")
.attr("class", "dot")
.style("stroke", "#999999")
.style("fill", "#66B132")
.attr("opacity", 0.8);
//Append the text
var newText = newGroup.append("text")
.text("43")
.style("fill", "#FFFFFF")
.style("font-family", "Arial")
.style("font-size", "24px")
.style("text-anchor", "middle")
.style("alignment-baseline", "central")
.style("readonly", "true");
}
//Create the SVG
var svg = d3.select("body").append("svg")
.attr("width", "100%")
.attr("height", "100%")
.on("click", click);
//Add a background to the SVG
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.style("stroke", "#999999")
.style("fill", "#F6F6F6")
//Add a Background-Picture
var pPic = d3.select("body").select("svg").append("image")
.attr("opacity", 1.0)
.attr("width", "100%")
.attr("height", "100%")
.attr("preserveAspectRatio", "xMidyMid")
.attr("xlink:href", "https://m.bmw.de/content/dam/bmw/common/all-models/m-series/x6m/2014/model-card/X6-M-F86_ModelCard.png")
//Move or delete
function selremove() {
if (d3.select(this).attr("drgg") == "")
{
d3.select(this).remove();
}
else
{
d3.select(this).attr("drgg", "");
}
}
function showinfo() {
//d3.select(this).attr("fill", "#000000");
var point = d3.mouse(this)
, p = {x: point[0], y: point[1] };
var newRect = svg.append("rectangle")
.attr("transform", "translate(" + p.x + "," + p.y + ")")
.attr("width", "25")
.attr("height", "25")
.style("stroke", "#999999")
.style("fill", "#FFFA83")
.attr("opacity", 1.0);
}
// Define drag beavior
var drag = d3.behavior.drag()
.on("drag", dragmove);
function dragmove()
{
var x = d3.event.x;
var y = d3.event.y;
d3.select(this)
.attr("transform", "translate(" + x + "," + y + ")")
.attr("drgg", "1");
}
}//]]>
</script>
</head>
<body>
<script>
// tell the embed parent frame the height of the content
if (window.parent && window.parent.parent){
window.parent.parent.postMessage(["resultsFrame", {
height: document.body.getBoundingClientRect().height,
slug: "None"
}], "*")
}
</script>
</body>
</html>

I first thought this could be achieved with relative units, but the changing aspect ratio of the SVG gets you into hot waters. So the best approach seems to come with clamping the SVG viewBox to the original image dimensions. These need to be known beforehand, as SVGImageElement is not able to extract them from the image source itself.
The price to pay for this is that the overlay circles have to be resized every time the window is resized.
This example does not concern itself with the drag functionality.
//an event counter
var counter = 0;
//image metadata
var pData = {
url: "https://m.bmw.de/content/dam/bmw/common/all-models/m-series/x6m/2014/model-card/X6-M-F86_ModelCard.png",
width: 890,
height: 501
}
//Create the SVG with viewBox at native image size
var svg = d3.select("body").append("svg")
.attr("xmlns:xlink", "http://www.w3.org/1999/xlink")
.attr("width", "100%")
.attr("height", "100%")
.attr('viewBox', "0 0 " + pData.width + " " + pData. height)
.attr("preserveAspectRatio", "xMidyMid")
.on("click", click);
var defs = svg.append("defs");
//Add a Background-Picture
var pPic = d3.select("body").select("svg").append("image")
.attr("width", "100%")
.attr("height", "100%")
.attr("xlink:href", pData.url)
function click() {
// Ignore the click event if it was suppressed
if (d3.event.defaultPrevented) return;
// Extract the click location relative to SVG
var point = d3.mouse(this);
// get SVG scaling
var ctm = svg.node().getScreenCTM(),
scale = "scale(" + (1 / ctm.a) + "," + (1 / ctm.d) + ")";
// Unique id
var id = "dot" + counter++;
//Append the group offscreen
var newGroup = defs.append("g")
.attr("id", id)
.attr("transform", scale);
//Append the circle
var newCircle = newGroup.append("circle")
.attr("r", "25")
.attr("class", "dot")
.style("stroke", "#999999")
.style("fill", "#66B132")
.attr("opacity", 0.8);
//Append the text
var newText = newGroup.append("text")
.text("43")
.style("fill", "#FFFFFF")
.style("font-family", "Arial")
.style("font-size", "24px")
.style("text-anchor", "middle")
.style("alignment-baseline", "central")
.style("readonly", "true");
// indirect rendering with a new viewport
svg.append("use")
.attr("xlink:href", "#" + id)
.attr("x", point[0])
.attr("y", point[1]);
}
// adjust group sizes on window resize
var resize;
window.addEventListener("resize", function() {
clearTimeout(resize);
resize = setTimeout(function () {
var ctm = svg.node().getScreenCTM();
// select all groups before they are repositioned
defs.selectAll('g').attr("transform", "scale(" + (1 / ctm.a) + "," + (1 / ctm.d) + ")");
}, 100);
});

Related

How to add svg rectangles as background behind axis tick labels in d3js if ticks are inside chart

I have drawan a chart using d3.
Then Put axises labels inside Now wanted is labels not seems to cross tick line. But its Label background should be white. for that I found adding rectangle in svg only works.
How to add svg rectangles as background behind axis tick labels in d3js if ticks are inside chart.
Here is existing code
<!DOCTYPE html>
<html>
<head>
<meta name="description" content="With 0 removal">
<title>
D3.js | D3.axis.tickSizeInner() Function
</title>
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js">
</script>
<style>
svg text {
fill: green;
font: 15px sans-serif;
text-anchor: center;
}
</style>
</head>
<body>
<script>
var width = 400,
height = 400;
const margin = 5;
const padding = 5;
const adj = 30;
var svg = d3.select("body")
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "-" +
adj + " -" +
adj + " " +
(width + adj * 3) + " " +
(height + adj * 3))
.style("padding", padding)
.style("margin", margin)
.attr("width", width)
.attr("height", height);
var xscale = d3.scaleLinear()
.domain([0, 10])
.range([0, width]);
var yscale = d3.scaleLinear()
.domain([0, 1])
.range([height, 0]);
var x_axis = d3.axisBottom(xscale)
.tickSizeInner(-(height))
.tickSizeOuter(0);
svg.append("g")
.classed('axis axis-x', true)
.attr("transform", "translate(0, " + height + ")")
.call(x_axis)
.selectAll(".tick text")
.attr("transform", "translate(0,-20)");
svg.select('.axis-x')
.selectAll('.tick')
.each(function(d) {
console.log(d)
if (Number(d) === 0) {
d3.select(this).remove();
}
})
var y_axis = d3.axisLeft(yscale)
.tickSize(-width);
svg.append("g")
.classed('axis axis-y', true)
.call(y_axis)
.selectAll(".tick text")
.attr("transform", "translate(30,0)");
svg.select('.axis-y')
.selectAll('.tick')
.each(function(d) {
console.log(d)
if (Number(d) === 0) {
d3.select(this).remove();
}
})
</script>
</body>
</html>
Links for JSBIN
https://jsbin.com/locosun/1/edit?html,output
The following code will add white backgrounds to the tick labels:
svg.select('.axis-y')
.selectAll('g.tick')
.insert('rect', 'text')
.attr('x', 3)
.attr('y', -10)
.attr('width', 30)
.attr('height', 20)
.style('fill', 'white')
svg.select('.axis-x')
.selectAll('g.tick')
.insert('rect', 'text')
.attr('x', -15)
.attr('y', -20)
.attr('width', 30)
.attr('height', 18)
.style('fill', 'white')
<!DOCTYPE html>
<html>
<head>
<meta name="description" content="With 0 removal">
<title>
D3.js | D3.axis.tickSizeInner() Function
</title>
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js">
</script>
<style>
svg text {
fill: green;
font: 15px sans-serif;
text-anchor: center;
}
</style>
</head>
<body>
<script>
var width = 400,
height = 400;
const margin = 5;
const padding = 5;
const adj = 30;
var svg = d3.select("body")
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "-" +
adj + " -" +
adj + " " +
(width + adj * 3) + " " +
(height + adj * 3))
.style("padding", padding)
.style("margin", margin)
.attr("width", width)
.attr("height", height);
var xscale = d3.scaleLinear()
.domain([0, 10])
.range([0, width]);
var yscale = d3.scaleLinear()
.domain([0, 1])
.range([height, 0]);
var x_axis = d3.axisBottom(xscale)
.tickSizeInner(-(height))
.tickSizeOuter(0);
svg.append("g")
.classed('axis axis-x', true)
.attr("transform", "translate(0, " + height + ")")
.call(x_axis)
.selectAll(".tick text")
.attr("transform", "translate(0,-20)");
svg.select('.axis-x')
.selectAll('.tick')
.each(function(d) {
console.log(d)
if (Number(d) === 0) {
d3.select(this).remove();
}
})
var y_axis = d3.axisLeft(yscale)
.tickSize(-width);
svg.append("g")
.classed('axis axis-y', true)
.call(y_axis)
.selectAll(".tick text")
.attr("transform", "translate(30,0)");
svg.select('.axis-y')
.selectAll('.tick')
.each(function(d) {
console.log(d)
if (Number(d) === 0) {
d3.select(this).remove();
}
})
svg.select('.axis-y')
.selectAll('g.tick')
.insert('rect', 'text')
.attr('x', 3)
.attr('y', -10)
.attr('width', 30)
.attr('height', 20)
.style('fill', 'white')
svg.select('.axis-x')
.selectAll('g.tick')
.insert('rect', 'text')
.attr('x', -15)
.attr('y', -20)
.attr('width', 30)
.attr('height', 18)
.style('fill', 'white')
</script>
</body>
</html>

D3.js heatmap with color

Hi I am trying to add in a color scale for my heat map. I Specifically want to use d3.schemeRdYlBu this color scheme but I am having a hard time implementing it. At the moment it just does black. I also have a hover feature with this so I would like that to still work but i am more concerned with just getting the color to work. Obviously having the lower numbers be blue and the higher numbers be red to indicate temp
<!DOCTYPE html>
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<!-- Load color palettes -->
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/d3-color.v1.min.js"></script>
<script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<script>
// set the dimensions and margins of the graph
var margin = {top: 80, right: 25, bottom: 30, left: 40},
width = 1000 - margin.left - margin.right,
height = 1000 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://raw.githubusercontent.com/Nataliemcg18/Data/master/NASA_Surface_Temperature.csv", function(data) {
// Labels of row and columns -> unique identifier of the column called 'group' and 'variable'
var myGroups = d3.map(data, function(d){return d.group;}).keys()
var myVars = d3.map(data, function(d){return d.variable;}).keys()
// Build X scales and axis:
var x = d3.scaleBand()
.range([ 0, width ])
.domain(myGroups)
.padding(0.05);
svg.append("g")
.style("font-size", 15)
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSize(0))
.select(".domain").remove()
// Build Y scales and axis:
var y = d3.scaleBand()
.range([ height, 0 ])
.domain(myVars)
.padding(0.05);
svg.append("g")
.style("font-size", 15)
.call(d3.axisLeft(y).tickSize(0))
.select(".domain").remove()
// Build color scale
var myColor = (d3.schemeRdYlBu[2])
// create a tooltip
var tooltip = d3.select("#my_dataviz")
.append("div")
.style("opacity", 0)
.attr("class", "tooltip")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px")
// Three function that change the tooltip when user hover / move / leave a cell
var mouseover = function(d) {
tooltip
.style("opacity", 1)
d3.select(this)
.style("stroke", "green")
.style("opacity", 1)
}
var mousemove = function(d) {
tooltip
.html("The exact value of this cell is: " + d.value, )
.style("left", (d3.mouse(this)[0]+70) + "px")
.style("top", (d3.mouse(this)[1]) + "px")
}
var mouseleave = function(d) {
tooltip
.style("opacity", 0)
d3.select(this)
.style("stroke", "none")
.style("opacity", 0.8)
}
// add the squares
svg.selectAll()
.data(data, function(d) {return d.group+':'+d.variable;})
.enter()
.append("rect")
.attr("x", function(d) { return x(d.group) })
.attr("y", function(d) { return y(d.variable) })
.attr("rx", 4)
.attr("ry", 4)
.attr("width", x.bandwidth() )
.attr("height", y.bandwidth() )
.style("fill", function(d) { return myColor(d.value)} )
.style("stroke-width", 4)
.style("stroke", "none")
.style("opacity", 0.8)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave)
})
// Add title to graph
svg.append("text")
.attr("x", 0)
.attr("y", -50)
.attr("text-anchor", "left")
.style("font-size", "22px")
.text("A d3.js heatmap");
// Add subtitle to graph
svg.append("text")
.attr("x", 0)
.attr("y", -20)
.attr("text-anchor", "left")
.style("font-size", "14px")
.style("fill", "grey")
.style("max-width", 400)
.text("A short description of the take-away message of this chart.");
</script>
You can use arrow function instead of the regular function to use your own binding of this for accessing myColor variable.
<!DOCTYPE html>
<meta charset="utf-8" />
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<!-- Load color palettes -->
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/d3-color.v1.min.js"></script>
<script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<script>
// set the dimensions and margins of the graph
var margin = { top: 80, right: 25, bottom: 30, left: 40 },
width = 1000 - margin.left - margin.right,
height = 1000 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3
.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv(
"https://raw.githubusercontent.com/Nataliemcg18/Data/master/NASA_Surface_Temperature.csv",
function (data) {
// Labels of row and columns -> unique identifier of the column called 'group' and 'variable'
var myGroups = d3
.map(data, function (d) {
return d.group;
})
.keys();
var myVars = d3
.map(data, function (d) {
return d.variable;
})
.keys();
// Build X scales and axis:
var x = d3.scaleBand().range([0, width]).domain(myGroups).padding(0.05);
svg
.append("g")
.style("font-size", 15)
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSize(0))
.select(".domain")
.remove();
// Build Y scales and axis:
var y = d3.scaleBand().range([height, 0]).domain(myVars).padding(0.05);
svg
.append("g")
.style("font-size", 15)
.call(d3.axisLeft(y).tickSize(0))
.select(".domain")
.remove();
// Build color scale
var myColor = d3.schemeRdYlBu[3][2];
// create a tooltip
var tooltip = d3
.select("#my_dataviz")
.append("div")
.style("opacity", 0)
.attr("class", "tooltip")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px");
// Three function that change the tooltip when user hover / move / leave a cell
var mouseover = function (d) {
tooltip.style("opacity", 1);
d3.select(this).style("stroke", "green").style("opacity", 1);
};
var mousemove = function (d) {
tooltip
.html("The exact value of this cell is: " + d.value)
.style("left", d3.mouse(this)[0] + 70 + "px")
.style("top", d3.mouse(this)[1] + "px");
};
var mouseleave = function (d) {
tooltip.style("opacity", 0);
d3.select(this).style("stroke", "none").style("opacity", 0.8);
};
// add the squares
svg
.selectAll()
.data(data, function (d) {
return d.group + ":" + d.variable;
})
.enter()
.append("rect")
.attr("x", function (d) {
return x(d.group);
})
.attr("y", function (d) {
return y(d.variable);
})
.attr("rx", 4)
.attr("ry", 4)
.attr("width", x.bandwidth())
.attr("height", y.bandwidth())
.style("fill", (d) => {
return myColor;
})
.style("stroke-width", 4)
.style("stroke", "none")
.style("opacity", 0.8)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave);
}
);
// Add title to graph
svg
.append("text")
.attr("x", 0)
.attr("y", -50)
.attr("text-anchor", "left")
.style("font-size", "22px")
.text("A d3.js heatmap");
// Add subtitle to graph
svg
.append("text")
.attr("x", 0)
.attr("y", -20)
.attr("text-anchor", "left")
.style("font-size", "14px")
.style("fill", "grey")
.style("max-width", 400)
.text("A short description of the take-away message of this chart.");
</script>
This is another way to get the desired results
var myColor = d3.scaleSequential()
.interpolator( d3.interpolateRdYlBu)
.domain([1.37, -.81])

d3 rotating wheel with fixed clip

I am trying to "clip" this spinning wheel: https://bl.ocks.org/mpmckenna8/7f1f0adbf7d9ed7520b3950103e8094c
I want to only make the top-half of the wheel visible. When I try to do this with "clip-path" I end up having a half-wheel rotating. (see: https://codepen.io/icklerly/pen/JMBdGX)
svg.append("clipPath") // define a clip path
.attr("id", "ellipse-clip") // give the clipPath an ID
.append("rect")
.attr("x", -100)
.attr("y", 0)
.attr("width", 200)
.attr("height", 200);
But I want the wheel to rotate and the clip window always on the same position top.
Any suggestions?
The issue is that you are rotating the g element on where you applied the clip-path. Instead you can add another g on where you apply the clip-path and keep the rotation on another g inside.
So intead of this :
var hub = svg.append('g')
.attr('transform', function(){
return "translate(" + width/2 + "," + height/2 + ")"
})
.attr('class', 'hub')
.attr("clip-path", "url(#rect-clip)")
Do this :
var hub = svg.append('g').attr("clip-path", "url(#rect-clip)") /* append first g with clip-path */
.append('g') /* then create the inside g with the remaining properties*/
.attr('transform', function(){
return "translate(" + width/2 + "," + height/2 + ")"
})
.attr('class', 'hub')
You can also adjust the clip-path and simply make its size half the wheel to avoid using negative value for x/y.
Full Code:
var svg = d3.select('svg')
var margin = 20;
var width = 200, // margin,
height = 200 // margin;
svg.append("clipPath") // define a clip path
.attr("id", "rect-clip") // give the clipPath an ID
.append("rect")
.attr("x", 0) // position the x-centre
.attr("y", 0) // position the y-centre
.attr("width", 200) // set the x radius
.attr("height", 100);
var hub = svg.append('g').attr("clip-path", "url(#rect-clip)").append('g')
.attr('transform', function() {
return "translate(" + width / 2 + "," + height / 2 + ")"
})
.attr('class', 'hub')
hub.append('circle')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', 10)
.attr('fill', 'pink')
hub.append('circle')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', 90)
.attr('stroke', 'red')
.attr('stroke-width', 5)
.attr('fill', 'none')
var linelen = [0, 90];
var line = d3.line().x(function(d) {
return (0)
})
.y(function(d) {
return (d)
})
const numberSpokes = 10;
for (let i = 0; i < numberSpokes; i++) {
var rotation = (360 / numberSpokes) * i;
var spoke = hub
.append('path')
.datum(linelen)
.attr('d', line)
.attr('stroke', 'blue')
.attr('transform', 'rotate(' + rotation + ')')
.attr('class', 'spoke')
}
const alreadyTransformed = hub.attr('transform')
rotateIt(false)
function rotateIt(much) {
//console.log(alreadyTransformed)
hub.transition().duration(4000)
.attr('transform', alreadyTransformed + ' rotate(' + (much ? 0 : 180) + ')')
.on('end', function() {
rotateIt(!much)
})
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg id="svger" width="200px" height="200px"></svg>

Using a conditional statement to draw polylines in d3.js

Currently drawing have a piechart made in d3, and want to add a set of polylines to each arc that will extrude out of each arc at a certain angle depending on where the arc lies.
<!doctype HTML>
<html>
<head>
<title>Page Title</title>
<meta charset="UTF-8">
<script type="text/javascript" src="js/d3.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<script type="text/javascript">
//=========================================================================================================================================
// initializing variables
var data = []; // empty array to hold the objects imported from the JSON file
var oRadius = 300; //var holding value for the outer radius of the arc
var iRadius = 80; //var holding the value for the inner radius of the arc
var cRadius = 3; //var holding the value for the corner radius of the arc
var colors = d3.scale.category20b();//built in D3 function to color pieces of data
var width = 1400; //setting the width of the svg
var height = 1000; //setting the height of the svg
var dRadius = 5; //setting the radius for the dots
var sColor = "white"; // color for the stroke of the arcs
var dStrokeColor = "#666";
var dFillColor = "#ccc"
var lineMaker = d3.svg.line().x(function(d) { return d.x; }).y(function(d) { return d.y; }).interpolate("linear");
var myArcMaker= d3.svg.arc().outerRadius(oRadius).innerRadius(iRadius).cornerRadius(cRadius); //var that creates the arc
var bigArcMaker= d3.svg.arc().outerRadius(400).innerRadius(oRadius).cornerRadius(cRadius);
var mySvg = d3.select('body')
.append('svg')
.attr('width', width)
.attr("height", height) //selecting the body and appending an, then svg setting the height and width properties for the svg
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")// centers the pie chart in the center of the svg
mySvg.append("g")
.attr("class", "slices");
mySvg.append("g")
.attr("class", "dots");
mySvg.append("g")
.attr("class", "lines");
mySvg.append("g")
.attr("class", "polyLines");
var myPie = d3.layout.pie()
.sort(null)
.startAngle(2*(Math.PI))
.endAngle(((Math.PI))/360)
.padAngle(-1.5*(2*(Math.PI))/360).value(function(d){return d.value}); //setting the values for that start angle, end angle and pad angle for the arcs and takes in the the values from the objects in the data array
//======================================================================================================================================================
d3.json("data.json", function (json) // importing the json file
{
data = json; // setting the empty data array equal to the values of the objects in the json file
visual(); // this function holds all the d3 code to create the arc
})
//======================================================================================================================================================
function visual() // this function prevents the code that creates the arc from running before the objects from the json file are added into the empty data array
{
// console.log(data); // checking to see if the objects are loaded into the data ray using the console in chrome
var slice = mySvg.select(".slices")
.selectAll("path.slice")
.data(myPie(data)) //
.enter()
.append("path")
.attr("class", "slice")
.attr("d", function(d) {
return myArcMaker(d)
})
.attr("fill", function(d, i) {
return colors(i);
}) //using the d3 color brewer to color each arc
.attr("stroke", "white") //giving each arc a stroke of white
var dots = mySvg.select("g.dots")
.selectAll("cirlces")
.data(myPie(data))
.enter()
.append("circle")
.attr("class", "g.dots")
.attr("transform", function(d)
{
return "translate(" + myArcMaker.centroid(d) + ")";
})
.attr("r", dRadius)
.attr("fill", dFillColor)
.attr("stroke", sColor)
//
var lines = mySvg.select(".lines")
.selectAll("path.lines")
.data(myPie(data)) //
.enter()
.append("path")
.attr("class", "lines")
.attr("d", function(d) {
return bigArcMaker(d)
}).attr("fill", "none")
.attr("stroke", "white")
var outerDots = mySvg.select("g.dots")
.selectAll("cirlces")
.data(myPie(data))
.enter()
.append("circle")
.attr("class", "g.dots")
.attr("transform", function(d)
{
return "translate(" + bigArcMaker.centroid(d) + ")";
})
.attr("r", dRadius)
.attr("fill", dFillColor)
.attr("stroke", sColor)
// var x1 = myArcMaker.centroid(d)[0];
// var y1 = myArcMaker.centroid(d)[1];
// var x2 = bigArcMaker.centroid(d)[0];
// var y2 = bigArcMaker.centroid(d)[1];
// var x3 = function(d){if(x2<0){return bigArcMaker.centroid(d)[0]-160}}
// var lineData = [{'x': x1},
// ]
var polyLines = mySvg.select(".polyLines")
.selectAll("polylines")
.data(data)
.enter()
.append("polyline")
.attr("class", "polyLines")
.attr("points", function(d)
{
return
myArcMaker.centroid(d)[0] + ',' + myArcMaker.centroid(d)[1] + ','
+ bigArcMaker.centroid(d)[0] + ',' + bigArcMaker.centroid(d)[1] + ','+
(bigArcMaker.centroid(d)[0] < 0 )
? (bigArcMaker.centroid(d)[0] - 160) : (bigArcMaker.centroid(d)[0] + 160) + ',' +
bigArcMaker.centroid(d)[1]
})
.attr("fill", "#ccc")
.attr("stroke", sColor)
}
</script>
</body>
</html>
I have the polylines being appending to my svg when I use the inspect element in chrome but they arent showing up, they have no points. This leads me to believe its something to do with my conditional statement, is there something I'm not seeing? I'm new to d3 and javascript so its possible I just wrote the entire conditional statement wrong.
Couple things.
1.) You forgot to "pie" your data in the data-binding when you generate your polylines.
2.) Your conditional is getting lost somewhere because of the string concatenation. I would suggest you re-write that into something readable like:
.attr("points", function(d) {
var p = "";
p += myArcMaker.centroid(d)[0] + ',' + myArcMaker.centroid(d)[1] + ',' + bigArcMaker.centroid(d)[0] + ',' + bigArcMaker.centroid(d)[1] + ',';
p += bigArcMaker.centroid(d)[0] < 0 ? bigArcMaker.centroid(d)[0] - 160 : bigArcMaker.centroid(d)[0] + 160;
p += ',' + bigArcMaker.centroid(d)[1];
return p;
})
Working code:
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<meta charset="UTF-8" />
<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 type="text/javascript">
//=========================================================================================================================================
// initializing variables
var data = []; // empty array to hold the objects imported from the JSON file
var oRadius = 300; //var holding value for the outer radius of the arc
var iRadius = 80; //var holding the value for the inner radius of the arc
var cRadius = 3; //var holding the value for the corner radius of the arc
var colors = d3.scale.category20b(); //built in D3 function to color pieces of data
var width = 1400; //setting the width of the svg
var height = 1000; //setting the height of the svg
var dRadius = 5; //setting the radius for the dots
var sColor = "white"; // color for the stroke of the arcs
var dStrokeColor = "#666";
var dFillColor = "#ccc"
var lineMaker = d3.svg.line().x(function(d) {
return d.x;
}).y(function(d) {
return d.y;
}).interpolate("linear");
var myArcMaker = d3.svg.arc().outerRadius(oRadius).innerRadius(iRadius).cornerRadius(cRadius); //var that creates the arc
var bigArcMaker = d3.svg.arc().outerRadius(400).innerRadius(oRadius).cornerRadius(cRadius);
var mySvg = d3.select('body')
.append('svg')
.attr('width', width)
.attr("height", height) //selecting the body and appending an, then svg setting the height and width properties for the svg
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") // centers the pie chart in the center of the svg
mySvg.append("g")
.attr("class", "slices");
mySvg.append("g")
.attr("class", "dots");
mySvg.append("g")
.attr("class", "lines");
mySvg.append("g")
.attr("class", "polyLines");
var myPie = d3.layout.pie()
.sort(null)
.startAngle(2 * (Math.PI))
.endAngle(((Math.PI)) / 360)
.padAngle(-1.5 * (2 * (Math.PI)) / 360).value(function(d) {
return d.value
}); //setting the values for that start angle, end angle and pad angle for the arcs and takes in the the values from the objects in the data array
data= [{
value: 10
},{
value: 20
},{
value: 30
}];
visual();
//======================================================================================================================================================
function visual() // this function prevents the code that creates the arc from running before the objects from the json file are added into the empty data array
{
var slice = mySvg.select(".slices")
.selectAll("path.slice")
.data(myPie(data)) //
.enter()
.append("path")
.attr("class", "slice")
.attr("d", function(d) {
return myArcMaker(d)
})
.attr("fill", function(d, i) {
return colors(i);
}) //using the d3 color brewer to color each arc
.attr("stroke", "white") //giving each arc a stroke of white
var dots = mySvg.select("g.dots")
.selectAll("cirlces")
.data(myPie(data))
.enter()
.append("circle")
.attr("class", "g.dots")
.attr("transform", function(d) {
return "translate(" + myArcMaker.centroid(d) + ")";
})
.attr("r", dRadius)
.attr("fill", dFillColor)
.attr("stroke", sColor)
//
var lines = mySvg.select(".lines")
.selectAll("path.lines")
.data(myPie(data)) //
.enter()
.append("path")
.attr("class", "lines")
.attr("d", function(d) {
return bigArcMaker(d)
}).attr("fill", "none")
.attr("stroke", "white")
var outerDots = mySvg.select("g.dots")
.selectAll("cirlces")
.data(myPie(data))
.enter()
.append("circle")
.attr("class", "g.dots")
.attr("transform", function(d) {
return "translate(" + bigArcMaker.centroid(d) + ")";
})
.attr("r", dRadius)
.attr("fill", dFillColor)
.attr("stroke", sColor)
var polyLines = mySvg.select(".polyLines")
.selectAll("polylines")
.data(myPie(data))
.enter()
.append("polyline")
.attr("class", "polyLines")
.attr("points", function(d) {
var p = "";
p += myArcMaker.centroid(d)[0] + ',' + myArcMaker.centroid(d)[1] + ',' + bigArcMaker.centroid(d)[0] + ',' + bigArcMaker.centroid(d)[1] + ',';
p += bigArcMaker.centroid(d)[0] < 0 ? bigArcMaker.centroid(d)[0] - 160 : bigArcMaker.centroid(d)[0] + 160;
p += ',' + bigArcMaker.centroid(d)[1];
return p;
})
.attr("fill", "#ccc")
.attr("stroke", sColor)
}
</script>
</body>
</html>

D3js: Dragging a group by using one of it's children

Jsfiddle: http://jsfiddle.net/6NBy2/
Code:
var in_editor_drag = d3.behavior.drag()
.origin(function() {
var g = this.parentNode;
return {x: d3.transform(g.getAttribute("transform")).translate[0],
y: d3.transform(g.getAttribute("transform")).translate[1]};
})
.on("drag", function(d,i) {
g = this.parentNode;
translate = d3.transform(g.getAttribute("transform")).translate;
x = d3.event.dx + translate[0],
y = d3.event.dy + translate[1];
d3.select(g).attr("transform", "translate(" + x + "," + y + ")");
d3.event.sourceEvent.stopPropagation();
});
svg = d3.select("svg");
d = {x: 20, y: 20 };
groups = svg
.append("g")
.attr("transform", "translate(20, 20)");
groups
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.style("fill", "green")
.call(in_editor_drag)
.style("opacity", 0.4);
I'm trying to drag a group by using one of it's children as a handle. Simply, what i'm trying to do is, when a groups child is dragged:
Get translation transformation of group
Get drag distance from d3.event.dx, d3.event.dy
Apply difference to group's transform attribute
When child dragged, group does not move as expected. It moves less than the dragged distance, and it begins to jump here and there.
What am I doing wrong here?
Edit:
Updated jsfiddle: http://jsfiddle.net/6NBy2/2/
I'm trying to drag the whole group by using one or more of it's children as dragging handles.
This is an old question, but not really answered. I had exactly the same problem and wanted to drag the group by only one child (not all child elements of the <g>).
The problem is, that the d3.event.dx/y is calculated relatively to the position of the <g>. And as soon as the <g> is moved by .attr(“transform”, “translate(x, y)”), the d3.event.dx/dy is adjusted to the new (smaller) value. This results in a jerky movement with approx. the half of the speed of the cursor. I found two possible solutions for this:
First (finally I ended up with this approach):
Append the drag handle rect directly to the svg and not to the <g>. So it is positioned relatively to the <svg> and not to the <g>. Then move both (the <rect> and the <g>) within the on drag function.
var svg = d3.select("svg");
var group = svg
.append("g").attr("id", "group")
.attr("transform", "translate(0, 0)");
group
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.style("fill", "green")
.style("opacity", 0.4);
group
.append("text")
.attr("x", 10)
.attr("y", 5)
.attr("dominant-baseline", "hanging")
.text("drag me");
handle = svg
.append("rect")
.data([{
// Position of the rectangle
x: 0,
y: 0
}])
.attr("class", "draghandle")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 20)
.style("fill", "blue")
.style("opacity", 0.4)
.attr("cursor", "move")
.call(d3.drag().on("drag", function (d) {
console.log("yep");
d.x += d3.event.dx;
d.y += d3.event.dy;
// Move handle rect
d3.select(this)
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y;
});
// Move Group
d3.select("#group").attr("transform", "translate(" + [d.x, d.y] + ")");
}));
<body>
<svg width="400" height="400"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
</body>
Second:
Check on which element the cursor was during the drag event with d3.event.sourceEvent.path[0] and run the drag function only if the handle <rect> was clicked. With this approach, all elements can be grouped within one <g> (no need for an additional <rect> outside the group). The downside of this method is, that the drag is also executed, if the cursor is moved over the drag handle with mouse down.
var svg = d3.select("svg");
var group = svg
.append("g")
.data([{
// Position of the rectangle
x: 0,
y: 0
}])
.attr("id", "group")
.attr("transform", function (d) {
return "translate(" + d.x + ", " + d.y + ")"
})
.call(d3.drag().on("drag", function (d) {
if (d3.event.sourceEvent.target.classList.value === "draghandle") {
console.log("yep");
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr("transform", function (d) {
return "translate(" + [d.x, d.y] + ")"
})
} else {
console.log("nope");
return;
}
}));
group
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.style("fill", "green")
.style("opacity", 0.4);
group
.append("text")
.attr("x", 10)
.attr("y", 5)
.attr("dominant-baseline", "hanging")
.text("drag me");
handle = group
.append("rect")
.attr("class", "draghandle")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 20)
.style("fill", "blue")
.style("opacity", 0.4)
.attr("cursor", "move");
<body>
<svg width="400" height="400"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
</body>
use g = this; instead of g = this.parentNode;
Use drag.container() to set the container accessor.
See the D3 docs.

Categories

Resources