当频率动态变化时,使振荡平滑地增加/减少

时间:2015-09-17 14:36:35

标签: javascript animation html5-canvas

我用html5画布模拟共振效果,使弹簧达到共振时动画。 还得到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

2 个答案:

答案 0 :(得分:1)

基于正弦曲线的基本公式:

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

其中:

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

我们希望频率变化之前和之后的当前值(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

更多mathy版本:

final formula

编辑:

查看修改过的小提琴:https://jsfiddle.net/potterjm/qy1s8395/1/

答案 1 :(得分:0)

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

以下是这样的想法:每当您需要以平稳的方式进行属性演变时,您必须区分目标值和当前值。如果出于某种原因,目标值发生变化,则只会以您决定的方式影响当前值。

要从当前值获取目标值,您有多种方法:

•最简单:只需在每个刻度上与给定比率更接近:

current += ratio * (target-current); 

其中比率为[0,1],0.1可能没问题。您可以看到,在第一次打勾时,比率== 1,当前==目标  请注意,此解决方案依赖于帧速率,并且您可能希望将值阈值化以在某个时刻获得相同的值,请探索:

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

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

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

现在我们不依赖于帧速率(你必须正确处理帧时间),但如果你没有应用最小值/最大值和阈值,你将会“弹跳”:

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

•您可以使用许多其他类型的插值/缓动。