好看的水滴登录页面

css 如何绘制水滴

  • 可以通过box-shadow 来显示阴影
  • 可以通过border-radius 改变水滴的形状
  • 当然如果像要使其更加灵活,可以使用animation+keyframes关键帧+border-radius,让水滴动起来

是不是很简单

来吧展示效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

html代码,就只有一个div,然后使用一些伪类结合


<div class="water-container">
    <div class="water"></div>
</div>

css代码

.water-container {
    width: 50vw;
    height: 50vh;
    background-color: #eff0f4;
    display: flex;
    justify-content: center;
    align-items: center;
}

.water {
    width: 120px;
    height: 120px;
    border-radius: 50%;
    box-shadow: inset 20px 20px 20px rgba(0, 0, 0, .05),
    25px 35px 20px rgba(0, 0, 0, .05),
    25px 30px 30px rgba(0, 0, 0, .05),
    inset -20px -20px 25px hsla(0, 0%, 100%, .9);
    position: relative;
}

.water::before {
    content: "";
    position: absolute;
    left: 24%;
    top: 25%;
    width: 16px;
    height: 16px;
    background: #fff;
    border-radius: 50%;
}

效果:
在这里插入图片描述

圆形水滴做好了,那我们是不是可以改变其现状呢,只要改变border-radius就可以了

.water {
    width: 120px;
    height: 120px;
    /*改变形状,使其弯曲*/
    border-radius: 30% 70% 70% 30% / 30% 35% 65% 70%;
    box-shadow: inset 20px 20px 20px rgba(0, 0, 0, .05),
    25px 35px 20px rgba(0, 0, 0, .05),
    25px 30px 30px rgba(0, 0, 0, .05),
    inset -20px -20px 25px hsla(0, 0%, 100%, .9);
    position: relative;
}

效果:
在这里插入图片描述

形状改变了,我们就让其动起来,动起来就算加上关键帧改变border-radius

.water {
  width: 120px;
  height: 120px;
  /*改变形状,使其弯曲*/
  border-radius: 30% 70% 70% 30% / 30% 35% 65% 70%;
  box-shadow: inset 20px 20px 20px rgba(0, 0, 0, .05),
  25px 35px 20px rgba(0, 0, 0, .05),
  25px 30px 30px rgba(0, 0, 0, .05),
  inset -20px -20px 25px hsla(0, 0%, 100%, .9);
  position: relative;
  /*添加动画关键帧*/
  animation: waterMove 4s linear infinite;
}

.water::before {
  content: "";
  position: absolute;
  left: 24%;
  top: 25%;
  width: 16px;
  height: 16px;
  background: #fff;
  border-radius: 30% 70% 70% 30% / 30% 35% 65% 70%;
  animation: waterMove 4s linear infinite;
}
/* 关键帧 */
@keyframes waterMove {
    20% {
        border-radius: 30% 70% 53% 47% / 28% 44% 56% 72%;
    }

    40% {
        border-radius: 30% 70% 39% 61% / 34% 39% 61% 66%;
    }

    60% {
        border-radius: 25% 75% 45% 55% / 40% 55% 45% 60%;
    }

    80% {
        border-radius: 28% 72% 31% 69% / 32% 39% 61% 68%;
    }
}

效果:
在这里插入图片描述

完成后我们就可以在这个水滴里面添加东西了,比如设计登录页面

 <div class="login-container">
    <div class="login-content">
      <div class="login-box">
        <form action="" class="login-form">
          <h2>LOGIN</h2>
          <div class="input-box">
            <input type="text" placeholder="用户名"/>
          </div>
          <div class="input-box">
            <input type="password" placeholder="密码"/>
          </div>
          <div class="input-box">
            <input type="submit" value="登录"/>
          </div>
        </form>
      </div>
      <div class="login-btn forget-pwd">
        <span>忘记</span>
        <span>密码</span>
      </div>
      <div class="login-btn register"><span>注册</span></div>
    </div>
  </div>
.login-container {
    width: 50vw;
    height: 50vh;
    background: #eff0f4;
    display: flex;
    justify-content: center;
    align-items: center;
    opacity: 0.9;
}

.login-content {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
}

.login-box {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 350px;
    position: relative;
    width: 350px;
    border-radius: 52% 48% 33% 67% / 38% 45% 55% 62%;
    box-shadow: inset 20px 20px 20px rgba(0, 0, 0, .05),
    25px 35px 20px rgba(0, 0, 0, .05),
    25px 30px 30px rgba(0, 0, 0, .05),
    inset -20px -20px 25px hsla(0, 0%, 100%, .9);
    /*transition: .5s;*/
    animation: waterMove 9s linear infinite;
}

/* 关键帧 */
@keyframes waterMove {
    20% {
        border-radius: 30% 70% 53% 47% / 28% 44% 56% 72%;
    }

    40% {
        border-radius: 30% 70% 39% 61% / 34% 39% 61% 66%;
    }

    60% {
        border-radius: 25% 75% 45% 55% / 40% 55% 45% 60%;
    }

    80% {
        border-radius: 28% 72% 31% 69% / 32% 39% 61% 68%;
    }
}

.login-box::before {
    content: '';
    position: absolute;
    top: 50px;
    left: 85px;
    width: 35px;
    height: 35px;
    background: #fff;
    border-radius: 52% 48% 33% 67% / 38% 45% 55% 62%;
    opacity: .9;
    animation: waterMove 9s linear infinite;
}

.login-box::after {
    content: '';
    top: 90px;
    position: absolute;
    left: 70px;
    width: 15px;
    height: 15px;
    border-radius: 52% 48% 33% 67% / 38% 45% 55% 62%;
    background: #fff;
    opacity: .9;
    animation: waterMove 9s linear infinite;
}

.login-form {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    padding: 40px;
    gap: 15px;
}

.login-form h2 {
    gap: 20px;
    letter-spacing: .3em;
}


.input-box {
    width: 225px;
    box-shadow: inset 2px 5px 10px rgba(0, 0, 0, .1),
    inset -2px -5px 10px rgba(255, 255, 255, 1),
    15px 15px 10px rgba(0, 0, 0, .05),
    15px 10px 15px rgba(0, 0, 0, .025);
    position: relative;
    border-radius: 25px;
}

.input-box:last-child {
    width: 120px;
    background: rgb(105, 199, 199);
    transition: .6s;
    box-shadow: inset 2px 5px 10px rgba(0, 0, 0, .1),
    15px 15px 10px rgba(0, 0, 0, .05),
    15px 10px 15px rgba(0, 0, 0, .025);
}

.input-box:last-child:hover {
    width: 150px;
}

.input-box::before {
    content: '';
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    width: 65%;
    height: 5px;
    background: rgba(255, 255, 255, .5);
    border-radius: 5px;
}

.input-box input {
    border: none;
    outline: none;
    background: transparent;
    width: 100%;
    font-size: 1em;
    padding: 10px 15px;
    box-sizing: border-box;
}

.input-box:last-child input {
    color: #fff;
    text-transform: uppercase;
    font-size: 1em;
    cursor: pointer;
    letter-spacing: .1em;
    font-weight: 500;
}

.login-btn {
    position: absolute;
    right: -120px;
    bottom: 0;
    width: 120px;
    height: 120px;
    background: #c61dff;
    color: #fff;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
    cursor: pointer;
    text-decoration: none;
    letter-spacing: .1em;
    font-size: 1em;
    transition: .25s;
    box-shadow: inset 10px 10px 10px rgba(190, 1, 254, .05),
    25px 35px 20px rgba(190, 1, 254, .1),
    25px 30px 30px rgba(190, 1, 254, .1),
    inset -10px -10px 15px rgba(255, 255, 255, .5);
    border-radius: 44% 56% 65% 35% / 57% 58% 42% 43%;
    user-select: none;
    animation: waterMove2 9s linear infinite;
}

.login-btn::before {
    content: '';
    position: absolute;
    top: 15px;
    left: 30px;
    width: 20px;
    height: 20px;
    border-radius: 44% 56% 35% / 57% 58% 42% 43%;
    background: #fff;
    opacity: .45;
}

.login-btn:hover, .login-btn:hover::before {
    border-radius: 50%;
}

.register {
    bottom: 150px;
    right: -120px;
    width: 80px;
    height: 80px;
    background: #01b4ff;
    border-radius: 49% 51% 52% 48% / 63% 59% 41% 37%;
    box-shadow: inset 10px 10px 10px rgba(1, 180, 255, .005),
    25px 35px 20px rgba(1, 180, 255, .1),
    25px 30px 30px rgba(1, 180, 255, .1),
    inset -10px -10px 15px rgba(255, 255, 255, .5);
}

.register::before {
    left: 20px;
    width: 15px;
    height: 15px;
    border-radius: 49% 51% 52% 48% / 63% 59% 41% 37%;
}


@keyframes waterMove2 {
    20% {
        border-radius: 44% 56% 65% 35% / 57% 58% 42% 43%;
    }

    40% {
        border-radius: 44% 56% 45% 45% / 67% 50% 46% 37%;
    }

    60% {
        border-radius: 39% 61% 70% 30% / 60% 68% 50% 43%;
    }

    80% {
        border-radius: 41% 58% 60% 40% / 52% 48% 70% 52%;
    }
}

效果展示:
在这里插入图片描述
在这里插入图片描述

在这基础上在增加 canvas 背景

背景图片
在这里插入图片描述
在这里插入图片描述

import bridge from "../../assets/bridge.png";

class WaterRipple {
    constructor(props) {
        this.init(props);
    }

    init(props) {
        this.boxRef = props.boxRef;
        this.canvas = props.canvas;
        this.ctx = this.canvas.getContext("2d", {willReadFrequently: true});
        this.width = this.canvas.width;
        this.height = this.canvas.height;

        //最大振幅
        this.max_amplitude = 1024;
        this.amplitude = props.amplitude || 512;

        this.background = props.background;
        this.load = false;

        this.img_data = null;
        this.new_img_data = null;
        this.animation_idx = null;

        this.mousemove_interval = props.mousemove_interval || 50;
        this.animation_interval = props.animation_interval || 20;
        //波纹圆角
        this.ripple_radius = props.ripple_radius || 3;

        this.bridge = new Image();
        this.bridge.src = bridge;

        if (props.resize) {
            this.setResize();
        }
    }

    drawBackGround() {
        if (this.background.complate) return;

        this.ctx.drawImage(this.background, 0, 0, this.width, this.height);
        this.img_data = this.ctx.getImageData(0, 0, this.width, this.height);
        this.new_img_data = this.ctx.getImageData(0, 0, this.width, this.height);

        this.buffer_1 = new Array(this.width).fill(0).map(() => new Array(this.height).fill(0));
        this.buffer_2 = new Array(this.width).fill(0).map(() => new Array(this.height).fill(0));

        this.load = true;
    }

    render() {
        if (!this.load) {
            this.drawBackGround();
            return;
        }

        const w = this.width;
        const h = this.height;
        const half_w = w >> 1;
        const half_h = h >> 1;
        const len = this.new_img_data.data.length;

        for (let i = 0; i < len; i += 4) {
            const buffer_idx = i >> 2;
            const x = buffer_idx % w;
            const y = (buffer_idx - x) / w;

            const left = Math.max(x - 1, 0);
            const right = Math.min(x + 1, w - 1);
            const top = Math.min(y + 1, h - 1);
            const bottom = Math.max(y - 1, 0);

            let next_amplitude =
                this.buffer_1[x][top] +
                this.buffer_1[x][bottom] +
                this.buffer_1[right][y] +
                this.buffer_1[left][y];
            next_amplitude >>= 1;
            next_amplitude -= this.buffer_2[x][y];
            next_amplitude -= next_amplitude >> 5;

            this.buffer_2[x][y] = next_amplitude;

            const ratio = (this.max_amplitude - next_amplitude) / this.max_amplitude;

            if (next_amplitude !== 0) {
                let offset_x = (((x - half_w) * ratio) << 0) + half_w;
                let offset_y = (((y - half_h) * ratio) << 0) + half_h;

                offset_x = Math.min(offset_x, w - 1);
                offset_x = Math.max(offset_x, 0);
                offset_y = Math.max(offset_y, 0);
                offset_y = Math.min(offset_y, h - 1);

                const img_data_idx = (offset_y * w + offset_x) << 2;
                const r = this.img_data.data[img_data_idx];
                const g = this.img_data.data[img_data_idx + 1];
                const b = this.img_data.data[img_data_idx + 2];

                this.new_img_data.data[i] = r;
                this.new_img_data.data[i + 1] = g;
                this.new_img_data.data[i + 2] = b;
            }
        }

        this.ctx.putImageData(this.new_img_data, 0, 0);
        this.ctx.drawImage(
            this.bridge,
            0,
            this.height * 0.35,
            this.width,
            this.height * 0.65
        );

        const temp_data = this.buffer_2;
        this.buffer_2 = this.buffer_1;
        this.buffer_1 = temp_data;
    }

    animate() {
        let start = new Date().getTime();
        const update = () => {
            const end = new Date().getTime();
            if (end - start > this.animation_interval) {
                this.render();
                start = end;
            }

            this.animation_idx = requestAnimationFrame(update);
        };
        update();
    }

    throttle(func, time) {
        let start = new Date().getTime();
        return function () {
            const end = new Date().getTime();
            if (end - start >= time) {
                start = end;
                func.apply(this, arguments);
            }
        };
    }

    ripple(x, y) {
        if (!this.load) return;
        const left = Math.max(x - this.ripple_radius, 0);
        const right = Math.min(x + this.ripple_radius, this.buffer_1.length - 1);
        const top = Math.max(y - this.ripple_radius, 0);
        const bottom = Math.min(
            y + this.ripple_radius,
            this.buffer_1[0].length - 1
        );

        for (let i = left; i < right; i++) {
            for (let j = top; j < bottom; j++) {
                this.buffer_1[i][j] = this.amplitude;
            }
        }
    }
   addMousemove() {
        this.canvas.addEventListener(
            "mousemove",
            this.throttle((e) => {
                const {top, left} = this.canvas.getBoundingClientRect();
                this.ripple(Math.floor(e.clientX - left), Math.floor(e.clientY - top));
            }, this.mousemove_interval)
        );
    }

    setResize() {
        window.addEventListener("resize", () => {
            const {offsetWidth, offsetHeight} = this.boxRef.value;
            this.canvas.width = offsetWidth;
            this.canvas.height = offsetHeight;

            this.width = this.canvas.width;
            this.height = this.canvas.height;

            this.load = false;
        });
    }
    stop() {
        cancelAnimationFrame(this.animation_idx);
    }

    resume() {
        this.animate();
    }
}

export default WaterRipple
<template>
  <div class="container" ref="boxRef">
    <canvas ref="canvasRef"></canvas>
    <login-page></login-page>
  </div>
</template>

<script setup>
import WaterRipple from "@/components/FloatWater/floatWater.js";
import waterBg from '@/assets/water.png'
import {onMounted, onUnmounted, ref} from "vue";
import LoginPage from "@/components/LoginPage.vue";

let canvasWidth = 600;
let canvasHeight = 600;
let timer = null;
let waterRipple = null;

const boxRef = ref(null);
const canvasRef = ref(null);

onMounted(() => {
  if (boxRef.value && canvasRef.value) {
    const {offsetWidth, offsetHeight} = boxRef.value;
    canvasWidth = offsetWidth;
    canvasHeight = offsetHeight;
    canvasRef.value.width = canvasWidth;
    canvasRef.value.height = canvasHeight;

    const waterImg = new Image();
    waterImg.src = waterBg;
    waterRipple = new WaterRipple({
      canvas: canvasRef.value,
      background: waterImg,
      boxRef,
    })
    waterRipple.animate();

    timer = window.setInterval(() => {
      const x = Math.floor(canvasWidth * Math.random());
      const y = Math.floor(canvasHeight * Math.random())
      waterRipple?.ripple(x, y)
    }, 1000)


    waterRipple.addMousemove()
  }
})
onUnmounted(() => {
  timer && clearInterval(timer);
  waterRipple?.stop()
})

</script>

<style scoped>

.container {
  boxSizing: border-box;
  width: 100vw;
  height: 100vh;
  display: flex;
  position: relative;
}

</style>

效果展示:
在这里插入图片描述

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值