属性动画案例:雅虎加载视差动画

实现效果:
这里写图片描述

这边其实是两个View,加载动画覆盖在需要进入的页面的上面
动画分为三种状态
1:一开始的小圆球旋转动画
2:小圆逃逸后再聚合动画
3:水波纹扩散动画

一、初始化基本参数

    private void init(Context context) {
        //这边mCircleColors为六个小球的颜色
        mCircleColors = context.getResources().getIntArray(R.array.splash_circle_colors);
        //画笔初始化
        //消除锯齿
        mPaint.setAntiAlias(true);
        mPaintBackground.setAntiAlias(true);//该Paint为加载动画白色背景墙
        //设置样式---边框样式--描边
        mPaintBackground.setStyle(Paint.Style.STROKE);
        mPaintBackground.setColor(mSplashBgColor);
    }

二、使用策略模式分别实现三种状态

    private SplashState mState=null;

    private abstract class SplashState{
        public abstract void drawSate(Canvas canvas);

    }

先定义好是要实现的策略类

1)旋转动画

    /**
     * 1.旋转动画
     * 控制各个小圆的坐标----控制小圆的角度变化------属性动画ValueAnimator
     */
    private class RotateState extends SplashState{
        public RotateState() {
            //1.动画的初始工作, 0~2π 圆周角的弧度数
            mAnimator=ValueAnimator.ofFloat(0f,(float)(Math.PI*2));
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    //计算某个时刻当前的角度是多少
                    mCurrentRotationAngle= (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            // 2.开启动画
            mAnimator.setInterpolator(new LinearInterpolator());
            mAnimator.setDuration(mRotationDuration);
            mAnimator.setRepeatCount(ValueAnimator.INFINITE);
            mAnimator.start();
        }

        public void cancel(){
            mAnimator.cancel();
        }

        @Override
        public void drawSate(Canvas canvas) {
            //1.背景---擦黑板,涂成白色
            drawBackground(canvas);
            //2.绘制小圆
            drawCircles(canvas);
        }
    }

构造方法中初始化好旋转动画所需的属性动画

绘制小圆的方法

private void drawCircles(Canvas canvas) {
        //每个小圆之间的间隔角度2π/小圆的个数
        float rotationAngel= (float) (2*Math.PI/mCircleColors.length);
        for (int i=0;i<mCircleColors.length;i++){
            /*
             *  x=r*cos(a)+centerX
             *  y=r*sin(a)+centerY
             */
            double angle=i*rotationAngel+mCurrentRotationAngle;
            //每个小圆i*间隔角度+旋转角度=当前小圆的真实角度
            float cx= (float) (mCurrentRotationRadius*Math.cos(angle)+mCenterX);
            float cy= (float) (mCurrentRotationRadius*Math.sin(angle)+mCenterY);
            mPaint.setColor(mCircleColors[i]);
            canvas.drawCircle(cx,cy,mCircleRadius,mPaint);
        }

    }

1.首先先求出一个由六个小圆组成大圆之间它们之间的间隔角度为2π/小球个数
2.再根据属性动画mCurrentRotationAngle计算的旋转角度以及间隔角度算出当前小球的真实角度
3.mCenterX和mCenterY取的是屏幕宽高的一半,mCurrentRotationRadius为定义的大圆的半径
这里写图片描述
4.这样就算出了小圆的圆心坐标

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(mState==null){
            //开启第一个动画,旋转动画
            mState=new RotateState();
        }

        //调用绘制方法
        mState.drawSate(canvas);
    }

在onDraw开启第一个动画,并调用绘制方法开始无限循环的旋转小球,在主界面顶一个延迟来结束第一个动画进入第二个动画

    private void startLoadData() {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //数据加载完毕,进入主界面---》开启后面的两个动画
                mSplashView.splashDisapper();
            }
        },3000);
    }

自定义View中:

    public void splashDisapper(){
        //开启后面两个动画

        //换模板if--换状态
        if(mState!=null&&mState instanceof RotateState){
            //结束旋转动画
            RotateState rotateState= (RotateState) mState;
            rotateState.cancel();

            post(new Runnable() {
                @Override
                public void run() {
                    mState=new MergingState();
                }
            });
        }
    }

2)聚合动画

    /**
     * 2.聚合动画
     * 要素:大圆的半径不断地变大---》变小----》小圆的坐标
     */
    private class MergingState extends SplashState{
        public MergingState() {
            //r~0的某个值
            mAnimator=ValueAnimator.ofFloat(mRotationRadius,0);
            mAnimator.setDuration(mRotationDuration);
            mAnimator.setInterpolator(new OvershootInterpolator(10f));
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    //某个时刻当前的大圆半径是多少?
                    mCurrentRotationRadius= (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    mState=new ExpandState();
                }
            });
            mAnimator.start();
        }

        @Override
        public void drawSate(Canvas canvas) {
            //1.背景---擦黑板,涂成白色
            drawBackground(canvas);
            //2.绘制小圆
            drawCircles(canvas);
        }
    }

1.mRotationRadius大圆初始半径到0的值,加入插值器OvershootInterpolator(向前甩一定值后再回到原来位置)
2.当持续时间完成调用下一个动画,这边drawState中同样是需要画白色背景墙和小圆的,只是这个时候mCurrentRotationRadius大圆半径进行了变化

3)水波纹扩散动画

    /**
     * 3.水波纹扩散动画
     * 画一个空心圆,让它的画笔的粗细变成很大---不断地减少画笔的粗细
     * 空心圆变化的范围:0~对角线/2
     */
    private class ExpandState extends SplashState{
        public ExpandState() {
            //1200ms,计算某个时刻当前的大圆半径是多少?r~对角线一半的某个值
            mAnimator=ValueAnimator.ofFloat(mCircleRadius,mDiagonalDist);
            mAnimator.setDuration(mRotationDuration);
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mHoleRadius= (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mAnimator.start();
        }

        @Override
        public void drawSate(Canvas canvas) {
            //1.背景---擦黑板,涂成白色
            drawBackground(canvas);
        }
    }

1.第三个动画实现方式根据画一个空心圆,控制其stroWidth大小,不断减小实现扩散
2.mHoleRadius为空心圆的初始半径,这个时候处在第三状态的一开始所有的小球是已经聚集在一起的,所以初始半径这边根据属性动画mCircleRadius(小圆半径)到mDiagonalDist
3.mDiagonalDist:为屏幕对角线的一半,至于为什么是这个需要看一下drawBackground方法

    private void drawBackground(Canvas canvas) {
        if(mHoleRadius>0f){//第三个动画开始执行
            //得到画笔的宽度---对角线的一半-空心圆的半径
            float stroWidth=mDiagonalDist-mHoleRadius;
            mPaintBackground.setStrokeWidth(stroWidth);
            //画圆的半径=空心圆的半径+画笔的宽度/2
            float radius=mHoleRadius+stroWidth/2;
            canvas.drawCircle(mCenterX,mCenterY,radius,mPaintBackground);
        }else{//其他两个动画
            canvas.drawColor(mSplashBgColor);
        }
    }

这里写图片描述

这样空心圆半径不断变大,stroWdith不断减少,圆半径随之改变,就实现了扩散的方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于 HTML5 Canvas 的全屏酷炫星光闪烁 3D 视差背景动画特效的 HTML 代码: ```html <!doctype html> <html> <head> <meta charset="UTF-8"> <title>Canvas Star Field</title> <style> canvas { background-color: #000; } </style> </head> <body> <canvas id="canvas"></canvas> <script> var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), stars = [], particles = [], maxStars = 1300, maxParticles = 100; // 创建星星 for (var i = 0; i < maxStars; i++) { stars.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, radius: Math.random() * 1.5, depth: Math.random() * 2000 + 500 }); } // 创建粒子 function emitParticle() { if (particles.length < maxParticles) { var particle = { x: canvas.width / 2, y: canvas.height / 2, vx: Math.random() * 2 - 1, vy: Math.random() * 2 - 1, radius: Math.random() * 2 + 2, alpha: Math.random() * 0.5 + 0.5, life: Math.random() * 200 + 100 }; particles.push(particle); } } // 更新粒子 function updateParticles() { particles.forEach(function(particle, index) { particle.x += particle.vx; particle.y += particle.vy; particle.life--; particle.alpha -= 0.01; if (particle.life <= 0 || particle.alpha <= 0) { particles.splice(index, 1); } }); } // 绘制星星 function drawStar(star) { var x = (star.x - canvas.width / 2) * (star.depth / canvas.width), y = (star.y - canvas.height / 2) * (star.depth / canvas.width), radius = star.radius * (star.depth / canvas.width); context.beginPath(); context.arc(x + canvas.width / 2, y + canvas.height / 2, radius, 0, Math.PI * 2); context.fillStyle = '#fff'; context.fill(); } // 绘制粒子 function drawParticle(particle) { context.beginPath(); context.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2); context.fillStyle = 'rgba(255, 255, 255, ' + particle.alpha + ')'; context.fill(); } // 绘制 function draw() { context.clearRect(0, 0, canvas.width, canvas.height); // 绘制星星 stars.forEach(function(star) { drawStar(star); }); // 绘制粒子 particles.forEach(function(particle) { drawParticle(particle); }); } // 循环 function loop() { emitParticle(); updateParticles(); draw(); requestAnimationFrame(loop); } // 初始化画布 function initCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; window.addEventListener('resize', function() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }); } // 初始化 function init() { initCanvas(); loop(); } // 执行初始化 init(); </script> </body> </html> ``` 这段代码创建了一个全屏的 Canvas 画布,并在其中绘制了星星和粒子。通过调整粒子的数量和大小,以及星星的深度和大小,可以得到不同的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值