平滑地将可旋转的对象捕捉到一定程度
Smoothly snap a rotatable object to degree
我已经研究这个问题一段时间了,似乎找不到解决方案(我不是三角函数的朋友)。
我正在尝试构建一个用户可以 "grab" 然后旋转的元素。我让它工作,除了最后一个我似乎无法弄清楚的功能:
http://codepen.io/megakoresh/pen/WbLLzZ?editors=001
(很长,但我只是想问一下 snapTo() 函数应该如何工作)。
我想要的是根据 increments
值将对象捕捉到度数。这意味着如果 snap==true
,代码应该计算离鼠标释放点最近的估计 target
s,并根据旋转方向平滑地将对象旋转到那个 target
旋转:
由于对象是 'grabbed',我计算了 mousedown 到对象当前旋转的偏移量,这就是它的来源,所以它不只是捕捉到鼠标。
因此在这种情况下,用户顺时针旋转对象并在对象旋转介于 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
}
}
我做错了什么?
*叹息
好吧,我必须自己想办法。如果有人想做这种事情,这里有一种方法:
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
事件 - "Current rotation" 上的角度。它包括偏移量(在我的代码中,在发布时它只是 rotation
的 "snapshot",然后符号也会反转,因为在 DOM 中坐标系是 y 翻转的并且我不喜欢和那个一起工作)。
a
是last
比上一个最近的增量点大多少。
diff
是图表上的 "target" - 最终旋转与 last
之间的区别
ease
只是根据您的缓动函数变化的值,您可以根据该值继续调用 RAF 或完成动画。
timeConstant
是以毫秒为单位的动画持续时间。你可以有这个或没有,取决于你的缓动公式
一般阅读的好资源:Understanding Easing Functions
此外 Desmos graphing calculator 非常适合开发缓动公式。
哦还有:如果有人想知道,似乎不可能将任何类型的非时间相关参数传递给 RAF 回调函数。如果我这样做,似乎会破坏它。所以唯一的解决办法是在代码的其他地方定义 increments
值。
Codepen of the working function
谁有更好的解决办法,我满眼都是
我已经研究这个问题一段时间了,似乎找不到解决方案(我不是三角函数的朋友)。 我正在尝试构建一个用户可以 "grab" 然后旋转的元素。我让它工作,除了最后一个我似乎无法弄清楚的功能: http://codepen.io/megakoresh/pen/WbLLzZ?editors=001
(很长,但我只是想问一下 snapTo() 函数应该如何工作)。
我想要的是根据 increments
值将对象捕捉到度数。这意味着如果 snap==true
,代码应该计算离鼠标释放点最近的估计 target
s,并根据旋转方向平滑地将对象旋转到那个 target
旋转:
因此在这种情况下,用户顺时针旋转对象并在对象旋转介于 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
}
}
我做错了什么?
*叹息 好吧,我必须自己想办法。如果有人想做这种事情,这里有一种方法:
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
事件 - "Current rotation" 上的角度。它包括偏移量(在我的代码中,在发布时它只是rotation
的 "snapshot",然后符号也会反转,因为在 DOM 中坐标系是 y 翻转的并且我不喜欢和那个一起工作)。a
是last
比上一个最近的增量点大多少。diff
是图表上的 "target" - 最终旋转与last
之间的区别
ease
只是根据您的缓动函数变化的值,您可以根据该值继续调用 RAF 或完成动画。timeConstant
是以毫秒为单位的动画持续时间。你可以有这个或没有,取决于你的缓动公式
一般阅读的好资源:Understanding Easing Functions
此外 Desmos graphing calculator 非常适合开发缓动公式。
哦还有:如果有人想知道,似乎不可能将任何类型的非时间相关参数传递给 RAF 回调函数。如果我这样做,似乎会破坏它。所以唯一的解决办法是在代码的其他地方定义 increments
值。
Codepen of the working function
谁有更好的解决办法,我满眼都是