├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── elm.json ├── src └── Ease.elm └── test ├── Test.elm └── elm.json /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | elm-package.json 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 2.0.1 2 | * Updated to 0.16 3 | 4 | ### 2.0.0 5 | * Remove `friction`, `gravity` 6 | 7 | ### 1.1.0 8 | * Added `friction`, `gravity` 9 | 10 | ### 1.04 11 | * Documentation and formatting changes 12 | 13 | ### 1.0.2 14 | * Export functions 15 | 16 | ### 1.0.0 17 | * Updated to 0.14 18 | 19 | ### 0.8 20 | * Changed number interpolations to float 21 | * added pair function 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Daniël Heres, 2016, Max Goldstein 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easing Functions 2 | 3 | An **easing function** is used in animation to make a transition between two values appear more lifelike or interesting. 4 | Easing functions can make sliding panels or bouncing menus appear to be physical objects. 5 | 6 | This library provides an Elm implementation of the functions listed on [easings.net](http://easings.net/), and a few 7 | helper functions to help you build up your own custom easing functions. 8 | 9 | Search the [package catalog](http://package.elm-lang.org/) for `animation` to find libraries that use easing functions. 10 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "elm-community/easing-functions", 4 | "summary": "Easing functions for animations.", 5 | "license": "BSD-3-Clause", 6 | "version": "2.0.0", 7 | "exposed-modules": [ 8 | "Ease" 9 | ], 10 | "elm-version": "0.19.0 <= v < 0.20.0", 11 | "dependencies": { 12 | "elm/core": "1.0.0 <= v < 2.0.0" 13 | }, 14 | "test-dependencies": {} 15 | } -------------------------------------------------------------------------------- /src/Ease.elm: -------------------------------------------------------------------------------- 1 | module Ease exposing 2 | ( Easing 3 | , bezier 4 | , linear 5 | , inQuad, outQuad, inOutQuad 6 | , inCubic, outCubic, inOutCubic 7 | , inQuart, outQuart, inOutQuart 8 | , inQuint, outQuint, inOutQuint 9 | , inSine, outSine, inOutSine 10 | , inExpo, outExpo, inOutExpo 11 | , inCirc, outCirc, inOutCirc 12 | , inBack, outBack, inOutBack 13 | , inBounce, outBounce, inOutBounce 14 | , inElastic, outElastic, inOutElastic 15 | , reverse, flip, inOut, retour 16 | ) 17 | 18 | {-| An easing function is used in animation to make a transition between two values appear more lifelike or interesting. 19 | Easing functions can make sliding panels or bouncing menus appear to be physical objects. 20 | 21 | All easing functions expect inputs to be bewteen zero and one, and will typically output in that range. Easing "in" 22 | happens at the start of the transition, easing "out" at the end, and "inOut" on both sides. The functions provided here 23 | are meant to match the graphical examples on [easings.net](http://easings.net/). 24 | 25 | import Ease 26 | n = 10 27 | 28 | List.map (\i -> Ease.inQuad (toFloat i / n)) (List.range 0 n) 29 | 30 | --> [0, 0.01, 0.04, 0.09, 0.16, 0.25, 0.36, 0.49, 0.64, 0.81, 1] 31 | 32 | List.map (\i -> Ease.outCubic (toFloat i / n)) (List.range 0 n) 33 | 34 | --> [0, 0.271, 0.488, 0.657, 0.784, 0.875, 0.936, 0.973, 0.992, 0.999, 1] 35 | 36 | 37 | # Easing functions 38 | 39 | @docs Easing 40 | @docs bezier 41 | @docs linear 42 | @docs inQuad, outQuad, inOutQuad 43 | @docs inCubic, outCubic, inOutCubic 44 | @docs inQuart, outQuart, inOutQuart 45 | @docs inQuint, outQuint, inOutQuint 46 | @docs inSine, outSine, inOutSine 47 | @docs inExpo, outExpo, inOutExpo 48 | @docs inCirc, outCirc, inOutCirc 49 | @docs inBack, outBack, inOutBack 50 | @docs inBounce, outBounce, inOutBounce 51 | @docs inElastic, outElastic, inOutElastic 52 | 53 | 54 | # Combining easing functions 55 | 56 | @docs reverse, flip, inOut, retour 57 | 58 | -} 59 | 60 | 61 | {-| A type alias to make it easier to refer to easing functions. 62 | -} 63 | type alias Easing = 64 | Float -> Float 65 | 66 | 67 | {-| A linear ease, equal to the identity function. Linear eases often appear mechanical and unphysical. 68 | -} 69 | linear : Easing 70 | linear = 71 | identity 72 | 73 | 74 | {-| A cubic bezier function using 4 parameters: x and y position of first control point, and x and y position of second control point. 75 | 76 | See [here](http://greweb.me/glsl-transition/example/ "glsl-transitions") for examples or [here](http://cubic-bezier.com/ "tester") to test. 77 | 78 | -} 79 | bezier : Float -> Float -> Float -> Float -> Easing 80 | bezier x1 y1 x2 y2 time = 81 | let 82 | lerp from to v = 83 | from + (to - from) * v 84 | 85 | pair interpolate ( a0, b0 ) ( a1, b1 ) v = 86 | ( interpolate a0 a1 v, interpolate b0 b1 v ) 87 | 88 | casteljau ps = 89 | case ps of 90 | [ ( x, y ) ] -> 91 | y 92 | 93 | xs -> 94 | List.map2 (\x y -> pair lerp x y time) xs (Maybe.withDefault [] (List.tail xs)) 95 | |> casteljau 96 | in 97 | casteljau [ ( 0, 0 ), ( x1, y1 ), ( x2, y2 ), ( 1, 1 ) ] 98 | 99 | 100 | {-| -} 101 | inQuad : Easing 102 | inQuad time = 103 | time ^ 2 104 | 105 | 106 | {-| -} 107 | outQuad : Easing 108 | outQuad = 109 | flip inQuad 110 | 111 | 112 | {-| -} 113 | inOutQuad : Easing 114 | inOutQuad = 115 | inOut inQuad outQuad 116 | 117 | 118 | {-| -} 119 | inCubic : Easing 120 | inCubic time = 121 | time ^ 3 122 | 123 | 124 | {-| -} 125 | outCubic : Easing 126 | outCubic = 127 | flip inCubic 128 | 129 | 130 | {-| -} 131 | inOutCubic : Easing 132 | inOutCubic = 133 | inOut inCubic outCubic 134 | 135 | 136 | {-| -} 137 | inQuart : Easing 138 | inQuart time = 139 | time ^ 4 140 | 141 | 142 | {-| -} 143 | outQuart : Easing 144 | outQuart = 145 | flip inQuart 146 | 147 | 148 | {-| -} 149 | inOutQuart : Easing 150 | inOutQuart = 151 | inOut inQuart outQuart 152 | 153 | 154 | {-| -} 155 | inQuint : Easing 156 | inQuint time = 157 | time ^ 5 158 | 159 | 160 | {-| -} 161 | outQuint : Easing 162 | outQuint = 163 | flip inQuint 164 | 165 | 166 | {-| -} 167 | inOutQuint : Easing 168 | inOutQuint = 169 | inOut inQuint outQuint 170 | 171 | 172 | {-| -} 173 | inSine : Easing 174 | inSine = 175 | flip outSine 176 | 177 | 178 | {-| -} 179 | outSine : Easing 180 | outSine time = 181 | sin (time * (pi / 2)) 182 | 183 | 184 | {-| -} 185 | inOutSine : Easing 186 | inOutSine = 187 | inOut inSine outSine 188 | 189 | 190 | {-| -} 191 | inExpo : Easing 192 | inExpo time = 193 | if time == 0.0 then 194 | 0.0 195 | -- exact zero instead of 2^-10 196 | 197 | else 198 | 2 ^ (10 * (time - 1)) 199 | 200 | 201 | {-| -} 202 | outExpo : Easing 203 | outExpo = 204 | flip inExpo 205 | 206 | 207 | {-| -} 208 | inOutExpo : Easing 209 | inOutExpo = 210 | inOut inExpo outExpo 211 | 212 | 213 | {-| -} 214 | inCirc : Easing 215 | inCirc = 216 | flip outCirc 217 | 218 | 219 | {-| -} 220 | outCirc : Easing 221 | outCirc time = 222 | sqrt (1 - (time - 1) ^ 2) 223 | 224 | 225 | {-| -} 226 | inOutCirc : Easing 227 | inOutCirc = 228 | inOut inCirc outCirc 229 | 230 | 231 | {-| -} 232 | inBack : Easing 233 | inBack time = 234 | time * time * (2.70158 * time - 1.70158) 235 | 236 | 237 | {-| -} 238 | outBack : Easing 239 | outBack = 240 | flip inBack 241 | 242 | 243 | {-| -} 244 | inOutBack : Easing 245 | inOutBack = 246 | inOut inBack outBack 247 | 248 | 249 | {-| -} 250 | inBounce : Easing 251 | inBounce = 252 | flip outBounce 253 | 254 | 255 | {-| -} 256 | outBounce : Easing 257 | outBounce time = 258 | let 259 | a = 260 | 7.5625 261 | 262 | t2 = 263 | time - (1.5 / 2.75) 264 | 265 | t3 = 266 | time - (2.25 / 2.75) 267 | 268 | t4 = 269 | time - (2.625 / 2.75) 270 | in 271 | if time < 1 / 2.75 then 272 | a * time * time 273 | 274 | else if time < 2 / 2.75 then 275 | a * t2 * t2 + 0.75 276 | 277 | else if time < 2.5 / 2.75 then 278 | a * t3 * t3 + 0.9375 279 | 280 | else 281 | a * t4 * t4 + 0.984375 282 | 283 | 284 | {-| -} 285 | inOutBounce : Easing 286 | inOutBounce = 287 | inOut inBounce outBounce 288 | 289 | 290 | {-| -} 291 | inElastic : Easing 292 | inElastic time = 293 | if time == 0.0 then 294 | 0.0 295 | 296 | else 297 | let 298 | s = 299 | 0.075 300 | 301 | p = 302 | 0.3 303 | 304 | t = 305 | time - 1 306 | in 307 | -((2 ^ (10 * t)) * sin ((t - s) * (2 * pi) / p)) 308 | 309 | 310 | {-| -} 311 | outElastic : Easing 312 | outElastic = 313 | flip inElastic 314 | 315 | 316 | {-| -} 317 | inOutElastic : Easing 318 | inOutElastic = 319 | inOut inElastic outElastic 320 | 321 | 322 | {-| Makes an easing function using two other easing functions. The first half the first `Easing` function is used, the other half the second. 323 | -} 324 | inOut : Easing -> Easing -> Easing 325 | inOut e1 e2 time = 326 | if time < 0.5 then 327 | e1 (time * 2) / 2 328 | 329 | else 330 | 0.5 + e2 ((time - 0.5) * 2) / 2 331 | 332 | 333 | {-| Flip an easing function. A transition that starts fast and continues slow now starts slow and continues fast. 334 | 335 | Graphically, this flips the function around x = 0.5 and then around y = 0.5. 336 | 337 | -} 338 | flip : Easing -> Easing 339 | flip easing time = 340 | 1 - easing (1 - time) 341 | 342 | 343 | {-| Reverse an `Easing` function. If an object follows an easing function and then the reversed easing function, it 344 | retraces exactly the same path, backwards. 345 | 346 | Graphically, this flips the function around x = 0.5. 347 | 348 | -} 349 | reverse : Easing -> Easing 350 | reverse easing time = 351 | easing (1 - time) 352 | 353 | 354 | {-| Makes an `Easing` function go to the end first and then back to the start. A transition that starts low and goes 355 | high now starts low, goes high at halfway, and then goes low again. 356 | -} 357 | retour : Easing -> Easing 358 | retour easing time = 359 | if time < 0.5 then 360 | easing (time * 2) 361 | 362 | else 363 | (time - 0.5) 364 | * 2 365 | |> flip easing 366 | -------------------------------------------------------------------------------- /test/Test.elm: -------------------------------------------------------------------------------- 1 | module Test exposing (main) 2 | 3 | import Ease exposing (..) 4 | import Svg exposing (Svg) 5 | import Svg.Attributes as SvgAttributes 6 | 7 | 8 | width = 9 | 120 10 | 11 | 12 | height = 13 | 64 14 | 15 | 16 | plot : Int -> ( Easing, String ) -> Svg a 17 | plot i ( f, name ) = 18 | Svg.g 19 | [ SvgAttributes.transform 20 | ("translate(" 21 | ++ String.fromInt ((width + 10) * modBy 6 i) 22 | ++ "," 23 | ++ String.fromInt ((height + 50) * (i // 6)) 24 | ++ ")" 25 | ) 26 | ] 27 | [ Svg.rect 28 | [ SvgAttributes.width (String.fromInt width) 29 | , SvgAttributes.height (String.fromInt 1) 30 | , SvgAttributes.fill "gray" 31 | ] 32 | [] 33 | , Svg.rect 34 | [ SvgAttributes.width (String.fromInt width) 35 | , SvgAttributes.height (String.fromInt 1) 36 | , SvgAttributes.y (String.fromInt height) 37 | , SvgAttributes.fill "gray" 38 | ] 39 | [] 40 | , Svg.path 41 | [ List.map 42 | (toFloat 43 | >> (\x -> 44 | ( x 45 | , height - f (x / width) * height 46 | ) 47 | ) 48 | ) 49 | (List.range 0 (round width)) 50 | |> path 51 | |> SvgAttributes.d 52 | , SvgAttributes.stroke "black" 53 | , SvgAttributes.strokeWidth "2" 54 | , SvgAttributes.fill "transparent" 55 | ] 56 | [] 57 | , Svg.text_ 58 | [ SvgAttributes.y (String.fromInt -15) 59 | , SvgAttributes.x (String.fromInt 30) 60 | , SvgAttributes.style "font: 14px sans-serif;" 61 | ] 62 | [ Svg.text name ] 63 | ] 64 | 65 | 66 | path : List ( Float, Float ) -> String 67 | path list = 68 | case list of 69 | ( x, y ) :: tail -> 70 | "M" 71 | ++ String.fromFloat x 72 | ++ "," 73 | ++ String.fromFloat y 74 | ++ String.join "" 75 | (List.map 76 | (\( a, b ) -> 77 | "L" 78 | ++ String.fromFloat a 79 | ++ "," 80 | ++ String.fromFloat b 81 | ) 82 | tail 83 | ) 84 | 85 | _ -> 86 | "" 87 | 88 | 89 | title : Svg a 90 | title = 91 | Svg.text_ 92 | [ SvgAttributes.y "-60" 93 | , SvgAttributes.x "80" 94 | , SvgAttributes.style "font: 14px sans-serif;" 95 | ] 96 | [ Svg.text "This is a replication of easings.net for testing purposes. You can see the plots are nearly identical." ] 97 | 98 | 99 | main : Svg a 100 | main = 101 | Svg.svg 102 | [ SvgAttributes.width "850" 103 | , SvgAttributes.height "650" 104 | , SvgAttributes.viewBox "-10 -60 840 590" 105 | , SvgAttributes.style "display:block;margin:auto;" 106 | ] 107 | (title :: List.indexedMap plot easingFunctions) 108 | 109 | 110 | easingFunctions = 111 | [ ( inSine, "inSine" ) 112 | , ( outSine, "outSine" ) 113 | , ( inOutSine, "inOutSine" ) 114 | , ( inQuad, "inQuad" ) 115 | , ( outQuad, "outQuad" ) 116 | , ( inOutQuad, "inOutQuad" ) 117 | , ( inCubic, "inCubic" ) 118 | , ( outCubic, "outCubic" ) 119 | , ( inOutCubic, "inOutCubic" ) 120 | , ( inQuart, "inQuart" ) 121 | , ( outQuart, "outQuart" ) 122 | , ( inOutQuart, "inOutQuart" ) 123 | , ( inQuint, "inQuint" ) 124 | , ( outQuint, "outQuint" ) 125 | , ( inOutQuint, "inOutQuint" ) 126 | , ( inExpo, "inExpo" ) 127 | , ( outExpo, "outExpo" ) 128 | , ( inOutExpo, "inOutExpo" ) 129 | , ( inCirc, "inCirc" ) 130 | , ( outCirc, "outCirc" ) 131 | , ( inOutCirc, "inOutCirc" ) 132 | , ( inBack, "inBack" ) 133 | , ( outBack, "outBack" ) 134 | , ( inOutBack, "inOutBack" ) 135 | , ( inElastic, "inElastic" ) 136 | , ( outElastic, "outElastic" ) 137 | , ( inOutElastic, "inOutElastic" ) 138 | , ( inBounce, "inBounce" ) 139 | , ( outBounce, "outBounce" ) 140 | , ( inOutBounce, "inOutBounce" ) 141 | ] 142 | -------------------------------------------------------------------------------- /test/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | ".", 5 | "../src" 6 | ], 7 | "elm-version": "0.19.0", 8 | "dependencies": { 9 | "direct": { 10 | "elm/core": "1.0.0", 11 | "elm/svg": "1.0.0" 12 | }, 13 | "indirect": { 14 | "elm/json": "1.0.0", 15 | "elm/html": "1.0.0", 16 | "elm/virtual-dom": "1.0.0" 17 | } 18 | }, 19 | "test-dependencies": { 20 | "direct": {}, 21 | "indirect": {} 22 | } 23 | } 24 | --------------------------------------------------------------------------------