2017-07-25 56 views
8

我試圖創造一個球遵循運動路徑,一個類似的模擬沿曲線路徑動畫:如何利用重力

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

然而,而是採用漸變,我想球的議案,由重力對象的速度來確定, - 類似過山車,是這樣的:

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

這是什麼我有這麼遠,但有一個小問題,即過山車略有獲得能量,而不是失去它,每一幀:

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

x

就如何解決這個就任何建議非常感謝!

回答

4

你可以試試這個JS Bin。我修改了代碼以適應我對重力影響的理解。在計算中,我使用的斜率的垂直分量,在當前位置算出(在每一側上一個小的增量,並且不依賴於以前的位置):

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

路徑的限制等反應檯球桌墊。我不知道你是否打算這麼做。反彈並不總是完全彈性的,所以達到極限的時候可以有輕微的增益或能量的損失。

我還介紹了摩擦係數,這是相當粗糙,但給出了一個可能實現的想法。

因爲我不知道,如果​​在非常固定的時間間隔被執行,我把實際的時間間隔考慮在計算中。這部分可能沒有必要。

下面是完整的代碼:

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

這是輝煌的,太感謝你了! – d13

-1

你似乎在朝着正確的方向走了。 d3.js是我能想到的最好的選擇,而且已經在這裏有外觀極好回答一些d3.js重力問題:

Convert d3.js bubbles into forced/gravity based layout

Understanding D3.js Force Layout - 8: gravity也可能有一定的幫助作爲一個地方開始。

至於「任何」忠告:失敗更快的(即保持編寫和測試一樣快的週期和自動地),以及文件,當您去,(將來你會很感激。)