@@ -26,9 +26,11 @@ import androidx.compose.animation.EnterExitState
2626import  androidx.compose.animation.SizeTransform 
2727import  androidx.compose.animation.animateColor 
2828import  androidx.compose.animation.animateContentSize 
29+ import  androidx.compose.animation.core.Animatable 
2930import  androidx.compose.animation.core.AnimationVector1D 
3031import  androidx.compose.animation.core.AnimationVector2D 
3132import  androidx.compose.animation.core.Easing 
33+ import  androidx.compose.animation.core.ExperimentalAnimationSpecApi 
3234import  androidx.compose.animation.core.ExperimentalTransitionApi 
3335import  androidx.compose.animation.core.FastOutLinearInEasing 
3436import  androidx.compose.animation.core.FastOutSlowInEasing 
@@ -43,11 +45,13 @@ import androidx.compose.animation.core.TwoWayConverter
4345import  androidx.compose.animation.core.VectorConverter 
4446import  androidx.compose.animation.core.animateDp 
4547import  androidx.compose.animation.core.animateFloatAsState 
48+ import  androidx.compose.animation.core.animateOffsetAsState 
4649import  androidx.compose.animation.core.animateRect 
4750import  androidx.compose.animation.core.animateValueAsState 
4851import  androidx.compose.animation.core.createChildTransition 
4952import  androidx.compose.animation.core.infiniteRepeatable 
5053import  androidx.compose.animation.core.keyframes 
54+ import  androidx.compose.animation.core.keyframesWithSpline 
5155import  androidx.compose.animation.core.rememberInfiniteTransition 
5256import  androidx.compose.animation.core.rememberTransition 
5357import  androidx.compose.animation.core.repeatable 
@@ -71,11 +75,13 @@ import androidx.compose.foundation.Image
7175import  androidx.compose.foundation.background 
7276import  androidx.compose.foundation.clickable 
7377import  androidx.compose.foundation.layout.Box 
78+ import  androidx.compose.foundation.layout.BoxWithConstraints 
7479import  androidx.compose.foundation.layout.Column 
7580import  androidx.compose.foundation.layout.Row 
7681import  androidx.compose.foundation.layout.fillMaxSize 
7782import  androidx.compose.foundation.layout.fillMaxWidth 
7883import  androidx.compose.foundation.layout.height 
84+ import  androidx.compose.foundation.layout.offset 
7985import  androidx.compose.foundation.layout.padding 
8086import  androidx.compose.foundation.layout.size 
8187import  androidx.compose.foundation.layout.sizeIn 
@@ -93,25 +99,34 @@ import androidx.compose.runtime.State
9399import  androidx.compose.runtime.getValue 
94100import  androidx.compose.runtime.mutableIntStateOf 
95101import  androidx.compose.runtime.mutableLongStateOf 
102+ import  androidx.compose.runtime.mutableStateListOf 
96103import  androidx.compose.runtime.mutableStateOf 
97104import  androidx.compose.runtime.remember 
98105import  androidx.compose.runtime.setValue 
99106import  androidx.compose.runtime.withFrameNanos 
100107import  androidx.compose.ui.Alignment 
101108import  androidx.compose.ui.Modifier 
109+ import  androidx.compose.ui.draw.drawBehind 
110+ import  androidx.compose.ui.geometry.Offset 
102111import  androidx.compose.ui.geometry.Rect 
103112import  androidx.compose.ui.graphics.Color 
113+ import  androidx.compose.ui.graphics.PathEffect 
114+ import  androidx.compose.ui.graphics.PointMode 
104115import  androidx.compose.ui.graphics.graphicsLayer 
105116import  androidx.compose.ui.layout.ContentScale 
117+ import  androidx.compose.ui.layout.boundsInParent 
118+ import  androidx.compose.ui.layout.onPlaced 
106119import  androidx.compose.ui.platform.LocalDensity 
107120import  androidx.compose.ui.tooling.preview.Preview 
108121import  androidx.compose.ui.unit.Dp 
109122import  androidx.compose.ui.unit.IntSize 
110123import  androidx.compose.ui.unit.dp 
124+ import  androidx.compose.ui.unit.round 
111125import  com.example.compose.snippets.R 
112126import  java.text.BreakIterator 
113127import  java.text.StringCharacterIterator 
114128import  kotlinx.coroutines.delay 
129+ import  kotlinx.coroutines.isActive 
115130
116131/* 
117132* Copyright 2023 The Android Open Source Project 
@@ -709,6 +724,101 @@ private fun AnimationSpecKeyframe() {
709724    //  [END android_compose_animations_spec_keyframe]
710725}
711726
727+ @OptIn(ExperimentalAnimationSpecApi ::class )
728+ @Composable
729+ private  fun  AnimationSpecKeyframeWithSpline () {
730+     //  [START android_compose_animation_spec_keyframes_with_spline]
731+     val  offset by animateOffsetAsState(
732+         targetValue =  Offset (300f , 300f ),
733+         animationSpec =  keyframesWithSpline {
734+             durationMillis =  6000 
735+             Offset (0f , 0f ) at 0 
736+             Offset (150f , 200f ) atFraction 0.5f 
737+             Offset (0f , 100f ) atFraction 0.7f 
738+         }
739+     )
740+     //  [END android_compose_animation_spec_keyframes_with_spline]
741+ }
742+ 
743+ @OptIn(ExperimentalAnimationSpecApi ::class )
744+ @Preview
745+ @Composable
746+ private  fun  OffsetKeyframeWithSplineDemo () {
747+     val  points =  remember { mutableStateListOf<Offset >() }
748+     val  offsetAnim =  remember {
749+         Animatable (
750+             Offset .Zero ,
751+             Offset .VectorConverter 
752+         )
753+     }
754+     val  density =  LocalDensity .current
755+ 
756+     BoxWithConstraints (
757+         Modifier .fillMaxSize().drawBehind {
758+             drawPoints(
759+                 points =  points,
760+                 pointMode =  PointMode .Lines ,
761+                 color =  Color .LightGray ,
762+                 strokeWidth =  4f ,
763+                 pathEffect =  PathEffect .dashPathEffect(floatArrayOf(30f , 20f ))
764+             )
765+         }
766+     ) {
767+         val  minDimension =  minOf(maxWidth, maxHeight)
768+         val  size =  minDimension /  4 
769+ 
770+         val  sizePx =  with (density) { size.toPx() }
771+         val  widthPx =  with (density) { maxWidth.toPx() }
772+         val  heightPx =  with (density) { maxHeight.toPx() }
773+ 
774+         val  maxXOff =  (widthPx -  sizePx) /  2f 
775+         val  maxYOff =  heightPx -  (sizePx /  2f )
776+ 
777+         Box (
778+             Modifier .align(Alignment .TopCenter )
779+                 .offset { offsetAnim.value.round() }
780+                 .size(size)
781+                 .background(Color .Red , RoundedCornerShape (50 ))
782+                 .onPlaced { points.add(it.boundsInParent().center) }
783+         )
784+ 
785+         LaunchedEffect (Unit ) {
786+             delay(1000 )
787+             while  (isActive) {
788+                 offsetAnim.animateTo(
789+                     targetValue =  Offset .Zero ,
790+                     animationSpec = 
791+                     keyframesWithSpline {
792+                         durationMillis =  4400 
793+ 
794+                         //  Increasingly approach the halfway point moving from side to side
795+                         for  (i in  0 .. 4 ) {
796+                             val  sign =  if  (i %  2  ==  0 ) 1  else  - 1 
797+                             Offset (
798+                                 x =  maxXOff *  (i.toFloat() /  5f ) *  sign,
799+                                 y =  (maxYOff) *  (i.toFloat() /  5f )
800+                             ) atFraction (0.1f  *  i)
801+                         }
802+ 
803+                         //  Halfway point (at bottom of the screen)
804+                         Offset (0f , maxYOff) atFraction 0.5f 
805+ 
806+                         //  Return with mirrored movement
807+                         for  (i in  0 .. 4 ) {
808+                             val  sign =  if  (i %  2  ==  0 ) 1  else  - 1 
809+                             Offset (
810+                                 x =  maxXOff *  (1f  -  i.toFloat() /  5f ) *  sign,
811+                                 y =  (maxYOff) *  (1f  -  i.toFloat() /  5f )
812+                             ) atFraction ((0.1f  *  i) +  0.5f )
813+                         }
814+                     }
815+                 )
816+                 points.clear()
817+             }
818+         }
819+     }
820+ }
821+ 
712822@Composable
713823private  fun  AnimationSpecRepeatable () {
714824    //  [START android_compose_animations_spec_repeatable]
0 commit comments