Skip to content

Instantly share code, notes, and snippets.

@alizbazar
Forked from anonymous/index.html
Last active March 2, 2018 17:47
Show Gist options
  • Save alizbazar/5c2b1e0ebdeafca6f38c44dfa144e9cc to your computer and use it in GitHub Desktop.
Save alizbazar/5c2b1e0ebdeafca6f38c44dfa144e9cc to your computer and use it in GitHub Desktop.
Animated ball rolling on a path emulating gravity https://jsbin.com/fuvomoz/1
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<style>
#line{
width: 100%;
margin: 20px 0;
height: 300px;
background: #eee;
}
path {
stroke: steelblue;
stroke-width: 1;
fill: none;
}
input {
width: 40px;
}
</style>
Speed: <input id="txtSpeed" type="text" />
Friction: <input id="txtFriction" type="text" />
&nbsp;&nbsp;
<button id="randomize">Restart</button>
<div id="line"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
</script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<script id="jsbin-javascript">
var svg = d3.select("#line").append("svg:svg").attr("width", "100%").attr("height", "100%");
var data = d3.range(50).map(function(){return Math.random()*10})
var x = d3.scale.linear().domain([0, 10]).range([0, 700]);
var y = d3.scale.linear().domain([0, 10]).range([10, 290]);
var line = d3.svg.line()
.interpolate("cardinal")
.x(function(d,i) {return x(i);})
.y(function(d) {return y(d);})
var path = svg.append("svg:path").attr("d", line(data));
var circle =
svg.append("circle")
.attr("cx", 100)
.attr("cy", 350)
.attr("r", 3)
.attr("fill", "red");
var circleBehind =
svg.append("circle")
.attr("cx", 50)
.attr("cy", 300)
.attr("r", 3)
.attr("fill", "blue");
var circleAhead =
svg.append("circle")
.attr("cx", 125)
.attr("cy", 375)
.attr("r", 3)
.attr("fill", "green");
var pathEl = path.node();
var pathLength = pathEl.getTotalLength();
var BBox = pathEl.getBBox();
var scale = pathLength/BBox.width;
var offsetLeft = document.getElementById("line").offsetLeft;
var randomizeButton = d3.select("#randomize");
var pathPos = 600;
var pos = {x: 0, y: 0};
var speed = 10;
var friction = 0;
var direction = true;
var gravity = 0.01;
var maxRange = 1500;
var speedChange;
var currentTime, prevTime, diffTime;
function getEffectiveGravityFactor() {
// Get the vertical component of the current slope
var delta = 0.001;
var pathPos1 = Math.min(maxRange, Math.max(delta, pathPos));
var pos1 = pathEl.getPointAtLength(pathPos1 - delta);
var pos2 = pathEl.getPointAtLength(pathPos1 + delta);
var dx, dy;
if (direction) {
dx = pos2.x - pos1.x;
dy = pos2.y - pos1.y;
} else {
dx = pos1.x - pos2.x;
dy = pos1.y - pos2.y;
}
var total = Math.sqrt(dx * dx + dy * dy);
return dy / total;
}
function play() {
requestAnimationFrame(play);
currentTime = Date.now();
diffTime = currentTime - prevTime;
if (diffTime > 20) {
prevTime = currentTime;
if (pathPos < 0 || pathPos > maxRange) {
// The limit was reached: change direction
direction = !direction;
pathPos = Math.min(maxRange, Math.max(0, pathPos));
} else {
speedChange = gravity * diffTime * getEffectiveGravityFactor();
if (speedChange < -speed) {
// Direction change caused by gravity
direction = !direction;
speed = 0;
} else {
speed += speedChange;
speed = Math.max(0, speed - friction * diffTime * (0.0002 + 0.00002 * speed));
}
}
pathPos += (direction ? 1 : -1) * speed;
pos = pathEl.getPointAtLength(pathPos);
circle
.attr("opacity", 1)
.attr("cx", pos.x)
.attr("cy", pos.y);
posBehind = pathEl.getPointAtLength(pathPos - 10);
circleBehind
.attr("opacity", 1)
.attr("cx", posBehind.x)
.attr("cy", posBehind.y);
posAhead = pathEl.getPointAtLength(pathPos + 10);
circleAhead
.attr("opacity", 1)
.attr("cx", posAhead.x)
.attr("cy", posAhead.y);
}
}
prevTime = Date.now();
play();
var txtSpeed = document.getElementById("txtSpeed");
var txtFriction = document.getElementById("txtFriction");
txtSpeed.value = speed;
txtFriction.value = friction;
randomizeButton.on("click", function(){
speed = parseFloat(txtSpeed.value);
friction = parseFloat(txtFriction.value);
pathPos = 400;
direction = true;
prevTime = Date.now();
data = d3.range(50).map(function(){return Math.random()*10;});
circle.attr("opacity", 0);
path
.transition()
.duration(300)
.attr("d", line(data));
});
</script>
<script id="jsbin-source-html" type="text/html"><!DOCTYPE html>
<meta charset="utf-8">
<body>
<style>
#line{
width: 100%;
margin: 20px 0;
height: 300px;
background: #eee;
}
path {
stroke: steelblue;
stroke-width: 1;
fill: none;
}
input {
width: 40px;
}
</style>
Speed: <input id="txtSpeed" type="text" />
Friction: <input id="txtFriction" type="text" />
&nbsp;&nbsp;
<button id="randomize">Restart</button>
<div id="line"></div>
<script src="//d3js.org/d3.v3.min.js"><\/script>
<script>
<\/script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
</body>
</html></script>
<script id="jsbin-source-javascript" type="text/javascript">var svg = d3.select("#line").append("svg:svg").attr("width", "100%").attr("height", "100%");
var data = d3.range(50).map(function(){return Math.random()*10})
var x = d3.scale.linear().domain([0, 10]).range([0, 700]);
var y = d3.scale.linear().domain([0, 10]).range([10, 290]);
var line = d3.svg.line()
.interpolate("cardinal")
.x(function(d,i) {return x(i);})
.y(function(d) {return y(d);})
var path = svg.append("svg:path").attr("d", line(data));
var circle =
svg.append("circle")
.attr("cx", 100)
.attr("cy", 350)
.attr("r", 3)
.attr("fill", "red");
var circleBehind =
svg.append("circle")
.attr("cx", 50)
.attr("cy", 300)
.attr("r", 3)
.attr("fill", "blue");
var circleAhead =
svg.append("circle")
.attr("cx", 125)
.attr("cy", 375)
.attr("r", 3)
.attr("fill", "green");
var pathEl = path.node();
var pathLength = pathEl.getTotalLength();
var BBox = pathEl.getBBox();
var scale = pathLength/BBox.width;
var offsetLeft = document.getElementById("line").offsetLeft;
var randomizeButton = d3.select("#randomize");
var pathPos = 600;
var pos = {x: 0, y: 0};
var speed = 10;
var friction = 0;
var direction = true;
var gravity = 0.01;
var maxRange = 1500;
var speedChange;
var currentTime, prevTime, diffTime;
function getEffectiveGravityFactor() {
// Get the vertical component of the current slope
var delta = 0.001;
var pathPos1 = Math.min(maxRange, Math.max(delta, pathPos));
var pos1 = pathEl.getPointAtLength(pathPos1 - delta);
var pos2 = pathEl.getPointAtLength(pathPos1 + delta);
var dx, dy;
if (direction) {
dx = pos2.x - pos1.x;
dy = pos2.y - pos1.y;
} else {
dx = pos1.x - pos2.x;
dy = pos1.y - pos2.y;
}
var total = Math.sqrt(dx * dx + dy * dy);
return dy / total;
}
function play() {
requestAnimationFrame(play);
currentTime = Date.now();
diffTime = currentTime - prevTime;
if (diffTime > 20) {
prevTime = currentTime;
if (pathPos < 0 || pathPos > maxRange) {
// The limit was reached: change direction
direction = !direction;
pathPos = Math.min(maxRange, Math.max(0, pathPos));
} else {
speedChange = gravity * diffTime * getEffectiveGravityFactor();
if (speedChange < -speed) {
// Direction change caused by gravity
direction = !direction;
speed = 0;
} else {
speed += speedChange;
speed = Math.max(0, speed - friction * diffTime * (0.0002 + 0.00002 * speed));
}
}
pathPos += (direction ? 1 : -1) * speed;
pos = pathEl.getPointAtLength(pathPos);
circle
.attr("opacity", 1)
.attr("cx", pos.x)
.attr("cy", pos.y);
posBehind = pathEl.getPointAtLength(pathPos - 10);
circleBehind
.attr("opacity", 1)
.attr("cx", posBehind.x)
.attr("cy", posBehind.y);
posAhead = pathEl.getPointAtLength(pathPos + 10);
circleAhead
.attr("opacity", 1)
.attr("cx", posAhead.x)
.attr("cy", posAhead.y);
}
}
prevTime = Date.now();
play();
var txtSpeed = document.getElementById("txtSpeed");
var txtFriction = document.getElementById("txtFriction");
txtSpeed.value = speed;
txtFriction.value = friction;
randomizeButton.on("click", function(){
speed = parseFloat(txtSpeed.value);
friction = parseFloat(txtFriction.value);
pathPos = 400;
direction = true;
prevTime = Date.now();
data = d3.range(50).map(function(){return Math.random()*10;});
circle.attr("opacity", 0);
path
.transition()
.duration(300)
.attr("d", line(data));
});
</script></body>
</html>
var svg = d3.select("#line").append("svg:svg").attr("width", "100%").attr("height", "100%");
var data = d3.range(50).map(function(){return Math.random()*10})
var x = d3.scale.linear().domain([0, 10]).range([0, 700]);
var y = d3.scale.linear().domain([0, 10]).range([10, 290]);
var line = d3.svg.line()
.interpolate("cardinal")
.x(function(d,i) {return x(i);})
.y(function(d) {return y(d);})
var path = svg.append("svg:path").attr("d", line(data));
var circle =
svg.append("circle")
.attr("cx", 100)
.attr("cy", 350)
.attr("r", 3)
.attr("fill", "red");
var circleBehind =
svg.append("circle")
.attr("cx", 50)
.attr("cy", 300)
.attr("r", 3)
.attr("fill", "blue");
var circleAhead =
svg.append("circle")
.attr("cx", 125)
.attr("cy", 375)
.attr("r", 3)
.attr("fill", "green");
var pathEl = path.node();
var pathLength = pathEl.getTotalLength();
var BBox = pathEl.getBBox();
var scale = pathLength/BBox.width;
var offsetLeft = document.getElementById("line").offsetLeft;
var randomizeButton = d3.select("#randomize");
var pathPos = 600;
var pos = {x: 0, y: 0};
var speed = 10;
var friction = 0;
var direction = true;
var gravity = 0.01;
var maxRange = 1500;
var speedChange;
var currentTime, prevTime, diffTime;
function getEffectiveGravityFactor() {
// Get the vertical component of the current slope
var delta = 0.001;
var pathPos1 = Math.min(maxRange, Math.max(delta, pathPos));
var pos1 = pathEl.getPointAtLength(pathPos1 - delta);
var pos2 = pathEl.getPointAtLength(pathPos1 + delta);
var dx, dy;
if (direction) {
dx = pos2.x - pos1.x;
dy = pos2.y - pos1.y;
} else {
dx = pos1.x - pos2.x;
dy = pos1.y - pos2.y;
}
var total = Math.sqrt(dx * dx + dy * dy);
return dy / total;
}
function play() {
requestAnimationFrame(play);
currentTime = Date.now();
diffTime = currentTime - prevTime;
if (diffTime > 20) {
prevTime = currentTime;
if (pathPos < 0 || pathPos > maxRange) {
// The limit was reached: change direction
direction = !direction;
pathPos = Math.min(maxRange, Math.max(0, pathPos));
} else {
speedChange = gravity * diffTime * getEffectiveGravityFactor();
if (speedChange < -speed) {
// Direction change caused by gravity
direction = !direction;
speed = 0;
} else {
speed += speedChange;
speed = Math.max(0, speed - friction * diffTime * (0.0002 + 0.00002 * speed));
}
}
pathPos += (direction ? 1 : -1) * speed;
pos = pathEl.getPointAtLength(pathPos);
circle
.attr("opacity", 1)
.attr("cx", pos.x)
.attr("cy", pos.y);
posBehind = pathEl.getPointAtLength(pathPos - 10);
circleBehind
.attr("opacity", 1)
.attr("cx", posBehind.x)
.attr("cy", posBehind.y);
posAhead = pathEl.getPointAtLength(pathPos + 10);
circleAhead
.attr("opacity", 1)
.attr("cx", posAhead.x)
.attr("cy", posAhead.y);
}
}
prevTime = Date.now();
play();
var txtSpeed = document.getElementById("txtSpeed");
var txtFriction = document.getElementById("txtFriction");
txtSpeed.value = speed;
txtFriction.value = friction;
randomizeButton.on("click", function(){
speed = parseFloat(txtSpeed.value);
friction = parseFloat(txtFriction.value);
pathPos = 400;
direction = true;
prevTime = Date.now();
data = d3.range(50).map(function(){return Math.random()*10;});
circle.attr("opacity", 0);
path
.transition()
.duration(300)
.attr("d", line(data));
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment