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

2022-08-07  本文已影响0人  Ringo_

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.全部代码

<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>
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();
};
上一篇下一篇

猜你喜欢

热点阅读