d3 line transition fails when drawing left - javascript

I am trying to create a function that will be the basis of a repeated drawing of lines and fading them out. I am roughly inspired by Peter Cook's Wind Map, which he discusses here: http://prcweb.co.uk/making-the-uk-wind-chart/. I am trying to recreate his test lines.
I can get the test lines to work -- but only if they draw or swing to the left, i.e. when the original x2 is greater than the new x2 they transition to. The code below works the way I expect. But if I change the x2 it is drawing to (marked with the comment 'ISSUE IS HERE') to be greater than 500, it won't draw. I'm very confused. Why would it care which direction it draws in?
<!DOCTYPE html>
<html>
<head>
<title>TITLE GOES HERE</title>
</head>
<body>
<svg></svg>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
function lineAnimate(selection) {
selection
.attr('x1', 500)
.attr('x2', 500)
.attr("stroke-width", 2)
.attr("stroke", "black")
.attr('y1', function(d) {return d;})
.attr('y2', function(d) {return d;})
.style('opacity', 0.5)
.transition()
.ease('linear')
.duration(1000)
// .delay(function(d) {return d*10;})
.attr('x2', 200) // ISSUE IS HERE
.transition()
.delay(1000)
.duration(1000)
.style('opacity', 0)
.each('end', function() {d3.select(this).call(lineAnimate)})
console.log("done");
}
var svg = d3.select('svg')
.selectAll('line')
.data([5, 10, 15, 20, 25])
.enter()
.append('line')
.call(lineAnimate);
console.log(svg.size());
</script>
</body>
</html>

The transition is working, regardless the fact that the new x2 is bigger or smaller than the original value, that's not the problem.
You don't see anything because, by default, an SVG has a width of 300px (so, in your "working" code, you're only seeing a small fraction of the line, from 300 to 200 in the x coordinate; all the segment from 500 to 300 is not visible).
Just change the width:
<svg width="600"></svg>
Here is your code:
<svg width="600"></svg>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
function lineAnimate(selection) {
selection
.attr('x1', 500)
.attr('x2', 500)
.attr("stroke-width", 2)
.attr("stroke", "black")
.attr('y1', function(d) {return d;})
.attr('y2', function(d) {return d;})
.style('opacity', 0.5)
.transition()
.ease('linear')
.duration(1000)
// .delay(function(d) {return d*10;})
.attr('x2', 600) // ISSUE IS HERE
.transition()
.delay(1000)
.duration(1000)
.style('opacity', 0)
.each('end', function() {d3.select(this).call(lineAnimate)})
}
var svg = d3.select('svg')
.selectAll('line')
.data([5, 10, 15, 20, 25])
.enter()
.append('line')
.call(lineAnimate);
</script>

Related

D3.select(this) but filter

I am working on an D3 forced graph and got a challenge. I added a "mouseenter" function which increase the radius of nodes if getting hovered.
.on("mouseenter", function(d) {
d3.select(this)
.transition()
.duration(200)
.attr("r", 50)
Now I want to exclude certain nodes and thought to filter would help. Unfortunately it doesn´t. Probably my code is wrong or I need to add those filtering later one. Any idea?
.on("mouseenter", function(d) {
d3.select(this)
.filter(function() {
return !this.graph.nodes.name("usv"))
})
.transition()
.duration(200)
.attr("r", 50)
Best,
Kristian
Here's an example implementation:
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg width="500" height="500"></svg>
<script>
let data = d3.range(10)
.map(function(d){
return {
name: Math.random() > 0.5 ? "usv" : "na",
x: Math.random() * 480,
y: Math.random() * 480
};
});
d3.select('svg')
.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 20)
.attr("fill", d => d.name == "usv" ? "red" : "black")
.filter((d) => {
return d.name === "usv";
})
.on("mouseenter", function(){
d3.select(this)
.transition()
.duration(200)
.attr("r", 50);
});
</script>
</body>
</html>

Drag-and-drop function to move a skier up and down a slope in D3.js

I want to create an interactive tutorial on web that can help students to understand the basic mechanics of skiing. The first thing I want to teach is the concept of conservation of mechanical energy. To do this, I want the students to interact with the skier. They should be able to move the skier up and down on the slope. And when they do this, the amount of potential energy should be calculated automatically. When the students drop the skier, the skier move/transit to the end of the slope where they can see the speed of the skier (ignoring applied forces such as air drag and friction).
When the students move the skier higher up on the slope, the slope should also follow the skier. I am coding the whole thing in D3.js. I don't know how I can do this at the moment, so I would appreciate if someone can guide me. This is my code thus far:
<!DOCTYPE html>
<html>
<head>
<title>Skier!</title>
<link rel="stylesheet" href="style.css" />
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="https://d3js.org/d3-path.v1.min.js"></script>
<script src="https://d3js.org/d3-shape.v1.min.js"></script>
</head>
<body>
<div>
<input type="range" id="nAngle" name="degrees" min="0" max="90">
<label for="degress">Angle</label>
</div>
</body>
<script>
// The values for my SVG canvas
let w = 960,
h = 500,
middleW = w/2,
middleH = h/2;
let svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h)
// Creating a Group element so I can interact with all the elements of the skier
let skier = svg.append('g')
// Main body of the skier
skier.append('rect')
.attr('x', middleW)
.attr('y', middleH)
.attr('width', 30)
.attr('height', 50)
.attr('rx', 15)
.attr('ry', 12)
.attr('fill', 'green')
.style("stroke", "black")
.style("stroke-width", 5) // set the stroke width // set the line colour
.attr("transform", "rotate(14, 50, 40)");
// Pole
skier.append('line')
.attr("x1", middleW - 100)
.attr("y1", middleH + 110)
.attr("x2", middleW - 10)
.attr("y2", middleH + 136)
.attr('stroke', 'black')
.style("stroke-width", 4);
// Skis
skier.append('line')
.attr("x1", middleW - 90)
.attr("y1", middleH + 170)
.attr("x2", middleW + 5)
.attr("y2", middleH + 190)
.attr('stroke', 'black')
.style("stroke-width", 4);
// Path generator of beanie
let arcBeanie = d3.arc()
.innerRadius(0)
.outerRadius(12)
.startAngle(0)
.endAngle(Math.PI);
skier.append('path')
.attr('d', arcBeanie)
.attr('fill', 'red')
.attr('stroke', 'black')
.style("stroke-width", 4)
.attr("transform", "translate(436, 334) rotate(-90)");
arcBeanie(); //
// Path generator of face
let arcFace = d3.arc()
.innerRadius(0)
.outerRadius(12)
.startAngle(0)
.endAngle(Math.PI);
skier.append('path')
.attr('d', arcFace)
.attr('fill', 'white')
.attr('stroke', 'black')
.style("stroke-width", 4)
.attr("transform", "translate(436, 334) rotate(90)");
arcFace(); //
// Legs
let arcLegs = d3.arc()
.innerRadius(22)
.outerRadius(24)
.startAngle(0)
.endAngle(Math.PI - 1.5);
skier.append('path')
.attr('d', arcLegs)
.attr('stroke', 'black')
.style("stroke-width", 4)
.attr("transform", "translate(415, 423) rotate(14)");
arcLegs(); //
// Arms
let arcArms = d3.arc()
.innerRadius(0.25)
.outerRadius(1)
.startAngle(0)
.endAngle(Math.PI - 1.4);
skier.append('path')
.attr('d', arcLegs)
.attr('stroke', 'black')
.style("stroke-width", 4)
.attr("transform", "translate(450, 360) rotate(170)");
arcLegs(); //
d3.selectAll("g")
.transition()
.attr('transform', 'translate(300,0) rotate(20, 300, 0)')
.attr('height', 600)
.style('fill', 'teal')
.duration(5000)
d3.select("#nAngle").on("input", function() {
update(+this.value);
});
// Initial starting angle of the skier
update(0);
// update the element
function update(nAngle) {
// adjust the text on the range slider
// d3.select("#nAngle-value").text(nAngle);
// d3.select("#nAngle").property("value", nAngle);
// rotate the skier
svg.select("g")
.attr("transform", "rotate("+nAngle+")");
}
</script>
</html>

D3js dynamically attach circles into a line with drag and drop

I am very new to d3js v3 and I was trying out a new program where there are lines and the according to the data, circles get embedded into them.
This is what I have so far.
var width = 500,
height = 500;
var animals = ['dog', 'cat', 'bat'];
var fruits = ['apple', 'banana'];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var line1 = svg.append("line")
.attr("x1", 350)
.attr("y1", 5)
.attr("x2", 350)
.attr("y2", 350)
.attr("stroke-width", 2)
.attr("stroke", "black");
var line2 = svg.append("line")
.attr("x1", 80)
.attr("y1", 5)
.attr("x2", 100)
.attr("y2", 350)
.attr("stroke-width", 2)
.attr("stroke", "black");
var animal_scale = d3.scale.ordinal()
.domain(animals)
.rangePoints([5, 350],.2);
var fruit_scale = d3.scale.ordinal()
.domain(fruits)
.rangePoints([5, 350],.2);
var animal_circles = svg.selectAll('circle')
.data(animals)
.enter()
.append('circle')
.attr('cx', function(d) {
// is there a way to calc it automatically according to line 1
})
.attr('cy', function(d) {
return animal_scale(d);
})
.attr('id', function(d) {
return d;
})
.attr('r', 20);
var fruits_circles = svg.selectAll('circle')
.data(fruits)
.enter()
.append('circle')
.attr('cx', function(d) {
// is there a way to calc it automatically according to line 2
})
.attr('cy', function(d) {
return fruit_scale(d);
})
.attr('id', function(d) {
return d;
})
.attr('r', 20);
<!DOCTYPE html>
<html>
<meta charset=utf-8>
<head>
<title></title>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
</body>
</html>
I looked at some sources and being new, its kinda hard to understand most of it. I eventually want to be able to move and drag the circles between lines at the end of the project.There are some issues with the current code, as it does not display the second set of circles too.
Could someone please help me understand further how to do this. It would be a great way for me to learn.
You can select objects by class name and set data. Here is my fast solution for drag-n-drop: jsFiddle. You can modify drag function to add limits to cx position

Unexpected transition behaviour D3

I am trying to loop over a three-fold D3 transition. For some reason the last command seems to be ignored by the script and I am not sure why.
The circle in the below script should diminish in radius and transition to white colour fill. I am not sure why it isn't -and nothing is cropping up in the console..?
Any ideas -and hoping it isn't something ridiculous. http://jsfiddle.net/Guill84/ww1r42ym/
Full code of transition:
function transitionx(size) {
marker.transition()
.duration(7500)
.ease('quad')
.style("fill", "red")
.attr("r", size)
.each("end", function() {
marker.transition()
.attr("r", size * 1.2)
.duration(3000)
.each("end", function() {
marker.transition()
.attr("r", size / 1.2)
.duration(3000)
.style("fill", "white")
.each("end", transitionx(size))
})
})
}
When you do this:
.each("end", transitionx(size))
You're calling transitionx immediately and passing its result.
If transitionx had no arguments, this would work:
.each("end", transitionx)
But, since transitionx has arguments, what you're doing right now is equivalent to:
.each("end", transitionx())
Solution: You'll have to wrap it in a function:
.each("end", function() {
transitionx(size)
})
Here is your code with that change (I divided all durations by 10, to make it quicker):
var size = 40
//Create a sized SVG surface within viz:
var sampleSVG = d3.select("body")
.append("svg")
.attr("width", 200)
.attr("height", 200);
//Add to the svg surface a circle, with size position,
var marker =
sampleSVG.append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", size)
.attr("cx", 50)
.attr("cy", 50)
transitionx(size);
//give the object some behaviour:
function transitionx(size) {
marker.transition()
.duration(750)
.ease('quad')
.style("fill", "red")
.attr("r", size)
.each("end", function() {
marker.transition()
.attr("r", size * 1.2)
.duration(300)
.each("end", function() {
marker.transition()
.attr("r", size / 1.2)
.duration(300)
.style("fill", "white")
.each("end", function() {
transitionx(size)
})
})
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

How do I animate the thickness of a stroke in D3.js?

i've successfully added transitions to my circles in a node graph, but i'm now trying to animate the mouseover of the connective line.
here's what I've tried:
//define the lines:
var edges = svg.selectAll("line")
.data(dataset.edges)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1)
.on("mouseover", lineMouseover)
.on("mouseout", lineMouseout);
//the callback functions for mouseover / mouseout
function lineMouseover() {
d3.select(this).select("line")
.transition()
.duration(100)
.style("stroke-width", 3);
}
function lineMouseout() {
d3.select(this).select("line")
.transition()
.duration(100)
.style("stroke-width", 1);
}
Nothing seems to happen at all when i mouse over the lines. so, either i'm capturing the line incorrectly, or the attributes i'm animating are the wrong attributes.
any insight into what I'm doing wrong here?
In your code, the thiscontext in the lineMouseOverand lineMouseOut functions is the line element. You could simply use d3.select(this)to select each line and set its attributes. I wrote a small fiddle http://jsfiddle.net/pnavarrc/4fgv4/2
svg.selectAll('path')
.data(data)
.enter()
.append('path')
.attr('d', function(d) { return line(d.p); })
.attr('stroke-width', function(d) { return d.w; })
.attr('stroke', function(d) { return d.c; })
.on('mouseover', mOver)
.on('mouseout', function(d) {
d3.select(this)
.transition()
.duration(300)
.style('stroke-width', d.w);
});
function mOver(d) {
d3.select(this)
.transition()
.duration(300)
.style('stroke-width', 6);
}
Regards,

Categories