Javascript 如何为跟踪鼠标移动的动画添加惯性?

Javascript 如何为跟踪鼠标移动的动画添加惯性?,javascript,canvas,pixi.js,Javascript,Canvas,Pixi.js,我正在尝试使用mouseMove事件围绕原点旋转三角形。 我使用touchstart和touchsmove事件获取触摸起点和当前触摸点,然后使用以下方法轻松找到方向角: alpha = y2 - y1 / x2 - x1; // alpha is the tangent of the angle beta= atan(alpha); // beta is the angle in radians 然后我在PIXI中旋转元素: function animTriangle (be

我正在尝试使用mouseMove事件围绕原点旋转三角形。
我使用
touchstart
touchsmove
事件获取触摸起点和当前触摸点,然后使用以下方法轻松找到方向角:

alpha = y2 - y1 / x2 - x1;     // alpha is the tangent of the angle
beta= atan(alpha);      // beta is the angle in radians
然后我在
PIXI
中旋转元素:

function animTriangle (beta) {

  // initially clear the previous shape
  myTriangle.clear();


  // draw a new shape in new positions
  myTriangle.beginFill(0x000000, 0.1);
  myTriangle.moveTo(origin.x, origin.y);
  myTriangle.lineTo(origin.x - 100, 0);
  myTriangle.lineTo(origin.x + 100, 0);
  myTriangle.lineTo(origin.x, origin.y);
  myTriangle.endFill();
  myTriangle.rotation = beta;

}
我正在使用
RequestAnimationFrame
loop来管理我的绘画。


问题是动画是起伏的,我需要转动的惯性。如何修复此函数?

尝试添加另一个变量,
betaCumulative
,并以较小的增量调整其值,如下所示:

if(beta < betaCumulative)betaCumulative += .01;
if(beta > betaCumulative)betaCumulative -= .01;
myTriangle.rotation = betaCumulative;
if(betaβ累积)β累积-=.01;
myTriangle.rotation=betaCumulative;

惯性、加速度和阻力

我使用的一种方法是使用模拟加速度和阻力(阻力)值的deltaV创建追逐值,以追逐所需的值。注意,这是一个简单的模拟

一步一步

定义所需值

var rotate = ?; // the value input by the user
var rotateChase; // this is the chasing value representing the displayed control
var rotateDelta; // this is the change in chase per frame
const rotateDrag = 0.4; // this is the friction or drag
const rotateAcceleration = 0.9; // this is how quickly the display responds
阻力是大于0且=0.5的值,会导致追逐值围绕所需值反弹,随着阻力值向1移动,反弹变得越来越明显

加速度值的范围大于0,=-1将从两端产生小的反弹。其他值创建有趣的FX

var rotateReflect = -0.5;  
然后是控制这种行为的代码

if (rotateChase < rotateMin) { 
   rotateChase = rotateMin;  // set to the min val
   if(rotateDelta < 0){      // only if delta is negative
       rotateDelta *= rotateReflect;
   }
}else
if (rotateChase > rotateMax) {
   rotateChase = rotateMax;  // set to the max
   if(rotateDelta > 0){      // only if delta is positive
       rotateDelta *= rotateReflect;
   }
}
让它变得简单

所有接缝都需要为一个值做大量工作。但我们是程序员,天生懒惰,所以让我们把它划分成一个简单的惰性类

// Define a Inertia object. Set Answer for details.
// Has methods 
// update(input); Called once pre animation frame with input being the value to chase
// setValue(input); Hard sets the chasing value. Not drag or inertia
// Has properties
// value;  The chasing value bounds checked
function Inertia (min, max, acceleration, drag, reflect) {
    // some may question why the constants, why not use the closure on arguments
    // Reason: Some JS environments will fail to optimise code if the input
    //         arguments change. It may be tempting to extend this Object to 
    //         change the min, max or others. I put this here to highlight the
    //         fact that argument closure variables should not be modified
    //         if performance is important.
    const ac = acceleration;  // set constants
    const dr = drag;
    const minV = min;
    const maxV = max;
    const ref = -Math.abs(reflect); // ensure a negative. Why? because I always forget this is a negative.
    this.value = min;
    var delta = 0;

    this.update = function (input) {
         delta += (input - this.value) * ac;
         delta *= dr;
         this.value += delta;
         if (this.value < minV) {
             this.value = minV;
             if(delta < 0){
                 delta *= ref;
             }
         } else
         if (this.value > maxV) {
             this.value = maxV;
             if(delta > 0){
                 delta *= ref;
             }
         }
         return this.value;
     };
     // this move the value to the required value without any inertial or drag
     // is bound checked
     this.setValue = function (input) {
         delta = 0;
         this.value = Math.min(maxV, Math.min(minV, input));
         return this.value;
     }
 }
更新

我添加了一些代码来显示各种设置以及它们如何影响惯性。该代码并不是作为代码样式或DOM接口最佳实践的示例,因为它在这两方面都远远不够。它使用我上面介绍的惯性物体。您可以在演示代码的顶部找到该对象

演示最好是全屏观看

//------------------------------------------------------
//答案惯性函数
//定义惯性对象。设置详细信息的答案。
//有办法
//更新(输入);调用一次动画前帧,输入为要追踪的值
//设置(输入);硬设置追逐值。不是阻力或惯性
//有属性
//价值观;值边界已检查
功能惯性(最小、最大、加速度、阻力、反射){
//有些人可能会问,为什么不在参数上使用闭包呢
//原因:如果输入
//参数更改。将此对象扩展到
//更改最小值、最大值或其他值。我将此项放在此处以突出显示
//不应修改参数闭包变量这一事实
//如果性能很重要。
常数ac=加速度;//设置常数
常数dr=阻力;
常数minV=min;
常数maxV=最大值;
const ref=-Math.abs(reflect);//确保为负数。为什么?因为我总是忘记这是负数。
该值=最小值;
这是真的;
var-delta=0;
this.update=函数(输入){
增量+=(输入-该值)*ac;
delta*=dr;
该值+=δ;
如果(该值<最小值){
this.value=minV;
if(δ<0){
δ*=ref;
}
}否则
如果(this.value>maxV){
该值=最大值;
如果(增量>0){
δ*=ref;
}
}
if(数学绝对值(增量)<(最大最小值)*0.001和&数学绝对值(此值输入)<0.1){
这是真的;
}否则{
this.quiet=false;
}
返回此.value;
};
//这将在没有任何惯性或阻力的情况下将值移动到所需值
//绑定是否已检查
this.setValue=函数(输入){
δ=0;
这是真的;
this.value=Math.min(maxV,Math.max(minV,input));
返回此.value;
}
}
//答案结束
//--------------------------------------------------------
//以下所有代码都不是答案的一部分。
//我没有格式化、注释和彻底测试它
/**mouseouldemo.js begin**/
var canvasMouseCallBack=未定义;//如果需要
函数createMouse(元素){
var demouse=(函数(){
变量鼠标={
x:0,y:0,w:0,alt:false,shift:false,ctrl:false,
lx:0,ly:0,
interfaceId:0,ButtonLaw:0,buttonRaw:0,
over:false,//鼠标位于元素上方
bm:[1,2,4,6,5,3],//用于设置和清除按钮原始位的掩码;
getInterfaceId:function(){返回this.interfaceId++;},//用于UI函数
startMouse:未定义,
};
函数mouseMove(e){
//控制台日志(e)
var t=e.type,m=mouse;
m、 lx=e.offsetX;m.ly=e.offsetY;
m、 x=e.clientX;m.y=e.clientY;
m、 alt=e.altKey;m.shift=e.shiftKey;m.ctrl=e.ctrlKey;
如果(t==“mousedown”){m.buttonRaw |=m.bm[e.which-1];
}如果(t==“mouseup”){m.buttonRaw&=m.bm[e.which+2];
}else如果(t==“mouseout”){m.over=false;
}else如果(t==“mouseover”){m.over=true;
}如果(t==“鼠标轮”){m.w=e.wheelDelta;
}else如果(t==“DOMMouseScroll”){m.w=-e.detail;}
if(canvasMouseCallBack){canvasMouseCallBack(m.x,m.y);}
e、 预防默认值();
}
功能启动模块(元件){
如果(元素===未定义){
元素=文件;
}
“mousemove,mousedown,mouseup,mouseout,mouseover,mo
myTriangle.rotation = rotateChase;
var rotateMin = 0;
var rotateMax = Math.PI*2;
var rotateReflect = -0.5;  
if (rotateChase < rotateMin) { 
   rotateChase = rotateMin;  // set to the min val
   if(rotateDelta < 0){      // only if delta is negative
       rotateDelta *= rotateReflect;
   }
}else
if (rotateChase > rotateMax) {
   rotateChase = rotateMax;  // set to the max
   if(rotateDelta > 0){      // only if delta is positive
       rotateDelta *= rotateReflect;
   }
}
device.volume = Number(rotateChase.toFixed(3));
// Define a Inertia object. Set Answer for details.
// Has methods 
// update(input); Called once pre animation frame with input being the value to chase
// setValue(input); Hard sets the chasing value. Not drag or inertia
// Has properties
// value;  The chasing value bounds checked
function Inertia (min, max, acceleration, drag, reflect) {
    // some may question why the constants, why not use the closure on arguments
    // Reason: Some JS environments will fail to optimise code if the input
    //         arguments change. It may be tempting to extend this Object to 
    //         change the min, max or others. I put this here to highlight the
    //         fact that argument closure variables should not be modified
    //         if performance is important.
    const ac = acceleration;  // set constants
    const dr = drag;
    const minV = min;
    const maxV = max;
    const ref = -Math.abs(reflect); // ensure a negative. Why? because I always forget this is a negative.
    this.value = min;
    var delta = 0;

    this.update = function (input) {
         delta += (input - this.value) * ac;
         delta *= dr;
         this.value += delta;
         if (this.value < minV) {
             this.value = minV;
             if(delta < 0){
                 delta *= ref;
             }
         } else
         if (this.value > maxV) {
             this.value = maxV;
             if(delta > 0){
                 delta *= ref;
             }
         }
         return this.value;
     };
     // this move the value to the required value without any inertial or drag
     // is bound checked
     this.setValue = function (input) {
         delta = 0;
         this.value = Math.min(maxV, Math.min(minV, input));
         return this.value;
     }
 }
// in init
var rotater = new Inertia(0, Math.PI*2, 0.9, 0.4, -0.1);

// in the animation frame
myTriange = rotater.update(beta);