github.com/Seikaijyu/gio@v0.0.1/internal/fling/animation.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package fling 4 5 import ( 6 "math" 7 "runtime" 8 "time" 9 10 "github.com/Seikaijyu/gio/unit" 11 ) 12 13 type Animation struct { 14 // Current offset in pixels. 15 x float32 16 // Initial time. 17 t0 time.Time 18 // Initial velocity in pixels pr second. 19 v0 float32 20 } 21 22 const ( 23 // dp/second. 24 minFlingVelocity = unit.Dp(50) 25 maxFlingVelocity = unit.Dp(8000) 26 thresholdVelocity = 1 27 ) 28 29 // Start a fling given a starting velocity. Returns whether a 30 // fling was started. 31 func (f *Animation) Start(c unit.Metric, now time.Time, velocity float32) bool { 32 min := float32(c.Dp(minFlingVelocity)) 33 v := velocity 34 if -min <= v && v <= min { 35 return false 36 } 37 max := float32(c.Dp(maxFlingVelocity)) 38 if v > max { 39 v = max 40 } else if v < -max { 41 v = -max 42 } 43 f.init(now, v) 44 return true 45 } 46 47 func (f *Animation) init(now time.Time, v0 float32) { 48 f.t0 = now 49 f.v0 = v0 50 f.x = 0 51 } 52 53 func (f *Animation) Active() bool { 54 return f.v0 != 0 55 } 56 57 // Tick computes and returns a fling distance since 58 // the last time Tick was called. 59 func (f *Animation) Tick(now time.Time) int { 60 if !f.Active() { 61 return 0 62 } 63 var k float32 64 if runtime.GOOS == "darwin" { 65 k = -2 // iOS 66 } else { 67 k = -4.2 // Android and default 68 } 69 t := now.Sub(f.t0) 70 // The acceleration x''(t) of a point mass with a drag 71 // force, f, proportional with velocity, x'(t), is 72 // governed by the equation 73 // 74 // x''(t) = kx'(t) 75 // 76 // Given the starting position x(0) = 0, the starting 77 // velocity x'(0) = v0, the position is then 78 // given by 79 // 80 // x(t) = v0*e^(k*t)/k - v0/k 81 // 82 ekt := float32(math.Exp(float64(k) * t.Seconds())) 83 x := f.v0*ekt/k - f.v0/k 84 dist := x - f.x 85 idist := int(dist) 86 f.x += float32(idist) 87 // Solving for the velocity x'(t) gives us 88 // 89 // x'(t) = v0*e^(k*t) 90 v := f.v0 * ekt 91 if -thresholdVelocity < v && v < thresholdVelocity { 92 f.v0 = 0 93 } 94 return idist 95 }