资讯专栏INFORMATION COLUMN

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

k00baa / 2836人阅读

摘要:关于节日圣诞节,元旦,看大家情侣在朋友圈里发各种庆祝的或者祝福的话语,甚是感动,然后悄悄拉黑了。预览效果本地下打开很卡,火狐正常圣诞树早先的时候是圣诞节的时候,看到各种用字符组成圣诞树的形式,于是自己就去试了下,还是比较简单的。

关于节日

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

</>复制代码

  1. 需要说明的是,所有的效果都是利用canvas来实现的。
祝福话语

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

基本原理是这样的:

canvas中把字画出来,渐变色效果,通过canvas的相关API获取imageData,就是像素点信息,同rgba。

遍历imageData,生成相关 dom。

设置定时,因为渲染不同的文字效果,当然,有过渡效果。

过程对应的代码:

canvas里写字,且渐变效果:

</>复制代码

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


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

获取imageData,生成相关 dom

</>复制代码

  1. const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  2. // 打印一下
  3. console.log(imageData);


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

</>复制代码

  1. {
  2. let i = 2000;
  3. const fragment = document.createDocumentFragment();
  4. while (i-- > 0) {
  5. fragment.appendChild(document.createElement("li"));
  6. }
  7. ul.appendChild(fragment);
  8. }
  9. let iLi = 0;
  10. for (let column = 0; column < imageData.width; column++) {
  11. for (let row = 0; row < imageData.height; row++) {
  12. // 第几个像素点起始位置,肯定是4的倍数
  13. const idx = ((row * imageData.width) + column) * 4;
  14. if (imageData.data[idx + 3] > 0) {
  15. const li = ul.children[iLi++];
  16. li.style.opacity = "1";
  17. // 观察css你会发现,所有显示的点初始位置都是在中心
  18. li.style.transform = `translate(
  19. ${column * rectWidth}px,
  20. ${row * rectWidth}px)
  21. scale(1.5)`;
  22. // 这里 scale 完全是为了好看
  23. li.style.background =
  24. `rgba(${imageData.data[idx]},${imageData.data[idx + 1]},${imageData.data[idx + 2]},${imageData.data[idx + 3] / 255})`;
  25. }
  26. }
  27. }
  28. while (iLi < 2000) {
  29. const li = ul.children[iLi++];
  30. li.style.opacity = "0";
  31. }

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

</>复制代码

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

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

圣诞树

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

</>复制代码

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

需要注意的点是:

因为是处理文件,所以我们需要借助 node

怎样处理图片,生成相应的代码

如何让切割后的代码仍然可以执行

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

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


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

</>复制代码

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

</>复制代码

  1. const fs = require("fs");
  2. const path = require("path");
  3. const { createCanvas, loadImage } = require("canvas");
  4. const canvas = createCanvas(80, 80)
  5. const ctx: CanvasRenderingContext2D = canvas.getContext("2d")
  6. async function transform(input: string, output: string) {
  7. const image: ImageBitmap = await loadImage(input);
  8. ctx.drawImage(image, 0, 0, 80, 80);
  9. const imageData: ImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  10. const { width, height, data } = imageData;
  11. let outStr = "";
  12. for (let col = 0; col < height; col++) {
  13. for (let row = 0; row < width; row++) {
  14. const index = ((col * height) + row) * 4;
  15. const r = data[index];
  16. const g = data[index + 1];
  17. const b = data[index + 2];
  18. const a = data[index + 3];
  19. // “黑色”区间, 找的图片不是完全黑色
  20. if (r < 100 && g < 100 && b < 100 && a === 255) {
  21. outStr += "+";
  22. } else {
  23. outStr += " ";
  24. }
  25. }
  26. outStr += "
  27. ";
  28. }
  29. console.log(outStr);
  30. fs.writeFileSync(output, outStr);
  31. }
  32. transform(path.join(__dirname, "../img/tree.jpg"), path.join(__dirname, "../outputs/demo2.txt"));

效果:

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

</>复制代码

  1. function test() {
  2. return
  3. function aa() {}
  4. }

</>复制代码

  1. function test() {
  2. return function aa() {}
  3. }

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

会动的字符

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

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

</>复制代码

  1. // 主要代码
  2. const mvPath = path.join(__dirname, "../mv/bad-apple.flv");
  3. const imgPath = path.join(__dirname, "../img");
  4. const setTime = (t: number) => new Promise((resolve) => {
  5. setTimeout(() => resolve(), t);
  6. });
  7. try {
  8. void async function main() {
  9. let img = fs.readdirSync(imgPath);
  10. let len = img.length;
  11. if (len <= 1) {
  12. await execSync(`cd ${imgPath} && ffmpeg -i ${mvPath} -f image2 -vf fps=fps=30 bad-%d.png`);
  13. img = fs.readdirSync(imgPath);
  14. len = img.length;
  15. }
  16. let start = 1;
  17. let count = len;
  18. (async function inter(i: number) {
  19. if (i < count) {
  20. await transform(path.join(__dirname, `../img/bad-${i}.png`));
  21. await setTime(33.33);
  22. await inter(++i);
  23. }
  24. })(start);
  25. }()
  26. } catch (err) {
  27. console.log(err);
  28. }


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

总结

GitHub源码

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

就酱,感谢阅读~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101260.html

相关文章

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

    摘要:关于节日圣诞节,元旦,看大家情侣在朋友圈里发各种庆祝的或者祝福的话语,甚是感动,然后悄悄拉黑了。预览效果本地下打开很卡,火狐正常圣诞树早先的时候是圣诞节的时候,看到各种用字符组成圣诞树的形式,于是自己就去试了下,还是比较简单的。 关于节日 圣诞节,元旦,看大家(情侣)在朋友圈里发各种庆祝的或者祝福的话语,甚是感动,然后悄悄拉黑了。作为单身狗,我们也有自己庆祝节日的方式,今天我们就来实现...

    cloud 评论0 收藏0
  • 庆祝新年?一颗诞树?还是...

    摘要:关于节日圣诞节,元旦,看大家情侣在朋友圈里发各种庆祝的或者祝福的话语,甚是感动,然后悄悄拉黑了。预览效果本地下打开很卡,火狐正常圣诞树早先的时候是圣诞节的时候,看到各种用字符组成圣诞树的形式,于是自己就去试了下,还是比较简单的。 关于节日 圣诞节,元旦,看大家(情侣)在朋友圈里发各种庆祝的或者祝福的话语,甚是感动,然后悄悄拉黑了。作为单身狗,我们也有自己庆祝节日的方式,今天我们就来实现...

    zhoutk 评论0 收藏0

发表评论

0条评论

k00baa

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<