精华内容
下载资源
问答
  • Android 下雪动画

    千次阅读 2015-12-30 18:22:04
    原文作者 : Styling Android 译文出自 : hanks.xyz 译者 : hanks-zyh 校对者: desmond1121 状态 : 完成 这本是一个愉快的季节,但是,呵呵,胡扯! 因为这篇文章的发表时间是2015年的圣诞节,所以我们需要给...

    这本是一个愉快的季节,但是,呵呵,胡扯! 因为这篇文章的发表时间是2015年的圣诞节,所以我们需要给Style Android用制造出一些节日气氛。感谢读者们,因为有的读者可能没有在庆祝圣诞,有些读者可能还是6月份。
    那么问题来了,我们应该做些什么来让这个节日像是真正的节日呢? 最简单的方法:带上圣诞帽,拍个照。

    tree

    看我多么欢乐!
    但是我感觉这个图片有些单调,所以来弄点雪花,让雪花飘下来。
    我们可以添加一个包含这个图片的自定义View

    res/layout/activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      tools:context="com.stylingandroid.snowfall.MainActivity">
    
      <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:contentDescription="@null"
        android:scaleType="fitCenter"
        android:src="@drawable/tree" />
    
      <com.stylingandroid.snowfall.SnowView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignBottom="@id/image"
        android:layout_alignEnd="@id/image"
        android:layout_alignLeft="@id/image"
        android:layout_alignRight="@id/image"
        android:layout_alignStart="@id/image"
        android:layout_alignTop="@id/image" />
    </RelativeLayout>

    尽管可以通过继承ImageView来实现自定义View,但我决定将 SnowView 和图片分开,这样每次刷新动画的时候不用重新渲染图片,只刷新 SnowView 就行了

    SnowView.java

    public class SnowView extends View {
        private static final int NUM_SNOWFLAKES = 150;
        private static final int DELAY = 5;
    
        private SnowFlake[] snowflakes;
    
        public SnowView(Context context) {
            super(context);
        }
    
        public SnowView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public SnowView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        protected void resize(int width, int height) {
            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setColor(Color.WHITE);
            paint.setStyle(Paint.Style.FILL);
            snowflakes = new SnowFlake[NUM_SNOWFLAKES];
            for (int i = 0; i < NUM_SNOWFLAKES; i++) {
                snowflakes[i] = SnowFlake.create(width, height, paint);
            }
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            if (w != oldw || h != oldh) {
                resize(w, h);
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            for (SnowFlake snowFlake : snowflakes) {
                snowFlake.draw(canvas);
            }
            getHandler().postDelayed(runnable, DELAY);
        }
    
        private Runnable runnable = new Runnable() {
            @Override
            public void run() {
                invalidate();
            }
        };
    }

    代码很简单。 在View 的 onSizeChanged 方法中初始化 150 个随机位置的雪花对象。 在onDraw方法中画出雪花,然后每隔一段时间就刷新一下位置,需要注意的是onDraw没有立即去执行,而是通过创建一个runnable,这样不会阻塞UI线程
    雪花下落是基于Samuel Arbesman的雪花下落的算法

    SnowFlake.java

    class SnowFlake {
        private static final float ANGE_RANGE = 0.1f;
        private static final float HALF_ANGLE_RANGE = ANGE_RANGE / 2f;
        private static final float HALF_PI = (float) Math.PI / 2f;
        private static final float ANGLE_SEED = 25f;
        private static final float ANGLE_DIVISOR = 10000f;
        private static final float INCREMENT_LOWER = 2f;
        private static final float INCREMENT_UPPER = 4f;
        private static final float FLAKE_SIZE_LOWER = 7f;
        private static final float FLAKE_SIZE_UPPER = 20f;
    
        private final Random random;
        private final Point position;
        private float angle;
        private final float increment;
        private final float flakeSize;
        private final Paint paint;
    
        public static SnowFlake create(int width, int height, Paint paint) {
            Random random = new Random();
            int x = random.getRandom(width);
            int y = random.getRandom(height);
            Point position = new Point(x, y);
            float angle = random.getRandom(ANGLE_SEED) / ANGLE_SEED * ANGE_RANGE + HALF_PI - HALF_ANGLE_RANGE;
            float increment = random.getRandom(INCREMENT_LOWER, INCREMENT_UPPER);
            float flakeSize = random.getRandom(FLAKE_SIZE_LOWER, FLAKE_SIZE_UPPER);
            return new SnowFlake(random, position, angle, increment, flakeSize, paint);
        }
    
        SnowFlake(Random random, Point position, float angle, float increment, float flakeSize, Paint paint) {
            this.random = random;
            this.position = position;
            this.angle = angle;
            this.increment = increment;
            this.flakeSize = flakeSize;
            this.paint = paint;
        }
    
        private void move(int width, int height) {
            double x = position.x + (increment * Math.cos(angle));
            double y = position.y + (increment * Math.sin(angle));
    
            angle += random.getRandom(-ANGLE_SEED, ANGLE_SEED) / ANGLE_DIVISOR;
    
            position.set((int) x, (int) y);
    
            if (!isInside(width, height)) {
                reset(width);
            }
        }
    
        private boolean isInside(int width, int height) {
            int x = position.x;
            int y = position.y;
            return x >= -flakeSize - 1 && x + flakeSize <= width && y >= -flakeSize - 1 && y - flakeSize < height;
        }
    
        private void reset(int width) {
            position.x = random.getRandom(width);
            position.y = (int) (-flakeSize - 1);
            angle = random.getRandom(ANGLE_SEED) / ANGLE_SEED * ANGE_RANGE + HALF_PI - HALF_ANGLE_RANGE;
        }
    
        public void draw(Canvas canvas) {
            int width = canvas.getWidth();
            int height = canvas.getHeight();
            move(width, height);
            canvas.drawCircle(position.x, position.y, flakeSize, paint);
        }
    }

    初始化的时候,雪花的随机位置就已经确定了。这是为了确保雪花不会每次画的时候都在开始的位置。当一个雪花的位置超出Canvas的边界之后,它就会被重新放到顶部的一个随机位置,这样就可以循环利用了,避免了重复创建。
    当画雪花下落的每一帧的时候,我们首先给SnowFlake添加一个随机数来改变位置,这样可以模仿有小风吹雪花。
    在把雪花画到canvas上之前,我们会进行边界检查(如果需要的话,超出边界的就重新放到顶部)

    我一直在不断的调整里面的常量来改变下雪的效果直到我感觉满意为止。

    最终效果如下:

    width="880" height="660" src="https://www.youtube.com/embed/pk66ZziTfOw" allowfullscreen="">

    当然了,在canvas里面塞这么多东西不是一个好的方法(有其他更好的 比如OpenGL),但是,我现在要去吃火鸡了,所以可能要等下一次了。

    源文件地址

    版权声明:
    Part of this code is based upon “Snowfall” by Sam Arbesman, licensed under Creative Commons Attribution-Share Alike 3.0 and GNU GPL license.
    Work: http://openprocessing.org/visuals/?visualID= 84771
    License:
    http://creativecommons.org/licenses/by-sa/3.0/
    http://creativecommons.org/licenses/GPL/2.0/

    © 2015, Mark Allison. All rights reserved. This article originally appeared on Styling Android.

    Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License

    展开全文
  • 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

    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<FallObject> 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;
        }
    }

    布局中引用自定义视图

    <com.shanjing.snowflake.FallingView
            android:id="@+id/fallingView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    代码设置动画属性并显示

    //初始化一个雪花样式的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

    <!doctype html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport"
              content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui">
        <title>原生js实现喜庆背景带炫酷雪花飘落动画特效代码</title>
        <meta name="keywords" content="原生js,喜庆背景,炫酷雪花,飘落动画,特效代码"/>
        <meta name="description"
              content="原生js实现喜庆背景带炫酷雪花飘落动画特效代码下载。基于原生JavaScript+CSS实现,不依靠任何第三方jQuery库,兼容手机移动端,新年倒计时自动获取,可循环使用,非常简单实用的一款新年倒计时js特效代码。"/>
        <meta name="author" content="js代码(www.jsdaima.com)"/>
        <meta name="copyright" content="js代码(www.jsdaima.com)"/>
        <link rel="stylesheet" type="text/css" href="css/www.jsdaima.com.css">
    </head>
    <body>
    <div class="middle">
        <h1 class="label" id="newYear">距离新年倒计时</h1>
        <div class="time">
    		<span>
    		<div id="d">
    			00
    		</div>
    		天 </span> <span>
    		<div id="h">
    			00
    		</div>
    		时 </span> <span>
    		<div id="m">
    			00
    		</div>
    		分 </span> <span>
    		<div id="s">
    			00
    		</div>
    		秒 </span>
        </div>
    </div>
    
    <script>
    class Snowflake {
      constructor() {
        this.x = 0;
        this.y = 0;
        this.vx = 0;
        this.vy = 0;
        this.radius = 0;
        this.alpha = 0;
    
        this.reset();
      }
    
      reset() {
        this.x = this.randBetween(0, window.innerWidth);
        this.y = this.randBetween(0, -window.innerHeight);
        this.vx = this.randBetween(-3, 3);
        this.vy = this.randBetween(2, 5);
        this.radius = this.randBetween(1, 4);
        this.alpha = this.randBetween(0.1, 0.9);
      }
    
      randBetween(min, max) {
        return min + Math.random() * (max - min);
      }
    
      update() {
        this.x += this.vx;
        this.y += this.vy;
    
        if (this.y + this.radius > window.innerHeight) {
          this.reset();
        }
      }
    }
    
    class Snow {
      constructor() {
        this.canvas = document.createElement("canvas");
        this.ctx = this.canvas.getContext("2d");
    
        document.body.appendChild(this.canvas);
    
        window.addEventListener("resize", () => this.onResize());
        this.onResize();
        this.updateBound = this.update.bind(this);
        requestAnimationFrame(this.updateBound);
    
        this.createSnowflakes();
      }
    
      onResize() {
        this.width = window.innerWidth;
        this.height = window.innerHeight;
        this.canvas.width = this.width;
        this.canvas.height = this.height;
      }
    
      createSnowflakes() {
        const flakes = window.innerWidth / 4;
    
        this.snowflakes = [];
    
        for (let s = 0; s < flakes; s++) {
          this.snowflakes.push(new Snowflake());
        }
      }
    
      update() {
        this.ctx.clearRect(0, 0, this.width, this.height);
    
        for (let flake of this.snowflakes) {
          flake.update();
    
          this.ctx.save();
          this.ctx.fillStyle = "#FFF";
          this.ctx.beginPath();
          this.ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2);
          this.ctx.closePath();
          this.ctx.globalAlpha = flake.alpha;
          this.ctx.fill();
          this.ctx.restore();
        }
        requestAnimationFrame(this.updateBound);
      }
    }
    
    new Snow();
    
    
    
    
    // Simple CountDown Clock
    var date = new Date;
    var year = date.getFullYear() + 1; 
    var NY = document.getElementById("newYear");
    NY.innerHTML = year+ " 年新年倒计时";
    const comingdate = new Date("Jan 1, "+year+" 00:00:00");
    
    const d = document.getElementById("d");
    const h = document.getElementById("h");
    const m = document.getElementById("m");
    const s = document.getElementById("s");
    
    const countdown = setInterval(() => {
      const now   = new Date();
      const des   = comingdate.getTime() - now.getTime();
      const days  = Math.floor(des / (1000 * 60 * 60 * 24));
      const hours = Math.floor((des % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
      const mins  = Math.floor((des % (1000 * 60 * 60)) / (1000 * 60));
      const secs  = Math.floor((des % (1000 * 60)) / 1000);
    
      d.innerHTML = getTrueNumber(days);
      h.innerHTML = getTrueNumber(hours);
      m.innerHTML = getTrueNumber(mins);
      s.innerHTML = getTrueNumber(secs);
    
      if (x <= 0) clearInterval(x);
    }, 1000);
    
    const getTrueNumber = x => (x < 10 ? "0" + x : x);
    
    
    </script>
    
    </body>
    </html>
    

    布局中使用WebView展示html网页

    <WebView
            android:id="@+id/wv"
            android:layout_width="match_parent"
            android:layout_height="240dp" />

    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"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/bg"
        android:orientation="vertical">
    
        <WebView
            android:id="@+id/wv"
            android:layout_width="match_parent"
            android:layout_height="240dp" />
    
        <com.shanjing.snowflake.FallingView
            android:id="@+id/fallingView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <androidx.coordinatorlayout.widget.CoordinatorLayout
            android:id="@+id/cl_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

    完整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下雪动画效果

    2014-01-07 14:54:49
    android下雪动画效果,用线程的效果来实现雪花的动画
  • android 下雪动画

    2013-05-03 12:30:00
    最近在模拟一个android游戏,其中看到一个下雪的效果。到网上搜了很久发现大部分用的都是搜索引擎用的粒子效果模拟的。 自己没用过android的游戏引擎,于是就决定自己写一个吧。   网上找到一个“天女散花”的...

    引用:http://blog.csdn.net/qiushibaiyi/article/details/8499963

    最近在模拟一个android游戏,其中看到一个下雪的效果。到网上搜了很久发现大部分用的都是搜索引擎用的粒子效果模拟的。

    自己没用过android的游戏引擎,于是就决定自己写一个吧。

     

    网上找到一个“天女散花”的demo,稍加改造就成这个下雪的demo。天女散花下载地址;点击打开链接

     

    下雪的效果,无非就是让屏幕上的“雪花”的图片不停的被重绘。

    这里下雪的动作,需要用一个线程控制。

     

    [java]  view plain copy
     
    1. /* 
    2.  * 负责做界面更新工作 ,实现下雪 
    3.  */  
    4. private RefreshHandler mRedrawHandler = new RefreshHandler();  
    5.   
    6. class RefreshHandler extends Handler {  
    7.   
    8.     @Override  
    9.     public void handleMessage(Message msg) {  
    10.         //snow.addRandomSnow();  
    11.         snow.invalidate();  
    12.         sleep(100);  
    13.     }  
    14.     public void sleep(long delayMillis) {  
    15.         this.removeMessages(0);  
    16.         sendMessageDelayed(obtainMessage(0), delayMillis);  
    17.     }  
    18. };  


    线程启动的时候,初始化雪花图片,把图片加载到内存之中。

     

    然后让线程每隔一段时间重绘一次,重绘的时候增加y轴的距离,达到下雪的效果。

     

     

    [java]  view plain copy
     
    1. @Override  
    2. public void onDraw(Canvas canvas) {  
    3.     super.onDraw(canvas);  
    4.     for (int x = 0; x < 10; x += 1) {  
    5.         if (snows[x].y >= view_height) {  
    6.             snows[x].y = 0;  
    7.             addRandomSnow();  
    8.         }  
    9.         // 雪花下落的速度  
    10.         snows[x].y += 5;  
    11.         //雪花飘动的效果  
    12.         if (RNG.nextBoolean()) {  
    13.             // 随机产生一个数字,让雪花有水平移动的效果  
    14.             int rng = RNG.nextInt(3);  
    15.             snows[x].x += 2 - rng;  
    16.         }  
    17.         canvas.drawBitmap(bitmap_snows[x / 6], ((float) snows[x].x),  
    18.                 ((float) snows[x].y), mPaint);  
    19.     }  
    20.   
    21. }  


    雪花的初始位置是y轴的顶点,我这里设置了10朵雪花。开始的时候,随机产生10个x轴方向的数字,作为雪花再x轴的起始位置。

     

    当雪花“飘”出了屏幕之后,y坐标重新置0,x坐标也重新产生。其中为了达到雪花灵动的效果,随机产生一个x轴的飘动效果。

    当然,做的效果不是太好,可以优化一下:比如,多增加雪花图片,达到下雪时雪花有近有远的效果。

    欢迎有兴趣的朋友补充……

     

    下载地址:android下雪demo

    展开全文
  • Android下雪动画的实现

    2019-02-06 21:22:11
    Android下雪动画的实现
                           
     

    这本是一个愉快的季节,但是,呵呵,胡扯! 因为这篇文章的发表时间是2015年的圣诞节,所以我们需要给Style Android用制造出一些节日气氛。感谢读者们,因为有的读者可能没有在庆祝圣诞,有些读者可能还是6月份。
    那么问题来了,我们应该做些什么来让这个节日像是真正的节日呢? 最简单的方法:带上圣诞帽,拍个照。

    tree

    看我多么欢乐!
    但是我感觉这个图片有些单调,所以来弄点雪花,让雪花飘下来。
    我们可以添加一个包含这个图片的自定义View

    res/layout/activity_main.xml

    <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:tools="http://schemas.android.com/tools"  android:layout_width="match_parent"  android:layout_height="match_parent"  tools:context="com.stylingandroid.snowfall.MainActivity">  <ImageView    android:id="@+id/image"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:layout_centerInParent="true"    android:contentDescription="@null"    android:scaleType="fitCenter"    android:src="@drawable/tree" />  <com.stylingandroid.snowfall.SnowView    android:layout_width="match_parent"    android:layout_height="match_parent"    android:layout_alignBottom="@id/image"    android:layout_alignEnd="@id/image"    android:layout_alignLeft="@id/image"    android:layout_alignRight="@id/image"    android:layout_alignStart="@id/image"    android:layout_alignTop="@id/image" /></RelativeLayout>
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    尽管可以通过继承ImageView来实现自定义View,但我决定将 SnowView 和图片分开,这样每次刷新动画的时候不用重新渲染图片,只刷新 SnowView 就行了

    SnowView.java

    public class SnowView extends View {    private static final int NUM_SNOWFLAKES = 150;    private static final int DELAY = 5;    private SnowFlake[] snowflakes;    public SnowView(Context context) {        super(context);    }    public SnowView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public SnowView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    protected void resize(int width, int height) {        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);        paint.setColor(Color.WHITE);        paint.setStyle(Paint.Style.FILL);        snowflakes = new SnowFlake[NUM_SNOWFLAKES];        for (int i = 0; i < NUM_SNOWFLAKES; i++) {            snowflakes[i] = SnowFlake.create(width, height, paint);        }    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        if (w != oldw || h != oldh) {            resize(w, h);        }    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        for (SnowFlake snowFlake : snowflakes) {            snowFlake.draw(canvas);        }        getHandler().postDelayed(runnable, DELAY);    }    private Runnable runnable = new Runnable() {        @Override        public void run() {            invalidate();        }    };}
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    代码很简单。 在View 的 onSizeChanged 方法中初始化 150 个随机位置的雪花对象。 在onDraw方法中画出雪花,然后每隔一段时间就刷新一下位置,需要注意的是onDraw没有立即去执行,而是通过创建一个runnable,这样不会阻塞UI线程
    雪花下落是基于Samuel Arbesman的雪花下落的算法

    SnowFlake.java

    class SnowFlake {    private static final float ANGE_RANGE = 0.1f;    private static final float HALF_ANGLE_RANGE = ANGE_RANGE / 2f;    private static final float HALF_PI = (float) Math.PI / 2f;    private static final float ANGLE_SEED = 25f;    private static final float ANGLE_DIVISOR = 10000f;    private static final float INCREMENT_LOWER = 2f;    private static final float INCREMENT_UPPER = 4f;    private static final float FLAKE_SIZE_LOWER = 7f;    private static final float FLAKE_SIZE_UPPER = 20f;    private final Random random;    private final Point position;    private float angle;    private final float increment;    private final float flakeSize;    private final Paint paint;    public static SnowFlake create(int width, int height, Paint paint) {        Random random = new Random();        int x = random.getRandom(width);        int y = random.getRandom(height);        Point position = new Point(x, y);        float angle = random.getRandom(ANGLE_SEED) / ANGLE_SEED * ANGE_RANGE + HALF_PI - HALF_ANGLE_RANGE;        float increment = random.getRandom(INCREMENT_LOWER, INCREMENT_UPPER);        float flakeSize = random.getRandom(FLAKE_SIZE_LOWER, FLAKE_SIZE_UPPER);        return new SnowFlake(random, position, angle, increment, flakeSize, paint);    }    SnowFlake(Random random, Point position, float angle, float increment, float flakeSize, Paint paint) {        this.random = random;        this.position = position;        this.angle = angle;        this.increment = increment;        this.flakeSize = flakeSize;        this.paint = paint;    }    private void move(int width, int height) {        double x = position.x + (increment * Math.cos(angle));        double y = position.y + (increment * Math.sin(angle));        angle += random.getRandom(-ANGLE_SEED, ANGLE_SEED) / ANGLE_DIVISOR;        position.set((int) x, (int) y);        if (!isInside(width, height)) {            reset(width);        }    }    private boolean isInside(int width, int height) {        int x = position.x;        int y = position.y;        return x >= -flakeSize - 1 && x + flakeSize <= width && y >= -flakeSize - 1 && y - flakeSize < height;    }    private void reset(int width) {        position.x = random.getRandom(width);        position.y = (int) (-flakeSize - 1);        angle = random.getRandom(ANGLE_SEED) / ANGLE_SEED * ANGE_RANGE + HALF_PI - HALF_ANGLE_RANGE;    }    public void draw(Canvas canvas) {        int width = canvas.getWidth();        int height = canvas.getHeight();        move(width, height);        canvas.drawCircle(position.x, position.y, flakeSize, paint);    }}
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    初始化的时候,雪花的随机位置就已经确定了。这是为了确保雪花不会每次画的时候都在开始的位置。当一个雪花的位置超出Canvas的边界之后,它就会被重新放到顶部的一个随机位置,这样就可以循环利用了,避免了重复创建。
    当画雪花下落的每一帧的时候,我们首先给SnowFlake添加一个随机数来改变位置,这样可以模仿有小风吹雪花。
    在把雪花画到canvas上之前,我们会进行边界检查(如果需要的话,超出边界的就重新放到顶部)

    我一直在不断的调整里面的常量来改变下雪的效果直到我感觉满意为止。

    最终效果如下:
    youtube

    当然了,在canvas里面塞这么多东西不是一个好的方法(有其他更好的 比如OpenGL),但是,我现在要去吃火鸡了,所以可能要等下一次了。

    源文件地址

    版权声明:
    Part of this code is based upon “Snowfall” by Sam Arbesman, licensed under Creative Commons Attribution-Share Alike 3.0 and GNU GPL license.
    Work: http://openprocessing.org/visuals/?visualID= 84771
    License:
    http://creativecommons.org/licenses/by-sa/3.0/
    http://creativecommons.org/licenses/GPL/2.0/

    © 2015, Mark Allison. All rights reserved. This article originally appeared on Styling Android.

    Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License

               

    再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

    展开全文
  • JavaScript语言:JaveScriptBabelCoffeeScript确定window.onload = function() {var canvas = document.getElementById("snowcanvas");var ctx = canvas.getContext("2d");var W = window.innerWidth;...
  • Android的漂浮动画,下雪动画效果剖析Android 的漂浮动画,下雪动画效果先看下效果:1.先得了解下canvas.drawBitmap(mBitmap, mSrcRect, mDestRect, mBitPaint);在绘制图片时,使用,参数分别,图片bitmap,绘制bitmap...
  • 本章目录Part One:构造方法Part Two:自定义属性Part Three:布局测量Part Four:绘制Part Five:重绘在了解了自定义View的基本绘制流程后,还需要大量的练习去巩固这方面的知识,所以这一节我们再练习个下雪案例。...
  • 二、前几天看到有人写了一个关于“下雪”的自定义View,很有意思,但是感觉雪花写得不是很满意,是用椭圆代替的,虽说现实中观看的确是一坨坨的,但是我还是想从细腻的角度去描绘一番(下图为代码所写)。先上个图看...
  • 在布局中直接通过自定义 SurfaceView 来绘制提高效率xmlns:tools="http://schemas.android.com/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent">...
  • Android 漂浮动画,下雪动画效果

    千次阅读 2016-12-24 11:57:45
    因工作需要最近在研究了动画,看到牛人博客,关于动画,自定义view,写的很详细,于是学习学习,来完成工作需求 1.先得了解下canvas.drawBi
  • Android-Android下雪效果

    2019-08-13 01:04:27
    Android 下雪效果
  • 使用UIView动画完成一个下雪动画 UIView动画是UIKit提供专门制作动画的API,其本质是对CoreAnimation的封装,使用UIView可以轻松方便的实现动画,不需要经过任何计算,本案例使用UIView制作一个下雪的动画,如图-...
  • 主要给大家介绍了关于利用SurfaceView实现下雨与下雪动画效果的相关资料,需要一些基本的View知识和会一些基础Kotlin语法,文中给出了详细的示例代码供大家参考学习,需要的朋友们下面随着小编来一起学习学习吧。
  • Android 仿下雪效果

    千次阅读 2017-02-11 22:27:20
    最近在做公司新产品的设计,想到下雪效果设计加入到项目应该挺不错的,所以我们想将这个设计理念加入到我们的产品中。 下面是效果图: 没截取动态图 XML 代码: xml version="1.0" encoding=...
  • Android 轮子之下雪动画效果自定义圆创建300个随机大小的白色圆并绘制添加动画 先来看看今天的效果吧: 还是咋们的老套路,先来分析一波: 效果图中的雪花其实是一些白色的圆 随机大小的圆 一直向下移动,其实是y轴向...
  • Android-Snowfall Fully customizable implementation of "Snowfall View" on Android. That's how we use it in our app Hotellook Compatibility This library is compatible from API 16 (Android 4.1). ...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 190
精华内容 76
关键字:

android下雪动画