idlize

Форк
0
165 строк · 7.4 Кб
1
/*
2
 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3
 * Licensed under the Apache License, Version 2.0 (the "License");
4
 * you may not use this file except in compliance with the License.
5
 * You may obtain a copy of the License at
6
 *
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 *
9
 * Unless required by applicable law or agreed to in writing, software
10
 * distributed under the License is distributed on an "AS IS" BASIS,
11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
 * See the License for the specific language governing permissions and
13
 * limitations under the License.
14
 */
15

16
import { float64, uint32 } from "@koalaui/common"
17
import { EasingSupport } from "./EasingSupport"
18

19
/**
20
 * Easing functions specify the rate of change of a parameter over time.
21
 * Our animations call them with input value in the ranging [0..1].
22
 * Usually easing functions return a value in the same range,
23
 * but some of them may exceed that range.
24
 */
25
export type EasingCurve = (value: float64) => float64
26

27
/**
28
 * This namespace provides predefined easing functions
29
 * to use with {@link animation} and {@link transition}.
30
 */
31
export class Easing {
32
    static readonly Linear: EasingCurve = (value: float64) => value
33
    static readonly LinearReversed: EasingCurve = (value: float64) => 1 - value
34
    static readonly EaseInSine: EasingCurve = (value: float64) => 1 - Math.cos(value * Math.PI / 2)
35
    static readonly EaseOutSine: EasingCurve = (value: float64) => Math.sin(value * Math.PI / 2)
36
    static readonly EaseInOutSine: EasingCurve = (value: float64) => (1 - Math.cos(value * Math.PI)) / 2
37
    static readonly Ease = Easing.cubicBezier(.25, .1, .25, 1) // https://cubic-bezier.com/#.25,.1,.25,1
38
    static readonly EaseIn = Easing.cubicBezier(.42, 0, 1, 1) // https://cubic-bezier.com/#.42,0,1,1
39
    static readonly EaseOut = Easing.cubicBezier(0, 0, .58, 1) // https://cubic-bezier.com/#0,0,.58,1
40
    static readonly EaseInOut = Easing.cubicBezier(.42, 0, .58, 1) // https://cubic-bezier.com/#.42,0,.58,1
41
    static readonly ViscousFluid = Easing.viscousFluid()
42
    static readonly Bubble = Easing.cubicBezier(.32, 2.2, .72, 2.2) // https://cubic-bezier.com/#.32,2.2,.72,2.2
43

44
    /**
45
     * @param easing - an easing function to invert
46
     * @returns easing function that reflects all points of the original shape
47
     *                               through the central point (0.5;0.5)
48
     */
49
    static inverted(easing: EasingCurve): EasingCurve {
50
        return (value: float64) => 1 - easing(1 - value)
51
    }
52

53
    /**
54
     * @param easing - an easing function to reverse
55
     * @returns easing function that reflects all points of the original shape
56
     *                               through the central vertical axis (0.5)
57
     */
58
    static reversed(easing: EasingCurve): EasingCurve {
59
        return (value: float64) => easing(1 - value)
60
    }
61

62
    /**
63
     * @param easing - an easing function to modify
64
     * @returns easing function that returns minimal value instead of maximal one
65
     * @internal
66
     */
67
    static restarted(easing: EasingCurve): EasingCurve {
68
        return (value: float64) => easing(value < 1 ? value : 0)
69
    }
70

71
    /**
72
     * @param easing - an easing function to join
73
     * @param count - an amount of iterations of the specified function
74
     * @returns easing function that repeats the specified one several times
75
     */
76
    static repeated(easing: EasingCurve, count: uint32): EasingCurve {
77
        if (count == 1) return easing
78
        if (!Number.isInteger(count) || (count < 1)) throw new Error("unexpected iteration count: " + count)
79
        return (value: float64) => {
80
            if (value <= 0) return easing(0)
81
            if (value < 1) {
82
                value *= count
83
                const index = Math.floor(value)
84
                if (index < count) return easing(value - index)
85
            }
86
            return easing(1)
87
        }
88
    }
89

90
    /**
91
     * @param easing - easing functions to join
92
     * @returns easing function that applies specified functions one by one
93
     */
94
    static joined(...easing: EasingCurve[]): EasingCurve {
95
        if (easing.length == 0) throw new Error("no easing functions to join")
96
        if (easing.length == 1) return easing[0]
97
        return (value: float64) => {
98
            if (value <= 0) return easing[0](0)
99
            if (value < 1) {
100
                value *= easing.length
101
                const index = Math.floor(value)
102
                if (index < easing.length) return easing[index as uint32](value - index)
103
            }
104
            return easing[easing.length - 1](1)
105
        }
106
    }
107

108
    /**
109
     * @param easing - an easing function to join
110
     * @returns easing function that joins the specified function with its reversed variant
111
     */
112
    static thereAndBackAgain(easing: EasingCurve): EasingCurve {
113
        return (value: float64) => value < 0.5 ? easing(2 * value) : easing(2 - 2 * value)
114
    }
115

116
    /**
117
     * @param p1x the horizontal position of the first point
118
     * @param p1y the vertical position of the first point
119
     * @param p2x the horizontal position of the second point
120
     * @param p2y the vertical position of the second point
121
     * @returns easing function based on the cubic bezier curve
122
     */
123
    static cubicBezier(p1x: float64, p1y: float64, p2x: float64, p2y: float64): EasingCurve {
124
        const matrix = EasingSupport.newCubicBezier(p1x, p1y, p2x, p2y)
125
        return (value: float64) => matrix.convert(value)
126
    }
127

128
    /**
129
     * @param stops - a number of frames with constant values
130
     * @param jump - a preferred method to generate these values
131
     * @returns easing function that displays an animation iteration along the specified stops,
132
     *                               displaying each stop for equal lengths of time
133
     * @see EasingStepJump
134
     */
135
    static steps(stops: uint32, jump:EasingStepJump = EasingStepJump.None): EasingCurve {
136
        if (stops == 1) {
137
            switch (jump) {
138
                case EasingStepJump.Start: return (value: float64) => 1.0
139
                case EasingStepJump.End: return (value: float64) => 0.0
140
                case EasingStepJump.Both: return (value: float64) => 0.5
141
                case EasingStepJump.None: throw new Error("easing with one stop must use jump other than EasingStepJump.None")
142
            }
143
        }
144
        if (!Number.isInteger(stops) || stops <= 1) throw new Error("easing stops must be a positive integer, but is " + stops)
145
        const delta = jump == EasingStepJump.None || jump == EasingStepJump.End ? 0 : 1
146
        const count: uint32 = jump == EasingStepJump.None ? stops - 1 : jump == EasingStepJump.Both ? stops + 1 : stops
147
        return (value: float64) => (value > 0 ? Math.min(stops - 1, Math.floor(stops * value)) + delta : delta) / count
148
    }
149

150
    static viscousFluid(v0: float64 = 1, f: float64 = 3): EasingCurve {
151
        return (value: float64) => (v0 / (-4.2) * f) * (Math.exp((-4.2) * value * f) - 1)
152
    }
153
}
154

155
// https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function
156
export enum EasingStepJump {
157
    /** Denotes a left-continuous function, so that the first jump happens when the animation begins. */
158
    Start,
159
    /** Denotes a right-continuous function, so that the last jump happens when the animation ends. */
160
    End,
161
    /** There is no jump on either end. Instead, holding at both the 0% mark and the 100% mark, each for 1/n of the duration. */
162
    None,
163
    /** Includes pauses at both the 0% and 100% marks, effectively adding a step during the animation iteration. */
164
    Both,
165
}
166

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.