平滑地将可旋转物体捕捉到特定角度

3

我已经在这个问题上研究了一段时间,但似乎找不到解决方案(我和三角函数不是朋友)。 我正在尝试构建一个元素,用户可以“抓住”并旋转。我已经让它工作了,除了最后一个功能我似乎无法解决: http://codepen.io/megakoresh/pen/WbLLzZ?editors=001

(很长,但我只问snapTo()函数应该如何工作)。

我想要的是对象根据increments值捕捉度数。这意味着如果snap==true,则代码应计算释放鼠标时最接近的估计target,并基于旋转方向平滑旋转对象到该target旋转: enter image description here 由于对象被“抓住”,所以我计算了鼠标按下时对象的当前旋转偏移量,因此它不会只是跟随鼠标捕捉。

因此,在这种情况下,用户顺时针旋转对象,并在对象旋转介于90°和45°之间时释放鼠标。由于方向(由angle变量的符号确定)是正的,因此目标将在当前旋转之后。

任务是计算该目标,然后平滑地将对象旋转到该目标。

我编写的函数基于autoSpin()函数(当spin==false时执行),它采用翻转的时间指数乘数delta(根据自鼠标释放以来的时间经过计算)。随着时间的推移,delta将沿着翻转指数减少,因此角度会减慢。

有一个spinTo()函数,请不要评判我,我觉得它非常愚蠢:

function snapTo() {      
    var elapsed, delta;    
    increments = (typeof increments === 'number') ? Math.floor(increments) : 4;    
    var ia = 360 / increments; //increment angle - snapping points should occur "every ia degrees"
    if (Math.abs(angle % ia) > 0) { //if user turned for more than 1 increment
      var a = lastRot % ia; //check the distance from 
      if (angle > 0){ //positive direction
        amplitude = 50; //if snapping is on, force amplitude
        target = (lastRot - a) + ia;
      }
      if (angle < 0){ //negative direction        
        amplitude = -50;
        target = lastRot - a;
      } 
    } else { //cancel the rotation
      target = rotation;
    }    
    elapsed = Date.now() - timestamp; //time passed since mouse was released
    delta = -amplitude * Math.exp(-elapsed / timeConstant); //the greater the time from mouse release, the smaller this value
    if (delta > 0.5 || delta < -0.5) { //while delta is decreasing...         
      rotate(target - delta - offset); 
      snapFrame = requestAnimationFrame(snapTo); //keep rotation
    } else {                
      rotate(target - offset); //when enough time passes (based on timeConstant), make one final rotation and stop
    }     
  }

我做错了什么?

1个回答

0

*叹气 好吧,我不得不自己想办法解决。如果有人想做这种事情,这是一种方法:

function snapTo() {      
    var elapsed, ease, diff;    
    increments = (typeof increments === 'number') ? Math.floor(increments) : 4;    
    var ia = 360 / increments; //increment angle - this is how large each increment will be
    var a = last % ia; //check the distance from point of mouse release to the previous increment    
    if (movement>0) {
      elapsed = Date.now() - timestamp; //time passed since mouse was released
      ease = 1 - Math.exp((-3*elapsed)/timeConstant); //your easing function formula goes here
      if(a > ia/2) { //if halfway through the increment...
        target = (last - a) + ia; //target next increment
      } 
      else { //else revert to previous increment to the point of mouse release
        target = last - a;
      } 
      diff = target - last; //difference between target and rotation at time of mouse release
      if(ease < 0.95){ //this depends on your easing formula
        rotate(last+diff * ease);
        requestAnimationFrame(snapTo); //don't forget to RAF
      } else { //and for your final frame...
        rotate(last+diff); //make it snappy :P
      }
      console.log(last); //traces of debugging were found in this derelict question...
    }
  }

所以在这里

  • elapsed 是鼠标释放后经过的时间
  • increments 是用户提供的参数:有多少个捕捉点
  • ia 是以度为单位计算的增量:360/increments
  • last 是记录在 mouseup 事件上的角度 - 图表中的 "当前旋转"。它包括偏移量(在我的代码中,它只是在释放点的 rotation 的 "快照",然后也反转符号,因为在 DOM 中坐标系是 y-翻转的,我不喜欢使用它)。
  • alast 比前一个最近的增量点大多少。
  • diff 是图表上的 "目标" - 最终旋转和 last 之间的差异
  • ease 只是根据您的缓动函数而变化的值,基于这个值,您可以继续调用 RAF 或完成动画。
  • timeConstant 是动画持续时间的毫秒数。您可以有或没有它,这取决于您的缓动公式。

一个好的资源可以阅读:理解缓动函数

此外,Desmos绘图计算器非常适合开发缓动公式。

哦,还有:如果有人想知道,似乎不可能将任何非时间相关的参数传递给RAF回调函数。如果这样做,似乎会破坏它。因此,唯一的解决方案是在代码的其他地方定义increments值。

工作函数的Codepen

如果有更好的解决方案,我全神贯注。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接