欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

翻转动画 - 旋转 3D 动画的应用

最编程 2024-03-20 19:27:56
...
演示图.gif

一.目的

应项目需求需要制作一个签到动画,类似于翻牌.每次点击签到,下面的卡牌翻转一圈.
(源码放末尾)


需求图

二.自定义动画

在Android实现3D效果方法有Open GL ES和Camera.我使用的是Camera.

关于canmara的解读,参考了博客 http://www.gcssloop.com/customview/matrix-3d-camera

下面是Rotate3dAnimation的代码:

public class Rotate3dAnimation extends Animation {
    private final float mFromDegrees;
    private final float mToDegrees;
    private final float mCenterX;
    private final float mCenterY;
    private final float mDepthZ;
    private final boolean mReverse;
    private Camera mCamera;
    float scale = 1;    // <------- 像素密度

    /**
     * 创建一个绕y轴旋转的3D动画效果,旋转过程中具有深度调节,可以指定旋转中心。
     * @param context     <------- 添加上下文,为获取像素密度准备
     * @param fromDegrees 起始时角度
     * @param toDegrees   结束时角度
     * @param centerX     旋转中心x坐标
     * @param centerY     旋转中心y坐标
     * @param depthZ      最远到达的z轴坐标
     * @param reverse     true 表示由从0到depthZ,false相反
     */
    public Rotate3dAnimation(Context context, float fromDegrees, float toDegrees,
                             float centerX, float centerY, float depthZ, boolean reverse) {
        mFromDegrees = fromDegrees;
        mToDegrees = toDegrees;
        mCenterX = centerX;
        mCenterY = centerY;
        mDepthZ = depthZ;
        mReverse = reverse;

        // 获取手机像素密度 (即dp与px的比例)
        scale = context.getResources().getDisplayMetrics().density;
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mCamera = new Camera();
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final float fromDegrees = mFromDegrees;
        float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
        final float centerX = mCenterX;
        final float centerY = mCenterY;
        final Camera camera = mCamera;
        final Matrix matrix = t.getMatrix();
        camera.save();

        // 调节深度
        if (mReverse) {
            camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
        } else {
            camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
        }

        // 绕y轴旋转
        camera.rotateY(degrees);

        camera.getMatrix(matrix);
        camera.restore();

        // 修正失真,主要修改 MPERSP_0 和 MPERSP_1
        float[] mValues = new float[9];
        matrix.getValues(mValues);              //获取数值
        mValues[6] = mValues[6]/scale;          //数值修正
        mValues[7] = mValues[7]/scale;          //数值修正
        matrix.setValues(mValues);              //重新赋值

        // 调节中心点
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }
}

三.使用

这部分的代码相对简单,主要就是为卡牌加上点击事件,点击时执行分步动画.

注意

1.第一段动画:卡牌旋转90度.旋转至看不见牌面的角度时--即动画结束时加入监听.此时将卡牌的画面变换成你需要的样子.再执行第二段动画
2.第二段动画:让卡牌从270度旋转到360度.为什么不是90到180,是因为转过去后卡面变镜像了.有疑惑的话可以自己试一下.
3.取卡牌宽高需要用到post方法否则取不到.
4.isDark是我用来判断正反面的,正面在上旋转时设置反面,反之亦然.如果是签到这种只需要旋转一次的就不需要加这个参数了.
package com.maomao.technology.rotate3ddemo;

import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.widget.ImageView;

import com.bumptech.glide.Glide;

public class MainActivity extends Activity {
    boolean isDark = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initview();
    }

    private void initview() {
        final ImageView card = findViewById(R.id.card);
        card.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //用post方法取card的宽高
                card.post(new Runnable() {
                    @Override
                    public void run() {
                        //取card中心点
                        final float centerX = card.getWidth() / 2f;
                        final float centerY = card.getHeight() / 2f;
                        // 构建3D旋转动画对象,旋转角度为0到90度
                        final Rotate3dAnimation rotation = new Rotate3dAnimation(MainActivity.this, 0, 90, centerX, centerY,
                                0f, false);
                        // 动画持续时间500毫秒
                        rotation.setDuration(500);
                        // 动画完成后保持完成的状态
                        rotation.setFillAfter(true);
                        rotation.setInterpolator(new AccelerateInterpolator());
                        card.startAnimation(rotation);
                        //监听器  翻转到90度的时候 卡面图片改变 然后将卡牌从270度翻转到360度刚好转回来
                        //这里注意不是90-180度,因为90-180翻转过来的图片是左右相反的镜像图
                        rotation.setAnimationListener(new Animation.AnimationListener() {
                            @Override
                            public void onAnimationStart(Animation animation) {
                            }

                            @Override
                            public void onAnimationEnd(Animation animation) {
                                //正反面判断
                                if (isDark) {
                                    isDark = false;
                                } else {
                                    isDark = true;
                                }
                                //点正面切换背面,反之亦然
                                if (isDark) {
                                    Glide.with(MainActivity.this).load(R.drawable.light).into(card);
                                } else {
                                    Glide.with(MainActivity.this).load(R.drawable.dark).into(card);
                                }
                                //270度翻转到360度
                                final Rotate3dAnimation rotation = new Rotate3dAnimation(MainActivity.this, 270, 360, centerX, centerY,
                                        0f, true);
                                rotation.setDuration(500);
                                // 动画完成后保持完成的状态
                                rotation.setFillAfter(false);
                                card.startAnimation(rotation);
                            }

                            @Override
                            public void onAnimationRepeat(Animation animation) {
                            }
                        });

                    }
                });
            }
        });
    }
}

三.源码

https://github.com/liumaomao0209/Rotate3DDemo