One, the cost of bragging force 👍
The previous three articles were composed UI related. Later I saw someone in the group posted some links, above are some fancy navigation bar design, of course, quite fancy. And then I thought I could do it, and I said, “I can do it in two hours,” which turned out to be a bit of a boast, and finally the bosses called me in and asked me to write it in eight hours. At that time feel very simple, began, the fact is a meal analysis fierce as tiger, two hours in situ pestle. The big boys have named me for writing, so let’s analyze and stack the code.
Jetpack-composing – Custom draw jetpack-composing – Flutter dynamic UI?
Mimicking UI effects
Review and analysis 👍👍
Remember that in the first three chapters we wrote the following:
In the last two sessions, we learned about Compose’s customization and posted a lot of articles about Compose’s customization. If you need to improve your customization skills, I suggest you put your mind to it. There is no shortcut. In this article we will challenge the following animation.
1, the decomposition
Animations can be a bit confusing at first, but giFs are so many frames of still images that you can right click the Selection system’s image Preview tool and analyze the effects at each stage of the animation.
Analysis of the following
1. The page is displayed by default
2. Transition
3. Water droplets separate Bootom
4. Restore the original position
As you walk through each frame of the above 4 steps, you will have your own ideas. Of course, different people have different ideas here, so as long as the effect is right, it is right. In my opinion, the drawing is divided into two parts: one is a moving water drop 💧 and the other is a raised bottom with high and low fluctuation effect.
2. Moving water droplets
Water droplets moving upward, and gradually become round, and elastic.
1. Water drop rendering
It is bezier curves that break through the ceiling. What thing does not have it to do not have, painting figures, landscape, birds and beasts…. They’re all dependent on curves. The water droplets analyzed above can of course be solved by using bezier curves.
Circles can also be drawn by Bezier curves, and bezier curves require factors. Factors determine the degree of the circle, factors of the circle0.551915024494 f
, I direct use of 0.5 is also very round, of course I’m in order to calculate conveniently, P1, P2, P4, P5, P7, P8, P10, P11 is the halfway point of the radius, P control points P1 – > P2 respectively as shown – > p3, P4 – > P5 – > p6 and P7 – > P8 – > p9, P10 – > P11 – > p0.
A wave of code
Draw the circle
1.Move the coordinate system to the center of the screen and transform the coordinate system dot to the center of the screen.2.Find all coordinate points between P1->P11.3.Draw PathCopy the code
val paint = Paint()
paint.color = Color.RED
paint.style = Paint.Style.FILL
paint.strokeWidth = 10f
paint.isAntiAlias = true
val r = 200.0 f
canvas.drawCircle(100f.100f.100f,paint)
canvas.scale(1f, -1f)
canvas.translate(width / 2f, -height / 2f)
// Calculate the coordinates of the circle and the center point
/ / 1. First, the origin (0 f, 0 f) and the radius r = 100 f -- - > the p6 (0 f, r), p5 = (r / 2, r), p4 (r, r / 2), p3 (r, 0 f)
P2 (r,-r/2),p1(r/2,-r),p0(0f,r)
P11 (-r/2,-r),p10(-r,-r/2),p9(-r,0f)
P8 (-r,r/2),p7(r/2,r),p6(0f,r)
val P0 = PointF(0f, -r)
val P1 = PointF(r / 2, -r)
val P2 = PointF(r, -r / 2)
val P3 = PointF(r, 0f)
val P4 = PointF(r, r / 2 )
val P5 = PointF(r / 2, r)
val P6 = PointF(0f, r )
val P7 = PointF(-r / 2, r )
val P8 = PointF(-r, r / 2)
val P9 = PointF(-r, 0f)
val P10 = PointF(-r, -r / 2)
val P11 = PointF(-r / 2, -r)
val path = Path()
path.moveTo(P0.x, P0.y )
//p1->p2->p3
path.cubicTo(P1.x, P1.y, P2.x, P2.y , P3.x, P3.y)
//p4->p5->p6
path.cubicTo(P4.x, P4.y, P5.x, P5.y, P6.x, P6.y)
//p7->p8->p9
path.cubicTo(P7.x, P7.y, P8.x, P8.y, P9.x, P9.y)
//p10->p11->p0
path.cubicTo(
P10.x,
P10.y ,
P11.x,
P11.y ,
P0.x,
P0.y
)
path.close()
canvas.drawPath(path, paint)
}
Copy the code
The effect is as follows:
Draw the water droplets
So here’s the picture of the water drop, the radians on the bottom and we can control P10,P11,P0,P1,P2 to control the radians on the bottom. Look at the Android customization-curve gradient fill I wrote earlier
val paint = Paint()
paint.color = Color.RED
paint.style = Paint.Style.FILL
paint.strokeWidth = 10f
paint.isAntiAlias = true
val r = 200.0 f
canvas.drawCircle(100f.100f.100f,paint)
canvas.scale(1f, -1f)
canvas.translate(width / 2f, -height / 2f)
// Calculate the coordinates of the circle and the center point
/ / 1. First, the origin (0 f, 0 f) and the radius r = 100 f -- - > the p6 (0 f, r), p5 = (r / 2, r), p4 (r, r / 2), p3 (r, 0 f)
P2 (r,-r/2),p1(r/2,-r),p0(0f,r)
P11 (-r/2,-r),p10(-r,-r/2),p9(-r,0f)
P8 (-r,r/2),p7(r/2,r),p6(0f,r)
val P0 = PointF(0f, -r)
val P1 = PointF(r / 2, -r)
val P2 = PointF(r, -r / 2)
val P3 = PointF(r, 0f)
val P4 = PointF(r, r / 2 )
val P5 = PointF(r / 2, r)
val P6 = PointF(0f, r )
val P7 = PointF(-r / 2, r )
val P8 = PointF(-r, r / 2)
val P9 = PointF(-r, 0f)
val P10 = PointF(-r, -r / 2)
val P11 = PointF(-r / 2, -r)
val path = Path()
path.moveTo(P0.x, P0.y-60 )
//p1->p2->p3
path.cubicTo(P1.x, P1.y-30, P2.x, P2.y-30 , P3.x, P3.y)
//p4->p5->p6
path.cubicTo(P4.x, P4.y, P5.x, P5.y, P6.x, P6.y)
//p7->p8->p9
path.cubicTo(P7.x, P7.y, P8.x, P8.y, P9.x, P9.y)
//p10->p11->p0
path.cubicTo(
P10.x,
P10.y-30 ,
P11.x,
P11.y-30 ,
P0.x,
P0.y-60
)
path.close()
canvas.drawPath(path, paint)
}
Copy the code
Water droplets transform into circles and move from the lower net
The circle and water drop have been drawn above. The difference is that the control points P10,P11,P0,P1 and P2 change in the vertical direction, and the water drop moves up and down only with increasing y value. The code is as follows:
package com.example.android_draw.view.worter
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
@SuppressLint("WrongConstant")
class LHC_WaterDrop_View @JvmOverloads constructor( context: Context? , attrs: AttributeSet? =null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private var mCurAnimValue: Float = 1f
private var mCurAnimValueY: Float = 1f
private var animator: ValueAnimator = ValueAnimator.ofFloat(1f.0f)
private var animatorY: ValueAnimator = ValueAnimator.ofFloat(0f.1f)
init {
animator.duration = 1500
animator.interpolator = AccelerateDecelerateInterpolator()
animator.addUpdateListener { animation ->
mCurAnimValue = animation.animatedValue as Float
invalidate()
}
animator.repeatMode = ValueAnimator.INFINITE
animator.start()
animatorY.duration = 1500
animatorY.interpolator = AccelerateDecelerateInterpolator()
animatorY.addUpdateListener { animation ->
mCurAnimValueY = animation.animatedValue as Float
invalidate()
}
animatorY.repeatMode = ValueAnimator.INFINITE
animatorY.start()
}
@SuppressLint("DrawAllocation")
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val paint = Paint()
paint.color = Color.RED
paint.style = Paint.Style.FILL
paint.strokeWidth = 10f
paint.isAntiAlias = true
val r = 100.0 f-60f* (1-mCurAnimValue)
canvas.drawCircle(100f.100f.100f,paint)
canvas.scale(1f, -1f)
canvas.translate(width / 2f, -height / 2f)
// Calculate the coordinates of the circle and the center point
/ / 1. First, the origin (0 f, 0 f) and the radius r = 100 f -- - > the p6 (0 f, r), p5 = (r / 2, r), p4 (r, r / 2), p3 (r, 0 f)
P2 (r,-r/2),p1(r/2,-r),p0(0f,r)
P11 (-r/2,-r),p10(-r,-r/2),p9(-r,0f)
P8 (-r,r/2),p7(r/2,r),p6(0f,r)
val P0 = PointF(0f, -r + mCurAnimValueY * 160)
val P1 = PointF(r / 2, -r + mCurAnimValueY * 160)
val P2 = PointF(r, -r / 2 + mCurAnimValueY * 160)
val P3 = PointF(r, 0f + mCurAnimValueY * 160)
val P4 = PointF(r, r / 2 + mCurAnimValueY * 160)
val P5 = PointF(r / 2, r + mCurAnimValueY * 160)
val P6 = PointF(0f, r + mCurAnimValueY * 160)
val P7 = PointF(-r / 2, r + mCurAnimValueY * 160)
val P8 = PointF(-r, r / 2 + mCurAnimValueY * 160)
val P9 = PointF(-r, 0f + mCurAnimValueY * 160)
val P10 = PointF(-r, -r / 2 + mCurAnimValueY * 160)
val P11 = PointF(-r / 2, -r + mCurAnimValueY * 160)
val path = Path()
path.moveTo(P0.x, P0.y - 60 * mCurAnimValue)
//p1->p2->p3
path.cubicTo(P1.x, P1.y - 30 * mCurAnimValue, P2.x, P2.y - 30 * mCurAnimValue, P3.x, P3.y)
//p4->p5->p6
path.cubicTo(P4.x, P4.y, P5.x, P5.y, P6.x, P6.y)
//p7->p8->p9
path.cubicTo(P7.x, P7.y, P8.x, P8.y, P9.x, P9.y)
//p10->p11->p0
path.cubicTo(
P10.x,
P10.y - 30 * mCurAnimValue,
P11.x,
P11.y - 30 * mCurAnimValue,
P0.x,
P0.y - 60 * mCurAnimValue
)
path.close()
canvas.drawPath(path, paint)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
animator.start()
animatorY.start()
}
}
return true}}Copy the code
Compose code 👍👍👍
1. Rising water drops 💧
First we divide the width into three equal parts. When clicking the first bottom navigation button, the coordinates change to the center of the black circle below, which matches the above part.
// The center of the circle needs to be shifted to the middle of each division every time you click the bottom navigation bar
val centerHdX = size.width / 3 / 2 + size.width / 3*clickIndex
// The coordinate position dot is the midpoint of the upper edge
canvas.translate(centerHdX, height)
Copy the code
Draw the water droplets
Similarly, we can draw each control point. Then simplify the complex numbers and set the curve.
Box(
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.clickable() {}
) {
androidx.compose.foundation.Canvas(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
drawIntoCanvas { canvas ->
// Draw the bottom curve to the bottom
canvas.translate(0f, size.height)
canvas.scale(1f, -1f)
//--------------------------------------------------------------------------
canvas.save()
// The middle part is raised
val centerHdX = size.width / 3 / 2 + size.width / 3 * homeViewModel.position.value!!
// The coordinate position dot is the midpoint of the upper edge
canvas.translate(centerHdX, height)
//--------------------------------------------------------------------------
// Draw the elastic sphere
// If index=0 is clicked, there are three bottom buttons
canvas.save()
/ / 1, 2, 3
// Move the coordinate system to the click position (). Take the click position as our coordinate system point
val centerX =
size.width / 3 / 2 + size.width / 3 * homeViewModel.position.value!!
Log.e("Dot"."LoginPage: $centerX")
canvas.translate(centerX, height * 2 / 3.2 f)
//canvas.drawCircle(Offset(0f, 0f), 100f, paint)
// Here we know the coordinates and then we draw our circle
val r = 100f - 50 * (1 - mCurAnimValue)
// Calculate the coordinates of the circle and the center point
/ / 1. First, the origin (0 f, 0 f) and the radius r = 100 f -- - > the p6 (0 f, r), p5 = (r / 2, r), p4 (r, r / 2), p3 (r, 0 f)
P2 (r,-r/2),p1(r/2,-r),p0(0f,r)
P11 (-r/2,-r),p10(-r,-r/2),p9(-r,0f)
P8 (-r,r/2),p7(r/2,r),p6(0f,r)
Log.e("mCurAnimValueY"."LoginPage=: $mCurAnimValueY")
val moveTopHeight = mCurAnimValueY * 250f
val P0 = Offset(0f, -r + moveTopHeight + animalScaleCanvasHeightValue)
val P1 = Offset(r / 2, -r + moveTopHeight + animalScaleCanvasHeightValue)
val P2 = Offset(r, -r / 2 + moveTopHeight + animalScaleCanvasHeightValue)
val P3 = Offset(r, 0f + moveTopHeight + animalScaleCanvasHeightValue)
val P4 = Offset(r, r / 2 + moveTopHeight + animalScaleCanvasHeightValue)
val P5 = Offset(r / 2, r + moveTopHeight + animalScaleCanvasHeightValue)
val P6 = Offset(0f, r + moveTopHeight + animalScaleCanvasHeightValue)
val P7 = Offset(-r / 2, r + moveTopHeight + animalScaleCanvasHeightValue)
val P8 = Offset(-r, r / 2 + moveTopHeight + animalScaleCanvasHeightValue)
val P9 = Offset(-r, 0f + moveTopHeight + animalScaleCanvasHeightValue)
val P10 = Offset(-r, -r / 2 + moveTopHeight + animalScaleCanvasHeightValue)
val P11 = Offset(-r / 2, -r + moveTopHeight + animalScaleCanvasHeightValue)
val heightController = 180f
val pathReult = Path()
pathReult.moveTo(P0.x, P0.y - heightController * mCurAnimValue)
//p1->p2->p3
pathReult.cubicTo(
P1.x,
P1.y - 30 * mCurAnimValue,
P2.x,
P2.y - 30 * mCurAnimValue,
P3.x,
P3.y
)
//p4->p5->p6
pathReult.cubicTo(P4.x, P4.y, P5.x, P5.y, P6.x, P6.y)
//p7->p8->p9
pathReult.cubicTo(P7.x, P7.y, P8.x, P8.y, P9.x, P9.y)
//p10->p11->p0
pathReult.cubicTo(
P10.x,
P10.y - 30 * mCurAnimValue,
P11.x,
P11.y - 30 * mCurAnimValue,
P0.x,
P0.y - heightController * mCurAnimValue
)
pathReult.close()
//
paint.color = Color(245.215.254, mCurAnimValueColor.value.toInt() * 255)
canvas.drawPath(pathReult, paint)
}
}
}
Copy the code
2, bottom radian transformation
We see that the bottom curve moves up, and as the drop moves up part of the way, the bottom curve gradually decreases and the radius shrinks. And once again, we can find the control point and we can scale the whole thing at the bottom and we can scale it horizontally in the X direction and vertically in the y direction.
// Draw the bottom curve to the bottom
// Draw the bottom curve to the bottom
canvas.translate(0f, size.height)
canvas.scale(1f, -1f)
val paint = androidx.compose.ui.graphics.Paint()
paint.strokeWidth = 2f
paint.style = PaintingStyle.Fill
paint.color = Color(245.215.254.255)
val height = 276f
val cicleHeight = height / 3
val ScaleHeight = animalScaleCanvasHeightValue
val ScaleWidth = animalScaleCanvasWidthValue
// Control the left side of the neck, always changing
val path = Path()
path.moveTo(0f + ScaleWidth, 0f)
path.lineTo(0f + ScaleWidth, height - cicleHeight + ScaleHeight)
path.quadraticBezierTo(
0f + ScaleWidth,
height + ScaleHeight,
cicleHeight,
height + ScaleHeight
)
// First left radian
path.lineTo(size.width - cicleHeight - ScaleWidth, height + ScaleHeight)
path.quadraticBezierTo(
size.width - ScaleWidth,
height + ScaleHeight,
size.width - ScaleWidth,
height - cicleHeight + ScaleHeight
)
path.lineTo(size.width - ScaleWidth, 0f)
path.close()
canvas.drawPath(path, paint)
//--------------------------------------------------------------------------
canvas.save()
// The middle part is raised
val centerHdX =
size.width / 3 / 2 + size.width / 3 * homeViewModel.position.value!!
// The coordinate position dot is the midpoint of the upper edge
canvas.translate(centerHdX, height)
val R = 30f
//0-50 is the variable majority
val RH = mCurAnimalHeight
//50 to -50 is flat
val p0 = Offset(-R, 0f + RH + animalScaleCanvasHeightValue)
val p1 = Offset(-R, R + RH + animalScaleCanvasHeightValue)
val p3 = Offset(0f.2 * R - 30f + RH + animalScaleCanvasHeightValue)
val p5 = Offset(R, R + RH + animalScaleCanvasHeightValue)
val p6 = Offset(R, 0f + RH + animalScaleCanvasHeightValue)
val p7 = Offset(100f, -10f + animalScaleCanvasHeightValue)
val pathCub = Path()
pathCub.moveTo(-100f.0f + animalScaleCanvasHeightValue)
pathCub.cubicTo(p0.x, p0.y, p1.x, p1.y, p3.x, p3.y)
pathCub.cubicTo(p5.x, p5.y, p6.x, p6.y, p7.x, p7.y)
canvas.drawPath(pathCub, paint)
// The middle bulge falls
canvas.restore()
Copy the code
4. Final code 👍👍👍👍
class HomeViewModel: ViewModel() {
// Home page Index of the selected item
private val _position = MutableLiveData(-1)
// Animation state
val animalBoolean = mutableStateOf(true)
var position:LiveData<Int> = _position
// Select index data refresh
var bootomType=true
fun positionChanged(selectedIndex: Int){
_position.value=selectedIndex
}
}
@InternalComposeApi
@Composable
fun BottomNavigationTwo(homeViewModel:HomeViewModel){
val applyContext = currentComposer.applyCoroutineContext
val clickTrue = remember { mutableStateOf(false)}val mCurAnimValueColor = remember { Animatable(1f)}val animalBooleanState: Float by animateFloatAsState(
if (homeViewModel.animalBoolean.value) {
0f
} else {
1f
}, animationSpec = TweenSpec(durationMillis = 600),
finishedListener = {
if (it>=0.9 f&&clickTrue.value){ homeViewModel.animalBoolean.value = ! homeViewModel.animalBoolean.value } } )val stiffness = 100f
val animalScaleCanvasWidthValue: Float by animateFloatAsState(
if(! clickTrue.value) {0f
} else {
30f
},
animationSpec = SpringSpec(stiffness = stiffness),
)
val animalScaleCanvasHeightValue: Float by animateFloatAsState(
if(! clickTrue.value) {0f
} else {
30f
},
animationSpec = SpringSpec(stiffness = stiffness),
)
val mCurAnimalHeight: Float by animateFloatAsState(
if(! clickTrue.value) { -30f
} else {
30f
},
animationSpec = SpringSpec(stiffness = stiffness),
)
val mCurAnimValueY: Float by animateFloatAsState(
if(! clickTrue.value) {0f
} else {
1f
}, animationSpec = SpringSpec(stiffness = stiffness),
finishedListener = {
if (it >= 0.9 f && clickTrue.value) {
CoroutineScope(applyContext).launch {
mCurAnimValueColor.animateTo(
0f,
animationSpec = SpringSpec(stiffness = stiffness)
)
}
}
if (it <= 0.01 f && !clickTrue.value) {
CoroutineScope(applyContext).launch {
mCurAnimValueColor.animateTo(
1f,
animationSpec = SpringSpec(stiffness = stiffness)
)
}
}
// End of animation -> return to original position
if (it > 0.9 f&& clickTrue.value) { clickTrue.value = ! clickTrue.value } }//TweenSpec(durationMillis = 1600)
// DurationBasedAnimationSpec, FloatSpringSpec, FloatTweenSpec, KeyframesSpec, RepeatableSpec, SnapSpec, SpringSpec, TweenSpec
)
// Determine the radius animation
val mCurAnimValue: Float by animateFloatAsState(
if (clickTrue.value) {
0f
} else {
1f
}, animationSpec = SpringSpec(dampingRatio = 1f, stiffness = 30f)
)
Box(contentAlignment = Alignment.BottomCenter,
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.clickable() {}
) {
androidx.compose.foundation.Canvas(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
drawIntoCanvas { canvas ->
// Draw the bottom curve to the bottom
canvas.translate(0f, size.height)
canvas.scale(1f, -1f)
val paint = androidx.compose.ui.graphics.Paint()
paint.strokeWidth = 2f
paint.style = PaintingStyle.Fill
paint.color = Color(245.215.254.255)
val height = 276f
val cicleHeight = height / 3
val ScaleHeight = animalScaleCanvasHeightValue
val ScaleWidth = animalScaleCanvasWidthValue
// Control the left side of the neck, always changing
val path = Path()
path.moveTo(0f + ScaleWidth, 0f)
path.lineTo(0f + ScaleWidth, height - cicleHeight + ScaleHeight)
path.quadraticBezierTo(
0f + ScaleWidth,
height + ScaleHeight,
cicleHeight,
height + ScaleHeight
)
// First left radian
path.lineTo(size.width - cicleHeight - ScaleWidth, height + ScaleHeight)
path.quadraticBezierTo(
size.width - ScaleWidth,
height + ScaleHeight,
size.width - ScaleWidth,
height - cicleHeight + ScaleHeight
)
path.lineTo(size.width - ScaleWidth, 0f)
path.close()
canvas.drawPath(path, paint)
//--------------------------------------------------------------------------
canvas.save()
// The middle part is raised
val centerHdX =
size.width / 3 / 2 + size.width / 3 * homeViewModel.position.value!!
// The coordinate position dot is the midpoint of the upper edge
canvas.translate(centerHdX, height)
val R = 30f
//0-50 is the variable majority
val RH = mCurAnimalHeight
//50 to -50 is flat
val p0 = Offset(-R, 0f + RH + animalScaleCanvasHeightValue)
val p1 = Offset(-R, R + RH + animalScaleCanvasHeightValue)
val p3 = Offset(0f.2 * R - 30f + RH + animalScaleCanvasHeightValue)
val p5 = Offset(R, R + RH + animalScaleCanvasHeightValue)
val p6 = Offset(R, 0f + RH + animalScaleCanvasHeightValue)
val p7 = Offset(100f, -10f + animalScaleCanvasHeightValue)
val pathCub = Path()
pathCub.moveTo(-100f.0f + animalScaleCanvasHeightValue)
pathCub.cubicTo(p0.x, p0.y, p1.x, p1.y, p3.x, p3.y)
pathCub.cubicTo(p5.x, p5.y, p6.x, p6.y, p7.x, p7.y)
canvas.drawPath(pathCub, paint)
// The middle bulge falls
canvas.restore()
//--------------------------------------------------------------------------
// Draw the elastic sphere
// If index=0 is clicked, there are three bottom buttons
canvas.save()
/ / 1, 2, 3
// Move the coordinate system to the click position (). Take the click position as our coordinate system point
val centerX =
size.width / 3 / 2 + size.width / 3 * homeViewModel.position.value!!
Log.e("Dot"."LoginPage: $centerX")
canvas.translate(centerX, height * 2 / 3.2 f)
//canvas.drawCircle(Offset(0f, 0f), 100f, paint)
// Here we know the coordinates and then we draw our circle
val r = 100f - 50 * (1 - mCurAnimValue)
// Calculate the coordinates of the circle and the center point
/ / 1. First, the origin (0 f, 0 f) and the radius r = 100 f -- - > the p6 (0 f, r), p5 = (r / 2, r), p4 (r, r / 2), p3 (r, 0 f)
P2 (r,-r/2),p1(r/2,-r),p0(0f,r)
P11 (-r/2,-r),p10(-r,-r/2),p9(-r,0f)
P8 (-r,r/2),p7(r/2,r),p6(0f,r)
Log.e("mCurAnimValueY"."LoginPage=: $mCurAnimValueY")
val moveTopHeight = mCurAnimValueY * 250f
val P0 = Offset(0f, -r + moveTopHeight + animalScaleCanvasHeightValue)
val P1 = Offset(r / 2, -r + moveTopHeight + animalScaleCanvasHeightValue)
val P2 = Offset(r, -r / 2 + moveTopHeight + animalScaleCanvasHeightValue)
val P3 = Offset(r, 0f + moveTopHeight + animalScaleCanvasHeightValue)
val P4 = Offset(r, r / 2 + moveTopHeight + animalScaleCanvasHeightValue)
val P5 = Offset(r / 2, r + moveTopHeight + animalScaleCanvasHeightValue)
val P6 = Offset(0f, r + moveTopHeight + animalScaleCanvasHeightValue)
val P7 = Offset(-r / 2, r + moveTopHeight + animalScaleCanvasHeightValue)
val P8 = Offset(-r, r / 2 + moveTopHeight + animalScaleCanvasHeightValue)
val P9 = Offset(-r, 0f + moveTopHeight + animalScaleCanvasHeightValue)
val P10 = Offset(-r, -r / 2 + moveTopHeight + animalScaleCanvasHeightValue)
val P11 = Offset(-r / 2, -r + moveTopHeight + animalScaleCanvasHeightValue)
val heightController = 180f
val pathReult = Path()
pathReult.moveTo(P0.x, P0.y - heightController * mCurAnimValue)
//p1->p2->p3
pathReult.cubicTo(
P1.x,
P1.y - 30 * mCurAnimValue,
P2.x,
P2.y - 30 * mCurAnimValue,
P3.x,
P3.y
)
//p4->p5->p6
pathReult.cubicTo(P4.x, P4.y, P5.x, P5.y, P6.x, P6.y)
//p7->p8->p9
pathReult.cubicTo(P7.x, P7.y, P8.x, P8.y, P9.x, P9.y)
//p10->p11->p0
pathReult.cubicTo(
P10.x,
P10.y - 30 * mCurAnimValue,
P11.x,
P11.y - 30 * mCurAnimValue,
P0.x,
P0.y - heightController * mCurAnimValue
)
pathReult.close()
//
paint.color = Color(245.215.254, mCurAnimValueColor.value.toInt() * 255)
//canvas.drawPath(pathReult, paint)
}
}
}
Row(
modifier = Modifier.fillMaxWidth().height(200.dp), horizontalArrangement = Arrangement.SpaceAround,verticalAlignment = Alignment.Bottom
) {
Image(
bitmap = getBitmap(resource = R.drawable.home),
contentDescription = "1",
modifier = Modifier
.modifiers(homeViewModel.position.value, 0, animalBooleanState)
.clickable {
homeViewModel.positionChanged(0) clickTrue.value = ! clickTrue.value homeViewModel.animalBoolean.value = ! homeViewModel.animalBoolean.value } ) Image( bitmap = getBitmap(resource = R.drawable.center), contentDescription ="1",
modifier = Modifier
.modifiers(homeViewModel.position.value, 1, animalBooleanState)
.clickable {
homeViewModel.positionChanged(1) clickTrue.value = ! clickTrue.value homeViewModel.animalBoolean.value = ! homeViewModel.animalBoolean.value } ) Image( bitmap = getBitmap(resource = R.drawable.min), contentDescription ="1",
modifier = Modifier
.modifiers(homeViewModel.position.value, 2, animalBooleanState)
.clickable {
homeViewModel.positionChanged(2) clickTrue.value = ! clickTrue.value homeViewModel.animalBoolean.value = ! homeViewModel.animalBoolean.value } ) } } }fun Modifier.modifiers(
animalCenterIndex: Int? , i:Int,
animalBooleanState: Float
): Modifier {
Log.e("currentValue="."modifiers: "+animalCenterIndex.toString()+"= ="+i )
return if (animalCenterIndex == i) {
Modifier
.padding(bottom = 35.dp + (animalBooleanState * 100).dp)
.width(25.dp)
.height(25.dp)
} else {
return Modifier
.padding(bottom = 35.dp - (animalBooleanState * 10).dp)
.width(25.dp)
.height(25.dp)
}
}
Copy the code
Five, the summary
Cool animation may not be suitable for ordinary applications, but the performance will be reduced or the interaction will be more complicated and inconvenient. However, there is nothing wrong with the technology. I believe that as long as we can design 2D animation effects or UI by software, we will be able to achieve the effect through the same principle. Over the course of these articles we’ve gone from basic UI to all sorts of fancy implementations, and UI should be challenging and experimental. In this article, due to time problems, the animation part is not carefully adjusted. If you carefully adjust the animationSpec animation rules, you can achieve a closer viscous effect of the original animation. Of course, the animation section will be divided into another chapter. If my article can bring you help or feel little cool then leave bosses who 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 and valuable opinions.
I will make up the cool design above and below when I have time.