前言

国庆节马上要到了,明天就教你如何从0到1应用canvas生成国庆风微信头像。

本文蕴含以下内容:

  • vue3我的项目搭建,需要剖析
  • canvas合成图片原理
  • github自动化部署
  • 开发过程遇到的问题及解决方案

文末附源码及在线体验地址~

搭建我的项目,剖析需要

我的项目的话就间接应用脚手架生成一个 Vue3 + TS我的项目

npm create vue@latest

为了不便,应用了Element PlusUI库

npm install element-plus --save

配置的话,能够查看文档,全局导入、按需导入都能够看本人的需要

我的项目搭建完后,就能够来剖析一下本次需要大略会波及哪些性能了

  • 上传头像

这是一个合成微信头像的工具,那就必须得让用户上传本人的微信头像了

  • 合成模版

为了不便,咱们当然还须要提供多种模版供用户本人抉择

  • 用户自定义内容

为了让生成的头像更具举世无双性,咱们还须要提供用户自定义内容的性能,比方:用户输出文字、抉择文字色彩等

  • 合成头像

本次需要的重点当然是合成头像了

  • 下载合成后的头像

用户合成完当然还得反对让他下载

性能开发

上传头像

<script setup lang="ts">// 用户头像const user_img = ref("");const change = (file: any, fileList: any) => {  console.log(file, fileList);  const fileReader = new FileReader();  fileReader.readAsDataURL(file.raw);  fileReader.onload = (e: any) => {    user_img.value = e.target.result;  };};// 删除用户头像const remove = () => {  user_img.value = "";};</script>

这部分比较简单,次要是用户上传本人的微信头像后再进行展现,UI局部就不贴了,前面有源码。

合成模版

合成模版局部,这里次要是须要思考各个模版所须要的合成性能有哪些

<script>const gqList = ref([  {    id: 1,    name: "模版1",    img: getImg("gq0", "jpg"),    template: getImg("tem1"),    has: ["text"],    textLabel: "请输出你的姓",    desc: "最多输出1个字",    text: "宋",    textLength: 1,  },  {    id: 2,    name: "模版2",    img: getImg("gq1", "jpg"),    template: getImg("tem2"),  },  {    id: 3,    name: "模版3",    img: getImg("gq2", "jpg"),    template: getImg("tem3"),  },  {    id: 4,    name: "模版4",    img: getImg("gq3", "jpg"),    template: getImg("tem4"),    has: ["text"],    textLabel: "请输出祝福语",    textColor: "#FED800",    text: "生在国旗下,长在春风里",    desc: "最多输出12个字, 请用中文逗号隔开",    textLength: 12,  },  { id: 5, name: "模版5", img: getImg("gq4"), template: getImg("tem5") },  {    id: 6,    name: "模版6",    img: getImg("gq5", "jpg"),    template: getImg("tem6"),    has: ["text"],    textLabel: "请输出祝福语",    textColor: "#FED800",    desc: "最多输出12个字, 请用中文逗号隔开",    text: "不负韶华,只争朝夕",    textLength: 12,  },  { id: 7, name: "模版7", img: getImg("gq6"), template: getImg("tem7") },]);const template_id = ref(1);// 抉择模版const gqChange = (val: any) => {  console.log(val);  template_id.value = val;  generateImgRef.value.clear();  generateImgRef.value.init();};</script>

合成图片

这里其实也不难,次要是应用canvas来绘制图片以及文字,因为各个模版的合成逻辑不一样,这里就不全副展现了,但整体上的合成流程是一样

// 模版4const drawImg4 = (ctx: any) => {  const img = new Image();  img.src = user_img.value;  const gqImg = new Image();  gqImg.src = gqList.value[template_id.value - 1].img;  img.onload = () => {    ctx.drawImage(img, 0, 0, 300, 300); // 绘制头像    gqImg.onload = () => {      ctx.drawImage(gqImg, 0, 0, 300, 300); // 绘制国庆图      ctx.fillStyle = textColor.value; // 设置文字色彩      ctx.font = "20px kaiti"; // 设置文字大小及字体      const textList = text.value?.split(",") ?? []; // 以中文逗号宰割文字      textList.forEach((item: string, i: number) => {        drawVerticalText(ctx, item ?? "", 20 + i * 20, 186 + i * 20, {          size: 20,        }); // 绘制文字      });    };    canDownload.value = true; // 合成实现  };};

这里次要的难点在于canvas默认不反对文字竖排绘制,所以这里须要非凡解决,原理其实就是遍历文字,计算文字高度,而后再一个一个去绘制

// 文字竖排const drawVerticalText = (  context: any,  text: string,  x: number,  y: number,  font: any) => {  context.save();  context.font = font;  for (var i = 0; i < text.length; i++) {    context.fillText(text[i], x, y + i * font.size);  }  context.restore();};

下载图片

这里次要是借助a标签的download属性,这里在手机上有点坑,前面会提到...

const downloadImg = () => {  if (!canDownload.value) {    ElMessage({      message: "请先合成头像~",      type: "warning",    });    return;  }  const url = canvas.value.toDataURL("image/png");  const a = document.createElement("a");  a.href = url;  a.download = "国庆头像.png";  a.click();};

自动化部署

这里其实之前有写过文章,次要是应用github action来实现

搭建完就是这样的,咱们写完代码只须要将代码提交下来就可能主动打包部署了

对这个不理解的能够去看我之前的文章:应用GitHub Actions实现自动化部署

体验

开发部署完就能够来体验一下了:体验地址

PC上体验下来,成果还能够。

问题及解决方案

开发过程中也遇到一些问题,来看看是如何解决的吧

保留图片不清晰

canvas绘制图片不清晰的起因次要是:

  • 图片被放大或放大
  • 图片没处于残缺像素的地位

因为canvas是点阵图,由一个个像素组成,当图像被放大时,一个像素会被强形拉伸至一个以上,多进去的像素平均的分部在图像中,计算机为了使拉伸后的图像看起来平滑,会给这些多进去的像素计算出一个过渡色,放大图像时,多个像素合成一个像素,计算机会用这多个像素的色调值计算出一个过渡色来填充这个像素,不论是放大还是放大,都会造成图像原有像素信息失落。

所以只须要加上以下代码就能解决

const dpr = window.devicePixelRatio || 1; // 获取设施的devicePixelRatiocanvas.value.width = 300 * dpr; // 画布宽高放大dpr倍,绘制后再放大dpr倍,解决含糊问题canvas.value.height = 300 * dpr; // 画布宽高放大dpr倍,绘制后再放大dpr倍,解决含糊问题canvas.value.style.width = "300px"; // 显示高canvas.value.style.height = "300px"; // 显示高ctx.value.scale(dpr, dpr); // 按比例缩放画布,解决含糊问题

优化完,清晰度晋升还是非常明显的

挪动端体验问题

手机上下载图片会失败,这次要是因为blob格局在手机上不能下载,base64格局有点大,那就只能上传CDN再进行下载了?

不须要,咱们能够利用手机上的长按图片保留来实现

const downloadImg = () => {  if (!canDownload.value) {    ElMessage({      message: "请先合成头像~",      type: "warning",    });    return;  }  const url = canvas.value.toDataURL("image/png");  if (devices.some((item) => ua.includes(item))) {    ElMessageBox.alert(      `    请长按图片保留    <img src="${url}" style="width: 100%;height: 100%;object-fit: contain;" />    `,      "保留图片",      {        dangerouslyUseHTMLString: true,      }    );    return;  }  const a = document.createElement("a");  a.href = url;  a.download = "国庆头像.png";  a.click();};

打包部署问题

打包生成的_plugin-vue_export-helper.cdc0426e.js文件拜访404,刚开始我还认为是打包门路配置的有问题,但如果是打包门路的问题的话也不会只有这一个文件有问题。

最终,我在viteissues中找到了答案

简略点讲就是Github Pages 阻止了以下划线字符结尾的文件,所以会导致这个文件拜访返回404.

解决办法就是批改打包逻辑:

const INVALID_CHAR_REGEX = /[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g;const DRIVE_LETTER_REGEX = /^[a-z]:/i;build: {    outDir: "dist",    assetsDir: "assets",    chunkSizeWarningLimit: 2000, // 解决包大小超过500kb的正告    rollupOptions: {      output: {        manualChunks: {          // elementPlus: ["element-plus"],          // highlightjs: ["highlight.js"],        },        chunkFileNames: "assets/[name]-[hash].js",        entryFileNames: "assets/[name]-[hash].js",        assetFileNames: "assets/[name]-[hash].[ext]",        sanitizeFileName: (name) => {          const match = DRIVE_LETTER_REGEX.exec(name);          const driveLetter = match ? match[0] : "";          return (            driveLetter +            name.slice(driveLetter.length).replace(INVALID_CHAR_REGEX, "") // 解决文件名中的非法字符          );        },      },    },  },

vite.config.ts中加上以上代码,从新提交部署就能够了。

最初

整个内容到这里就完结了

  • 体验地址:https://bettersong.github.io/nanjiu-tools/#/generate_image
  • 源码:公众号回复国庆高兴即可获取