内容: canvas详细使用和解题思路
效果: 如下图
一、使用canvas画线
<canvas id="canvas" ref="canvasRef"></canvas>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
onMounted(() => {
init()
})
function init() {
const canvas = canvasRef.value // 获取canvas
const ctx = canvas.getContext('2d') // 建立一个二维渲染上下文
[查看api](https://www.canvasapi.cn/HTMLCanvasElement/getContext)
canvas.width = window.innerWidth; // canvas宽度为窗口的文档显示区的宽度
canvas.height = window.innerHeight; // canvas高度为窗口的文档显示区的高度
ctx.lineWidth = 0.3 // 设置线宽,默认为1
ctx.strokeStyle = 'red'; // 设置描边颜色
ctx.beginPath(); // 开始绘制
ctx.moveTo(100, 100); // 起始点移动到100,100
ctx.lineTo(mouse.value.x, mouse.value.y); // 从100,100到鼠标点击位置
ctx.stroke() // 绘制
ctx.closePath() // 结束
}
// 好了,线画完了,基本的canvas使用就这么简单
<script>
二、实现随机在画布上放置点
先理一下思路,既然是随机放置点,点的颜色要不同,点的位置要随机,点的数量要多,我们定义一个点类,里面有颜色,位置等属性和画点的方法,在init()函数中循环调用,就可以随机画点了
class Point {
private _x: number;
private _y: number;
private _ctx: any;
private _random: number;
private _color: string;
private _scale: number;
public canvas: any;
constructor(x: number, y: number, ctx: any, canvas: any) {
this._x = x; // 循环的时候传如画布宽度内的随机数
this._y = y; // 循环的时候传如画布高度内的随机数
this._ctx = ctx; // 使用ctx
this.canvas = canvas; // 使用canvas
this._random = Math.random() * 100; // 生成随机数给四种情况赋值
this._color = color(); // 点的颜色
this._scale = randomNumber(1, 2) // 点的大小
this.draw() // 绘制
}
draw() {
const ctx = this._ctx
try {
ctx.fillStyle = this._color // 设置填充颜色
ctx.beginPath() // 开始绘制
ctx.arc(this._x, this._y, this._scale, 0, 2 * Math.PI) // 绘制
ctx.closePath() // 结束
ctx.fill() // 填充
}
catch (err) {
}
}
}
/**
* @description: 生成min-max之间随机数
*/
function randomNumber(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1) + min);
}
/**
* @description: 生成随机颜色
*/
function color(): string {
let sum = ""
for (let i = 0; i < 6; i++) {
sum += Math.floor(Math.random() * 16).toString(16)
}
return '#' + sum
}
function init() {
...
// 在init中画200个点,之前的线可以删掉了
for (let i = 0; i < 200; i++) {
const [x, y] = [randomNumber(0, window.innerWidth), randomNumber(0, window.innerHeight)]
new Point(x, y, ctx)
}
}
// 到这里,两百个随机点就放上了
三、让点动起来
使用requestAnimationFrame(),(让网页动画效果能够有一个统一的刷新机制,请百度or谷歌,目前鱼儿是百度搜索引擎工程师),并定义点的render方法,在实例建立后不断的让实例的x和y属性变化,就动起来了
class Point {
...
render() {
const random = this._random // [0,100)随机数
switch (true) {
case random >= 0 && random < 25:
this._x = this._x + 0.4
this._y = this._y + 0.4
break; // 右下
case random >= 25 && random < 50:
this._x = this._x - 0.4
this._y = this._y + 0.4
break; // 左下
case random >= 50 && random < 75:
this._x = this._x - 0.4
this._y = this._y - 0.4
break; // 左上
case random >= 75 && random < 100:
this._x = this._x + 0.4
this._y = this._y - 0.4
break; // 右上
}
if (this._x < 0 || this._x > this.canvas.width) { // 如果小于0或者大于画布宽度,则随机重新放置
this._x = randomNumber(0, window.innerWidth)
}
if (this._y < 0 || this._y > this.canvas.height) { // 如果小于0或者大于画布高度,则随机重新放置
this._y = randomNumber(0, window.innerHeight)
}
this.draw()
}
}
function init() {
...
const pointBox = [] as any // 收集点实例,以便使用实例的属性
for (let i = 0; i < 200; i++) {
const [x, y] = [randomNumber(0, window.innerWidth), randomNumber(0, window.innerHeight)]
const point = new Point(x, y, ctx, canvas) // 渲染的时候循环调用这些点自身的方法
pointBox.push(point)
}
function render(): void {
ctx.clearRect(0, 0, canvas.width, canvas.height); // 不断清除画布内容再绘制,形成动画效果
for (let i = 0; i < pointBox.length; i++) {
pointBox[i].render() // 调用点的render()方法,改变x,y属性
}
requestAnimationFrame(render) // 重绘移动点
}
render()
}
// 你的点动起来了吗?这里再塞一个个监听事件
window.addEventListener('resize', function () { // 窗口变化也要重新绘制
init()
});
四、开始画线
现在一共有200个点,遍历每个点,从每个点出发,找到它周围一定距离的所有点,画线连接即可
// 先声明线类
class Line {
private _ctx;
private _ordinal: any; // 起始点
private _target: any; // 目标点
private _color: string; // 线的颜色,为传入的起始点的颜色
constructor(ctx: any, _ordinal: object, target: object = { x: 100, y: 100 }, color: string) {
this._ctx = ctx;
this._ordinal = _ordinal;
this._target = target;
this._color = color;
this.draw();
}
draw() {
const ordinal = this._ordinal
const target = this._target
const ctx = this._ctx
ctx.lineWidth = 0.3 // 设置线宽,默认为1
ctx.strokeStyle = this._color; // 设置描边颜色
ctx.beginPath(); // 开始绘制
ctx.moveTo(ordinal.x, ordinal.y); // 起始点
ctx.lineTo(target.x, target.y); // 目标点
ctx.stroke() // 绘制
ctx.closePath() // 结束
}
}
const mouse = ref({ // 记录鼠标位置
x: 0,
y: 0
})
function init() {
...
function render(): void {
ctx.clearRect(0, 0, canvas.width, canvas.height); // 不断清除画布内容再绘制,形成动画效果
for (let i = 0; i < pointBox.length; i++) { // 每个点都要寻找自己附近的点作为target去链接
for (let j = 0; j < pointBox.length; j++) { // 再次遍历。寻找周围点
if ((pointBox[j]._x < (pointBox[i]._x + 100) && pointBox[j]._x > (pointBox[i]._x - 100))
&& (pointBox[j]._y < (pointBox[i]._y + 100)) && pointBox[j]._y > (pointBox[i]._y - 100)) {
// 满足上下左右不超过pointBox[i]的100,就是它附近的点,这里已经可以绘制线了
if ((pointBox[j]._x < (mouse.value.x + 100) && pointBox[j]._x > (mouse.value.x - 100))
&& (pointBox[j]._y < (mouse.value.y + 100)) && pointBox[j]._y > (mouse.value.y - 100)) {
// 加一个鼠标那在哪里,哪里就有线的判断,满足鼠标区域内才绘制点
new Line(ctx, { x: pointBox[i]._x, y: pointBox[i]._y }, { x: pointBox[j]._x, y: pointBox[j]._y }, pointBox[i]._color)
}
}
}
pointBox[i].render() // 重新调用这些点,发现点直接移动出了画布,需要加判断条件
}
requestAnimationFrame(render)
}
render()
}
window.addEventListener('mousemove', function (e) {
// 鼠标位置,用于满足鼠标区域内才绘制点判断
mouse.value.x = e.pageX;
mouse.value.y = e.pageY;
});
好了,粒子线条实现了.其实可以拓展出很多很多,比如下雨、撒花场景,或者增加其它鼠标特效,关键是看我们想做什么了