@@ -2,6 +2,7 @@ package com.smarttoolfactory.tutorial1_1basics.chapter9_animation
2
2
3
3
import android.graphics.Bitmap
4
4
import android.util.Log
5
+ import android.widget.Button
5
6
import android.widget.Toast
6
7
import androidx.compose.animation.core.Animatable
7
8
import androidx.compose.animation.core.AnimationSpec
@@ -23,6 +24,7 @@ import androidx.compose.foundation.layout.padding
23
24
import androidx.compose.foundation.layout.size
24
25
import androidx.compose.foundation.rememberScrollState
25
26
import androidx.compose.foundation.verticalScroll
27
+ import androidx.compose.material.Button
26
28
import androidx.compose.material.Slider
27
29
import androidx.compose.material.Text
28
30
import androidx.compose.runtime.Composable
@@ -34,6 +36,7 @@ import androidx.compose.runtime.mutableFloatStateOf
34
36
import androidx.compose.runtime.mutableStateListOf
35
37
import androidx.compose.runtime.mutableStateOf
36
38
import androidx.compose.runtime.remember
39
+ import androidx.compose.runtime.rememberCoroutineScope
37
40
import androidx.compose.runtime.setValue
38
41
import androidx.compose.runtime.snapshots.SnapshotStateList
39
42
import androidx.compose.ui.Alignment
@@ -42,12 +45,15 @@ import androidx.compose.ui.composed
42
45
import androidx.compose.ui.draw.drawWithCache
43
46
import androidx.compose.ui.geometry.Offset
44
47
import androidx.compose.ui.geometry.Size
48
+ import androidx.compose.ui.graphics.BlendMode
45
49
import androidx.compose.ui.graphics.Color
46
50
import androidx.compose.ui.graphics.ImageBitmap
47
51
import androidx.compose.ui.graphics.TransformOrigin
48
52
import androidx.compose.ui.graphics.asAndroidBitmap
49
53
import androidx.compose.ui.graphics.asImageBitmap
50
54
import androidx.compose.ui.graphics.drawscope.DrawScope
55
+ import androidx.compose.ui.graphics.drawscope.clipRect
56
+ import androidx.compose.ui.graphics.graphicsLayer
51
57
import androidx.compose.ui.graphics.nativeCanvas
52
58
import androidx.compose.ui.graphics.rememberGraphicsLayer
53
59
import androidx.compose.ui.platform.LocalContext
@@ -71,11 +77,94 @@ import com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.scale
71
77
import com.smarttoolfactory.tutorial1_1basics.ui.Pink400
72
78
import kotlinx.coroutines.CancellationException
73
79
import kotlinx.coroutines.Dispatchers
80
+ import kotlinx.coroutines.launch
74
81
import kotlinx.coroutines.withContext
75
82
import kotlin.math.cos
76
83
import kotlin.math.roundToInt
77
84
import kotlin.math.sin
78
85
86
+ @Preview
87
+ @Composable
88
+ fun ShakeTest () {
89
+
90
+
91
+ val animatable = remember {
92
+ Animatable (0f )
93
+ }
94
+
95
+ val coroutineScope = rememberCoroutineScope()
96
+
97
+ Column (
98
+ modifier = Modifier .fillMaxSize().padding(32 .dp)
99
+ ) {
100
+
101
+ Image (
102
+ modifier = Modifier
103
+ .size(100 .dp)
104
+ .graphicsLayer {
105
+
106
+ val progress = animatable.value
107
+ val endValue = .95f
108
+ val scale = if (progress < endValue) 1f else scale(endValue, 1f , progress, 1f , 0f )
109
+
110
+ translationX = if (progress < endValue) size.width * .05f * progress * randomInRange(
111
+ - 1f ,
112
+ 1f
113
+ ) else 1f
114
+ translationY = if (progress < endValue) size.height * .05f * progress * randomInRange(
115
+ - 1f ,
116
+ 1f
117
+ ) else 1f
118
+
119
+ scaleX = scale
120
+ scaleY = scale
121
+ alpha = scale
122
+ },
123
+ painter = painterResource(R .drawable.avatar_2_raster),
124
+ contentDescription = null
125
+ )
126
+
127
+
128
+
129
+ Spacer (modifier = Modifier .height(30 .dp))
130
+ Button (
131
+ modifier = Modifier .fillMaxWidth(),
132
+ onClick = {
133
+ coroutineScope.launch {
134
+ animatable.snapTo(0f )
135
+ animatable.animateTo(
136
+ targetValue = 1f ,
137
+ animationSpec = tween(800 )
138
+ )
139
+ }
140
+ }
141
+ ) {
142
+ Text (" Shake" )
143
+ }
144
+
145
+
146
+ }
147
+ }
148
+
149
+ fun Modifier.shake () = composed {
150
+ val animatable = remember {
151
+ Animatable (0f )
152
+ }
153
+
154
+
155
+ LaunchedEffect (Unit ) {
156
+ animatable.animateTo(
157
+ targetValue = 1f ,
158
+ animationSpec = tween(1000 )
159
+ )
160
+ }
161
+ Modifier .graphicsLayer {
162
+ translationX = size.width * .1f * animatable.value * randomInRange(- 1f , 1f )
163
+ translationY = size.width * .1f * animatable.value * randomInRange(- 1f , 1f )
164
+ }
165
+
166
+ }
167
+
79
168
@Preview
80
169
@Composable
81
170
fun SingleParticleTrajectorySample () {
@@ -229,8 +318,8 @@ fun ParticleAnimationSample() {
229
318
}
230
319
231
320
val particleState = rememberParticleState(
232
- particleSize = 2 .dp,
233
- animationSpec = tween(durationMillis = 1500 , easing = FastOutSlowInEasing )
321
+ particleSize = 1.5 .dp,
322
+ animationSpec = tween(durationMillis = 1800 , easing = FastOutSlowInEasing )
234
323
)
235
324
236
325
val particleState2 = rememberParticleState(
@@ -599,7 +688,7 @@ open class DisintegrateStrategy : ParticleStrategy {
599
688
val particleSizePx = particleSize.toFloat()
600
689
601
690
val initialSize = setInitialSize(particleBoundaries, particleSizePx)
602
- val endSize = setEndSize(particleBoundaries, particleSizePx.toInt() )
691
+ val endSize = setEndSize(particleBoundaries, particleSizePx)
603
692
604
693
// Set alpha
605
694
val alphaStart = setInitialAlpha(particleBoundaries)
@@ -673,7 +762,7 @@ open class DisintegrateStrategy : ParticleStrategy {
673
762
674
763
override fun setEndSize (
675
764
particleBoundaries : ParticleBoundaries ? ,
676
- particleSize : Int
765
+ particleSize : Float
677
766
): Size {
678
767
val endMinSize =
679
768
(particleBoundaries?.endSizeLowerBound?.width) ? : (particleSize * .4f )
@@ -762,16 +851,16 @@ open class DisintegrateStrategy : ParticleStrategy {
762
851
particleBoundaries : ParticleBoundaries ?
763
852
) {
764
853
with (drawScope) {
765
- // drawWithLayer {
766
- particleList.forEach { particle ->
767
- if (particle.isActive) {
854
+ drawWithLayer {
855
+ particleList.forEach { particle ->
856
+ // if (particle.isActive) {
768
857
updateParticle(
769
858
progress = progress,
770
859
particle = particle
771
860
)
772
861
773
862
val color = particle.color
774
- val radius = particle.currentSize.width * .65f
863
+ val radius = particle.currentSize.width * .5f
775
864
val position = particle.currentPosition
776
865
val alpha = particle.alpha
777
866
@@ -782,26 +871,36 @@ open class DisintegrateStrategy : ParticleStrategy {
782
871
center = position,
783
872
alpha = alpha
784
873
)
874
+ // }
785
875
}
786
- }
787
876
788
- // clipRect(
789
- // left = progress * size.width * 2f
790
- // ) {
791
- // drawImage(
792
- // image = imageBitmap,
793
- // blendMode = BlendMode.SrcOut
794
- // )
795
- // }
877
+ if (progress < .65f ) {
878
+ val coEfficient = if (progress < .2f ) {
879
+ progress * .8f
880
+ } else progress * 1.5f
881
+ clipRect(
882
+ left = size.width * coEfficient
883
+ ) {
884
+ drawImage(
885
+ image = imageBitmap,
886
+ blendMode = BlendMode .DstIn
887
+ )
796
888
797
- // For debugging
889
+ drawImage(
890
+ image = imageBitmap,
891
+ blendMode = BlendMode .SrcOut
892
+ )
893
+ }
894
+ }
895
+
896
+ // For debugging
798
897
// drawRect(
799
898
// color = Color.Black,
800
899
// topLeft = Offset(progress * size.width, 0f),
801
900
// size = Size(size.width - progress * size.width, size.height),
802
901
// style = Stroke(4.dp.toPx())
803
902
// )
804
- // }
903
+ }
805
904
}
806
905
}
807
906
@@ -831,7 +930,7 @@ open class DisintegrateStrategy : ParticleStrategy {
831
930
// Set alpha
832
931
// While trajectory progress is less than 40% have full alpha then slowly
833
932
// reduce to zero for particles to disappear
834
- alpha = if (trajectoryProgress == 0f ) 1f
933
+ alpha = if (trajectoryProgress == 0f ) 0f
835
934
else if (trajectoryProgress < .4f ) 1f
836
935
else scale(.4f , 1f , trajectoryProgress, particle.initialAlpha, particle.endAlpha)
837
936
@@ -948,7 +1047,7 @@ open class DefaultStrategy : ParticleStrategy {
948
1047
// Set initial and final sizes
949
1048
val initialSize = setInitialSize(particleBoundaries, particleSize.toFloat())
950
1049
951
- val endSize = setEndSize(particleBoundaries, particleSize)
1050
+ val endSize = setEndSize(particleBoundaries, particleSize.toFloat() )
952
1051
953
1052
// Set alpha
954
1053
val alphaStart = setInitialAlpha(particleBoundaries)
@@ -1005,7 +1104,7 @@ open class DefaultStrategy : ParticleStrategy {
1005
1104
1006
1105
override fun setEndSize (
1007
1106
particleBoundaries : ParticleBoundaries ? ,
1008
- particleSize : Int
1107
+ particleSize : Float
1009
1108
): Size {
1010
1109
val endSizePx = if (randomBoolean(8 )) {
1011
1110
randomInRange(particleSize * 1f , particleSize.toFloat() * 2.5f )
@@ -1196,7 +1295,7 @@ interface ParticleStrategy {
1196
1295
halfHeight : Float
1197
1296
): Offset
1198
1297
1199
- fun setEndSize (particleBoundaries : ParticleBoundaries ? , particleSize : Int ): Size
1298
+ fun setEndSize (particleBoundaries : ParticleBoundaries ? , particleSize : Float ): Size
1200
1299
1201
1300
fun setInitialSize (particleBoundaries : ParticleBoundaries ? , particleSize : Float ): Size
1202
1301
0 commit comments