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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Day3 javaweb开发——登录认证

登录功能 没什么好写的&#xff0c;就是LoginController层里面要注入empService的对象 登录校验&#xff08;重点&#xff09; 没有校验的情况 没有登录之前&#xff0c;访问数据的网址需要跳转到登录页面。 http是无状态的&#xff0c;处理其他业务时没有判断他是否登录 …

LINUX读取RTC实时时钟时间

linux 读写RTC时间_linux rtc 读写-CSDN博客

[newstarctf2023] --RE wp

AndroGenshin: rc4加密表&#xff0c;base64换表&#xff1a; 脚本梭就行 python username b"genshinimpact" base64_table [125, 239, 101, 151, 77, 163, 163, 110, 58, 230, 186, 206, 84, 84, 189, 193, 30, 63, 104, 178, 130, 211,164, 94, 75, 16, 32, 33…

anomalib1.0学习纪实-续4:做个小结

我们就以padim为例。 一、主入口&#xff1a; 二、Padim类。 这个Padim类就在src\anomalib\models\image\padim文件夹下。 这个Padim类的父类就是 AnomalyModule&#xff0c;这个父类你不能改动了&#xff0c;里面的内容写死了。 这个Padim类&#xff0c;最重要的是&#xf…

基于Spring Boot的学生评奖评优管理系统,计算机毕业设计(带源码+论文)

源码获取地址&#xff1a; 码呢-一个专注于技术分享的博客平台一个专注于技术分享的博客平台,大家以共同学习,乐于分享,拥抱开源的价值观进行学习交流http://www.xmbiao.cn/resource-details/1760641819451928577

vue 非父子通信-event bus 事件总线

1.作用 非父子组件之间&#xff0c;进行简易消息传递。(复杂场景→ Vuex) 2.步骤 创建一个都能访问的事件总线 &#xff08;空Vue实例&#xff09; import Vue from vue const Bus new Vue() export default Bus A组件&#xff08;接受方&#xff09;&#xff0c;监听Bus的…

jvm垃圾收集器-三色标记算法

1.对象已死吗? 在堆里面存放着Java世界中几乎所有的对象实例&#xff0c;垃圾收集器在对堆进行回收前&#xff0c;第一件事情就是要确定这些对象之中哪些还“存活”着&#xff0c;哪些已经“死去”&#xff08;即不可能再被任何途径使用的对象). 引计数法 引用计数算法是一…

MyBatisPlus常用注解

目录 一、TableName 二、TableId 三、TableField 四、TableLogic 一、TableName 在使用MyBatis-Plus实现基本的CRUD时&#xff0c;我们并没有指定要操作的表&#xff0c;只是在Mapper接口继承BaseMapper时&#xff0c;设置了泛型User&#xff0c;而操作的表为user表 由此得出…

飞天使-k8s知识点22-kubernetes实操7-ingress

文章目录 ingress环境准备准备service和pod验证效果 https 代理效果 ingress 在 Kubernetes 中&#xff0c;Ingress 是一种 API 对象&#xff0c;它管理外部访问集群内部服务的规则。你可以将其视为一个入口&#xff0c;它可以将来自集群外部的 HTTP 和 HTTPS 路由到集群内部的…

海思3559 yolov5 wk模型部署笔记

文章目录 安装3559工具链编译opencv编译项目总结 安装3559工具链 将3559工具链copy到虚拟机上&#xff0c;并解压得到安装包 解压&#xff1a; tar -zxvf aarch64-himix100-linux.tgz解压后会得到安装包文件夹&#xff1a; 安装工具链&#xff1a; sudo ./aarch64-himix100…

用 Python 自动化处理无聊的事情

“编程最棒的部分就是看到机器做一些有用的事情而获得的胜利。用 Python 将无聊的事情自动化将所有编程视为这些小小的胜利&#xff1b;它让无聊变得有趣。” Hilary Mason&#xff0c;数据科学家兼 Fast Forward Labs 创始人 “我很享受打破东西然后把它们重新组合起来的乐趣…

SPSSAU【文本分析】|文本聚类

SPSSAU共提供两种文本聚类方式&#xff0c;分别是按词聚类和按行聚类。按词聚类是指将需要分析的关键词进行聚类分析&#xff0c;并且进行可视化展示&#xff0c;即针对关键词进行聚类&#xff0c;此处关键词可以自由选择。按行聚类分析是指针对以‘行’为单位进行聚类分析&…

陈世元:被保送中科院,两次被裁,人生不能老是按部就班的走下去,需要冒险,尝试一下新的东西

《程客有话说》是我们最新推出的一个访谈栏目&#xff0c;邀请了一些国内外有趣的程序员来分享他们的经验、观点与成长故事&#xff0c;我们尝试建立一个程序员交流与学习的平台&#xff0c;也欢迎大家推荐朋友或自己来参加我们的节目&#xff0c;一起加油。 本期我们邀请的程…

【Qt学习】QLineEdit 控件 属性与实例(登录界面,验证密码,正则表达式)

文章目录 1. 介绍2. 实例使用2.1 登录界面2.2 对比两次密码是否相同2.3 通过按钮显示当前输入的密码&#xff08;并对2.2进行优化&#xff09;2.4 结语 3. 正则表达式3.1 QRegExp3.2 验证输入内容 4. 资源代码 1. 介绍 关于 QLineEdit 的详细介绍&#xff0c;可以去查阅官方文…

Web安全之浅见

备注&#xff1a;这是我在2017年在自己的网站上写的文章&#xff0c;今天迁移过来。 昨天去参加了公司组织的一个关于网络安全的培训&#xff0c;了解了很多关于网络安全方面的知识&#xff0c;也才意识到网络安全是一项极其重要的领域。 本篇文章主要聊聊Web安全。不过我对于网…

vue3使用pinia-plugin-persistedstate 持久化不生效的问题

如下图所示&#xff0c;需要在路由拦截器里面写。不然一个都不生效。 【即使以前生效的&#xff0c;在下图【红框】中定义一下&#xff0c;也会失效。得挪到路由拦截器里面才行】 终于出来了

【GPTs分享】每日GPTs分享之Canva

简介 Canva&#xff0c;旨在帮助用户通过Canva的用户友好设计平台释放用户的创造力。无论用户是想设计海报、社交媒体帖子还是商业名片&#xff0c;Canva都在这里协助用户将创意转化为现实。 主要功能 设计生成&#xff1a;根据用户的描述和创意需求&#xff0c;生成定制的设…

MyBatis-Plus 优雅实现数据加密存储

文章目录 前言一、数据库字段加解密实现1. 定义加密类型枚举2. 定义AES密钥和偏移量3. 配置定义使用的加密类型4. 加密解密接口5. 解密解密异常类6. 加密解密实现类6.1 AES加密解密实现类6.2 Base64加密解密实现类 7. 实现数据库的字段保存加密与查询解密处理类8. MybatisPlus配…

Sora热潮下,如何充分利用AI减少人工测试需求?

近日&#xff0c;OpenAI发布视频生成模型Sora&#xff0c;再次引发全球科技圈讨论热潮。Sora可以根据用户输入的简短文本指令&#xff0c;生成长达1分钟的高清视频&#xff0c;视频画面具有真实感&#xff0c;带有些许电影质感。 根据IDC的预测&#xff0c;未来五年内&#xff…

Java项目:20 基于SSM实现的支教管理系统

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 ssm支教管理系统&#xff08;前台后台&#xff09; 前台角色&#xff1a;支教学校志愿者 支教学校功能模块&#xff1a;支教学校查询报名职位发布已…