Android下雪动画的实现

自定义View

package com.shanjing.snowflake;import android.content.Context;import android.graphics.Canvas;import android.util.AttributeSet;import android.view.View;import android.view.ViewTreeObserver;import androidx.annotation.Nullable;import java.util.ArrayList;import java.util.List;public class FallingView extends View {    private Context mContext;    private AttributeSet mAttrs;    private List fallObjects;    private int viewWidth;    private int viewHeight;    private static final int defaultWidth = 600;//默认宽度    private static final int defaultHeight = 1000;//默认高度    private static final int intervalTime = 5;//重绘间隔时间    public FallingView(Context context) {        super(context);        mContext = context;        init();    }    public FallingView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        mContext = context;        mAttrs = attrs;        init();    }    private void init() {        fallObjects = new ArrayList<>();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int height = measureSize(defaultHeight, heightMeasureSpec);        int width = measureSize(defaultWidth, widthMeasureSpec);        setMeasuredDimension(width, height);        viewWidth = width;        viewHeight = height;    }    private int measureSize(int defaultSize, int measureSpec) {        int result = defaultSize;        int specMode = View.MeasureSpec.getMode(measureSpec);        int specSize = View.MeasureSpec.getSize(measureSpec);        if (specMode == View.MeasureSpec.EXACTLY) {            result = specSize;        } else if (specMode == View.MeasureSpec.AT_MOST) {            result = Math.min(result, specSize);        }        return result;    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if (fallObjects.size() > 0) {            for (int i = 0; i < fallObjects.size(); i++) {                //然后进行绘制                fallObjects.get(i).drawObject(canvas);            }            // 隔一段时间重绘一次, 动画效果            getHandler().postDelayed(runnable, intervalTime);        }    }    // 重绘线程    private Runnable runnable = new Runnable() {        @Override        public void run() {            invalidate();        }    };    /**     * 向View添加下落物体对象     *     * @param fallObject 下落物体对象     * @param num     */    public void addFallObject(final FallObject fallObject, final int num) {        getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {            @Override            public boolean onPreDraw() {                getViewTreeObserver().removeOnPreDrawListener(this);                for (int i = 0; i < num; i++) {                    FallObject newFallObject = new FallObject(fallObject.builder, viewWidth, viewHeight);                    fallObjects.add(newFallObject);                }                invalidate();                return true;            }        });    }}
package com.shanjing.snowflake;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Matrix;import android.graphics.PixelFormat;import android.graphics.drawable.Drawable;import java.util.Random;public class FallObject {    private int initX;    private int initY;    private Random random;    private int parentWidth;//父容器宽度    private int parentHeight;//父容器高度    private float objectWidth;//下落物体宽度    private float objectHeight;//下落物体高度    public int initSpeed;//初始下降速度    public int initWindLevel;//初始风力等级    public float presentX;//当前位置X坐标    public float presentY;//当前位置Y坐标    public float presentSpeed;//当前下降速度    private float angle;//物体下落角度    private Bitmap bitmap;    public Builder builder;    private boolean isSpeedRandom;//物体初始下降速度比例是否随机    private boolean isSizeRandom;//物体初始大小比例是否随机    private boolean isWindRandom;//物体初始风向和风力大小比例是否随机    private boolean isWindChange;//物体下落过程中风向和风力是否产生随机变化    private static final int defaultSpeed = 10;//默认下降速度    private static final int defaultWindLevel = 0;//默认风力等级    private static final int defaultWindSpeed = 10;//默认单位风速    private static final float HALF_PI = (float) Math.PI / 2;//π/2    public FallObject(Builder builder, int parentWidth, int parentHeight) {        random = new Random();        this.parentWidth = parentWidth;        this.parentHeight = parentHeight;        initX = random.nextInt(parentWidth);        initY = random.nextInt(parentHeight) - parentHeight;        presentX = initX;        presentY = initY;        this.builder = builder;        isSpeedRandom = builder.isSpeedRandom;        isSizeRandom = builder.isSizeRandom;        isWindRandom = builder.isWindRandom;        isWindChange = builder.isWindChange;        initSpeed = builder.initSpeed;        randomSpeed();        randomSize();        randomWind();    }    private FallObject(Builder builder) {        this.builder = builder;        initSpeed = builder.initSpeed;        bitmap = builder.bitmap;        isSpeedRandom = builder.isSpeedRandom;        isSizeRandom = builder.isSizeRandom;        isWindRandom = builder.isWindRandom;        isWindChange = builder.isWindChange;    }    public static final class Builder {        private int initSpeed;        private int initWindLevel;        private Bitmap bitmap;        private boolean isSpeedRandom;        private boolean isSizeRandom;        private boolean isWindRandom;        private boolean isWindChange;        public Builder(Bitmap bitmap) {            this.initSpeed = defaultSpeed;            this.initWindLevel = defaultWindLevel;            this.bitmap = bitmap;            this.isSpeedRandom = false;            this.isSizeRandom = false;            this.isWindRandom = false;            this.isWindChange = false;        }        public Builder(Drawable drawable) {            this.initSpeed = defaultSpeed;            this.initWindLevel = defaultWindLevel;            this.bitmap = drawableToBitmap(drawable);            this.isSpeedRandom = false;            this.isSizeRandom = false;            this.isWindRandom = false;            this.isWindChange = false;        }        /**         * 设置物体的初始下落速度         *         * @param speed         * @return         */        public Builder setSpeed(int speed) {            this.initSpeed = speed;            return this;        }        /**         * 设置物体的初始下落速度         *         * @param speed         * @param isRandomSpeed 物体初始下降速度比例是否随机         * @return         */        public Builder setSpeed(int speed, boolean isRandomSpeed) {            this.initSpeed = speed;            this.isSpeedRandom = isRandomSpeed;            return this;        }        /**         * 设置物体大小         *         * @param w         * @param h         * @return         */        public Builder setSize(int w, int h) {            this.bitmap = changeBitmapSize(this.bitmap, w, h);            return this;        }        /**         * 设置物体大小         *         * @param w         * @param h         * @param isRandomSize 物体初始大小比例是否随机         * @return         */        public Builder setSize(int w, int h, boolean isRandomSize) {            this.bitmap = changeBitmapSize(this.bitmap, w, h);            this.isSizeRandom = isRandomSize;            return this;        }        /**         * 设置风力等级、方向以及随机因素         *         * @param level        风力等级(绝对值为 5 时效果会比较好),为正时风从左向右吹(物体向X轴正方向偏移),为负时则相反         * @param isWindRandom 物体初始风向和风力大小比例是否随机         * @param isWindChange 在物体下落过程中风的风向和风力是否会产生随机变化         * @return         */        public Builder setWind(int level, boolean isWindRandom, boolean isWindChange) {            this.initWindLevel = level;            this.isWindRandom = isWindRandom;            this.isWindChange = isWindChange;            return this;        }        public FallObject build() {            return new FallObject(this);        }    }    /**     * 绘制物体对象     *     * @param canvas     */    public void drawObject(Canvas canvas) {        moveObject();        canvas.drawBitmap(bitmap, presentX, presentY, null);    }    /**     * 移动物体对象     */    private void moveObject() {        moveX();        moveY();        if (presentY > parentHeight || presentX < -bitmap.getWidth() || presentX > parentWidth + bitmap.getWidth()) {            reset();        }    }    /**     * X轴上的移动逻辑     */    private void moveX() {        presentX += defaultWindSpeed * Math.sin(angle);        if (isWindChange) {            angle += (float) (random.nextBoolean() ? -1 : 1) * Math.random() * 0.0025;        }    }    /**     * Y轴上的移动逻辑     */    private void moveY() {        presentY += presentSpeed;    }    /**     * 重置object位置     */    private void reset() {        presentY = -objectHeight;        randomSpeed();//记得重置时速度也一起重置,这样效果会好很多        randomWind();//记得重置一下初始角度,不然雪花会越下越少(因为角度累加会让雪花越下越偏)    }    /**     * 随机物体初始下落速度     */    private void randomSpeed() {        if (isSpeedRandom) {            presentSpeed = (float) ((random.nextInt(3) + 1) * 0.1 + 1) * initSpeed;//这些随机数大家可以按自己的需要进行调整        } else {            presentSpeed = initSpeed;        }    }    /**     * 随机物体初始大小比例     */    private void randomSize() {        if (isSizeRandom) {            float r = (random.nextInt(10) + 1) * 0.1f;            float rW = r * builder.bitmap.getWidth();            float rH = r * builder.bitmap.getHeight();            bitmap = changeBitmapSize(builder.bitmap, (int) rW, (int) rH);        } else {            bitmap = builder.bitmap;        }        objectWidth = bitmap.getWidth();        objectHeight = bitmap.getHeight();    }    /**     * 随机风的风向和风力大小比例,即随机物体初始下落角度     */    private void randomWind() {        if (isWindRandom) {            angle = (float) ((random.nextBoolean() ? -1 : 1) * Math.random() * initWindLevel / 50);        } else {            angle = (float) initWindLevel / 50;        }        //限制angle的最大最小值        if (angle > HALF_PI) {            angle = HALF_PI;        } else if (angle < -HALF_PI) {            angle = -HALF_PI;        }    }    /**     * drawable图片资源转bitmap     *     * @param drawable     * @return     */    public static Bitmap drawableToBitmap(Drawable drawable) {        Bitmap bitmap = Bitmap.createBitmap(                drawable.getIntrinsicWidth(),                drawable.getIntrinsicHeight(),                drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888                        : Bitmap.Config.RGB_565);        Canvas canvas = new Canvas(bitmap);        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());        drawable.draw(canvas);        return bitmap;    }    /**     * 改变bitmap的大小     *     * @param bitmap 目标bitmap     * @param newW   目标宽度     * @param newH   目标高度     * @return     */    public static Bitmap changeBitmapSize(Bitmap bitmap, int newW, int newH) {        int oldW = bitmap.getWidth();        int oldH = bitmap.getHeight();        // 计算缩放比例        float scaleWidth = ((float) newW) / oldW;        float scaleHeight = ((float) newH) / oldH;        // 取得想要缩放的matrix参数        Matrix matrix = new Matrix();        matrix.postScale(scaleWidth, scaleHeight);        // 得到新的图片        bitmap = Bitmap.createBitmap(bitmap, 0, 0, oldW, oldH, matrix, true);        return bitmap;    }}

布局中引用自定义视图

代码设置动画属性并显示

//初始化一个雪花样式的fallObject        FallObject.Builder builder = new FallObject.Builder(getResources().getDrawable(R.drawable.snow_flake));        FallObject fallObject = builder                .setSpeed(6, true)                .setSize(40, 40, true)                .setWind(5, true, true)                .build();        fallingView = findViewById(R.id.fallingView);        fallingView.addFallObject(fallObject, 100);//添加下落物体对象

原生JS实现(JS资源来自PHP中文网)

assets/css/www.jsdaima.com.css

/*js代码(www.ph.cn)是IT资源下载与IT技能学习平台。我们拒绝滥竽充数,只提供精品IT资源!*/:root {  font-family: "Microsoft Yahei", sans-serif;}html,body {  width: 100%;  height: 100%;  padding: 0;  margin: 0;  background: rgb(119, 13, 13);  background: radial-gradient(    circle,    rgba(119, 13, 13, 0.92) 64%,    rgba(0, 0, 0, 0.6) 100%  );}canvas {  width: 100%;  height: 100%;}.label {  font-size: 2.2rem;  background: url("../img/6368077651977322227241996.png");  background-clip: text;  -webkit-background-clip: text;  color: transparent;  animation: moveBg 30s linear infinite;}@keyframes moveBg {  0% {    background-position: 0% 30%;  }  100% {    background-position: 1000% 500%;  }}.middle {  position: absolute;  top: 50%;  left: 50%;  transform: translate(-50%, -50%);  text-align: center;  user-select: none;}.time {  color: #d99c3b;  text-transform: uppercase;  display: flex;  justify-content: center;}.time span {  padding: 0 14px;  font-size: 0.8rem;}.time span div {  font-size: 3rem;}@media (max-width: 740px) {  .label {    font-size: 1.7rem;  }  .time span {    padding: 0 16px;    font-size: 0.6rem;  }  .time span div {    font-size: 2rem;  }}/*Powered by www.php.cn*/

assets/index.html

            原生js实现喜庆背景带炫酷雪花飘落动画特效代码                        

距离新年倒计时

00天 00时 00分 00秒

布局中使用WebView展示html网页

java代码进行加载网页

wv = findViewById(R.id.wv);        // 设置WebView属性,能够执行Javascript脚本        wv.getSettings().setJavaScriptEnabled(true);        //语言设置防止加载乱码        wv.getSettings().setDefaultTextEncodingName("GBK");        // 即asserts文件夹下有一个color2.html        wv.loadUrl("file:///android_asset/index.html");

最后是沉浸状态栏

导入依赖库

//沉浸状态栏    implementation 'com.jaeger.statusbarutil:library:1.5.1'

布局中需要使用CoordinatorLayout布局要不然沉浸不起作用,完整布局入下:

<?xml version="1.0" encoding="utf-8"?>            

完整Java代码如下:

package com.shanjing.snowflake;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.webkit.WebView;import com.jaeger.library.StatusBarUtil;public class MainActivity extends AppCompatActivity {    private WebView wv;    private View cl_view;    private FallingView fallingView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        cl_view = findViewById(R.id.cl_view);        StatusBarUtil.setTranslucentForImageView(MainActivity.this, 0, cl_view);        wv = findViewById(R.id.wv);        // 设置WebView属性,能够执行Javascript脚本        wv.getSettings().setJavaScriptEnabled(true);        //语言设置防止加载乱码        wv.getSettings().setDefaultTextEncodingName("GBK");        // 即asserts文件夹下有一个color2.html        wv.loadUrl("file:///android_asset/index.html");        //初始化一个雪花样式的fallObject        FallObject.Builder builder = new FallObject.Builder(getResources().getDrawable(R.drawable.snow_flake));        FallObject fallObject = builder                .setSpeed(6, true)                .setSize(40, 40, true)                .setWind(5, true, true)                .build();        fallingView = findViewById(R.id.fallingView);        fallingView.addFallObject(fallObject, 100);//添加下落物体对象    }}

Demo:https://github.com/cuiwenju2017/Snowflake

Android下雪动画 VS JS下雪动画_第1张图片

更多相关文章

  1. 【py交易】算法竞赛入门经典6.3.1 小球下落 python
  2. 浅析Android的资源打包和安装后Apk文件的下落

随机推荐

  1. 转:Android联系人数据库全解析
  2. 解决Eclipse3.6中Android(安卓)代码自动
  3. android studio AndroidManifest.xml命名
  4. Android(安卓)Universal Image Loader 源
  5. Android布局属性详解
  6. 编译代码报出Android(安卓)library proje
  7. android触屏手势识别全解析
  8. 模仿天天动听的seekbar
  9. Android(安卓)关于WebView的相关属性
  10. Android(安卓)获取系统权限的代码