zoukankan      html  css  js  c++  java
  • 那些年下过的大雨

    想了解一下用纯CSS和JS怎么实现一段下雨的动画,于是去CodePen上面搜了一下,发现了很多很有意思的东西。有空可以常去上面逛逛,在对技术产生敬畏的同时也能学到好多好多东西。以下是自己理解的几个代码实现过程,所有源码均出自于CodePen

    代码:github

    效果:http://rynxiao.com/The-heavy-rain-in-those-years/

    PS: 所有效果均在代码中以注释形式给出,不再额外进行补充,如果想了解更多细节,麻烦参考源码

    淅沥淅沥

    像一阵细雨打湿你心田,那感觉如此甜蜜。

    gif1

    /**
     * 雨滴容器
     * 宽度为15px,高度为120px
     * 0.5秒内从屏幕上方移动到屏幕90%的高度
     * 模仿雨滴的下降过程
     */
    .drop {
        position: absolute;
        bottom: 100%;
         15px;
        height: 120px;
        pointer-events: none;
        animation: drop 0.5s linear infinite;
    }
    @keyframes drop {
        0% {
          	transform: translateY(0vh);
        }
        75% {
          	transform: translateY(90vh);
        }
        100% {
          	transform: translateY(90vh);
        }
    }
    

    雨滴容器模仿的是下降的过程,在下降过程的同时,改变真正雨滴的透明度,模仿出雨滴划过的轨迹

    /**
     * 雨滴
     * 宽度为1px,高度为120 * 0.6 = 72px
     * 设置从上到下的渐变色,模仿雨滴划过的轨迹
     * 0.5s内由不透明变为透明,模仿雨滴下落碰撞到物体之后消失的情景
     */
    .stem {
         1px;
        height: 60%;
        margin-left: 7px;
        background: linear-gradient(to bottom, rgba(255, 255, 255, 0), 
          	rgba(255, 255, 255, 0.25));
        animation: stem 0.5s linear infinite;
    }
    @keyframes stem {
        0% {
          	opacity: 1;
        }
        65% {
          	opacity: 1;
        }
        75% {
          	opacity: 0;
        }
        100% {
          	opacity: 0;
        }
    }
    /**
     * 水花
     * 雨滴碰撞到地面会溅起水花
     * 宽度为15px,高度为10px
     * 设置上边框为点状,加上设置圆角,模拟水花溅起时的弧形
     * 设置动画,当雨滴下降到地面时,透明度设置为1,同时通过设置缩放
     * 模拟水花放大的过程
     */
    .splat {
         15px;
        height: 10px;
        border-top: 2px dotted rgba(255, 255, 255, 0.5);
        border-radius: 50%;
        opacity: 1;
        transform: scale(0);
        animation: splat 0.5s linear infinite;
        /*display: none;*/
    }
    @keyframes splat {
        0% {
            opacity: 1;
            transform: scale(0);
        }
        80% {
            opacity: 1;
            transform: scale(0);
        }
        90% {
            opacity: 0.5;
            transform: scale(1);
        }
        100% {
            opacity: 0;
            transform: scale(1.5);
        }
    }
    

    源码:https://codepen.io/arickle/pen/XKjMZY

    是离愁

    犹记当时杏花雨,晓幕晨钟去孤舟。说好不回头,止不住,是离愁。

    rain2

    /**
     * 雨滴
     * 下落过程透明度由0变为1
     * 高度由40px变为5px
     */
    .drop {
        position: absolute;
        background: #fff;
         2px;
        height: 40px;
        opacity: 0;
        left: 150px;
        animation: fall 0.2s ease-in forwards;
    }
    @keyframes fall {
        0% {
            top: -100px;
            opacity: 0;
            height: 100px;
        }
        99% {
          	opacity: 1;
        }
        100% {
            top: 150px;
            opacity: 0;
            height: 5px;
        }
    }
    

    雨滴的下降过程中,高度逐渐变小,因此造成了一种下落的堕落感,这点和第一个动画略有不同。

    /**
     * 波纹
     * 沿X轴旋转75度,造成椭圆效果
     * 动画效果:
     * 中心定格,设置波纹延迟,波纹逐渐变大造成扩散效果
     * 先上在下再上,造成波纹的跌宕效果
     */
    #ripples {
        position: absolute;
         300px;
        height: 300px;
        transform: rotateX(75deg);
    }
    .ripple {
        position: absolute;
        /* ... */
        border: 6px solid rgba(255, 255, 255, 0.2);
        animation: grow ease-in-out forwards 1;
    }
    .ripple:nth-child(1) {
      animation-delay: 0s;
      animation-duration: 2s;
    }
    /* ... */
    @keyframes grow {
        0% {
             1px;
            height: 1px;
            opacity: 0;
            transform: translateY(20px);
        }
        25% {
            opacity: 0.5;
            transform: translateY(-10px);	
        }
        50% {
            transform: translateY(10px);
        }
        100% {
             250px;
            height: 250px;
            opacity: 0;
            transform: translateY(0);
        }
    }
    

    所有过程都是中心对齐,然后像周围扩散,例如下面的水珠,模拟的其实就是从中心点的原点上跳过程。

    /**
    * 水珠
    * 中心定位
    * 通过控制水花垂直方向位移,造成溅起效果
    * 通过控制水平方向便宜,造成运动效果
    */
    @keyframes bounce1 {
      	/* 中心点 */
        0% {
            left: 150px;
            top: 150px;
            opacity: 0;
        }
      	/* 透明度 */
        5% {
          	opacity: 0.5;
        }
      	/* 上升 */
        50% {
          	top: 104px;
        }
      	/* 下降,同时左偏 */
        100% {
            left: 125px;
            top: 150px;
            opacity: 0;
        }
    }
    

    源码:https://codepen.io/Yakudoo/pen/doObLw

    错过

    青春灿烂,炫如夏花。你说看过彩色的瀑布,我摸着你的头,笑得像个傻瓜。

    gif1

    // 整个动画的关键点
    // 整个动画其实是由一条条细线组成,根据下落的时差,造成瀑布的效果
    // 根据计算出的屏幕宽度,每条细线1px,然后在屏幕上均匀分布360色
    function anim() {
        window.requestAnimationFrame(anim);
    	
      	// 通过每次在绘制的细线上在绘制透明黑色,因此最先绘制的就会变暗,造成了尾巴的效果
        ctx.fillStyle = repaintColor;
        ctx.fillRect(0, 0, w, h);
    
        for(var i = 0; i < total; ++i){
            var currentY = dots[i] - 1;
          	// 不断增加每次线段的长度,绘制每次最亮的那一部分
            dots[i] += dotsVel[i] += accelleration;
    
          	// 颜色其实在最开始就已经被分配出来了,根据点在的位置而定
            ctx.fillStyle = 'hsl('+ colors[i] + ', 80%, 50%)';
            ctx.fillRect(occupation * i, currentY, size, dotsVel[i] + 1);
    
          	// 从上面出现的条件,也是造成时差的条件
            if(dots[i] > h && Math.random() < .01){
              	dots[i] = dotsVel[i] = 0;
            }
        }
    }
    anim();
    

    源码:https://codepen.io/towc/pen/VYbYvQ

    路过

    一个人的记忆就是座城市,时间腐蚀着一切建筑,把高楼和道路全部沙化。如果你不往前走,就会被沙子掩埋。所以我们泪流满面,步步回头,可是只能往前走。

    gif1

    var RAIN_DROP = function(width, height, toInit, renderer){
        this.width = width;
        this.height = height;
        this.toInit = toInit;
        this.renderer = renderer;
    
        this.init();
    };
    RAIN_DROP.prototype = {
        SCALE_RANGE : {min : 0.2, max : 1},			// 缩放比例
        VELOCITY_RANGE : {min : -1.5, max : -1},	// 水平速度范围
        VELOCITY_RATE : 3,							// 垂直速度基值
        LENGTH_RATE : 20,							// 雨滴长度
        ACCELARATION_RATE : 0.01,					// 加速度比例
        VERTICAL_OFFSET_RATE : 0.04,				// 垂直偏移量
        FRONT_THRESHOLD : 0.8,						// 前景临界值(前面的雨滴不至于过小,后面的雨滴不至于过大)
        REFLECTION_RADIUS_RATE : 0.02,				// 雨滴水花半径基值
        COLOR : 'rgba(255, 255, 255, 0.5)',			// 雨滴颜色
        RADIUS_RATE : 0.2,							// 用于设置全局透明度
        THRESHOLD_RATE : 0.6,						 
    
        init: function(){
            // 获取随机缩放比例
            this.scale = this.renderer.getRandomValue(this.SCALE_RANGE);
            // 雨滴长度
            this.length = this.LENGTH_RATE * this.scale;
            // 雨滴水平方向速度
            this.vx = this.renderer.getRandomValue(this.VELOCITY_RANGE) * this.scale;
            // 雨滴垂直方向速度
            this.vy = this.VELOCITY_RATE * this.scale;
            // 雨滴垂直方向加速度
            this.ay = this.ACCELARATION_RATE * this.scale;
            // 角度
            this.theta = Math.atan2(this.vy, this.vx);
            // 用于计算垂直方向是否出边界的偏移量
            this.offset = this.height * this.VERTICAL_OFFSET_RATE;
            // 雨滴水平坐标值
            this.x = this.renderer.getRandomValue({min : 0, max : this.width - this.height * Math.cos(this.theta)});
            // 雨滴垂直坐标值
            this.y = (this.toInit ? this.renderer.getRandomValue({min : 0, max : this.height}) : 0) - this.offset;
            // 水花半径
            this.radius = this.length * this.REFLECTION_RADIUS_RATE;
        },
        render: function(context, toFront){
            // 如果雨滴在前面,但是缩放比例小于规定临界值,则去掉雨滴
            // 如果雨滴在后面,但是缩放比例大于规定临界值,则去掉雨滴
            // 保证了在背景前面的雨滴不至于太小,在背景靠后的雨滴不至于太大
            if (toFront && this.scale < this.FRONT_THRESHOLD || !toFront && this.scale >= this.FRONT_THRESHOLD) {
              	return true;
            }
            context.save();
            context.strokeStyle = this.COLOR;
    
            // 如果雨滴的垂直方向坐标大于了临界值,则以水花的形式展示
            // 否则以雨滴下落的方式展示
            if (this.y >= this.height * (1 - (1 - this.scale) * this.THRESHOLD_RATE) - this.offset) {
                context.lineWidth = 3;
                context.globalAlpha = (1 - this.radius / this.length / this.RADIUS_RATE) * 0.5;
                context.beginPath();
                context.arc(this.x, this.y, this.radius, Math.PI, Math.PI * 2, false);
                context.stroke();
                context.restore();
    
                this.radius *= 1.05;
    
                if (this.radius > this.length * this.RADIUS_RATE){
                   return false;
                }
            } else {
                context.lineWidth = 1;
                context.beginPath();
                context.moveTo(this.x, this.y);
                context.lineTo(this.x + this.length * Math.cos(this.theta), this.y + this.length * Math.sin(this.theta));
                context.stroke();
                context.restore();
    
                // 通过水平速度改变水平位移
                this.x += this.vx;
                // 通过垂直速度改变垂直位移
                this.y += this.vy;
                // 通过加速度,增加垂直方向的速度
                // 因此雨滴并不是沿着直线运动,而是有一定的弧度
                this.vy += this.ay;
            }
            return true;
        }
    };
    

    源码:https://codepen.io/K-T/pen/YVYvdW

    都是你

    曾经想征服全世界,到最后回首才发现,这世界滴滴点点全部都是你。

    gif1

    // 关键点
    function loop() {
        requestAnimationFrame(loop);
        // 不断绘制半透明画布,用来造成尾巴效果
        context.fillStyle = 'rgba(0, 0, 0, 0.2)';
        context.fillRect(0, 0, canvas.width, canvas.height);
    
        var particle;
        var i, j, ilen, jlen;
        cursor.position.x += (mouseX - cursor.position.x)*0.1;
        cursor.position.y += (mouseY - cursor.position.y)*0.1;
        for (i = 0, ilen = particles.length; i < ilen; i++) {
            particle = particles[i];
            // 通过速度控制角度大小,用来控制流星划过时的速度
            particle.angle += particle.speed;
    
            // 以相同转动的速度向鼠标中心点不停的移动
            particle.shift.x += (cursor.position.x - particle.shift.x) * particle.speed;
            particle.shift.y += (cursor.position.y - particle.shift.y) * particle.speed;
    
            // sin/cos 用来控制画圆
            // orbit 半径轨道不断增加, force用来控制轨道系数,最低为0
            // Math.sinx * (particle.orbit*particle.force) 表示以particle.orbit*particle.force为半径画圆
            // 加上中心点坐标,造成不断围绕中心点旋转的效果
            particle.position.x = particle.shift.x + Math.sin(i + particle.angle) * (particle.orbit*particle.force);
            particle.position.y = particle.shift.y + Math.cos(i + particle.angle) * (particle.orbit*particle.force);
    
            // 不断增加点的半径,最高为100
            particle.orbit += (cursor.orbit - particle.orbit) * 0.01;
    
            context.beginPath();
            // 根据点所在的位置,随机颜色
            context.fillStyle = "hsl("+((particle.position.x/canvas.width + particle.position.y/canvas.height) * 180) + ", 100%, 70%)";  
            context.arc(particle.position.x, particle.position.y, particle.size/2, 0, Math.PI*2, true);
            context.fill();
        }
    } 
    

    源码:https://codepen.io/vennsoh/pen/DuLbo

  • 相关阅读:
    对Spring的简单理解
    对Hibernate的简单认识
    对Struts的简单理解
    浅谈实体类
    xdebug配置
    hosts文件修改完无效的解决办法
    CentOS6.4 中文输入法
    python加密解密
    windows运行命令大全
    vm虚拟机centos文件共享目录设置
  • 原文地址:https://www.cnblogs.com/rynxiao/p/8398167.html
Copyright © 2011-2022 走看看