动态改变频率时使振荡平滑increase/decrease

Make oscillations smoothly increase/decrease when frequency being changed dynamically

我正在用 html5 canvas 模拟共振效果,以便在达到共振时为 spring 设置动画。 还获得了 jquery ui 滑块(最大范围),可在动画期间动态更改振荡频率 (w)。问题是当它发生变化时,由于某种原因正弦波在某些点中断并且动画不流畅。这只发生在改变频率时,幅度要好得多。 我渲染每一帧的主要功能是:

function doSways() {
var spring = springs[0],
        a = 0,
        A = params.standParams.A,
        w = params.standParams.w;

animIntervalId = setInterval(function() {
    ctx.clearRect(0, 0, cnvW, cnvH);
    A = params.standParams.A;       
    w = params.standParams.w;                       

    /*if (w < params.standParams.w) { // with this expression and commented <w = params.standParams.w> it works a bit smoother but still some issues can be noticed, for example sharp increases just after the new change of the frequency (w) on the slider
        w += 0.01;
    }*/

    stand.y = A*Math.sin(a*degToRad*w) + offsetY;       

    stand.draw();
    spring.draw(stand.y, A, w);

    if (a++ >= 360) {           //  avoid overflow
        a = 0;
    }
},
25);
}

这是我如何更改滑块上的频率 (w) 并将其分配给 params.standParams.w

$( "#standfreq_slider" ).slider({
    range: "max",
    min: 1,
    max: 25,
    step: 1,
    value: 5,
    slide: function( event, ui ) {
      params.standParams.w = parseInt(ui.value);

    }
  });
 );

如果 doSways 函数中的表达式有点工作但它会导致另一个问题,我需要知道滑动的方向以确定我是否需要 += 或 -= 0.01.. 如何让一切都完美运行?

现场问题说明 jsfiddle

好吧,我在下面解释的模式是我经常使用的模式。
(它很可能有一个名字 - 请 S.O。如果有,请告诉我。)

思路是这样的:每当你想要一个属性平稳发展的时候,你必须区分target值,和当前 值。如果由于某种原因 target 值发生变化,它只会影响您决定的 current 值。

要从当前值到目标值,你有很多方法:

• 最简单:只需在每次报价时使其更接近给定比率:

current += ratio * (target-current); 

其中比率在 [0, 1] 范围内,0.1 可能没问题。您会看到,比率 == 1,当前 == 目标在第一次滴答时。
请注意,此解决方案依赖于帧速率,并且您可能希望对值进行阈值处理以在某个时候获得完全相同的值,expl :

var threshold = 0.01 ;
if (Math.abs(target-current)<threshold) current = target;

• 您也可以以给定的速度到达目标:

var sign = (target - current) > 0 ? 1 : -1;
current  += sign * speedToReachTarget * dt;

现在我们不依赖于帧速率(您必须正确处理帧时间),但如果您不应用 min/max 和阈值,您将有 'bouncing':

if (Math.abs(target-current)<0.01) {
      current = target;
      return;
} 
var sign = (target - current) > 0 ? 1 : -1;
current  += sign * speedToReachTarget * dt;
current = (( sign > 0) ? Math.min : Math.max )( target, current);

• 您可能会使用许多其他类型的 interpolation/easing。

基于正弦波的基本公式:

V = V0 + A * sin(2 * pi * f * t + phi)

其中:

  • V:当前值
  • V0:中心值
  • A:振幅
  • f: 频率 (Hz)
  • t: 时间(秒)
  • phi: 相位

我们希望电流值(V)在频率变化前后相同。为此,我们需要更改前后的频率 (f) 和相位 (phi)。

首先我们可以设置第一个等式等于第二个:

V0 + A * sin(2 * pi * f1 * t + phi1) = V0 + A * sin(2 * pi * f2 * t + phi2)

做一些取消:

2 * pi * f1 * t + phi1 = 2 * pi * f2 * t + phi2

求解最终公式的新阶段:

phi2 = 2 * pi * t * (f1 - f2) + phi1

更多数学版本:

已编辑:

签出修改 fiddle:https://jsfiddle.net/potterjm/qy1s8395/1/