2017-07-25 5 views
8

Je suis en train de créer une simulation dans laquelle une balle suit une trajectoire de mouvement, semblable à ceci:Comment animer le long d'une trajectoire courbe par gravité

https://bl.ocks.org/mbostock/1705868

Cependant, au lieu d'utiliser tweening, je voudrait le mouvement de la balle à déterminer par gravité et la vitesse de l'objet - semblable à des montagnes russes, comme ceci:

https://www.myphysicslab.com/roller/roller-single-en.html

C'est ce que J'ai jusqu'à présent, mais il y a un petit problème dans ce que les montagnes russes gagne un peu d'énergie, au lieu de le perdre, chaque image:

https://jsbin.com/jidazom/edit?html,js,output

x

Toutes les suggestions sur la façon de résoudre ce problème serait soyez grandement apprécié!

Répondre

4

Vous peut essayer ceci JS Bin. J'ai modifié le code pour correspondre à ma propre compréhension des effets de la gravité. Dans les calculs, j'utilise la composante verticale de la pente, calculé à la position courante (à l'aide d'un petit delta de chaque côté, et ne pas compter sur la position précédente):

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; 
} 

Les limites de la trajectoire réagissent comme coussins de table de billard. Je ne sais pas si c'est ce que tu as l'intention de faire. Le rebond n'est pas toujours parfaitement élastique, il peut donc y avoir un léger gain ou perte d'énergie lorsqu'une limite est atteinte. J'ai également introduit un coefficient de frottement, assez grossier mais qui donne une idée d'une éventuelle mise en œuvre.

Puisque je ne suis pas sûr si requestAnimationFrame est exécuté à des intervalles très fixes, j'ai pris en compte l'intervalle de temps réel dans les calculs. Cette partie peut ne pas être nécessaire.

Voici le code complet:

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)); 
}); 
+1

C'est génial, merci beaucoup !!! – d13

-1

Vous semblez aller dans la bonne direction. d3.js est la meilleure option que je peux penser, et il y a des questions de gravité de d3.js déjà ici qui ont des réponses excelent:

Convert d3.js bubbles into forced/gravity based layout

Understanding D3.js Force Layout - 8: gravity pourrait aussi être d'une aide en tant que point de départ.

En ce qui concerne « tout » conseil: Fail rapide, (c.-à garder le cycle de l'écriture et de test aussi rapide et automatisée que possible), et le document que vous allez, (futur vous sera reconnaissant.)