我希望能够以一定的速率运行函数,根据数学函数(如曲线)可以增加或减少函数......就像放宽easeIn
和{easeOut
等函数一样。 {1}}在CSS和JQuery中工作。
以下是“easeInOut”类型场景的粗略说明。该行代表时间,o
是函数调用。
o-o--o---o-----o----------o-----o---o--o-o
实施可能类似于:
trigger(5000, "easeInOut", callback); // Over five seconds, "callback()" is called with an easeInOut ease.
function triggerWithEase(duration, ease, callback){
// ???
}
function callback(){
console.log("Heyo!");
}
是否有针对此的Javascript / JQuery解决方案?如果没有,这怎么可能实现呢?
时序图本身的一些灵感:
对于那些实际使用我在描述中使用的小ASCII图作为功能输入的创意人,我印象深刻!我正在寻找一个更加数学解决方案,但是......
这是我正在思考的更好的例子。假设我们在20秒的持续时间内使用二次公式(ax ^ 2 + bx + c)。
传递函数一些参数并让它以正确的时间间隔触发回调真是太酷了这样做:
我正在玩的一些完全未经测试的,背面的伪造的伪代码:
function easeTrigger(callback, functionType, functionOptions, duration, numberOfCalls){
switch("functionType"){
case "quadratic":
quadtratic(functionOptions);
break;
case "sine":
sine(functionOptions);
break;
/* ... */
default:
linear(functionOptions);
break;
}
function quadratic(options){
var x = numberOfCalls;
var result;
var delay;
var timer;
for (var i = x - 1; i >= 0; i--) {
result = a * Math.pow(x, 2) + (b * x) + c;
delay = result * 1000; // using the result to calculate the time between function calls
}
// ???
for (var s = numberOfCalls - 1; s >= 0; s--) {
delay = result * 1000; // probably need to chain this somehow, a for loop might not be the best way to go about this.
timer = setTimeout(result, caller);
}
// ???
// PROFIT!
}
function caller(duration){
clearTimeout(timer);
timer = setTimeout(delay, callback);
}
}
// How this might be implemented...
easeTrigger(callback, "quadratic", {a: 1, b: 2, c:0}, 5000, 20);
easeTrigger(callback, "linear", {a: 1, b: 2}, 5000, 10);
// Callback to call at the end of every period
function callback(){
console.log("Yo!");
}
答案 0 :(得分:3)
在action
中,你应该在你提到的曲线上设置你需要做的任何事情。
function TimeLine(unit, curve, action) {
this.unit = unit;
this.curve = curve;
this.action = action;
this.tick = 0;
}
TimeLine.prototype.start = function() {
var me = this;
console.log('Start.');
me.id = setInterval(function() {
me.onRun();
}, me.unit);
me.onRun();
};
TimeLine.prototype.stop = function() {
var me = this;
console.log('Stop.');
clearInterval(me.id);
delete me.id;
};
TimeLine.prototype.onRun = function() {
var me = this;
if (me.curve.charAt(me.tick++) === 'o') {
me.action && me.action();
} else {
console.log('Idle...');
}
if (me.tick > me.curve.length) {
me.stop();
}
}
var log = function() {
console.log('Ping:', (new Date).getTime());
},
t = new TimeLine(200, 'o----o----o--o-o-o-o-ooo', log);
t.start();

修改强> 与上次编辑相关,计算一些函数将被调用的区间,一些更正:
所以你看到的时间轴是这样的:
`t0 -------------- tx --------------------------------------- tN`
并且您说在时间间隔duration
(= tN-t0)上,您将调用函数numberOfTimes
因此函数将在[t0,...,ti,.. tx]中调用,其中x = numberOfTimes
,并且每个时间都是用某个函数计算的
function quadratic(options){
var x = numberOfCalls,
delay = 0,
result, , timer;
for (var i = x - 1; i >= 0; i--) {
result = a * Math.pow(x, 2) + (b * x) + c;
delay += result * 1000; // cumulate these time values so you can lunch the call already
// so you call them in a loop but they will start at the precalculated times
setTimeout(caller, delay);
}
}
虽然我不知道如何将持续时间强制为特定的持续时间,但这将有效。除非它以某种方式作为函数的参数。
答案 1 :(得分:1)
如果您能够使用外部库,那么我建议您使用像xstream
这样的反应式库。这通常是反应式编程解决的问题!
在observables上有一个很好的方法,允许你根据图表发出的东西(这正是你所拥有的)。方法是fromDiagram
所以在你的情况下,你可以做到
import fromDiagram from 'xstream/extra/fromDiagram'
const stream = fromDiagram('o-o--o---o-----o----------o-----o---o--o-o', {
timeUnit: 20 // the number of ms, would have to be computed manually
})
stream.addListener({
next: callback
})
唯一要做的就是根据动画的持续时间和步数计算timeUnit
选项。
添加一个对象,它将一个缓动函数的名称与一个图表相关联,并将所有内容包装在一个函数中,你可以去:)
希望它有所帮助。
答案 2 :(得分:1)
假设
f(current time, start value, change in value, duration)
Google很快回答了Flash时代的宽松方程式。我从Robert Penner借了一些。
const ease = {
inQuad: (t, b, c, d) => {
t /= d
return c * t * t + b
},
outQuad: (t, b, c, d) => {
t /= d
return -c * t * (t - 2) + b
}
}
let triggerWithEase = (duration, ease, callback) => {
const rate = 12 // Lower value makes easing more distinguishable to naked eye
const totalFrames = Math.floor((duration / 1000) * rate)
for (let currentFrame = 0; currentFrame < totalFrames; currentFrame += 1) {
const delay = ease(currentFrame, 0, duration, totalFrames)
setTimeout(callback, delay)
}
}
triggerWithEase(
10000,
ease.outQuad,
() => console.log(Date.now())
)
修改:刚刚意识到我是另一个Why Can't Programmers Read示例。进行了编辑以纠正此问题。
答案 3 :(得分:1)
我喜欢这个问题以及它是如何呈现的。所以只是为了好玩,我试着以同样的方式回答。
我从here获得了一堆缓动公式,并将其应用于此案例。
这很简单。我设置easing
对象来保存所有公式。所有单选按钮'"change"
事件侦听器都分配有drawScale
函数。一旦"change"
事件被触发,它就会查找相应的函数并执行它的显示。代码被评论,希望更容易理解。
最好以全屏模式观看它。
function drawScale(e) {
// inserting span elements at appropriate time
function insertSpan() {
window.requestAnimationFrame(function() {
se = document.createElement("span");
se.textContent = ">";
se.classList.add("tick");
scale.appendChild(se);
});
}
// recursively registers functions at the event looop according to their corresping delay
// returns setTimeout ids array (stoids) to be cleared if invoked before previous finishes
function registerSetTimeOut(t, ...ts) {
return ts.length ? registerSetTimeOut(...ts).concat(setTimeout(insertSpan, t)) :
[setTimeout(insertSpan, t)];
}
var datar = base.map((v, i) => easing[e.currentTarget.value](v, 0, 5000, 5000));
stoids.forEach(stoid => clearTimeout(stoid)); // clear all awaiting setTimeouts if any
stoids = []; // resets setTimeout ids array
stoids = registerSetTimeOut(...datar); // stoids array gets populated with new setTimeout ids
// clears previously appended span elements at appropriate time
window.requestAnimationFrame(function() {
while (scale.firstChild) scale.removeChild(scale.firstChild);
});
}
var easing = {
"linear": (t, b, c, d) => c * t / d + b,
"inQuad": (t, b, c, d) => (t /= d, c * t * t + b),
"outQuad": (t, b, c, d) => (t /= d, -c * t * (t - 2) + b),
"inOutQuad": (t, b, c, d) => (t /= d / 2, t < 1 ? c / 2 * t * t + b : (t--, -c / 2 * (t * (t - 2) - 1) + b)),
"inCubic": (t, b, c, d) => (t /= d, c * t * t * t + b),
"outCubic": (t, b, c, d) => (t /= d, t--, c * (t * t * t + 1) + b),
"inOutCubic": (t, b, c, d) => (t /= d / 2, t < 1 ? c / 2 * t * t * t + b : (t -= 2, c / 2 * (t * t * t + 2) + b)),
"inQuart": (t, b, c, d) => (t /= d, c * t * t * t * t + b),
"outQuart": (t, b, c, d) => (t /= d, t--, -c * (t * t * t * t - 1) + b),
"inOutQuart": (t, b, c, d) => (t /= d / 2, t < 1 ? c / 2 * t * t * t * t + b : (t -= 2, -c / 2 * (t * t * t * t - 2) + b)),
"inQuint": (t, b, c, d) => (t /= d, c * t * t * t * t * t + b),
"outQuint": (t, b, c, d) => (t /= d, t--, c * (t * t * t * t * t + 1) + b),
"inOutQuint": (t, b, c, d) => (t /= d / 2, t < 1 ? c / 2 * t * t * t * t * t + b : (t -= 2, c / 2 * (t * t * t * t * t + 2) + b)),
"inSine": (t, b, c, d) => -c * Math.cos(t / d * (Math.PI / 2)) + c + b,
"outSine": (t, b, c, d) => c * Math.sin(t / d * (Math.PI / 2)) + b,
"inOutSine": (t, b, c, d) => -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b,
"inExpo": (t, b, c, d) => c * Math.pow(2, 10 * (t / d - 1)) + b,
"outExpo": (t, b, c, d) => c * (-Math.pow(2, -10 * t / d) + 1) + b,
"inOutExpo": (t, b, c, d) => (t /= d / 2, t < 1 ? c / 2 * Math.pow(2, 10 * (t - 1)) + b : (t--, c / 2 * (-Math.pow(2, -10 * t) + 2) + b)),
"inCirc": (t, b, c, d) => (t /= d, -c * (Math.sqrt(1 - t * t) - 1) + b),
"outCirc": (t, b, c, d) => (t /= d, t--, c * Math.sqrt(1 - t * t) + b),
"inOutCirc": (t, b, c, d) => (t /= d / 2, t < 1 ? -c / 2 * (Math.sqrt(1 - t * t) - 1) + b : (t -= 2, c / 2 * (Math.sqrt(1 - t * t) + 1) + b))
},
base = Array.from({
length: 21
}).map((_, i) => i * 250),
stoids = [],
scale = document.getElementById("scale"),
radios = document.querySelectorAll('td [type="radio"]');
radios.forEach(r => r.addEventListener("change", drawScale));
table {
width: 33.3vw;
font-size: 0.8vw;
margin: auto;
}
#scale {
margin-left: 33vw;
display: inline-block;
border: solid 1px black;
overflow: hidden;
}
th {
vertical-align: top;
padding: 0.5em;
border: solid black 1px;
border-radius: 0.5em;
background-color: #d7c0c0;
}
tr:nth-child(2n+1){
background-color: #ccc;
}
td:nth-child(1) {
text-align: right;
font-weight: bold;
padding: 0 0.5em;
}
td:nth-child(n+2) {
text-align: center;
width: 25%;
}
.desc {
display: block;
font-family: monospace;
font-size: 0.8em;
}
.tick {
float:left;
width: 1.57vw;
text-align: center;
font-weight: bold;
background-color: #d7c0c0;
}
<div id="container">
<table>
<tr>
<th>Math</th>
<th>Ease In<span class="desc">Accelerating from zero velocity</span></th>
<th>Ease Out<span class="desc">Decelerating to zero velocity</span></th>
<th>Ease In/Out<span class="desc">Acceleration until halfway, then deceleration</span></th>
</tr>
<tr>
<td>Linear</td>
<td colspan=3><input type="radio" name="math" value="linear"></td>
</tr>
<tr>
<td>Quadratic</td>
<td><input type="radio" name="math" value="inQuad"></td>
<td><input type="radio" name="math" value="outQuad"></td>
<td><input type="radio" name="math" value="inOutQuad"></td>
</tr>
<tr>
<td>Cubic</td>
<td><input type="radio" name="math" value="inCubic"></td>
<td><input type="radio" name="math" value="outCubic"></td>
<td><input type="radio" name="math" value="inOutCubic"></td>
</tr>
<tr>
<td>Quartic</td>
<td><input type="radio" name="math" value="inQuart"></td>
<td><input type="radio" name="math" value="outQuart"></td>
<td><input type="radio" name="math" value="inOutQuart"></td>
</tr>
<tr>
<td>Quintic</td>
<td><input type="radio" name="math" value="inQuint"></td>
<td><input type="radio" name="math" value="outQuint"></td>
<td><input type="radio" name="math" value="inOutQuint"></td>
</tr>
<tr>
<td>Sinusoidal</td>
<td><input type="radio" name="math" value="inSine"></td>
<td><input type="radio" name="math" value="outSine"></td>
<td><input type="radio" name="math" value="inOutSine"></td>
</tr>
<tr>
<td>Exponential</td>
<td><input type="radio" name="math" value="inExpo"></td>
<td><input type="radio" name="math" value="outExpo"></td>
<td><input type="radio" name="math" value="inOutExpo"></td>
</tr>
<tr>
<td>Circular</td>
<td><input type="radio" name="math" value="inCirc"></td>
<td><input type="radio" name="math" value="outCirc"></td>
<td><input type="radio" name="math" value="inOutCirc"></td>
</tr>
</table>
<div id="scale"></div>
</div>