【Canvas】使用Vue3+TS+Canvas实现星星连线

1.效果预览

动画.gif

👉 项目地址

2.实现思路

1.绘制一颗星星,让这颗星星在画布中运动
2.在画布中绘制一组星星,每颗星星都在画布中运动
3.当星星之间的距离小于某个数值时进行连线
4.当鼠标点击画布时,新增5颗星星到画布中
5.鼠标移入画布时,在鼠标处新建一颗星星,跟随鼠标移动

3.实现代码

1.画布设置

  • 新建画布
<div class="star_content">
  <canvas id="star_canvas" ref="star_canvas"></canvas>
</div>
  • 给画布设置背景,也可以选择图片背景
#star_canvas {
  background-color: #333333;
}
  • 设置画布大小,获取上下文对象
const star_canvas = ref<HTMLCanvasElement>();
star_canvas.value!.width = 1000;
star_canvas.value!.height = 800;
let ctx: CanvasRenderingContext2D;
ctx = star_canvas.value!.getContext("2d") as CanvasRenderingContext2D;
  • 清空画布
ctx.clearRect(0, 0, 1000, 800);
  • 设置画笔的边缘和填充颜色
ctx.fillStyle = "white";
ctx.strokeStyle = "white";

2.绘制单个移动的星星

  • 每个星星的结构包含坐标、星星半径和移动速度
interface Star {
  x: number;
  y: number;
  r: number;
  speedX: number;
  speedY: number;
}
  • 星星的坐标、半径和移动速度都随机生成
const randomSpeed = () => {
  return Math.random() * 3 * Math.pow(-1, Math.round(Math.random()));
};
  • 生成一颗星星
const generateStar = () => {
  const star: Star = {
    x: 0,
    y: 0,
    r: 0,
    speedX: 0,
    speedY: 0,
  };
  star.x = Math.random() * 1000;
  star.y = Math.random() * 800;
  star.r = Math.random() * 5;
  star.speedX = randomSpeed();
  star.speedY = randomSpeed();
  return star;
};
let star = generateStar();
  • 将星星绘制在画布上
const Draw = (ctx: CanvasRenderingContext2D, star: Star) => {
  ctx.beginPath();
  ctx.arc(star.x, star.y, star.r, 0, Math.PI * 2);
  ctx.fill();
  ctx.closePath();
};
Draw(ctx, star);
  • 使星星移动,碰到画布边缘时,改变星星的移动方向
const Move = (star: Star) => {
  star.x -= star.speedX;
  star.y -= star.speedY;
  if (star.x < 0 || star.x > 1000) {
    star.speedX *= -1;
  }
  if (star.y < 0 || star.y > 800) {
    star.speedY *= -1;
  }
};
let timer;
timer = setInterval(() => {
  ctx.clearRect(0, 0, 1000, 800);
  Draw(ctx, star);
  Move(star);
}, 50);

3.绘制一组星星,在画布中移动

let starArr: Star[] = [];
for (let i = 0; i < 20; i++) {
let star = generateStar();
  starArr.push(star);
}
timer = setInterval(() => {
  ctx.clearRect(0, 0, 1000, 800);
  starArr.forEach((star: Star) => {
    Draw(ctx, star);
    Move(star);
  });
}, 50);

4.判断星星之间的距离,进行连线

  • 两颗星星进行连线
const DrawLine = (startX: number, startY: number, endX: number, endY: number, ctx: CanvasRenderingContext2D) => {
  ctx.beginPath();
  ctx.moveTo(startX, startY);
  ctx.lineTo(endX, endY);
  ctx.stroke();
  ctx.closePath();
};
  • 判断距离
starArr.forEach((star: Star, index: number) => {
  for (let i = index + 1; i < starArr.length; i++) {
    if (Math.abs(star.x - starArr[i].x) < 50 && Math.abs(star.y - starArr[i].y) < 50) {
      DrawLine(star.x, star.y, starArr[i].x, starArr[i].y, ctx);
    }
  }
});

5.点击添加星星和鼠标星星跟随

  • 点击添加星星
star_canvas.value!.onclick = e => {
  for (let i = 0; i < 5; i++) {
    let star = generateStar();
    star.x = e.offsetX;
    star.y = e.offsetY;
    starArr.push(star);
  }
};
  • 鼠标跟随
const mouse_star = generateStar();
mouse_star.speedX = 0;
mouse_star.speedY = 0;
star_canvas.value!.onmousemove = e => {
  mouse_star.x = e.offsetX;
  mouse_star.y = e.offsetY;
};
timer = setInterval(() => {
  ctx.clearRect(0, 0, 1000, 800);
  // 鼠标star移动
  Draw(ctx, mouse_star);
  // star移动
  starArr.forEach((star: Star) => {
    Draw(ctx, star);
    Move(star);
  });

  // 比较star和所有其他star的距离,小于50连线
  starArr.forEach((star: Star, index: number) => {
    for (let i = index + 1; i < starArr.length; i++) {
      if (Math.abs(star.x - starArr[i].x) < 50 && Math.abs(star.y - starArr[i].y) < 50) {
        DrawLine(star.x, star.y, starArr[i].x, starArr[i].y, ctx);
      }
    }
  });
  // 比较鼠标star和所有star的距离
  for (let i = 0; i < starArr.length; i++) {
    if (Math.abs(mouse_star.x - starArr[i].x) < 50 && 
  Math.abs(mouse_star.y - starArr[i].y) < 50) {
      DrawLine(mouse_star.x, mouse_star.y, starArr[i].x, 
  starArr[i].y, ctx);
    }
  }
}, 50);
  • 离开页面前清除定时器
onBeforeUnmount(() => {
  clearInterval(timer);
});

3.全部代码

  • index.vue
<template>
  <div class="star_content">
    <canvas id="star_canvas" ref="star_canvas"></canvas>
  </div>
</template>

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from "vue";
import { Star, Move, Draw, DrawLine, generateStar } from "./index";

const star_canvas = ref<HTMLCanvasElement>();
let ctx: CanvasRenderingContext2D;

let timer: any;

const initStar = () => {
  ctx = star_canvas.value!.getContext("2d") as CanvasRenderingContext2D;
  ctx.fillStyle = "white";
  ctx.strokeStyle = "white";
  let starArr: Star[] = [];
  for (let i = 0; i < 20; i++) {
    let star = generateStar();
    starArr.push(star);
  }

  // 鼠标star
  const mouse_star = generateStar();
  mouse_star.speedX = 0;
  mouse_star.speedY = 0;
  // 鼠标star跟随移动
  star_canvas.value!.onmousemove = e => {
    mouse_star.x = e.offsetX;
    mouse_star.y = e.offsetY;
  };

  timer = setInterval(() => {
    ctx.clearRect(0, 0, 1000, 800);
    // 鼠标star移动
    Draw(ctx, mouse_star);
    // star移动
    starArr.forEach((star: Star) => {
      Draw(ctx, star);
      Move(star);
    });

    // 比较star和所有其他star的距离,小于50连线
    starArr.forEach((star: Star, index: number) => {
      for (let i = index + 1; i < starArr.length; i++) {
        if (Math.abs(star.x - starArr[i].x) < 50 && Math.abs(star.y - starArr[i].y) < 50) {
          DrawLine(star.x, star.y, starArr[i].x, starArr[i].y, ctx);
        }
      }
    });
    // 比较鼠标star和所有star的距离
    for (let i = 0; i < starArr.length; i++) {
      if (Math.abs(mouse_star.x - starArr[i].x) < 50 && Math.abs(mouse_star.y - starArr[i].y) < 50) {
        DrawLine(mouse_star.x, mouse_star.y, starArr[i].x, starArr[i].y, ctx);
      }
    }
  }, 50);

  // 点击添加star
  star_canvas.value!.onclick = e => {
    for (let i = 0; i < 5; i++) {
      let star = generateStar();
      star.x = e.offsetX;
      star.y = e.offsetY;
      starArr.push(star);
    }
  };
};
const initCanvas = () => {
  ctx = star_canvas.value?.getContext("2d") as CanvasRenderingContext2D;
  star_canvas.value!.width = 1000;
  star_canvas.value!.height = 800;
  initStar();
};
onMounted(() => {
  initCanvas();
});
onBeforeUnmount(() => {
  clearInterval(timer);
});
</script>

<style lang="less" scoped>
#star_canvas {
  background-color: #333333;
}
</style>
  • index.ts
export interface Star {
  x: number;
  y: number;
  r: number;
  speedX: number;
  speedY: number;
}

export const generateStar = () => {
  const star: Star = {
    x: 0,
    y: 0,
    r: 0,
    speedX: 0,
    speedY: 0,
  };
  star.x = Math.random() * 1000;
  star.y = Math.random() * 800;
  star.r = Math.random() * 5;
  star.speedX = randomSpeed();
  star.speedY = randomSpeed();
  return star;
};

export const randomSpeed = () => {
  return Math.random() * 3 * Math.pow(-1, Math.round(Math.random()));
};

export const Draw = (ctx: CanvasRenderingContext2D, star: Star) => {
  ctx.beginPath();
  ctx.arc(star.x, star.y, star.r, 0, Math.PI * 2);
  ctx.fill();
  ctx.closePath();
};

export const Move = (star: Star) => {
  star.x -= star.speedX;
  star.y -= star.speedY;
  if (star.x < 0 || star.x > 1000) {
    star.speedX *= -1;
  }
  if (star.y < 0 || star.y > 800) {
    star.speedY *= -1;
  }
};

export const DrawLine = (startX: number, startY: number, endX: number, endY: number, ctx: CanvasRenderingContext2D) => {
  ctx.beginPath();
  ctx.moveTo(startX, startY);
  ctx.lineTo(endX, endY);
  ctx.stroke();
  ctx.closePath();
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容