国庆微信头像DIY:轻松打造个性化头像
前言
国庆节马上要到了,今天就教你如何从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来绘制图片以及文字,由于各个模版的合成逻辑不一样,这里就不全部展示了,但整体上的合成流程是一样
// 模版4
const 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; // 获取设备的devicePixelRatio
canvas.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,刚开始我还以为是打包路径配置的有问题,但如果是打包路径的问题的话也不会只有这一个文件有问题。

最终,我在vite的issues中找到了答案

简单点讲就是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
- 源码:公众号回复国庆快乐即可获取
国庆微信头像DIY:轻松打造个性化头像的更多相关文章
- android 实现类似微信缓存和即时更新好友头像
引言 使用微信时我们会发现,首次进入微信的好友列表时,会加载好友头像,但是再次进入时,就不用重新加载了,而且其他页面都不用重新加载,说明微信的好友头像是缓存在本地的,然后好友修改头像后,又会及时的更新 ...
- 微信小程序-获取用户头像信息以及修改用户头像
这里主要用到button的open-type功能,官网已有说明: 给button设置open-type="chooseAvatar",来使bindchooseavatar方法生效, ...
- js会员头像上传拖动处理头像类
js会员头像上传拖动处理头像类 点击下载源码文件
- 用WPF窗体打造个性化界面的图片浏览器
原文:用WPF窗体打造个性化界面的图片浏览器 本文使用WPF窗体(XAML及C#)与Win Form控件(FolderBrowserDialog)结合的方式, 演示制作了一个简易漂亮的WPF图片浏览器 ...
- 用WPF轻松打造iTunes CoverFlow效果
原文:用WPF轻松打造iTunes CoverFlow效果 用WPF轻松打造iTunes CoverFlow效果 ...
- 【Microsoft Azure 的1024种玩法】八. 基于Azure云端轻松打造一款好用的私有云笔记
[简介] Leanote一款开源云笔记软件,它使用Go的Web框架revel和MongoDB开发完成的,其是目前为止发现的最有bigger的云笔记,它支持markdown输入,代码高亮,多人协作,笔记 ...
- android头像上传(获取头像加剪切)
因为项目中需要用到头像上传的功能,所以就下个Ddmo先来实现下. demo我是类似仿微信的,在一个GridView中展示所有的图片,其中第一个item可以去照相:获取到图片后再进行剪切. 图片的剪切是 ...
- IT人如何打造个性化的个人网站(在线简历)
前言 众所周知,IT行业人员在求职时,如果拥有自己的技术博客和个人网站多少是可以加些分的,因为这也是IT人的技术证明之一.内容丰富的技术博客就不必多少了,往往技术博客大神市场上多是供不应求的,而且技术 ...
- piwik获取访客头像,自定义显示访问者头像(URL)和描述(标题和替代)
访客头像 自定义显示访问者头像(URL)和描述(标题和替代) 链接地址:https://plugins.matomo.org/VisitorAvatar#description
- 在U盘打造个性化PE工具箱+KALI(Persistence)+存储的工作站
基本工具: kali-linux-2018.2-amd64 原版镜像:https://www.kali.org/downloadsWin32DiskImager yunfile 下载较慢,建议自行百度 ...
随机推荐
- bulkWrite探秘
MongoDB有很多有趣的内置方法,其中为了批量处理一些写入操作,并且可以按照一定顺序执行,自从3.2版本之后提供了该批量方法:bulkWrite. 它的语法很简单: db.collection.bu ...
- JuiceFS 社区版 v1.1- Beta 发布,新增五个实用功能
我们很高兴地宣布 JuiceFS v1.1-Beta 版本正式发布啦!这是一个功能丰富的版本,带来了许多实用的新功能和改进.在这个版本中我们新增了以下功能: 目录配额:为目录设置配额限制,控制其大小和 ...
- 深入探索C++对象模型(Inside the C++ object model) -- 摘阅笔记(关于对象 - esp 1)
Object Lessons 关于对象 在C语言中,"数据"和"处理数据的操作(函数)"是分开声明的,也就是说 ,语言本身并没有支持"数据和函数&qu ...
- 【Netty实战】1~3章学习笔记
1. Netty总体结构 1.1 Netty简介 Netty是一款用于创建高性能网络应用程序的高级框架.它的基于 Java NIO 的异步的和事件驱动的实现,保证了高负载下应用程序性能的最大化和可 ...
- 使用Git进行版本控制和协作:代码共享、协作和版本管理
目录 引言 Git 是一款开源的分布式版本控制系统,它已经成为了现代软件开发中必不可少的工具之一.在这篇文章中,我们将介绍如何使用 Git 进行版本控制和协作,以实现代码共享.协作和版本管理.Git ...
- 大模型微调技术LoRA与QLoRA
LoRA: Low-Rank Adaptation of Large Language Models 动机 大模型的参数量都在100B级别,由于算力的吃紧,在这个基础上进行所有参数的微调变得不可能.L ...
- 在linux上启动arthas报“Can not find java process”
发生背景 完整报错信息: [***@localhost ~]$ java -jar arthas-boot.jar [INFO] JAVA_HOME: /usr/lib/jvm/java-1.8.0- ...
- SQL ERVER 表转化为C#实体(SQL 代码)
本文推出SqlServer表转化为实体的sql代码 在VS中有可以自带生成实体类的快捷操作,但是生成的代码比较杂乱,很多东西都是不需要的,一个一个去敲又很浪费时间,关键太无聊了 在闲暇之余写一份代码供 ...
- C#中IsNullOrEmpty和IsNullOrWhiteSpace的使用方法有什么区别?
前言 今天我们将探讨C#中两个常用的字符串处理方法:IsNullOrEmpty和IsNullOrWhiteSpace.这两个方法在处理字符串时非常常见,但是它们之间存在一些细微的区别.在本文中,我们将 ...
- Federated Learning001
联邦学习--笔记001 2022.11.16周三 今天学习了联邦学习的开山之作---Communication-Efficient Learning of Deep Networks from Dec ...