庆祝新年?画一颗圣诞树?还是...

栏目: Html5 · 发布时间: 5年前

内容简介:圣诞节,元旦,看大家(情侣)在朋友圈里发各种庆祝的或者祝福的话语,甚是感动,然后悄悄拉黑了。作为单身狗,我们也有自己庆祝节日的方式,今天我们就来实现一些祝福的效果。需要说明的是,所有的效果都是利用

关于节日

圣诞节,元旦,看大家(情侣)在朋友圈里发各种庆祝的或者祝福的话语,甚是感动,然后悄悄拉黑了。作为单身狗,我们也有自己庆祝节日的方式,今天我们就来实现一些祝福的效果。

需要说明的是,所有的效果都是利用 canvas 来实现的。

祝福话语

庆祝新年?画一颗圣诞树?还是...

偷了朋友的图,很基本的庆祝方式,展示不同的文字,一段时间切换一次,普普通通,但是对于低像素来说,是最好的方法了,也是庆祝节日用的最多的了,我们这里做个效果多一点的版本

效果展示:

庆祝新年?画一颗圣诞树?还是...

基本原理是这样的:

  1. canvas 中把字画出来,渐变色效果,通过 canvas 的相关API获取 imageData ,就是像素点信息,同rgba。
  2. 遍历 imageData ,生成相关 dom。
  3. 设置定时,因为渲染不同的文字效果,当然,有过渡效果。

过程对应的代码:

  1. canvas 里写字,且渐变效果:

    // 像素点的单位长度
    const rectWidth =
      parseFloat(document.documentElement.style.getPropertyValue('--rect-width'));
    const canvas = document.createElement('canvas');
    canvas.width = 100;
    canvas.height = 20;
    
    const ctx = canvas.getContext('2d');
    ctx.font = '100 18px monospace';
    ctx.textBaseline = 'top'; // 设置文字基线
    ctx.textAlign = 'center';
    // 将区域内所有像素点设置成透明
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    // 渐变效果
    const gradient = ctx.createLinearGradient(10, 0, canvas.width - 10, 0);
    gradient.addColorStop(0, 'red');
    gradient.addColorStop(1 / 6, 'orange');
    gradient.addColorStop(2 / 6, 'yellow');
    gradient.addColorStop(3 / 6, 'green');
    gradient.addColorStop(4 / 6, 'blue');
    gradient.addColorStop(5 / 6, 'indigo');
    gradient.addColorStop(1, 'violet');
    ctx.fillStyle = gradient;
    
    // y设置2,是因为火狐浏览器下效果有异常...
    ctx.fillText('这是测试', canvas.width / 2, 2);
    // 插入
    document.body.appendChild(canvas);

庆祝新年?画一颗圣诞树?还是...

像素点过多会卡顿,所以这里尽量用少的点去完成效果

  1. 获取 imageData ,生成相关 dom
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// 打印一下
console.log(imageData);

庆祝新年?画一颗圣诞树?还是...

imageData 包含三个属性,data,width和height,data是一个一维数组, [[0-255], [0-255], [0-255], [0-255]] ,长度是4的倍数,4个算一小组,相当于rgba,只不过透明度范围也是 0~255 ,width和height相当于长宽,像素点数量 = (高 宽) 4

{
  let i = 2000;
  const fragment = document.createDocumentFragment();
  while (i-- > 0) {
    fragment.appendChild(document.createElement('li'));
  }
  ul.appendChild(fragment);
}
let iLi = 0;
for (let column = 0; column < imageData.width; column++) {
  for (let row = 0; row < imageData.height; row++) {
    // 第几个像素点起始位置,肯定是4的倍数
    const idx = ((row * imageData.width) + column) * 4;
    if (imageData.data[idx + 3] > 0) {
      const li = ul.children[iLi++];
      li.style.opacity = '1';
      // 观察css你会发现,所有显示的点初始位置都是在中心
      li.style.transform = `translate(
        ${column * rectWidth}px,
        ${row * rectWidth}px)
        scale(1.5)`;
      // 这里 scale 完全是为了好看
      li.style.background =
        `rgba(${imageData.data[idx]},${imageData.data[idx + 1]},${imageData.data[idx + 2]},${imageData.data[idx + 3] / 255})`;
    }
  }
}
while (iLi < 2000) {
  const li = ul.children[iLi++];
  li.style.opacity = '0';
}

庆祝新年?画一颗圣诞树?还是...

  1. 定时器比较简单,就不写了,具体可以看源码。

注意的点,Chrome下有点卡顿,Safari和Firefox下没有卡顿,原因未知。

预览效果 -本地Chrome下打开很卡,火狐、safari正常

圣诞树

早先的时候是圣诞节的时候,看到各种用字符组成圣诞树的形式,于是自己就去试了下,还是比较简单的。

庆祝新年?画一颗圣诞树?还是...

这段用的是项目里的js代码,不过一看就是不可执行的,因为我是按照空格分割的。

需要注意的点是:

  1. 因为是处理文件,所以我们需要借助 node
  2. 怎样处理图片,生成相应的代码
  3. 如何让切割后的代码仍然可以执行

对于上面的几点,做以下分析:

关于第一点和第二点,和上面的例子一样,我们还是需要 canvas,node 环境并没有 canvas 这个 element,需要借助第三方的库 node-canvas ( npm )

例子:

庆祝新年?画一颗圣诞树?还是...

绘制好图片,我们就能像上面一样拿到需要的 ImageData ,然后就是写文件,基本上是非常简单了,写的时候考虑到 canvas 的API比较多,用了 typescript,不影响阅读,都9102年了,你可以不用,你也应该全局装以下 typescript (毕竟如今typescript已经成了社交语言,“哎呦,你也在用typescript的啊,我也在用呢~”)

先写个简单版本,用 text 格式,展示基本图形

const fs = require("fs");
const path = require('path');
const { createCanvas, loadImage } = require('canvas');

const canvas = createCanvas(80, 80)
const ctx: CanvasRenderingContext2D = canvas.getContext('2d')

async function transform(input: string, output: string) {
  const image: ImageBitmap = await loadImage(input);

  ctx.drawImage(image, 0, 0, 80, 80);

  const imageData: ImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const { width, height, data } = imageData;
  let outStr = '';

  for (let col = 0; col < height; col++) {
    for (let row = 0; row < width; row++) {
      const index = ((col * height) + row) * 4;
      const r = data[index];
      const g = data[index + 1];
      const b = data[index + 2];
      const a = data[index + 3];

      // “黑色”区间, 找的图片不是完全黑色
      if (r < 100 && g < 100 && b < 100 && a === 255) {
        outStr += '+';
      } else {
        outStr += ' ';
      }
    }

    outStr += '\n';
  }

  console.log(outStr);
  fs.writeFileSync(output, outStr);
}

transform(path.join(__dirname, '../img/tree.jpg'), path.join(__dirname, '../outputs/demo2.txt'));

效果:

庆祝新年?画一颗圣诞树?还是...

关于把js代码切割成可执行的样子,这块我想了很久,刚开始只是是想把js文件按空格切割成数组,给定一个初始的变量 start ,记录到什么位置,因为一些变量名是不能分割,但js一些语法特性不好处理,比如说

function test() {
    return 
    function aa() {}
}

function test() {
    return function aa() {}
}

完全是两个函数,后面在网上看了下,发现了芋头大大很久以前写过一篇类似的, 地址 ,有兴趣的小伙伴可以看看,这块不做过多说明,实现还是有点麻烦的

会动的字符

上面说了字符和图片,自然而然的,下面说的应该就是视频了。视频的话,也是非常简单的,因为视频是由连续的图片组成的,也就是不断变化的图片,就是所谓的“帧”。也就是,如果我们能拿到视频所有定格的图片,就能作出相应的动画效果。

需要把视频“拆成”图片,需要借助第三方的工具, ffmpeg ,功能比较强大,具体不做说明,需要安装到全局,利用 brew ,运行 brew install ffmpeg 就好了(大概,我好像是这样装的233),windows用户下载要配置环境变量之类的,自己查一下吧。

// 主要代码
const mvPath = path.join(__dirname, '../mv/bad-apple.flv');
const imgPath = path.join(__dirname, '../img');

const setTime = (t: number) => new Promise((resolve) => {
  setTimeout(() => resolve(), t);
});

try {
  void async function main() {
    let img = fs.readdirSync(imgPath);
    let len = img.length;
    if (len <= 1) {
      await execSync(`cd ${imgPath} && ffmpeg -i ${mvPath} -f image2 -vf fps=fps=30 bad-%d.png`);
      img = fs.readdirSync(imgPath);
      len = img.length;
    }
    let start = 1;
    let count = len;

    (async function inter(i: number) {
      if (i < count) {
        await transform(path.join(__dirname, `../img/bad-${i}.png`));
        await setTime(33.33);
        await inter(++i);
      }
    })(start);
  }()
} catch (err) {
  console.log(err);
}

庆祝新年?画一颗圣诞树?还是...

工具的配置非常多,文档看起来也是很麻烦,有个 npm 包, node-fluent-ffmpeg ,用着也还可以,我刚开始用了,但是感觉功能不能满足,而且使用这个包的前提是你全局安装了 ffmpeg ...

总结

GitHub源码

这个我拖了比较久,有的东西有点记不清楚,可能有些东西表达的不好,说的不是很细,一些 api 的说明我都省略了,这些 MDN 上都有,就没做过多说明,当然,你可以做些更有趣的事情, 文档 ,本来自己还想做些有趣的东西,但后面没啥时间,就没继续做下去了,希望有兴趣的朋友可以去尝试一波,还是很有意思的。

就酱,感谢阅读~


以上所述就是小编给大家介绍的《庆祝新年?画一颗圣诞树?还是...》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Data Structures and Algorithm Analysis in Java

Data Structures and Algorithm Analysis in Java

Mark A. Weiss / Pearson / 2011-11-18 / GBP 129.99

Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course wa......一起来看看 《Data Structures and Algorithm Analysis in Java》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试