Ty możesz spróbować tego JS Bin. Zmodyfikowałem kod, aby dopasować się do mojego rozumienia skutków grawitacji. W obliczeniach użyć składowe pionowe zbocza, obliczoną w bieżącym położeniu (przy użyciu niewielkiej delta z każdej strony, nie powołując się na poprzednim stanowisku)
function getEffectiveGravityFactor() {
// Get the vertical component of the slope at current position
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;
}
Granice drodze reakcji jak poduszki na stół bilardowy. Nie wiem, czy to właśnie zamierzasz zrobić. Odbicie nie zawsze jest doskonale sprężyste, więc po osiągnięciu limitu może nastąpić nieznaczne wzmocnienie lub utrata energii.
Wprowadziłem również współczynnik tarcia, który jest dość trudny, ale daje wyobrażenie o możliwej implementacji.
Ponieważ nie jestem pewien, czy requestAnimationFrame
jest wykonywany w bardzo stałych odstępach czasu, wziąłem pod uwagę faktyczny przedział czasu w obliczeniach. Ta część może nie być konieczna.
Oto kompletny kod:
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 slope at current position
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));
});
To jest genialne, bardzo dziękuję !!! – d13