文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JavaScript+Canvas实现文字粒子流特效

2023-01-14 12:01

关注

动手前思考

首先要在特定的位置生成粒子,要获取到canvas上像素的点位,通过canvas的getImageData函数我们可以得到canvas像素点的信息,获取像素点中透明度大于0的位置。

绘制文字

新建一个canvas画布,在画布上绘制任意的文字

ctx.font = "200px Arial";
ctx.fontWeight = "900";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillStyle = "red";
ctx.fillText("稀土掘金", 0, 0);

获取像素点位

canvas的getImageData函数返回了imageData对象,该对象复制画布上指定矩形的像素数据,数组中四个值一组存放像素的RGBA信息,其中A代表透明度,当透明度大于0时表示该位置上可以生成粒子。

对于 ImageData 对象中的每个像素,都存在着四方面的信息,即 RGBA 值:

也就是

red=imgData.data[0];
green=imgData.data[1];
blue=imgData.data[2];
alpha=imgData.data[3];

调用getImageData函数,有4个参数,x/y表示开始复制的左上角位置的xy坐标,width/height表示将要复制的矩形区域的宽度和高度。

var imgData=context.getImageData(x,y,width,height);

imageData对象信息如下:

如果绘制文字较小最好截取所在矩形位置,以减少获取的data数组。获取canvas绘制文字的宽度方法如下:

const { width } = ctx.measureText(this.text);

遍历数组,得到可用像素点:

const gap = 4;
for (let i = 0, wl = this.canvas.width * gap; i < length; i += gap) {
    if (data[i + gap - 1]) {
      // 根据透明度判断
      const x = (i % wl) / gap;
      const y = parseInt(i / wl);
      this.textPoints.push([x, y]);
    }
}

渲染粒子

获取到像素点位textPoints数据之后就可以开始渲染粒子了

let startX;
let startY;
const points = [];
for (let i = 0; i < this.textPoints.length; i++) {
    let point = this.textPoints[i];
    let x = point[0];
    let y = point[1];
    const radius = 5;
    // const radius = Math.random() * 10; // 随机生成粒子宽度
    const color = parseInt(Math.random() * 0xffffff).toString(16); // 随机生成粒子颜色
    const { x: x0, y: y0 } = this.adjustPoint(x, y); // 矫正粒子相对于画布所在位置
    if (i == 0 || ((x - startX) % 10 == 0 && (y - startY) % 10 == 0)) {
      startX = x;
      startY = y;
      const params = { x: x0 + radius, y: y0, radius, color };
      points.push(params);
    }
}

优化展示效果

this.points = points.sort((a, b) => (Math.random() > 0.5 ? -1 : 1)); // 随机排序
adjustPoint(x, y) {
  const { width, height } = this.canvasLizi;
  return {
    x: x + (width - this.textWidth) / 2,
    y: y + (height - this.textSize) / 2,
  };
}

简单的动画效果

1、随机选择四个方向中的某一个方向,生成初始坐标

for (let item of this.points) {
    let direction;
    const num = Math.random() * 1;
    if (num < 0.25) {
      direction = "left";
      item.initX = 0;
      item.initY = Math.random() * height;
    } else if (num < 0.5) {
      direction = "right";
      item.initX = width;
      item.initY = Math.random() * height;
    } else if (num < 0.75) {
      direction = "top";
      item.initX = Math.random() * width;
      item.initY = 0;
    } else {
      direction = "bottom";
      item.initX = Math.random() * width;
      item.initY = height;
    }
}

2、从初始位置运动到实际位置

animatDot() {
  if (!this.points.find((item) => item.hasOwnProperty("initX"))) {
    // 当不存在运动点时取消动画
    cancelAnimationFrame(this.animatDot.bind(this));
    return;
  }
  this.points.forEach((item) => {
    const offsetX = item.x - item.initX;
    const offsetY = item.y - item.initY;
    if (Math.abs(offsetX) > 0 || Math.abs(offsetY) > 0) {
      const rate = offsetX / 10; // 速率等于坐标差除以10,不断缩小运动距离
      const x = item.initX + rate;
      if (Math.abs(rate) < 1) {
        item.initX = item.x; // 当运动距离小于1时,等于实际坐标
      } else {
        if (offsetX > 0) {
          item.initX = x < item.x ? x : item.x;
        } else {
          item.initX = x > item.x ? x : item.x;
        }
      }
      const k = offsetY / offsetX; // 计算斜率
      const y = k * item.initX;
      if (offsetY > 0) {
        item.initY = y < item.y ? y : item.y;
      } else {
        item.initY = y > item.y ? y : item.y;
      }
    } else {
      delete item.initX; // 当运动点坐标和实际坐标相同时,删除初始坐标
      delete item.initY;
    }
  });
  this.drawPoint(); // 绘制粒子函数
  requestAnimationFrame(this.animatDot.bind(this), 1000 / 60);
}

3、点击文字炸开的特效

this.clickRange = 30; // 点击范围
this.spreadRange = 30; // 扩散范围
PBomb() {
  const that = this;
  function animaion(time) {
    that.PBomb();
  }
  if (!this.points.find((item) => item.bomb)) {
    cancelAnimationFrame(animaion); // 停止动画判断
    return;
  }
  const step = 10; // x轴步长,每帧增加或减少的大小
  this.points.forEach((point) => {
    if (point.bomb) {
      if (point.bombX > this.clickPointX) {
        if (point.bombX < point.x + this.spreadRange) {
          point.bombX += step;
        } else {
          point.bomb = false;
        }
      }
      if (point.bombX < this.clickPointX) {
        if (point.bombX > point.x - this.spreadRange) {
          point.bombX -= step;
        } else {
          point.bomb = false;
        }
      }
      const k =
        point.x - this.clickPointX == 0
          ? 1
          : Math.abs(
              (point.y - this.clickPointY) /
                (point.x - this.clickPointX),
            ).toFixed(2); // 计算斜率
      if (point.bombY > this.clickPointY) {
        if (point.bombY < point.y + this.spreadRange) {
          point.bombY += k * step;
        } else {
          point.bomb = false;
        }
      }
      if (point.bombY < this.clickPointY) {
        if (point.bombY > point.y - this.spreadRange) {
          point.bombY -= k * step;
        } else {
          point.bomb = false;
        }
      }
    }
  });
  this.drawPoint();
  setTimeout(() => {
    requestAnimationFrame(animaion);
  }, 1000 / 10);
}

最终效果:

最后加上修改文字内容和大小的功能就完美了

到此这篇关于JavaScript+Canvas实现文字粒子流特效的文章就介绍到这了,更多相关JavaScript Canvas文字粒子流内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯