粒子文字效果

其实很早之前就发过一个,但是呢很粗糙,今天有空索性重新写了一个,先看效果

页面结构就一个canvas 和 一点基本样式

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>粒子文字</title>
  <style>
    * {
      padding: 0;
      margin: 0;
    }

    body {
      position: relative;
      width: 100vw;
      height: 100vh;
      background-color: #000000;
      overflow: hidden;
    }
  </style>
</head>

<body>
  <canvas id="canvas"></canvas>
</body>
<script src="./index.js"></script>

</html>

重点是 JS 怎么写

这里把全部的JS代码分成了四块 合起来按顺序放在一个js文件就行了

第一步

//当前窗口高宽
function getWid_Hei() {
  let width, height;
  if (window.innerWidth) {
    width = window.innerWidth;
    height = window.innerHeight;
  } else if (document.compatMode === "BackCompat") {
    width = document.body.clientWidth;
    height = document.body.clientHeight;
  } else {
    width = document.documentElement.clientWidth;
    height = document.documentElement.clientHeight;
  }
  return {
    clientWidth: Math.floor(width),
    clientHeight: Math.floor(height)
  }
}
//解构得到宽高 并保存
let { clientWidth, clientHeight } = getWid_Hei();
//画布
let canvas = document.getElementById('canvas');
//初始化
canvas.width = clientWidth;
canvas.height = clientHeight;
//拿到画笔
const ctx = canvas.getContext('2d');

第二步

//先用canvas把文字画出来
ctx.font = "900 100px Arial";
//用户自己输入 文字 
let text = prompt('输入几个字 字越多所需时间越长', '');
if (text == null) {
  text = '空';
}
ctx.textAlign = "center";
//拿到画布上文字区域宽度 ctx.measureText(text).width
let text_width = ctx.measureText(text).width;
//把文字画出来
ctx.fillText(text, clientWidth / 2, clientHeight / 2);
//获取文字图片的一个图片信息 ,就是像素信息  我们要用到imgData.data  这里面是像素信息了
let imgData = ctx.getImageData((clientWidth / 2 - text_width / 2), clientHeight / 2 - 100, text_width, 120);
//清空 画布 ,不要文字显示出来
ctx.clearRect(0, 0, clientWidth, clientHeight);

第三步

//所有的粒子
let allParticle = [];
//运动中的粒子
let particle = [];
//所有到达目的地的粒子
let targetParticle = [];
//已经生成了多少粒子
let nowParticle = 0;
//装饰背景的粒子
let backParticle = [];
//最大装饰粒子数量
let maxBgParticle = 100;
//已有装饰粒子数量
let bgParticle = 0;
/**
 * 获取有用的粒子位置 因为有部分是透明的 不需要
 * 然后这些粒子都是即将运动的粒子 push 到 allParticle 中
 */
(function filterate() {
  //数组中每四位数构成一个像素点 是 rgba 的形式 找到透明度为 0 的 去掉
  for (let i = 0, max = imgData.data.length; i < max; i += 4) {
    //这个像素点的透明度
    const alpha = imgData.data[i + 3];
    //获取像素点在数组中的位置
    const x = Math.floor(i / 4) % imgData.width;
    const y = Math.floor(i / 4 / imgData.width);
    //透明度不是 0 外加 额外舍去一部分
    if (alpha && x % 3 === 0 && y % 3 === 0) {
      //数组中的位置
      allParticle.push({
        x: x + clientWidth / 2 - text_width / 2,
        y: y + clientHeight / 2
      });
    }
  }
})();
/**
 * 在画布上随机生成粒子
 */
function produceParticle() {
  //粒子全部生成后不在生成
  if (nowParticle == allParticle.length) {
    return;
  }
  //粒子的初始位置
  let sx = Math.floor(Math.random() * clientWidth);
  let sy = Math.floor(Math.random() * clientHeight);
  //粒子颜色随机
  let color = `hsl(${Math.floor(Math.random() * 360)}, 50%, 50%)`;
  //粒子移动速度
  let speed = Math.floor(Math.random() * 10 + 10) * 10;
  //粒子 x y 每次偏移量
  let speedX = (allParticle[nowParticle].x - sx) / speed;
  let speedY = (allParticle[nowParticle].y - sy) / speed;
  /*
   * 放到移动粒子数组中
   * sx sy 起点位置 zx zy 终点位置 color 粒子颜色 speedX speedY 粒子每次移动偏移量
  */
  particle.push({
    sx: sx,
    sy: sy,
    color: color,
    zx: allParticle[nowParticle].x,
    zy: allParticle[nowParticle].y,
    speedX: speedX,
    speedY: speedY,
    arrived: false
  });
  nowParticle += 1;
}
/**
 * 粒子向各自的目标移动移动
 */
function moveParticle() {
  //粒子全部到达目的地
  if (particle.length == targetParticle.length) {
    return;
  }
  //粒子移动
  for (let i = 0; i < particle.length; i++) {
    //每次移动一个偏移量
    particle[i].sx += particle[i].speedX;
    particle[i].sy += particle[i].speedY;
    //到达终点 取绝对值 1.5 的误差认为到了
    if (Math.abs(particle[i].sx - particle[i].zx) <= 1.5 && Math.abs(particle[i].sy - particle[i].zy) <= 1.5 && !particle[i].arrived) {
      //保存到达目的地的粒子信息 只需要最终位置
      targetParticle.push({
        x: particle[i].zx,
        y: particle[i].zy
      })
      particle[i].arrived = true;
      particle[i].color = 'rgba(0,0,0,0)';
      //每当一个粒子到达目的地 就生成一个装饰粒子
      noEndParticle();
    }
  }
  //装饰粒子移动
  for (let i = 0; i < backParticle.length; i++) {
    //每次移动一个偏移量
    backParticle[i].sx += backParticle[i].speedX;
    backParticle[i].sy += backParticle[i].speedY;
    //到达终点 取绝对值 1.5 的误差认为到了
    if (Math.abs(backParticle[i].sx - backParticle[i].zx) <= 1.5 && Math.abs(backParticle[i].sy - backParticle[i].zy) <= 1.5) {
      backParticle.splice(i, 1);
      bgParticle -= 1;
      i -= 1;
      noEndParticle();
    }
  }
}
/**
 * 绘画粒子
 * 画出在移动的粒子
 * 画出到达目的地的粒子
 */
function drawParticle() {
  //清空画布
  ctx.clearRect(0, 0, clientWidth, clientHeight);
  //如果粒子全部到达目的地
  if (particle.length == targetParticle.length) {
    particle = [];
  }
  //在移动的粒子
  for (let i = 0; i < particle.length; i++) {
    ctx.beginPath();
    ctx.fillStyle = particle[i].color;
    ctx.arc(particle[i].sx, particle[i].sy, 5, 0, 2 * Math.PI, false);
    ctx.fill();
    ctx.closePath();
  }
  //到达目的地的粒子
  for (let i = 0; i < targetParticle.length; i++) {
    ctx.beginPath();
    ctx.fillStyle = 'orange';
    ctx.fillRect(targetParticle[i].x, targetParticle[i].y, 2, 2);
    ctx.closePath();
  }
  //装饰粒子
  for (let i = 0; i < backParticle.length; i++) {
    ctx.beginPath();
    ctx.fillStyle = backParticle[i].color;
    ctx.arc(backParticle[i].sx, backParticle[i].sy, 5, 0, 2 * Math.PI, false);
    ctx.fill();
    ctx.closePath();
  }
}
/**
 * 移动粒子到达目的地后 生成一个漫无目的的粒子 装饰背景
 */
function noEndParticle() {
  //到达最大装饰粒子数量不再生成
  if (bgParticle == maxBgParticle) {
    return;
  }
  //粒子的初始位置
  let sx = Math.floor(Math.random() * clientWidth);
  let sy = Math.floor(Math.random() * clientHeight);
  //粒子的终点位置
  let zx = Math.floor(Math.random() * clientWidth);
  let zy = Math.floor(Math.random() * clientHeight);
  //粒子颜色随机
  let color = `hsl(${Math.floor(Math.random() * 360)}, 50%, 50%)`;
  //粒子移动速度
  let speed = Math.floor(Math.random() * 10 + 10) * 10;
  //粒子 x y 每次偏移量
  let speedX = (zx - sx) / speed;
  let speedY = (zy - sy) / speed;
  /*
   * 放到移动粒子数组中
   * sx sy 起点位置 zx zy 终点位置 color 粒子颜色 speedX speedY 粒子每次移动偏移量
  */
  backParticle.push({
    sx: sx,
    sy: sy,
    color: color,
    zx: zx,
    zy: zy,
    speedX: speedX,
    speedY: speedY,
  });
  bgParticle += 1;
}

第四步

这里用的是 请求关键帧 用定时器也可以

//开始
(function init() {
  produceParticle();
  moveParticle();
  drawParticle();

  requestAnimationFrame(init);
})();

OK 感谢观看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值