In daily development, often will encounter all sorts of visual effect, some effect may in a glance can let a person feel very complicated, but we must be clear: all complex dynamic effect is the basis of can be decomposed into a single action, such as scaling, translation and rotation these basic units, and then all the basic unit are combined, can produce a let a person shine at the moment of visual effect.
First take a look at the following effect:
Break it down as we mentioned above:
- The Logo name LitePlayer is split into a single text
- All the text is randomly scattered around the screen
- The middle Logo is hidden
- Logo text moves from a random position to a fixed position on the page
- The Logo image in the middle is gradually displayed with a small shift from bottom to top
- The Logo is combined with the broken text to form a name
- After the Logo is combined into the name, a gradual halo effect moves from left to right
- End of the animation
Once we disassemble the animation, we can construct an implementation for each disassembly unit.
- First of all, let’s get right
logo
Text animation to achieve:
- First for the data source, we expect to pass in one
logo
Internally disassemble the string into a single literal array:
// fill the text to array
private void fillLogoTextArray(String logoName) {
if (TextUtils.isEmpty(logoName)) {
return;
}
if (mLogoTexts.size() > 0) {
mLogoTexts.clear();
}
for (int i = 0; i < logoName.length(); i++) {
charc = logoName.charAt(i); mLogoTexts.put(i, String.valueOf(c)); }}Copy the code
- All the text needs to be randomly scattered around the screen, because when it comes to coordinates, we can
onSizeChanged
In thelogo
Initialization of random position of text, at the same time, we build two sets to store the coordinate state of each text after being broken up and combined:
// Coordinates of the final synthesized logo
private SparseArray<PointF> mQuietPoints = new SparseArray<>();
// The logo is randomly scattered coordinates
private SparseArray<PointF> mRadonPoints = new SparseArray<>();
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
initLogoCoordinate();
}
private void initLogoCoordinate(a) {
float centerY = mHeight / 2f + mPaint.getTextSize() / 2 + mLogoOffset;
// calculate the final xy of the text
float totalLength = 0;
for (int i = 0; i < mLogoTexts.size(); i++) {
String str = mLogoTexts.get(i);
float currentLength = mPaint.measureText(str);
if(i ! = mLogoTexts.size() -1) {
totalLength += currentLength + mTextPadding;
} else{ totalLength += currentLength; }}// the draw width of the logo must small than the width of this AnimLogoView
if (totalLength > mWidth) {
throw new IllegalStateException("This view can not display all text of logoName, please change text size.");
}
float startX = (mWidth - totalLength) / 2;
if (mQuietPoints.size() > 0) {
mQuietPoints.clear();
}
for (int i = 0; i < mLogoTexts.size(); i++) {
String str = mLogoTexts.get(i);
float currentLength = mPaint.measureText(str);
mQuietPoints.put(i, new PointF(startX, centerY));
startX += currentLength + mTextPadding;
}
// generate random start xy of the text
if (mRadonPoints.size() > 0) {
mRadonPoints.clear();
}
// Build random initial coordinates
for (int i = 0; i < mLogoTexts.size(); i++) {
mRadonPoints.put(i, new PointF((float) Math.random() * mWidth, (float) Math.random() * mHeight)); }}Copy the code
- Construct the animation process, define an attribute animation to calculate the progress from 0-1, and redraw the text to move from the messy scattered coordinates to the final combined coordinates during the animation process:
// init the translation animation
private void initOffsetAnimation(a) {
mOffsetAnimator = ValueAnimator.ofFloat(0.1);
mOffsetAnimator.setDuration(mOffsetDuration);
mOffsetAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (mQuietPoints.size() <= 0 || mRadonPoints.size() <= 0) {
return;
}
mOffsetAnimProgress = (float) animation.getAnimatedValue(); invalidate(); }}); }@Override
protected void onDraw(Canvas canvas) {
if(! isOffsetAnimEnd) {// offset animation
mPaint.setAlpha((int) Math.min(255.255 * mOffsetAnimProgress + 100));
for (int i = 0; i < mQuietPoints.size(); i++) {
PointF quietP = mQuietPoints.get(i);
PointF radonP = mRadonPoints.get(i);
float x = radonP.x + (quietP.x - radonP.x) * mOffsetAnimProgress;
floaty = radonP.y + (quietP.y - radonP.y) * mOffsetAnimProgress; canvas.drawText(mLogoTexts.get(i), x, y, mPaint); }}}Copy the code
- At this point we’ve got the
logo
Now that the text animation is done, let’s look at step 7 of the disassembly, and the lighting effect. For this lighting effect, the preferred solution is throughGradient
+Shader
The implementation. Since drawing gradients also involves coordinates, initialization of the animation is also includedonSizeChanged
To:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
initLogoCoordinate();// Initialize the coordinate animation
initGradientAnimation(w);// Initialize the gradient animation
}
// init the gradient animation
private void initGradientAnimation(int width) {
mGradientAnimator = ValueAnimator.ofInt(0.2 * width);
if(mGradientListener ! =null) {
mGradientAnimator.addListener(mGradientListener);
}
mGradientAnimator.setDuration(mGradientDuration);
mGradientAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mMatrixTranslate = (int) animation.getAnimatedValue(); invalidate(); }}); mLinearGradient =new LinearGradient(-width, 0.0.0.new int[]{mTextColor, mGradientColor, mTextColor},
new float[] {0.0.5 f.1}, Shader.TileMode.CLAMP);
mGradientMatrix = new Matrix();
}
Copy the code
- The gradient animation automatically plays after the text move animation ends, so we can initialize the text move animation to listen for the end of the animation and draw at the same time
onDraw
To draw text in:
// init the translation animation
private void initOffsetAnimation(a) {...// Initialize the movement animation. mOffsetAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if(mGradientAnimator ! =null && isShowGradient) {
isOffsetAnimEnd = true; mPaint.setShader(mLinearGradient); mGradientAnimator.start(); }}}); }@Override
protected void onDraw(Canvas canvas) {
if(! isOffsetAnimEnd) {// offset animation.// Text moving animation. }else {// gradient animation
for (int i = 0; i < mQuietPoints.size(); i++) {
PointF quietP = mQuietPoints.get(i);
canvas.drawText(mLogoTexts.get(i), quietP.x, quietP.y, mPaint);
}
mGradientMatrix.setTranslate(mMatrixTranslate, 0); mLinearGradient.setLocalMatrix(mGradientMatrix); }}Copy the code
- At this point, text animation has been implemented. What’s left is the definition of some custom properties, providing some external properties
setter
andgetter
At the same time, you need to consider resource release of the animation during the page life cycle. Ok, let’s see what we achieved:
- For the above Logo picture animation can be a separate
ImageView
Pan + transparency animation implementation, I don’t need the space to describe it here.
I believe that most of the students have mastered the custom View, but for complex animation, whether can use these skilled ability on the blade, maybe some students will be at a loss to see a gorgeous effect. This paper does not carry out in-depth analysis of animation, nor involve complex data operation. It just illustrates a general method of dynamic effect analysis through a simple example. Through this way of thinking, you can clearly understand the realization and goals of each step.
Finally, to sum up, for custom motion effects, we can first let THE UI provide the final visual effect, analyze the single frame through the tool, observe the action relationship between each frame, and break it down into each basic unit. Each unit step is then implemented, and finally integrated to achieve a coherent effect. This is the idea, and once you’re comfortable with this idea, you need to have some knowledge of mathematics, trigonometric functions, matrix operations, and so on. As long as these two aspects of ability are well cultivated, any complex dynamic effect in daily development is not to be feared.
Attached project source code address: github.com/seagazer/an…