做一個簡單好看的ViewPager翻轉動畫

FlyingSnowBean發表於2017-05-18

一直都很喜歡Instagram的快拍(Story)功能,也很喜歡他們的翻轉效果,是一種簡單的3D翻轉效果。大致效果如下:

做一個簡單好看的ViewPager翻轉動畫
instagramstory.gif

貌似最近微博也出了一個差不多的Story的功能,用的翻轉動畫也是和Instagram一樣。

思路

看到這樣的效果,很容易想到用ViewPager的Transformer動畫來實現。當然這種翻轉效果網上也有相應的實現,就是以View的左邊或右邊為旋轉軸進行空間上Y軸的旋轉。
於是很容易我們可以寫出下面的程式碼

public class StereoPagerTransformer implements ViewPager.PageTransformer {
  private static final float MAX_ROTATE_Y = 90;
  private final float pageWidth;

  public StereoPagerTransformer(float pageWidth) {
    this.pageWidth = pageWidth;
  }

  public void transformPage(View view, float position) {
    view.setPivotY(view.getHeight() / 2);
    if (position < -1) { // [-Infinity,-1)
      // This page is way off-screen to the left.
      view.setPivotX(0);
      view.setRotationY(90);
    } else if (position <= 0) { // [-1,0]
      view.setPivotX(pageWidth);
      view.setRotationY(position * MAX_ROTATE_Y);
    } else if (position <= 1) { // (0,1]
      view.setPivotX(0);
      view.setRotationY(position * MAX_ROTATE_Y);
    } else { // (1,+Infinity]
      // This page is way off-screen to the right.
      view.setPivotX(0);
      view.setRotationY(90);
    }
  }
}複製程式碼

然後執行程式碼看一下我們實現的效果:

做一個簡單好看的ViewPager翻轉動畫
badtransformer.gif

額,總覺哪裡不對,嗯,就是動畫的速度不對,上面的寫法就是一個勻速進行的動畫,效果非常不理想,這時我們就要重新寫一個插值器(TimeInterpolator ),在嘗試了系統自帶的差值器後,發現效果仍然不是很理想。於是決定自己寫一個插值器。

關於插值器

根據TimeInterpolator程式碼中的文件可以得知,插值器用於控制動畫進行的速度,其輸入值為0~1,輸出值也為0~1。

/**
 * A time interpolator defines the rate of change of an animation. This allows animations
 * to have non-linear motion, such as acceleration and deceleration.
 */
public interface TimeInterpolator {

    /**
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getInterpolation(float input);
}複製程式碼

經過簡單的分析,這次我們的動畫應該在動畫前半段時進行緩慢一些(也就是輸入值在0到某個值之間),在後半段時(也就是輸入值在某個值到1之間)進行的快速一些。
經過簡單的調整,最終我寫了如下的插值器

private static final TimeInterpolator sInterpolator = new TimeInterpolator() {
        @Override
        public float getInterpolation(float input) {
            if (input < 0.7) {
                return input * (float) pow(0.7, 3) * MAX_ROTATE_Y;
            } else {
                return (float) pow(input, 4) * MAX_ROTATE_Y;
            }
        }
    };複製程式碼

再次執行程式碼,這次效果看上去好多了,哈哈,一個簡單又好看的效果就完成了。

做一個簡單好看的ViewPager翻轉動畫
goodtransformer.gif

最後附上完整的程式碼:

import android.animation.TimeInterpolator;
import android.support.v4.view.ViewPager;
import android.view.View;

import static java.lang.Math.pow;

/**
 * @author wupanjie
 */

public class StereoPagerTransformer implements ViewPager.PageTransformer {
    private static final float MAX_ROTATE_Y = 90;

    private static final TimeInterpolator sInterpolator = new TimeInterpolator() {
        @Override
        public float getInterpolation(float input) {
            if (input < 0.7) {
                return input * (float) pow(0.7, 3) * MAX_ROTATE_Y;
            } else {
                return (float) pow(input, 4) * MAX_ROTATE_Y;
            }
        }
    };

    private final float pageWidth;

    public StereoPagerTransformer(float pageWidth) {
        this.pageWidth = pageWidth;
    }

    public void transformPage(View view, float position) {
        view.setPivotY(view.getHeight() / 2);
        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setPivotX(0);
            view.setRotationY(90);
        } else if (position <= 0) { // [-1,0]
            view.setPivotX(pageWidth);
            view.setRotationY(-sInterpolator.getInterpolation(-position));
        } else if (position <= 1) { // (0,1]
            view.setPivotX(0);
            view.setRotationY(sInterpolator.getInterpolation(position));
        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setPivotX(0);
            view.setRotationY(90);
        }
    }
}複製程式碼

總結

動畫的靈魂在於它的插值器,插值器控制了動畫進行的速度,這次我選擇自己寫了一個插值器作為練手,其實我這次寫的插值器效果仍不是很平滑,動畫的插值器也應該用貝塞爾曲線來製作,這樣我們的動畫就會進行的更平滑。具體大家可以參考自帶的PathInterpolator,是在API 21以後引入的,當然它也有對應的相容包。
完整專案地址: github.com/wuapnjie/Da…

相關文章