在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()
函数中,首先定义了三个雨滴和雨点的初始位置和大小,并通过循环创建了对应数量的Dot
和Rain
对象,并调用它们的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>
- 获取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>
效果如下: