I have a polygon and rectangle svg created and I also created a toggle button for a polygon and a rectangle as shown below and jfiddle as well.
What I am trying to achieve is initially both the rectangle and polygon should be displayed on the webpage, then if I click on "toggle polygon" the polygon should be shown and the rectangle should be hidden, and if I click on "toggle rect" the rectangle should be shown and polygon should be hidden.
I wrote the code below to achieve that but for some reason,both rectangle and polygon arent displayed first together and also the toggle buttons aren't even displaying proper shape even though I provided different function names to both.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<button onclick="myfunc" >Toggle polygon</button>
<button onclick="func">toggle rect</button>
</body>
...
...
var vis = d3.select("body").append("svg")
.attr("width", 1000)
.attr("height", 667),
scaleX = d3.scale.linear()
.domain([-30,30])
.range([0,600]),
scaleY = d3.scale.linear()
.domain([0,50])
.range([500,0]),
poly = [{"x":0.0, "y":25.0},
{"x":8.5,"y":23.4},
{"x":13.0,"y":21.0},
{"x":19.0,"y":15.5}];
var path;
d3.select('button').on('click', function myfunc() {
if ( path ) {
path.remove();
// Remove dots
path = null;
} else {
path=vis.select("polygon")
.data([poly])
.enter().append("polygon")
.attr("points",function(d) {
return d.map(function(d) { return [scaleX(d.x),scaleY(d.y)].join(","); }).join(" ");})
.attr("stroke","black")
.attr("stroke-width",2);
}
});
var rect;
d3.select('button').on('click', function func() {
if ( rect ) {
rect.remove();
// Remove dots
rect= null;
} else {
rect = vis
.append("rect")
.attr("x", 165)
.attr("y", 25)
.attr("height", 100)
.attr("width", 100)
.attr("fill", "#420a91")
.attr("stroke", "#FF00FF")
.attr("stroke-width", "4")
.attr("stroke-dasharray", "10,10");
}
});
...
http://jsfiddle.net/e2juf7op/1/
When you do...
d3.select('button').on('click',
... you are selecting the first button and setting a different listener to that same button (the last one overrides the first one).
The solution is either dropping the selection.on and relying on the inline onclick event or, alternatively, give those buttons different IDs. Also, remember that selection.data() follows selectAll.
Here is your code with those changes:
var vis = d3.select("body").append("svg")
.attr("width", 1000)
.attr("height", 667),
scaleX = d3.scale.linear()
.domain([-30, 30])
.range([0, 600]),
scaleY = d3.scale.linear()
.domain([0, 50])
.range([500, 0]),
poly = [{
"x": 0.0,
"y": 25.0
},
{
"x": 8.5,
"y": 23.4
},
{
"x": 13.0,
"y": 21.0
},
{
"x": 19.0,
"y": 15.5
}
];
var path;
d3.select('#btn1').on('click', function myfunc() {
if (path) {
path.remove();
// Remove dots
path = null;
} else {
path = vis.selectAll("polygon")
.data([poly])
.enter().append("polygon")
.attr("points", function(d) {
return d.map(function(d) {
return [scaleX(d.x), scaleY(d.y)].join(",");
}).join(" ");
})
.attr("stroke", "black")
.attr("stroke-width", 2);
}
});
var rect;
d3.select('#btn2').on('click', function func() {
if (rect) {
rect.remove();
// Remove dots
rect = null;
} else {
rect = vis
.append("rect")
.attr("x", 165)
.attr("y", 25)
.attr("height", 100)
.attr("width", 100)
.attr("fill", "#420a91")
.attr("stroke", "#FF00FF")
.attr("stroke-width", "4")
.attr("stroke-dasharray", "10,10");
}
});
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<button id="btn1">Toggle polygon</button>
<button id="btn2">toggle rect</button>
</body>
Related
I know that similar questions have been asked before here on stackoverflow, but I have fairly specific requirements. I'm trying to generate a pulsing dot for a D3 chart.
I modified some code on codepen.io and came up with this.
How would I do the same thing using a D3 transition() rather than the (cheesy) CSS classes?
Something more along the lines of:
circle = circle.transition()
.duration(2000)
.attr("stroke-width", 20)
.attr("r", 10)
.transition()
.duration(2000)
.attr('stroke-width', 0.5)
.attr("r", 200)
.ease('sine')
.each("end", repeat);
Feel free to fork my sample pen.
Thanks!
Have a look at the example on GitHub by whityiu
Note that this is using d3 version 3.
I have adapted that code to produce something like you are after in the fiddle below.
var width = 500,
height = 450,
radius = 2.5,
dotFill = "#700f44",
outlineColor = "#700f44",
pulseLineColor = "#e61b8a",
bgColor = "#000",
pulseAnimationIntervalId;
var nodesArray = [{
"x": 100,
"y": 100
}];
// Set up the SVG display area
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("fill", bgColor)
.classed('visual-area', true);
var bgRect = d3.select("svg").append("rect")
.attr("width", width)
.attr("height", height);
var linkSet = null,
nodeSet = null;
// Create data object sets
nodeSet = svg.selectAll(".node").data(nodesArray)
.enter().append("g")
.attr("class", "node")
.on('click', function() {
// Clear the pulse animation
clearInterval(pulseAnimationIntervalId);
});
// Draw outlines
nodeSet.append("circle")
.classed("outline pulse", true)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("fill", 'none')
.attr("stroke", pulseLineColor)
.attr("r", radius);
// Draw nodes on top of outlines
nodeSet.append("circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("fill", dotFill)
.attr("stroke", outlineColor)
.attr("r", radius);
// Set pulse animation on interval
pulseAnimationIntervalId = setInterval(function() {
var times = 100,
distance = 8,
duration = 1000;
var outlines = svg.selectAll(".pulse");
// Function to handle one pulse animation
function repeat(iteration) {
if (iteration < times) {
outlines.transition()
.duration(duration)
.each("start", function() {
d3.select(".outline").attr("r", radius).attr("stroke", pulseLineColor);
})
.attrTween("r", function() {
return d3.interpolate(radius, radius + distance);
})
.styleTween("stroke", function() {
return d3.interpolate(pulseLineColor, bgColor);
})
.each("end", function() {
repeat(iteration + 1);
});
}
}
if (!outlines.empty()) {
repeat(0);
}
}, 6000);
Fiddle
I have problem with adding the axis to stem plot. I'm using D3.js in version 3.
I draw my own stems that consist of circles and lines.
I have two scenarios:
1.
1.1 First I add the stems
1.2 Then I add axis
The Y-axis is plotted on the stem. On the following image the magenta line covers green one (I want the opposite, stem should cover axis)
2
2.1 First I add axis
2.2 Then I add the stems
The lines of stems are not plotted.
I need someone to explain me why the lines are not drawn.
File js/stem-functions.js
var svgParams = {
// Graph field
graphWidth : 200,
graphHeight : 120,
// Margins
leftPadding : 30,
rightPadding : 10,
upPadding : 15,
downPadding : 25,
}
function setParametersSvg() {
// Size of the SVG object
this.svgWidth = this.graphWidth + this.leftPadding + this.rightPadding;
this.svgHeight = this.graphHeight + this.upPadding + this.downPadding;
}
setParametersSvg.apply(svgParams);
// Create Scale functions
var xScale = (function ustawSkaleX(minX, maxX, svgParam) {
var xSc = d3.scale.linear()
.domain([minX, maxX])
.range([svgParam.leftPadding, svgParam.leftPadding + svgParam.graphWidth]);
return xSc;
} (0, 5, svgParams) );
var yScale = (function ustawSkaleY(minY, maxY, svgParam) {
var ySc = d3.scale.linear()
.domain([minY, maxY])
.range([svgParam.upPadding + svgParam.graphHeight, svgParam.upPadding]);
return ySc;
} (0, 0.5, svgParams) );
function addAxis(svg, svgParam, xScale, yScale) {
// Functions drawing axis X
var xAxis = d3.svg.axis();
xAxis.scale(xScale) // Scale function for X
.orient("bottom") // Location of label
.ticks(7); // Ticks
// Add group
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (svgParam.svgHeight - svgParam.downPadding) +")")
.call(xAxis);
// Functions drawing axis Y
var yAxis = d3.svg.axis();
yAxis.scale(yScale) // Scale function for Y
.orient("left") // Location of label
.ticks(4); // Ticks
// Add group
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + (svgParam.leftPadding) + ", 0)")
.call(yAxis);
}
function addStems(svg, dataset, xScale, yScale) {
var circles = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("fill", "green")
.attr("cx", function(d) { return xScale(d.x); } )
.attr("cy", function(d) { return yScale(d.n); } )
.attr("r", 4);
var lines = svg.selectAll("line")
.data(dataset)
.enter()
.append("line")
.attr("class", "stem-line")
.attr("stroke", "green")
.attr("stroke-width", "1")
.attr("x1", function(d) { return xScale(d.x); } )
.attr("x2", function(d) { return xScale(d.x); } )
.attr("y1", function(d) { return yScale(0); } )
.attr("y2", function(d) { return yScale(d.n); } );
}
File js/stem-examples.js
// Data set
var p = [
{ x: 0, n: 0.15 },
{ x: 1, n: 0.25 },
{ x: 2, n: 0.40 },
{ x: 3, n: 0.15 },
{ x: 4, n: 0.05 }
];
console.log('probabilities ', p);
d3.select("body").append("h4").html("Call Stems printing before Axis printing");
// Stems before Axis => Axis is over stem
var svg1 = d3.select("body")
.append("svg")
.attr("width", svgParams.svgWidth)
.attr("height", svgParams.svgHeight);
addStems(svg1, p, xScale, yScale);
addAxis(svg1, svgParams, xScale, yScale);
d3.select("body").append("br");
d3.select("body").append("h4").html("Call Axis printing before Stems printing");
// Axis before Stems => stem lines are gone (why?)
var svg2 = d3.select("body")
.append("svg")
.attr("width", svgParams.svgWidth)
.attr("height", svgParams.svgHeight);
addAxis(svg2, svgParams, xScale, yScale);
addStems(svg2, p, xScale, yScale);
File css/styl.js
svg { border: teal 1px solid; }
.axis path, .axis line {
fill: none;
stroke: magenta;
stroke-width: 1;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 12px;
fill: DarkViolet;
}
File index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Stem plot with axis</title>
<link rel="stylesheet" href="css/styl.css">
</head>
<body>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script> <!-- V3 -->
<!-- TODO: try version 4 of D3.js --> <!-- V4 -->
<script src="js/stem-functions.js"></script>
<script src="js/stem-examples.js"></script>
</body>
</html>
Appending the axis first and then the stems is the correct approach. The problem is not that.
The problem is that, when you do this...
var lines = svg.selectAll("line")
.data(dataset)
.enter()
.append("line")
//etc...
... you are selecting lines that already exist in that SVG, and binding data to them.
Solution
Do this:
var lines = svg.selectAll(null)
.data(dataset)
.enter()
.append("line")
//etc...
To understand why I'm selecting null, have a look at this question/answer of mine here: Selecting null: what is the reason of using 'selectAll(null)' in D3.js?
I am trying to add color options for my heat-map visualization. I have a predefined colors array at the beginning, and I draw rectangles like this:
plotChart.selectAll(".cell")
.data(data)
.enter().append("rect")
.attr("class", "cell")
.attr("x", function (d) { return x(d.timestamp); })
.attr("y", function (d) { return y(d.hour); })
.attr("width", function (d) { return x(d3.timeWeek.offset(d.timestamp, 1)) - x(d.timestamp); })
.attr("height", function (d) { return y(d.hour + 1) - y(d.hour); })
.attr("fill", function (d) { return colorScale(d.value); });
When I click a link in a dropdown menu, I do this:
$(".colorMenu").click(function (event) {
event.preventDefault();
// remove # from clicked link
var addressValue = $(this).attr("href").substring(1);
// get color scheme array
var newColorScheme = colorDict[addressValue];
// update color scale range
colorScale.range(newColorScheme);
// here I need to repaint with colors
});
My color scale is quantile scale, so I cannot use invert function to find values of each rectangle. I don't want to read the data again because it would be a burden, so how can I change fill colors of my rectangles?
I don't want to read the data again...
Well, you don't need to read the data again. Once the data was bound to the element, the datum remains there, unless you change/overwrite it.
So, this can simply be done with:
.attr("fill", d => colorScale(d.value));
Check this demo:
var width = 500,
height = 100;
var ranges = {};
ranges.range1 = ['#f7fbff','#deebf7','#c6dbef','#9ecae1','#6baed6','#4292c6','#2171b5','#08519c','#08306b'];
ranges.range2 = ['#fff5eb','#fee6ce','#fdd0a2','#fdae6b','#fd8d3c','#f16913','#d94801','#a63603','#7f2704'];
ranges.range3 = ['#f7fcf5','#e5f5e0','#c7e9c0','#a1d99b','#74c476','#41ab5d','#238b45','#006d2c','#00441b'];
ranges.range4 = ['#fff5f0','#fee0d2','#fcbba1','#fc9272','#fb6a4a','#ef3b2c','#cb181d','#a50f15','#67000d'];
var color = d3.scaleQuantile()
.domain([0, 15])
.range(ranges.range1);
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var data = d3.range(15);
var rects = svg.selectAll(".rects")
.data(data)
.enter()
.append("rect");
rects.attr("y", 40)
.attr("x", d => d * 25)
.attr("height", 20)
.attr("width", 20)
.attr("stroke", "gray")
.attr("fill", d => color(d));
d3.selectAll("button").on("click", function() {
color.range(ranges[this.value]);
rects.attr("fill", d => color(d))
})
<script src="https://d3js.org/d3.v4.min.js"></script>
<button value="range1">Range1</button>
<button value="range2">Range2</button>
<button value="range3">Range3</button>
<button value="range4">Range4</button>
The idea is to make an object reacting on mouse moves in vicinity of the object.
That is how far I got on this by now:
//declaring a canvas
var canvas = d3.select("body")
.append("svg")
.attr("width", 100)
.attr("height", 100);
//create circle with attributes and listeners
var circles = canvas.selectAll("circle")
.data([1])
.enter()
.append("circle")
.attr("cy", 50).attr("cx", 50).attr("r", 20)
.style({"fill": "grey"})
.on("mousemove", handleMouseMove);
//listener on mouse move inside the canvas
function handleMouseMove(){
var coordinates = [0, 0];
coordinates = d3.mouse(this);
var x = coordinates[0];
var y = coordinates[1];
console.log(x,y);
console.log(this.attributes);
}
See example on codepen
I can get the object with it's properties only when hovering over it - see last console.log();. And I am stuck on it. Please share your ideas regarding the solution if any.
Maybe the cleanest way, if you can, is by putting another circle with bigger radius just under your existing circle with a fill of transparent:
var g = canvas.selectAll("g")
.data([1])
.enter()
.append("g")
.on("mouseover", handleMouseOver) // event handlers here are applied
.on("mouseout", handleMouseOut) // to both 'circle'
g.append('circle').classed('invisible', true) // this goes before the
.attr("cy", 50) // visible circle
.attr("cx", 50)
.attr("r", 40)
.style({"fill": "transparent"});
g.append('circle').classed('visible', true)
.attr("cy", 50)
.attr("cx", 50)
.attr("r", 20)
.style({"fill": "grey"});
function handleMouseOver(d,i){
d3.select(this).select('.visible').transition()
.style({"fill": "red"});
};
function handleMouseOut(d,i){
d3.select(this).select('.visible').transition()
.style({"fill": "green"});
};
Or if you want to use mouse positions:
var circles = canvas.selectAll("circle")
.data([1])
.enter()
.append("circle")
.attr("cy", 50)
.attr("cx", 50)
.attr("r", 20)
.style({"fill": "grey"})
.on("mousemove", handleMouseMove);
function handleMouseMove(){
var coordinates = [];
coordinates = d3.mouse(this);
var x = coordinates[0];
var y = coordinates[1];
if (x>10 && x<90 && y>10 && y<90) { // example values; this is a rectangle but you can use more complex shapes
d3.select('.visible').style({"fill": "red"});
}
else {
d3.select('.visible').style({"fill": "green"});
}
Here is the FIDDLE where you can see both versions.
Hope it helps...
If you want to detect that the mouse is near the circle you need to set up your event handler on the object that contains the circle, in this case the svg contained in your canvas variable. Then to determine if the mouse is close, I'd use the point distance formula.
function handleMouseMove(){
var coordinates = d3.mouse(this),
x = coordinates[0],
y = coordinates[1];
var dist = Math.sqrt(Math.pow(circPos.x - x, 2) + Math.pow(circPos.y - y, 2));
console.log("distance to circle center is " + dist);
}
UPDATE FOR COMMENTS
var canvas = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500)
.on("mousemove", handleMouseMove);
var data = [];
for (var i = 0; i < 10; i++) {
data.push({
x: Math.random() * 500,
y: Math.random() * 500
});
}
var circles = canvas.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cy", function(d) {
return d.y;
})
.attr("cx", function(d) {
return d.x;
})
.attr("r", 10)
.style({
"fill": "grey"
});
function handleMouseMove() {
var coordinates = d3.mouse(this),
x = coordinates[0],
y = coordinates[1];
circles.style("fill", "grey");
var closestCircle = {
obj: null,
dist: 1e100
};
circles.each(function(d) {
var dist = Math.sqrt(Math.pow(d.x - x, 2) + Math.pow(d.y - y, 2));
if (dist < closestCircle.dist) {
closestCircle = {
obj: this,
dist: dist
};
}
});
d3.select(closestCircle.obj).style("fill", "green");
}
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
Voroni Example
var canvas = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500);
var data = [];
for (var i = 0; i < 10; i++) {
data.push({
x: Math.random() * 500,
y: Math.random() * 500,
id: i
});
}
var circles = canvas.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cy", function(d) {
return d.y;
})
.attr("cx", function(d) {
return d.x;
})
.attr("id", function(d) {
return "circle" + d.id;
})
.attr("r", 10)
.style("fill", "grey");
var voronoi = d3.voronoi()
.extent([
[-1, -1],
[500 + 1, 500 + 1]
])
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
})
var voronoiGroup = canvas.append("g")
.attr("class", "voronoi");
voronoiGroup.selectAll("path")
.data(voronoi.polygons(data))
.enter().append("path")
.attr("d", function(d) {
return d ? "M" + d.join("L") + "Z" : null;
})
.style("pointer-events", "all")
.style("fill", "none")
.style("stroke", "steelblue")
.style("opacity", "0.5")
.on("mouseover", mouseover);
function mouseover(d) {
circles.style("fill", "grey");
d3.select("#circle" + d.data.id).style("fill", "green");
}
<script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
I've created a sample Asp.Net MVC 4 application where I've used D3.js to append an SVG element and then inside the SVG I've appended a text element (see code below). This all works fine until I try to append an img to the SVG using a local png file. The img gets appended to the DOM, but the img is not rendered on the page. Any ideas what I'm doing wrong here, and how to go about fixing it?
#{
ViewBag.Title = "Home Page";
}
<script src="~/Scripts/d3.v3.js"></script>
<script type="text/javascript">
var svg = d3.select("body")
.append("svg")
.attr("width", 200)
.attr("height", 100)
.style("border", "1px solid black");
var text = svg.selectAll("text")
.data([0])
.enter()
.append("text")
.text("Testing")
.attr("x", "40")
.attr("y", "60");
var imgs = svg.selectAll("img").data([0]);
imgs.enter()
.append("img")
.attr("xlink:href", "#Url.Content("~/Content/images/icons/refresh.png")")
.attr("x", "60")
.attr("y", "60")
.attr("width", "20")
.attr("height", "20");
</script>
#Richard Marr - Below is an attempt to do the same thing in straight HTML, which gives me the same result. I'm not sure about my code to get the refresh.png file from the local drive this way.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v2.js"></script>
</head>
<body>
<script type="text/javascript">
var svg = d3.select("body")
.append("svg")
.attr("width", 200)
.attr("height", 100)
.style("border", "1px solid black");
var text = svg.selectAll("text")
.data([0])
.enter()
.append("text")
.text("Testing")
.attr("x", "40")
.attr("y", "60");
var imgs = svg.selectAll("img").data([0]);
imgs.enter()
.append("svg:img")
.attr("xlink:href", "file:///D:/d3js_projects/refresh.png")
.attr("x", "60")
.attr("y", "60")
.attr("width", "20")
.attr("height", "20");
</script>
</body>
</html>
nodeEnter.append("svg:image")
.attr('x', -9)
.attr('y', -12)
.attr('width', 20)
.attr('height', 24)
.attr("xlink:href", "resources/images/check.png")
In SVG (contrasted with HTML), you will want to use <image> instead of <img> for elements.
Try changing your last block with:
var imgs = svg.selectAll("image").data([0]);
imgs.enter()
.append("svg:image")
...
My team also wanted to add images inside d3-drawn circles, and came up with the following (fiddle):
index.html:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="timeline.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
<script src="https://code.jquery.com/jquery-2.2.4.js"
integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI="
crossorigin="anonymous"></script>
<script src="./timeline.js"></script>
</head>
<body>
<div class="timeline"></div>
</body>
</html>
timeline.css:
.axis path,
.axis line,
.tick line,
.line {
fill: none;
stroke: #000000;
stroke-width: 1px;
}
timeline.js:
// container target
var elem = ".timeline";
var props = {
width: 1000,
height: 600,
class: "timeline-point",
// margins
marginTop: 100,
marginRight: 40,
marginBottom: 100,
marginLeft: 60,
// data inputs
data: [
{
x: 10,
y: 20,
key: "a",
image: "https://unsplash.it/300/300",
id: "a"
},
{
x: 20,
y: 10,
key: "a",
image: "https://unsplash.it/300/300",
id: "b"
},
{
x: 60,
y: 30,
key: "a",
image: "https://unsplash.it/300/300",
id: "c"
},
{
x: 40,
y: 30,
key: "a",
image: "https://unsplash.it/300/300",
id: "d"
},
{
x: 50,
y: 70,
key: "a",
image: "https://unsplash.it/300/300",
id: "e"
},
{
x: 30,
y: 50,
key: "a",
image: "https://unsplash.it/300/300",
id: "f"
},
{
x: 50,
y: 60,
key: "a",
image: "https://unsplash.it/300/300",
id: "g"
}
],
// y label
yLabel: "Y label",
yLabelLength: 50,
// axis ticks
xTicks: 10,
yTicks: 10
}
// component start
var Timeline = {};
/***
*
* Create the svg canvas on which the chart will be rendered
*
***/
Timeline.create = function(elem, props) {
// build the chart foundation
var svg = d3.select(elem).append('svg')
.attr('width', props.width)
.attr('height', props.height);
var g = svg.append('g')
.attr('class', 'point-container')
.attr("transform",
"translate(" + props.marginLeft + "," + props.marginTop + ")");
var g = svg.append('g')
.attr('class', 'line-container')
.attr("transform",
"translate(" + props.marginLeft + "," + props.marginTop + ")");
var xAxis = g.append('g')
.attr("class", "x axis")
.attr("transform", "translate(0," + (props.height - props.marginTop - props.marginBottom) + ")");
var yAxis = g.append('g')
.attr("class", "y axis");
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", 1)
.attr("x", 0 - ((props.height - props.yLabelLength)/2) )
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text(props.yLabel);
// add placeholders for the axes
this.update(elem, props);
};
/***
*
* Update the svg scales and lines given new data
*
***/
Timeline.update = function(elem, props) {
var self = this;
var domain = self.getDomain(props);
var scales = self.scales(elem, props, domain);
self.drawPoints(elem, props, scales);
};
/***
*
* Use the range of values in the x,y attributes
* of the incoming data to identify the plot domain
*
***/
Timeline.getDomain = function(props) {
var domain = {};
domain.x = props.xDomain || d3.extent(props.data, function(d) { return d.x; });
domain.y = props.yDomain || d3.extent(props.data, function(d) { return d.y; });
return domain;
};
/***
*
* Compute the chart scales
*
***/
Timeline.scales = function(elem, props, domain) {
if (!domain) {
return null;
}
var width = props.width - props.marginRight - props.marginLeft;
var height = props.height - props.marginTop - props.marginBottom;
var x = d3.scale.linear()
.range([0, width])
.domain(domain.x);
var y = d3.scale.linear()
.range([height, 0])
.domain(domain.y);
return {x: x, y: y};
};
/***
*
* Create the chart axes
*
***/
Timeline.axes = function(props, scales) {
var xAxis = d3.svg.axis()
.scale(scales.x)
.orient("bottom")
.ticks(props.xTicks)
.tickFormat(d3.format("d"));
var yAxis = d3.svg.axis()
.scale(scales.y)
.orient("left")
.ticks(props.yTicks);
return {
xAxis: xAxis,
yAxis: yAxis
}
};
/***
*
* Use the general update pattern to draw the points
*
***/
Timeline.drawPoints = function(elem, props, scales, prevScales, dispatcher) {
var g = d3.select(elem).selectAll('.point-container');
var color = d3.scale.category10();
// add images
var image = g.selectAll('.image')
.data(props.data)
image.enter()
.append("pattern")
.attr("id", function(d) {return d.id})
.attr("class", "svg-image")
.attr("x", "0")
.attr("y", "0")
.attr("height", "70px")
.attr("width", "70px")
.append("image")
.attr("x", "0")
.attr("y", "0")
.attr("height", "70px")
.attr("width", "70px")
.attr("xlink:href", function(d) {return d.image})
var point = g.selectAll('.point')
.data(props.data);
// enter
point.enter()
.append("circle")
.attr("class", "point")
.on('mouseover', function(d) {
d3.select(elem).selectAll(".point").classed("active", false);
d3.select(this).classed("active", true);
if (props.onMouseover) {
props.onMouseover(d)
};
})
.on('mouseout', function(d) {
if (props.onMouseout) {
props.onMouseout(d)
};
})
// enter and update
point.transition()
.duration(1000)
.attr("cx", function(d) {
return scales.x(d.x);
})
.attr("cy", function(d) {
return scales.y(d.y);
})
.attr("r", 30)
.style("stroke", function(d) {
if (props.pointStroke) {
return d.color = props.pointStroke;
} else {
return d.color = color(d.key);
}
})
.style("fill", function(d) {
if (d.image) {
return ("url(#" + d.id + ")");
}
if (props.pointFill) {
return d.color = props.pointFill;
} else {
return d.color = color(d.key);
}
});
// exit
point.exit()
.remove();
// update the axes
var axes = this.axes(props, scales);
d3.select(elem).selectAll('g.x.axis')
.transition()
.duration(1000)
.call(axes.xAxis);
d3.select(elem).selectAll('g.y.axis')
.transition()
.duration(1000)
.call(axes.yAxis);
};
$(document).ready(function() {
Timeline.create(elem, props);
})
I do not know why, but the image should not be duplicated, tripled, etc ... should remove the previous one and load it again but with another rotation data. This is my code:
data.csv
enter image description here
d3.csv("data/data.csv").then(function(data){
//console.log(data);
// Clean data
formattedData = data.map(function(id){
id.rot_1 = +id.rot_1;
id.trans_1 = +id.trans_1;
return id;
});
// First run of the visualization
update(formattedData[0]);})
$("#play-button")
.on("click", function(){
var button = $(this);
if (button.text() == "Play"){
button.text("Pause");
interval = setInterval(step, 1000);
}
else {
button.text("Play");
clearInterval(interval);
}})
function step(){
// At the end of our data, loop back
time = (time < 76) ? time+1 : 0
update(formattedData[time]); }
function update(data) {
// Standard transition time for the visualization
var t = d3.transition()
.duration(1000);
//console.log(d3.selectAll(data));
//console.log(data)
// original
var imgs1 = g.append("image") // en vez de g es svg
.attr("xlink:href", "img/picturetest.png");
// EXIT old elements not present in new data.
imgs1.exit()
.attr("class", "exit")
.selectAll("svg:image")
.remove();
//console.log(data)
// ENTER new elements present in new data.
imgs1.enter()
.append("svg:image") // svg:image
//.attr("xlink:href", "img/picturetest.png")
.attr("class", "enter")
.merge(imgs1)
.transition(t)
.attr("x", 0) // 150
.attr("y", 0) // 80
.attr("width", 200)
.attr("height", 200)
.attr("transform", "rotate("+data.rot_1+") translate("+data.trans_1+")" ); }`
var svg = d3.select("body")
.append("svg")
.style("width", 200)
.style("height", 100)