创意绘制:基于 Canvas 的字符画生成,让龙动起来!

在本篇文章中,我将分享如何使用 Canvas 和 JavaScript 创建一个独特的字符画生成器。通过此生成器,我们可以将图片转换为由字符构成的作品,并通过一些JavaScript属性让这些字符动起来。结合现代的 Web 技术,我们在网页上动态地生成字符画,并且通过添加一些动画效果,使其更富有趣味性。

准备工作

首先,我们需要一些基础的 HTML 结构和 JavaScript 代码,用于加载图片并创建 Canvas。

html 复制代码
<body>
  <div class="glass">
    <img id="myImg" src="./xxx.png" style="display: none;"></img>
    <div id="canvasDiv">
      <canvas id="canvas" width="600" height="600" style="display: none;"></canvas>
    </div>
    <div id="textDiv" class="textDiv" style="line-height: 0.9;"></div>
  </div>
  <script>
    // ...(后续代码将在下文详细介绍)
  </script>
</body>

图片和canvas都不需要展示,设置它们为display: none;

图片加载与转换

window.onload 事件中,我们加载图片并将其绘制到 Canvas 上。为了保持生成的字符画不失真,我们需要进行一些调整。

javascript 复制代码
window.onload = () => {
     canvas = document.getElementById("canvas");
     ctx = canvas.getContext("2d");
     const img = document.querySelector('#myImg');
     changeImg(img);
     let newimgText = imgText.split('</br>').map((e) => "<div class='amiDiv' style='display:flex'>" + e + "</div>") .join('');
      document.getElementById("textDiv").innerHTML = newimgText;
};

首先获取canvas标签,并且创建一个2D渲染上下文ctx,以便后续使用。接下来,该代码获取图片,并且将其作为参数传递给changeImg函数,该函数将会对这个图片进行处理,将其转换为字符画并将结果存储在全局变量imgText中。然后,定义新的字符串变量newimgText,将imgText字符串每一行分隔开,方便一会儿分别给每一行添加动态效果。

Canvas 操作与字符生成

changeImg 函数中,我们使用 Canvas 获取图片像素信息,并逐个读取每个像素的RGB值,计算出对应的灰度值并映射到字符上。同时,我们也可以调整字符大小控制显示图像的精度。

javascript 复制代码
function changeImg(img) {
      // 清空字符画
      imgText = "";
      // 先记录比率,如果宽,那么先缩放宽,再用比率算出长,反之同理,这里是保证不超过不超过设定规模
      let rate = img.width / img.height;
      if (rate > 1) {
        img.style.width = IMAGE_SIZE + 'px';
        img.style.height = IMAGE_SIZE / rate + 'px';
        img.width = IMAGE_SIZE;
        img.height = IMAGE_SIZE / rate;
      } else {
        img.style.height = IMAGE_SIZE + 'px';
        img.style.width = IMAGE_SIZE * rate + 'px';
        img.height = IMAGE_SIZE;
        img.width = IMAGE_SIZE * rate;
      }

      ctx.clearRect(0, 0, canvas.width, canvas.height)
      // 将图片绘制在canvas上
      ctx.drawImage(img, 0, 0, img.width, img.height);

      // 获取像素信息
      let pixelInfo;
      try {
        pixelInfo = ctx.getImageData(0, 0, img.width, img.height);
      } catch {
        console("图片有问题!");
      }
      // 数值越小,精度越高
      let size = 2;
      // 设置zoom缩放
      document.querySelector("#textDiv").style.zoom = size / 6;
      // 生成主体,逐个读取字符
      for (let i = 0; i < img.height; i = i + size) {
        for (let j = 0; j < img.width; j = j + size) {
          const curPoint = (i * img.width + j) * 4; // ×4是因为,1为r,2为g,3为b,4为a,四个是一组rgba值
          const [r, g, b] = pixelInfo.data.slice(curPoint, curPoint + 3);
          const gray = r * 0.3 + g * 0.6 + b * 0.1; // 计算灰度值
          const color = `rgba(${r},${g},${b})`; // 保存像素点rgb值
          toText(gray, color)
        }
        imgText += "</br>";
      }
    }

拼接字符画

将字符拼接成一行一行的文本。

javascript 复制代码
    function toText(g, color) {
      if (color) imgText += `<span style='color:${color}'>`;
      if (g == 255) imgText += "&nbsp;&nbsp;&nbsp;";
      else imgText += "@";
      if (color) imgText += "</span>";
    }

添加动态效果

最后,使用JavaScript的正弦函数来控制每个文字元素的位置和偏移量,从而实现字符画龙在页面上的动态效果。

javascript 复制代码
  const container = document.querySelectorAll(".amiDiv");

  // 创建曲线运动
  function createCurve(func, range) {
    container.forEach(e => {
      const points = getCurvePoints(func, range, e.children.length, e.clientWidth);
      for (let i = 0; i < points.length; i++) {
        e.children[i].style.transform = `translateY(${points[i]}px)`;
      }
    });
  }

  let offset = 0;
  createCurve((x) => Math.sin(x), [offset, offset + 2 * Math.PI]);

  setInterval(() => {
    offset += 0.1;
    createCurve((x) => Math.sin(x), [offset, offset + 2 * Math.PI]);
  }, 30);

  // 辅助函数:获取曲线上的点

  function getCurvePoints(curveFunc, range, number, xLength) {
    if (number < 1) {
      return [];
    }
    if (number === 1) {
      return [0];
    }
    const piece = (range[1] - range[0]) / (number - 1);
    const result = [];
    const scale = xLength / (range[1] - range[0]);
    for (let i = 0; i < number; i++) {
      result.push(-curveFunc(i * piece + range[0]) * scale);
    }
    return result;
  }

结语

在这个龙年,愿我们的代码如同神龙一般威猛,技术如同飞龙一般翱翔,最后祝大家新的一年:龙行龘龘,前程朤朤!🐉✨

灵感来源1

灵感来源2

相关推荐
To_OC4 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
To_OC5 小时前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
vipbic5 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy5 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志5 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble7 小时前
c端系统,其实更像一个信息展示平台
前端
你怎么知道我是队长7 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
李姆斯7 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜8 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司8 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能