- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
TL;DR:我编写了基于绘图的 javascript 数学摆模拟。它工作起来很慢。我正在寻找有关如何优化它的想法。目前正在尝试“裸”d3.js 并努力解决 SVG 坐标和我自己的逻辑坐标之间的坐标转换问题。
我正在编写关于常微分方程的网络教科书,希望包括数学摆的交互式模拟和可视化。可视化应包含摆本身、其势能图和全能等值线图。然后用户可以通过点击能量等值线图来选择初始条件,然后动画应该开始显示点如何在相空间中移动。
我写了一个这样的模拟例子:
https://jsfiddle.net/ischurov/p1krqnt6/
它是基于 plotly 的。我创建了三个轴并在其中放置了必要的图表。表示系统当前状态的点也是 plotly 中的图形(即只有一个点的散点图)。
动画的工作原理如下:我得到相空间中点的当前坐标,一段时间后计算这个点的新位置,然后根据这个新位置更新我的图形。对应代码:
var div = document.getElementById('myDiv');
function updateState(phi, v) {
var update = {x: [[phi], [phi], [0, Math.sin(phi)]], y: [[v],
[PotentialEnergy(phi)], [0, -Math.cos(phi)]]};
Plotly.restyle(div, update, [phaseDotIndex, 3, 4]);
}
myPlot.on('plotly_click', function(data){
if(data.points[0].data.type == 'contour'){
updateState(data.points[0].x, data.points[0].y);
}
});
var animate = null;
$('.animate_button').click(function(){
var div = document.getElementById('myDiv');
if(animate === null) {
var phi = div.data[phaseDotIndex].x[0],
v = div.data[phaseDotIndex].y[0],
E = FullEnergy(phi, v);
animate = setInterval(function() {
var phi = div.data[phaseDotIndex].x[0],
v = div.data[phaseDotIndex].y[0],
step = 0.1, newphi, newv, update;
newphi = phi + v * step;
newv = v + step * Force(phi);
/* skip some tweaks here */
updateState(phi, v);
},
100)
}
else
{
clearInterval(animate);
animate = null;
}
}
此代码几乎按预期工作,但确实很慢且不流畅 — 至少在 Firefox 下(如果我减少更新间隔,它会工作得更糟)。
我正在寻找优化方法。
我认为性能问题是由于 plotly 的更新过程造成的:为了移动一个点,它必须重新计算整个图片,而且速度很慢。所以我正在寻找以不同方式做到这一点的方法。
有什么想法吗?
我正在寻找一些可以更快的直接 d3.js 方法。我在这里看到以下步骤:
要继续第 1 步,我可以使用第三方 d3.js 库,例如 conrec对于等高线图和/或优秀的maurizzzio's function plot甚至 plotly 本身(但我不打算使用 plotly 来更新图表)。第2步似乎是可行的,但我还没有尝试过。目前最困难的是第 3 步和第 4 步,因为我不明白如何将 SVG 坐标转换为我的图形坐标(使用某些库绘制),反之亦然。
或者也许有更简单的方法来做到这一点?
最佳答案
我是 function plot 的作者它建立在 d3 之上,幸运的是 d3 有在 d3-scale 中执行映射的方法。所以假设你有一个 width x height
尺寸的 Canvas ,它应该线性映射到二维欧氏空间中的矩形 [xMin, yMin] x [xMax, yMax]
你需要创建两个线性刻度
var xScale = d3.scale.linear()
.domain([xMin, xMax])
.range([0, width])
var yScale = d3.scale.linear()
.domain([yMin, yMax])
.range([height, 0])
请注意,在 SVG 中,y 轴是翻转的,因此 yScale
范围也翻转了,然后任何 2D 欧氏点都转换为 SVG 坐标,如下所示
var xCanvas = xScale(point.x)
var yCanvas = yScale(point.y)
逆变换由
var xLogical = xScale.invert(point.x)
var xLogical = yScale.invert(point.y)
我使用上面的方法写给你的问题的一个可能的解决方案是
var instance = functionPlot({
target: '#demo',
disableZoom: true,
data: [{
fn: 'sin(10*(-cos(x) + y^2/2-1))',
fnType: 'implicit'
}]
})
var xScale = instance.meta.xScale
var yScale = instance.meta.yScale
var canvas = instance.canvas
var circle = canvas.append('circle')
.attr("r", 5)
.style("fill", "purple")
var start = Date.now()
function animate() {
// move the point along the circle of radius 1
var t = (Date.now() - start) * 0.003
var xLogical = Math.cos(t)
var yLogical = Math.sin(t)
var xCanvas = xScale(xLogical)
var yCanvas = yScale(yLogical)
circle
.attr('cx', xCanvas)
.attr('cy', yCanvas)
requestAnimationFrame(animate)
}
animate()
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://maurizzzio.github.io/function-plot/js/function-plot.js"></script>
<div id="demo"></div>
关于javascript - 优化钟摆动画(javascript),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36066080/
我是一名优秀的程序员,十分优秀!