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

news2024/11/15 11:19:02

在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>
  • 直线、圆圈、圆弧的绘制:
    具体请参考HTML Canvas参考手册
<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>

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1463892.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java零基础 - 赋值运算符

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

redis入门指南

文章目录 Redis概述Redis基本数据类型Redis与MySQL的区别以及使用场景如何保持双写一致性&#xff08;缓存一致性&#xff09;1. 延迟双删2. 分布式锁&#xff08;强一致性时使用&#xff09;3. 中间件 Redis持久化机制RDB&#xff08;redis database&#xff09;AOF&#xff0…

Python算法100例-1.10 数制转换

完整源代码项目地址&#xff0c;关注博主私信源代码后可获取 1.问题描述2.问题分析3.算法设计4.确定程序框架5.字符与数字进行转换6.其他数制转换成十进制7.十进制转换成其他数制8.完整的程序 1&#xff0e;问题描述 给定一个M进制的数x&#xff0c;实现对x向任意一个非M进制…

SwiftUI 集合视图(Grid)拖放交换 Cell 的极简实现

概览 自从 SwiftUI 横空出世那天起&#xff0c;小伙伴们都感受到了它惊人的简单与便捷。而在本课中&#xff0c;我们将会用一个小“栗子”更直观的让大家体验到它无与伦比简洁的描述性特质&#xff1a; 如上图所示&#xff0c;我们在 SwiftUI 中实现了 Grid 中拖放交换 Cell 的…

开开开开开,干

大家新年快乐&#xff0c;开工啦啦啦啦 其实每天很多人都会问&#xff1a; 有协同过滤的算法吗&#xff0c;有的&#xff0c;可以给你解释原理… 有的小伙伴只开了一部分逻辑&#xff0c;我要实现用户可以下单功能 但是细细考虑下单&#xff0c;需要现有用户&#xff0c;维护…

[Git] 配置Access Token 解决Github 认证弹窗

[Git] 配置Access Token 解决Github 认证弹窗 1. 前言2. 解决2.1 申请Personal Access Token2.2. 配置Token2.3. 授权激活Token 博主热门文章推荐&#xff1a; 1. 前言 最近从bitbucket切换到了Github Enterprise, 刚使用几次发现 每次操作 都有弹窗认证&#xff0c; 虽然手动点…

波奇学Linux:进程通信管道

进程通信 管道&#xff1a;基于文件级别的单向通信 创建父子进程&#xff0c;使得进程的struct file*fd_array[]的文件描述符指向同一个struct file文件&#xff0c;这个文件是内存级文件。 父进程关写端&#xff0c;子进程再关闭读端。实现单向通信 子进程写入&#xff0c;父进…

个人博客搭建

使用彩虹云主机百度云域名WordPress 下载WordPress https://cn.wordpress.org/ 购买主机 购买彩虹云主机&#xff0c;购买香港高防主机https://www.cccyun.net/ 购买之后点击 管理 进入后点 击前往控制面板 -> 一键登录控制面板 可进入控制面板。 选择文件管理 在线…

cuda加速:memory coalescing,Bank Conflicts

cuda加速&#xff1a;memory coalescing 1.memory coalescing2.Shared Memory Bank Conflicts参考文献 1.memory coalescing 参考【1】中给出的定义&#xff1a;一个warp中&#xff0c;thread 0到thread 31访问连续的内存空间&#xff0c;则这些线程的访问被合并为一次访问。 …

2.21学习总结

1.【模板】ST 表 2.Balanced Lineup G 3.景区导游 4.最近公共祖先&#xff08;LCA&#xff09; 倍增思想&#xff1a;主要用于LCA问题&#xff0c;RMQ问题。在进行 递推 时&#xff0c;如果 状态空间很大&#xff0c;通常的 线性递推 无法满足 时间 与 空间复杂度 的要求&…

BabylonJS 6.0文档 Deep Dive 动画(一):动画介绍

1. 动画介绍 无论动画如何实现&#xff0c;它都必须考虑所需的动作、时间、产生所需流动性所需的帧数以及序列中的关键点。这个介绍应该有助于理解Babylon.js是如何进行动画的&#xff0c;以及它们是如何实现的。 动画由一系列图像、帧生成&#xff0c;这些图像、帧一个接一个地…

做跨境电商,为什么要建独立站,2024年的机会在哪里?一次性讲清楚...

做跨境电商&#xff0c; 是选择依托第三方平台&#xff1f; 还是自建独立网站&#xff1f; 01 什么是自建独立站 最简单的说法&#xff1a;独立站对于我们跨境电商这个行业来说&#xff0c;就是那些不是主流平台的网站&#xff0c;是某个企业或者个人自己做和运营的搭建电商…

记录一次调用奇门api报错信息Invalid signature (签名无效)

奇门有msg对应错误的记录 我可能是 3.1 以下是还原场景 第一个方法 private static String getSellerItem() throws IOException {Map<String, String> params new HashMap<String, String>();// 公共参数params.put("page_index", "100"…

如何用USB服务器解决网银U盾连接管理问题?好用吗?

反复插拔效率低、分散管理隐患大 随着信息化建设的深入推进&#xff0c;网银U盾已经成为企业办公中不可或缺的工具。然而&#xff0c;在使用U盾的过程中&#xff0c;常常会遇到一些问题&#xff0c;其中最突出的问题之一就是U盾的连接和管理问题。反复插拔效率低、分散管理隐患…

Leetcode - 周赛385

目录 一&#xff0c;3042. 统计前后缀下标对 I 二&#xff0c;3043. 最长公共前缀的长度 三&#xff0c;3044. 出现频率最高的质数 四&#xff0c;3045. 统计前后缀下标对 II 一&#xff0c;3042. 统计前后缀下标对 I 该题数据范围小&#xff0c;可直接暴力求解&#xff0c;…

18.贪心算法

排序贪心 区间贪心 删数贪心 统计二进制下有多少1 int Getbit_1(int n){int cnt0;while(n){nn&(n-1);cnt;}return cnt; }暴力加一维前缀和优化 #include <iostream> #include <climits> using namespace std; #define int long long const int N2e510; in…

利用AI做Q版影视剧人物头像,轻松涨粉过万

粉丝在问,能不能出一个SD制作热播影视剧Q版人物的教程。刚好我最近也刷到了几个号,对这方面也感兴趣,于是去研究了出图的逻辑。热播电视剧的Q版人物,一方面不缺流量,IP也能带来一波回忆杀。 下面这组图是一只小鹿鸭的作品,在某音和某书都有很好的数据。 公众号刷到了一组…

128.乐理基础-五线谱-纯四度、纯五度

内容参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;127.乐理基础-五线谱-纯一度、纯八度-CSDN博客 上一个内容里练习的答案&#xff1a; 纯四度、纯五度的结论 纯四度例子&#xff1a; 例子1&#xff1a; 例子2&#xff1a; 纯四度两个条件&#xff0c;音数是2.5&a…

微信小程序开发学习笔记——2.10video视频组件及uniCloud云存储网络地址用法

>>跟着b站up主“咸虾米_”学习微信小程序开发中&#xff0c;把学习记录存到这方便后续查找。 课程连接&#xff1a; https://www.bilibili.com/video/BV19G4y1K74d?p13&vd_source9b149469177ab5fdc47515e14cf3cf74 一、最简单的放video的例子 视频尽量不要放本…

华为OD机试真题-整数对最小和-2023年OD统一考试(C卷)-- Python3-开源

题目&#xff1a; 考察内容&#xff1a;双循环sortsum 代码&#xff1a; """ 题目分析&#xff1a; 求随机组合最小和 输入&#xff1a; 数组a个数&#xff0c; 数组元素 数组b个数&#xff0c;数组元素 对数个数输出&#xff1a; 和的最小值3 1 1 2 3 1 2 3…