diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2020-08-28 20:24:11 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2020-08-28 20:24:11 +0200 |
| commit | 15d5f168363b9592cd5f201fe2ce2a31d3d692bd (patch) | |
| tree | 0f79d47a5fbaff9a3858d0aa1037aebcb6a1c66a /animism-align/frontend/app/utils | |
| parent | 6dea2acdf558f39c868be92c5657190d9fb6bd46 (diff) | |
tween-scroll the transcript
Diffstat (limited to 'animism-align/frontend/app/utils')
| -rw-r--r-- | animism-align/frontend/app/utils/index.js | 2 | ||||
| -rw-r--r-- | animism-align/frontend/app/utils/oktween.js | 163 |
2 files changed, 165 insertions, 0 deletions
diff --git a/animism-align/frontend/app/utils/index.js b/animism-align/frontend/app/utils/index.js index 0f5a1dd..855fcaf 100644 --- a/animism-align/frontend/app/utils/index.js +++ b/animism-align/frontend/app/utils/index.js @@ -8,6 +8,7 @@ export const formatDateTime = dateStr => format(new Date(dateStr), 'd MMM yyyy H export const formatDate = dateStr => format(new Date(dateStr), 'd MMM yyyy') export const formatTime = dateStr => format(new Date(dateStr), 'H:mm') export const formatAge = dateStr => formatDistance(new Date(), new Date(dateStr)) + ' ago.' +export const toArray = a => Array.from(a) /* Mobile check */ @@ -93,6 +94,7 @@ export const clamp = (n, a=0, b=1) => n < a ? a : n < b ? n : b export const dist = (x1, y1, x2, y2) => Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) export const mod = (n, m) => n - (m * Math.floor(n / m)) export const angle = (x1, y1, x2, y2) => Math.atan2(y2 - y1, x2 - x1) +export const lerp = (n,a,b) => (b-a)*n+a export const floatLT = (a,b) => ((a*10|0) < (b*10|0)) export const floatLTE = (a,b) => ((a*10|0) === (b*10|0) || floatLT(a,b)) export const floatGT = (a,b) => ((a*10|0) > (b*10|0)) diff --git a/animism-align/frontend/app/utils/oktween.js b/animism-align/frontend/app/utils/oktween.js new file mode 100644 index 0000000..4388ad4 --- /dev/null +++ b/animism-align/frontend/app/utils/oktween.js @@ -0,0 +1,163 @@ +/* + oktween.add({ + obj: el.style, + units: "px", + from: { left: 0 }, + to: { left: 100 }, + duration: 1000, + easing: oktween.easing.circ_out, + update: function(obj){ + console.log(obj.left) + } + finished: function(){ + console.log("done") + } + }) +*/ + +import { lerp } from 'app/utils' + +const oktween = {} +let tweens = [] + +let last_t = 0 +let id = 0 + +oktween.speed = 1 +oktween.add = (tween) => { + tween.id = id++ + tween.obj = tween.obj || {} + if (tween.easing) { + if (typeof tween.easing === "string") { + tween.easing = oktween.easing[tween.easing] + } + } else { + tween.easing = oktween.easing.linear + } + if (!('from' in tween) && !('to' in tween)) { + tween.keys = [] + } else if (!('from' in tween)) { + tween.from = {} + tween.keys = Object.keys(tween.to) + tween.keys.forEach(function(prop) { + tween.from[prop] = parseFloat(tween.obj[prop]) + }) + } else { + tween.keys = Object.keys(tween.from) + } + tween.delay = tween.delay || 0 + tween.start = last_t + tween.delay + tween.done = false + tween.after = tween.after || [] + tween.then = (fn) => { tween.after.push(fn); return tween } + tween.tick = 0 + tween.skip = tween.skip || 1 + tween.dt = 0 + tweens.push(tween) + return tween +} +oktween.update = (t) => { + let done = false + requestAnimationFrame(oktween.update) + last_t = t * oktween.speed + if (tweens.length === 0) return + tweens.forEach((tween, i) => { + const dt = Math.min(1.0, (t - tween.start) / tween.duration) + tween.tick++ + if (dt < 0 || (dt < 1 && (tween.tick % tween.skip != 0))) return + const ddt = tween.easing(dt) + tween.dt = ddt + tween.keys.forEach((prop) => { + let val = lerp(ddt, tween.from[prop], tween.to[prop]) + if (tween.round) val = Math.round(val) + if (tween.units) val = (Math.round(val)) + tween.units + tween.obj[prop] = val + }) + if (tween.update) { + tween.update(tween.obj, dt) + } + if (dt === 1) { + if (tween.finished) { + tween.finished(tween) + } + if (tween.after.length) { + const twn = tween.after.shift() + twn.obj = twn.obj || tween.obj + twn.after = tween.after + oktween.add(twn) + } + if (tween.loop) { + tween.start = t + tween.delay + } + else { + done = true + tween.done = true + } + } + }) + if (done) { + tweens = tweens.filter(tween => !tween.done) + } +} + +requestAnimationFrame(oktween.update) + +oktween.easing = { + linear: (t) => { + return t + }, + circ_out: (t) => { + return Math.sqrt(1 - (t = t - 1) * t) + }, + circ_in: (t) => { + return -(Math.sqrt(1 - (t * t)) - 1) + }, + circ_in_out: (t) => { + return ((t*=2) < 1) ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1) + }, + quad_in: (n) => { + return Math.pow(n, 2) + }, + quad_out: (n) => { + return n * (n - 2) * -1 + }, + quad_in_out: (n) => { + n = n * 2 + if (n < 1) { return Math.pow(n, 2) / 2 } + return -1 * ((--n) * (n - 2) - 1) / 2 + }, + cubic_bezier: (mX1, mY1, mX2, mY2) => { + function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1 } + function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1 } + function C(aA1) { return 3.0 * aA1 } + + // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. + function CalcBezier(aT, aA1, aA2) { + return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT + } + + // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. + function GetSlope(aT, aA1, aA2) { + return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1) + } + + function GetTForX(aX) { + // Newton raphson iteration + let aGuessT = aX + for (let i = 0; i < 10; ++i) { + const currentSlope = GetSlope(aGuessT, mX1, mX2) + if (currentSlope == 0.0) return aGuessT + const currentX = CalcBezier(aGuessT, mX1, mX2) - aX + aGuessT -= currentX / currentSlope + } + return aGuessT + } + + return function (aX) { + if (mX1 == mY1 && mX2 == mY2) return aX // linear + return CalcBezier(aX, mY1, mY2) + } + } +} + +export default oktween |
