forked from microsoft/fluentui-android
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Shimmer.kt
150 lines (145 loc) · 4.93 KB
/
Shimmer.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package com.microsoft.fluentui.tokenized.shimmer
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.microsoft.fluentui.theme.FluentTheme
import com.microsoft.fluentui.theme.token.ControlTokens
import com.microsoft.fluentui.theme.token.controlTokens.ShimmerInfo
import com.microsoft.fluentui.theme.token.controlTokens.ShimmerTokens
import com.microsoft.fluentui.util.dpToPx
import kotlin.math.absoluteValue
import kotlin.math.sqrt
private const val DEFAULT_CORNER_RADIUS = 4
/**
* Create an empty Shimmer effect
*
* @param modifier Modifier for shimmer
* @param shimmerTokens Token values for shimmer
*
*/
@Composable
fun Shimmer(
modifier: Modifier = Modifier,
shimmerTokens: ShimmerTokens? = null
) {
InternalShimmer(
cornerRadius = DEFAULT_CORNER_RADIUS.dp,
modifier = modifier,
shimmerTokens = shimmerTokens
)
}
/**
* Create Shimmer effect on some content
*
* @param cornerRadius Corner radius of the shimmer
* @param modifier Modifier for shimmer
* @param shimmerTokens Token values for shimmer
* @param content Content to be shimmered
*
*/
@Composable
fun Shimmer(
cornerRadius: Dp,
modifier: Modifier = Modifier,
shimmerTokens: ShimmerTokens? = null,
content: @Composable () -> Unit,
) {
InternalShimmer(
cornerRadius = cornerRadius,
modifier = modifier,
shimmerTokens = shimmerTokens
) {
content()
}
}
@Composable
internal fun InternalShimmer(
cornerRadius: Dp,
modifier: Modifier = Modifier,
shimmerTokens: ShimmerTokens? = null,
content: (@Composable () -> Unit)? = null,
) {
val themeID =
FluentTheme.themeID //Adding This only for recomposition in case of Token Updates. Unused otherwise.
val tokens = shimmerTokens
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.ShimmerControlType] as ShimmerTokens
val configuration = LocalConfiguration.current
val screenHeight = dpToPx(configuration.screenHeightDp.dp)
val screenWidth = dpToPx(configuration.screenWidthDp.dp)
val diagonal =
sqrt((screenHeight * screenHeight + screenWidth * screenWidth).toDouble()).toFloat()
val shimmerInfo = ShimmerInfo()
val shimmerBackgroundColor = if (content != null) {
Color.Transparent
} else {
tokens.color(shimmerInfo)
}
val shimmerKnockoutEffectColor = tokens.knockoutEffectColor(shimmerInfo)
val cornerRadius =
dpToPx(cornerRadius)
val shimmerDelay = tokens.delay(shimmerInfo)
val infiniteTransition = rememberInfiniteTransition()
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
val initialValue = if (isLtr) 0f else screenWidth
val targetValue = if (isLtr) diagonal else 0f
val shimmerEffect by infiniteTransition.animateFloat(
initialValue,
targetValue,
infiniteRepeatable(
animation = tween(
durationMillis = shimmerDelay,
easing = LinearEasing
)
)
)
val gradientColor = Brush.linearGradient(
0f to shimmerBackgroundColor,
0.5f to shimmerKnockoutEffectColor,
1.0f to shimmerBackgroundColor,
start = Offset.Zero,
end = Offset(shimmerEffect.absoluteValue, shimmerEffect.absoluteValue)
)
if (content != null) {
Box(
modifier
.width(IntrinsicSize.Max)
.height(IntrinsicSize.Max)
) {
content()
Spacer(
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(cornerRadius))
.background(gradientColor)
)
}
} else {
Spacer(
modifier = modifier
.clip(RoundedCornerShape(cornerRadius))
.background(gradientColor)
)
}
}