1. Introduction
When BROWSING GitHub the day before yesterday, I found a project that imitates the Loading effect of Gif. I feel the effect is very good and quite creative, as follows:
GitHub has several projects that do this, but few are fully implemented and some are buggy, so it took two days to implement it.
The effect is as follows:
GitHub project is here at LeavesLoading
2. The analysis
Implementation requirements:
- leaves
- Randomly generated
- The oscillation trajectory is a sine function and has random amplitude
- Flapping is accompanied by self-rotation, which is more consistent with physical laws
- The progress bar seems to blend in
- fan
- Can rotate
- Loading == 100% displays an animation
- details
- Fan and leaf adaptive View size
- Leaves cannot visually float out of RountRect boundaries
3. Core implementation
3.1 Random generation of leaves
The essence is to generate a certain number of leaves in advance, and the amplitude, phase and rotation direction of these leaves are random when they float, and the drift is periodic, that is, when the leaves float to the left, they return to the right again.
The Leaf class:
private class Leaf{
float x,y;/ / coordinates
AmplitudeType type;// Leaf flutter amplitude
int rotateAngle;// Rotate the Angle
RotateDir rotateDir;// Rotation direction
long startTime;// Start time
int n;// Initial phase
}
Copy the code
Leaf generation method:
Leaf generateLeaf(a){
Leaf leaf = new Leaf();
// Random amplitude
int randomType = mRandom.nextInt(3);
switch (randomType){
case 0:
/ / small amplitude
leaf.type = AmplitudeType.LITTLE;
break;
case 1:
// Medium amplitude
leaf.type = AmplitudeType.MIDDLE;
break;
default:
/ / large amplitude
leaf.type = AmplitudeType.BIG;
break;
}
// Rotate the direction randomly
int dir = mRandom.nextInt(2);
switch (dir){
case 0:
/ / counterclockwise
leaf.rotateDir = RotateDir.ANTICLOCKWISE;
break;
default:
/ / clockwise
leaf.rotateDir = RotateDir.CLOCKWISE;
break;
}
// Random starting Angle
leaf.rotateAngle = mRandom.nextInt(360);
leaf.n = mRandom.nextInt(20);
mAddTime += mRandom.nextInt((int)mLeafFloatTime);
leaf.startTime = System.currentTimeMillis() + mAddTime;
return leaf;
}
Copy the code
3.2 Leaf flapping trajectory is a sine function
Determine the coordinates of Leaf at a time (x, y) :
/** * get the (x,y) position of the leaf *@paramLeaf *@paramCurrentTime currentTime */
private void getLeafLocation(Leaf leaf,long currentTime){
long intervalTime = currentTime - leaf.startTime;// Float duration
if (intervalTime <= 0) {// The Leaf is not ready to float
return;
}else if (intervalTime > mLeafFloatTime){
// Leaf flapping time is greater than the specified flapping time, that is, Leaf flapping to the left, should return to the right
leaf.startTime = currentTime + new Random().nextInt((int)mLeafFloatTime);
}
// Calculate the movement factor
float fraction = (float) intervalTime / mLeafFloatTime;
leaf.x = (1-fraction)*mProgressLen;
leaf.y = getLeafLocationY(leaf);
if (leaf.x <= mYellowOvalHeight / 4) {// The leaf floats to the far left and may exceed the RoundRect boundary, so special treatment is made in advance
leaf.startTime = currentTime + new Random().nextInt((int)mLeafFloatTime); leaf.x = mProgressLen; leaf.y = getLeafLocationY(leaf); }}Copy the code
In order to make the floating trajectory of Leaf a sine function, the key is to determine the Y-axis coordinates of Leaf:
/** * get the Y coordinate of the leaf *@paramLeaf *@returnThe calculated Y-axis coordinates of the leaf */
private float getLeafLocationY(Leaf leaf){
float w = (float) (Math.PI * 2 / mProgressLen);/ / angular frequency
float A;// Calculate the amplitude value
switch (leaf.type){
case LITTLE:
A = mLeafLen/3;
break;
case MIDDLE:
A = mLeafLen*2/3;
break;
default:
A = mLeafLen;
break;
}
// (mheight-mleaflen)/2 is to center the Y axis of Leaf
return (float) (A * Math.sin(w * leaf.x + leaf.n)+(mHeight-mLeafLen)/2);
}
Copy the code
3.3 The leaf rotates when it flutters
This involves the drawing of Leaf. In fact, the Leaf and fan in Gif can be directly drawn using Canves, but there are two problems:
- Difficult to draw: it takes a lot of effort to draw a satisfactory figure and rotate, scale and translate it.
- Low flexibility: you have to redesign the drawing process if you want to change styles.
Therefore, the method of canves.drawbitmap () is used to draw, and the existing picture is directly used as the leaf and fan. Meanwhile, an overloaded method of canves.drawbitmap () is used to easily achieve rotation, scaling and translation:
void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) ;
Copy the code
It is the Matrix Matrix that encapsulates postScale(), postTranslate, postRotate() and other methods, which can help us quickly rotate, scale, translate and perform other operations on bitmaps. Remember to use it in conjunction with Canves’ save() and restore(), otherwise the desired effect will not be achieved.
For those of you who are not familiar with this area, see HenCoder’s custom View tutorial 1-4.
Method of drawing Leaf:
private void drawLeaves(Canvas canvas){
long currentTime = System.currentTimeMillis();
for (Leaf leaf : mLeafList) {
if(currentTime > leaf.startTime && leaf.startTime ! =0) {// Get the current coordinates of LEAF
getLeafLocation(leaf,currentTime);
canvas.save();
Matrix matrix = new Matrix();
// Zoom ADAPTS to View size
float scaleX = (float) mLeafLen / mLeafBitmapWidth;
float scaleY = (float) mLeafLen / mLeafBitmapHeight;
matrix.postScale(scaleX,scaleY);
/ / displacement
float transX = leaf.x;
float transY = leaf.y;
matrix.postTranslate(transX,transY);
/ / rotation
// Compute the rotation factor
float rotateFraction = ((currentTime - leaf.startTime) % mLeafRotateTime)
/(float)mLeafRotateTime;
float rotate;
switch (leaf.rotateDir){
case CLOCKWISE:
/ / clockwise
rotate = rotateFraction * 360 + leaf.rotateAngle;
break;
default:
/ / counterclockwise
rotate = -rotateFraction * 360 + leaf.rotateAngle;
break;
}
// Rotate the center of Leaf
matrix.postRotate(rotate,transX + mLeafLen / 2,transY + mLeafLen / 2); canvas.drawBitmap(mLeafBitmap,matrix,mBitmapPaint); canvas.restore(); }}Copy the code
3.4 Loading == 100% Animation
Add a judgment field isLoadingCompleted and select the corresponding draw strategy in onDraw().
IsLoadingCompleted is set according to progress in setProgress() :
/** * Set the progress (automatic refresh) *@param progress 0-100
*/
public void setProgress(int progress){
if (progress < 0){
mProgress = 0;
}else if (progress > 100){
mProgress = 100;
}else {
mProgress = progress;
}
if (progress == 100){
isLoadingCompleted = true;
}else {
isLoadingCompleted = false;
}
// 255 is opaque
mCompletedFanPaint.setAlpha(255);
postInvalidate();
}
Copy the code
Leavesload.ondraw ()
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); .if (isLoadingCompleted){
// Draw the finished effect
drawCompleted(canvas);
}else {
// Draw the fan leaf
drawFan(canvas,mFanLen,mBitmapPaint);
}
/ / refresh
postInvalidate();
}
Copy the code
DrawCompleted () :
private void drawCompleted(Canvas canvas) {
// Each time the fan is drawn, the opacity decreases by 10
int alpha = mCompletedFanPaint.getAlpha() - 10;
if (alpha <= 0){
alpha = 0;
}
mCompletedFanPaint.setAlpha(alpha);
// Text transparency is just the opposite of fan
mCompletedTextPaint.setAlpha(255-alpha);
// Calculate the transparency factor
float fraction = alpha / 255f;
// The leaf size and the text size also change inversely
float fanLen = fraction * mFanLen;
float textSize = (1 - fraction) * mCompletedTextSize;
mCompletedTextPaint.setTextSize(textSize);
// Measure the space taken up by text
Rect bounds = new Rect();
mCompletedTextPaint.getTextBounds(
LOADING_COMPLETED,
0,
LOADING_COMPLETED.length(),
bounds);
DrawLeaf () = drawLeaf()
drawFan(canvas, (int) fanLen, mCompletedFanPaint);
/ / draw text
canvas.drawText(
LOADING_COMPLETED,
0,
LOADING_COMPLETED.length(),
mFanCx-bounds.width()/2f,
mFanCy+bounds.height()/2f,
mCompletedTextPaint);
}
Copy the code
Process: calculate fan and text transparency -> calculate fan and text size and text space -> draw.
End of 4.
If there are any mistakes in this article, please comment in the comments section.
If you think LeavesLoading is of any help to you, I hope you can get your Star on GitHub!
Thanks:
- This article from GAStudio provides the core code reference