github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/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/gop9/olt/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  var (
    23  	// Pixels/second.
    24  	minFlingVelocity = unit.Dp(50)
    25  	maxFlingVelocity = unit.Dp(8000)
    26  )
    27  
    28  const (
    29  	thresholdVelocity = 1
    30  )
    31  
    32  // Start a fling given a starting velocity. Returns whether a
    33  // fling was started.
    34  func (f *Animation) Start(c unit.Converter, now time.Time, velocity float32) bool {
    35  	min := float32(c.Px(minFlingVelocity))
    36  	v := velocity
    37  	if -min <= v && v <= min {
    38  		return false
    39  	}
    40  	max := float32(c.Px(maxFlingVelocity))
    41  	if v > max {
    42  		v = max
    43  	} else if v < -max {
    44  		v = -max
    45  	}
    46  	f.init(now, v)
    47  	return true
    48  }
    49  
    50  func (f *Animation) init(now time.Time, v0 float32) {
    51  	f.t0 = now
    52  	f.v0 = v0
    53  	f.x = 0
    54  }
    55  
    56  func (f *Animation) Active() bool {
    57  	return f.v0 != 0
    58  }
    59  
    60  // Tick computes and returns a fling distance since
    61  // the last time Tick was called.
    62  func (f *Animation) Tick(now time.Time) int {
    63  	if !f.Active() {
    64  		return 0
    65  	}
    66  	var k float32
    67  	if runtime.GOOS == "darwin" {
    68  		k = -2 // iOS
    69  	} else {
    70  		k = -4.2 // Android and default
    71  	}
    72  	t := now.Sub(f.t0)
    73  	// The acceleration x''(t) of a point mass with a drag
    74  	// force, f, proportional with velocity, x'(t), is
    75  	// governed by the equation
    76  	//
    77  	// x''(t) = kx'(t)
    78  	//
    79  	// Given the starting position x(0) = 0, the starting
    80  	// velocity x'(0) = v0, the position is then
    81  	// given by
    82  	//
    83  	// x(t) = v0*e^(k*t)/k - v0/k
    84  	//
    85  	ekt := float32(math.Exp(float64(k) * t.Seconds()))
    86  	x := f.v0*ekt/k - f.v0/k
    87  	dist := x - f.x
    88  	idist := int(dist)
    89  	f.x += float32(idist)
    90  	// Solving for the velocity x'(t) gives us
    91  	//
    92  	// x'(t) = v0*e^(k*t)
    93  	v := f.v0 * ekt
    94  	if -thresholdVelocity < v && v < thresholdVelocity {
    95  		f.v0 = 0
    96  	}
    97  	return idist
    98  }