在vue3中使用canvas实现雨滴效果

在vue3中使用canvas实现雨滴效果

这是封装的一个组件DotAndRain(

<script setup>
import {
    ref, onMounted } from "vue";
import {
    onUnmounted } from "vue";

let animationFrameId = null;

const el = ref(null);
let canvas = null;
let ctx = null;
let dots = [];
let rains = [];

onMounted(() => {
   
	canvas = el.value;
	canvas.width = 162;
	canvas.height = 146;
	ctx = canvas.getContext("2d");
	draw();
	animate();
});

onUnmounted(() => {
   
	cancelAnimationFrame(animationFrameId);
});

function draw() {
   
	const positions = [
		[[54, 16], 10],
		[[28, 80], 80],
		[[130, 114], 120]
	];
	for (const arr of positions) {
   
		const dot = new Dot(...arr[0]);
		dot.draw();
		dots.push(dot);
		const rain = new Rain(arr[1]);
		rain.draw();
		rains.push(rain);
	}
}

function animate() {
   
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	dots.forEach((dot) => {
   
		dot.move();
	});
	rains.forEach((rain) => {
   
		rain.move();
	});
	animationFrameId = requestAnimationFrame(animate);
}

class Dot {
   
	radius = 3;
	speed = 0.08;
	range = 10;
	angle = Math.random() * Math.PI * 2;
	constructor(x, y) {
   
		this.x = x;
		this.y = y;
		this.originX = x;
		this.originY = y;
	}
	draw() {
   
		ctx.beginPath();
		ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);

		const line = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.radius);
		line.addColorStop(0, "#fff");
		line.addColorStop(1, "#62E9F9");
		ctx.fillStyle = line;

		// ctx.fillStyle = "#62E9F9";
		ctx.fill();
		ctx.closePath();
	}
	move() {
   
		// 计算下一个位置
		const nextX = this.x + Math.cos(this.angle) * this.speed;
		const nextY = this.y + Math.sin(this.angle) * this.speed;

		// 判断是否超出边界
		if (nextX > this.originX - this.range && nextX < this.originX + this.range && nextY > this.originY - this.range && nextY < this.originY + this.range) {
   
			this.x = nextX;
			this.y = nextY;
		} else {
   
			// 如果超出边界,则随机生成新的角度
			this.angle = Math.random() * Math.PI * 2;
		}
		this.draw();
	}
}

class Rain {
   
	alpha = 0.8;
	width = 2;
	y = canvas.height;
	constructor(x) {
   
		this.x = x;
		this.init();
	}
	init() {
   
		this.alpha = 1;
		this.speed = Math.random() + 1;
		this.height = Math.random() * 40 + 30;
		this.y = canvas.height;
	}
	draw() {
   
		ctx.beginPath();
		ctx.lineWidth = 3; //宽度
		// ctx.globalAlpha = this.alpha; //设置透明度
		//创建横向渐变颜色,起点坐标至终点坐标
		const line = ctx.createLinearGradient(this.x, this.y, this.x + this.width, this.y + this.height);
		line.addColorStop(0, `rgba(62, 192, 255, ${
     this.alpha})`);
		line.addColorStop(0.6, `rgba(62, 192, 255, ${
     this.alpha / 2})`);
		line.addColorStop(1, "transparent");
		ctx.strokeStyle = line;
		ctx.moveTo(this.x, this.y);
		ctx.lineTo(this.x, this.y + this.height);
		ctx.closePath();
		ctx.stroke();
	}

	move() {
   
		this.alpha -= 0.01;
		this.y -= this.speed;
		if (this.y < 0) {
   
			this.init();
		}
		this.draw();
	}
}
</script>

<template>
	<canvas ref="el" style="width: 162px; height: 146px"></canvas>
</template>

<style scoped lang="scss"></style>

上述代码实现了一个简单的雨滴效果,主要包括绘制雨滴和下落动画两个部分。下面我会详细解释代码中涉及到的关键部分:

1.初始化和绘制:

  • draw() 函数中,首先定义了三个雨滴和雨点的初始位置和大小,并通过循环创建了对应数量的 DotRain 对象,并调用它们的draw() 方法进行绘制。
  • Dot 类用于绘制雨滴的水滴效果,包括设置半径、速度、范围、角度等属性,并实现了 draw()move()
    方法来绘制和移动雨滴。
  • Rain 类用于绘制雨滴的下落效果,包括设置透明度、宽度、高度等属性,并实现了 init()draw()move()
    方法来初始化、绘制和控制雨滴的下落。

2.动画循环:

  • animate() 函数中,使用 requestAnimationFrame()
    创建了一个动画循环,不断清除画布内容并重新绘制雨滴和雨点,实现动态效果。
  • 在每一帧中,分别调用雨滴和雨点对象的 move() 方法,更新它们的位置和状态,并重新绘制在画布上。

3.雨滴效果绘制:

  • Dot 类通过绘制圆形并利用径向渐变填充,实现了水滴的效果,颜色由白色渐变为蓝色。
  • Rain 类通过绘制线条并利用线性渐变描边,实现了雨滴的下落效果,颜色从蓝色透明度逐渐减小到透明。

在App.vue文件中直接使用即可

<script setup>
import assets from '/src/assets/assets_item.png';
import DotAndRain from './components/DotAndRain.vue';
</script>

<template>
  <div style="position: relative;width: 162px; height: 146px;">
    <div class="item-shadow"></div>
    <div class="item-bg"></div>
    <DotAndRain/>
  </div>
</template>

<style scoped>
/* 电子围墙 */
.item-bg {
   
  background-image: url("/src/assets/assets_item.png");
  width: 162px;
  height: 146px;
  position: absolute;
  z-index: 4;
}

.item-shadow::before {
   
  content: "";
  position: absolute;
  left: 0;
  bottom: 40px;
  width: 100%;
  height: 0;
  z-index: 2;
  background-image: linear-gradient(0deg, rgba(21, 54, 90, 1), transparent);
  background-repeat: repeat-y;
  background-size: 100% 100%;
  animation: wall 3s linear infinite;
}

.item-shadow::after {
   
  content: "";
  position: absolute;
  left: 0;
  bottom: 40px;
  width: 100%;
  height: 0;
  z-index: 3;
  background-image: linear-gradient(0deg, rgba(21, 54, 90, 1), transparent);
  background-repeat: repeat-y;
  background-size: 100% 100%;
  animation: wall 3s linear infinite 1.5s;
}

@keyframes wall {
   
  0% {
   
    height: 0;
  }

  20% {
   
    opacity: 1;
  }

  60% {
   
    height: calc(100% - 50px);
  }

  100% {
   
    opacity: 0;
  }
}
</style>

实现效果如下:

cavans实现雨滴

cavans快速入门

1.创建cavans

<script setup>
</script>

<template>
  <div>
    <canvas ref="canvas" height="600px" width="600px"></canvas>
  </div>
</template>

<style scoped>
canvas {
   
  border: 1px solid #ccc;
}

在这里插入图片描述
2.获取CanvasRenderingContext2D对象进行绘制

  • 给canvas添加一个ref属性:
<canvas ref="canvas" height="300px" width="300px"></canvas>
  1. 获取canvas对象:
<script setup>
import {
    ref} from 'vue';

const canvas = ref(null);
</script>
  • 渲染完成后获取CanvasRenderingContext2D对象:
<script setup>
import {
    ref, onMounted } from 'vue';

const canvas = ref(null);
onMounted(() => {
   
  const ctx = canvas.value.getContext('2d');
});
</script>
<script setup>
import {
    ref, onMounted } from 'vue';
const canvas = ref(null);

onMounted(() => {
   
  const ctx = canvas.value.getContext('2d');
  
  //直线绘制
  // ctx.moveTo(100, 100);
  // ctx.lineTo(200, 200);
  // ctx.stroke();
  
  //圆圈绘制
  ctx.beginPath();
  ctx.arc(100, 75, 50, 0, 2 * Math.PI);
  ctx.stroke();
  
  //圆弧绘制
  // ctx.beginPath();
  // ctx.arc(100,75,50,90/180*Math.PI,2*Math.PI);
  // ctx.stroke();
});
</script>

完整模板如下:

<script setup>
import {
    ref, onMounted } from 'vue';
const canvas = ref(null);

onMounted(() => {
   
  const ctx = canvas.value.getContext('2d');
  ctx.beginPath();
  ctx.arc(100, 75, 50, 0, 2 * Math.PI);
  ctx.stroke();
});
</script>

<template>
  <div>
    <canvas ref="canvas" height="300px" width="300px"></canvas>
  </div>
</template>

<style scoped>
canvas {
   
  border: 1px solid #ccc;
}
</style>

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

相关推荐

  1. 如何Vue3实现无缝热重载:提升你的开发效率

    2024-02-22 13:48:02       35 阅读
  2. CSS实现一个雨滴滑落效果

    2024-02-22 13:48:02       9 阅读
  3. vue3使用pinia

    2024-02-22 13:48:02       13 阅读
  4. Vue3 使用 styled-components

    2024-02-22 13:48:02       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-22 13:48:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-22 13:48:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-22 13:48:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-22 13:48:02       20 阅读

热门阅读

  1. github新手用法详解

    2024-02-22 13:48:02       35 阅读
  2. 用c实现diikstra算法

    2024-02-22 13:48:02       28 阅读
  3. 算法提升——LeetCode第385场周赛总结

    2024-02-22 13:48:02       31 阅读
  4. 字符串split切割逻辑

    2024-02-22 13:48:02       27 阅读
  5. H5/CSS 笔试面试考题(91-100)

    2024-02-22 13:48:02       29 阅读
  6. 平台组成-门户服务

    2024-02-22 13:48:02       99 阅读
  7. ADO.NET+Redis 实现缓存

    2024-02-22 13:48:02       23 阅读
  8. C/C++实现药房管理系统

    2024-02-22 13:48:02       24 阅读
  9. 深入URP之Shader篇15: Shader关键字和变体

    2024-02-22 13:48:02       28 阅读
  10. python保存网页为PDF

    2024-02-22 13:48:02       25 阅读
  11. 量化粒度是什么???

    2024-02-22 13:48:02       22 阅读