前言
前几天大家都在朋友圈@微信官方,求赐一面国旗,没有得到微信官方的小盆友们,有没有很失落呀~心血来潮的我,决定用js来写一个给头像加国旗的功能,然后我就送了自己一面国旗,事实证明,想要的东西还是只能靠自己的双手去创造。哈哈哈,这鸡汤洒的触不及防~,回归正题,下面就一起来看看我是怎么实现的吧~~
实现过程
理清思路
其实给头像加国旗的实现原理很简单,就是拿到你的图片资源(就是你的头像),用canvas绘制该图片,然后在图片的右下角再绘制一个小国旗,这样基础的功能就实现了。下面我们来一步步实现给头像加国旗的功能。源码地址
应评论区需求,我这里贴一个代码处理后的头像的效果图(图像是有一个白色圆角边框的,可能会与博客的白色背景重叠,审美不太好,凑合看哈~).
我把我开发该功能的整个思考流程梳理如下:
- 创建input标签,定义onchange事件,读取图片资源,拿到头像的base64编码;
- 创建canvas标签,网上找一个国旗的图片,同样拿到该图片的base64编码,依次使用canvas的drawImage api把头像和国旗绘制在特定位置。
- 直角的头像和国旗太生硬,于是写了个方法给头像和国旗加了一个白色的圆角边框。
- 写死画布的高度会导致图片被拉伸,所以我希望固定画布的宽度,画布的高度根据图片资深的宽高比,和已知的宽度,把按比例计算出来的高度设置为画布的高度。因为国旗的绘制区域是相对于画布的右下角的位置,所以需要在画布高度确定之后(也就是头像绘制完成之后)再进行绘制。
- 把canvas绘制好的图片下载,编写下载图片的逻辑。
开始编码
html代码:
<div>
<div><button class="input-container">
上传头像
<input accept="image/gif,image/jpeg,image/png"
class="file-input" id="fileInput" type="file" />
</button>
<button class="save-btn" onclick="downLoad()">保存图片</button>
</div>
<canvas id="canvas" class="canvas"></canvas>
</div>
js代码:
// 这一过程是定义一些可配置的参数,以便根据需要做修改
const options = {
avatarImgX: 0, // 头像开始剪切的x坐标
avatarImgY: 0, // 头像开始剪切的y坐标
avatarX: 10, // 画布上头像开始绘制的x坐标
avatarY: 10, // 画布上头像开始绘制的y坐标
avatarWidth: 200, // 画布上要绘制的头像的宽度
avatarHeight: 200, // 画布上要绘制的头像的宽度
radius: 5, // 头像的圆角半径
guoqiRadius: 3, // 国旗的圆角半径
guoqiX: 0, // 国旗开始剪切的x坐标
guoqiY: 0, // 国旗开始剪切的y坐标
guoqiWidth: 50, // 画布上要绘制的国旗的宽度
guoqiHeight: 33, // 画布上要绘制的国旗的宽度
// 存放的是国旗的base64编码,源码地址点开可查看完整的
guoqi: ""
}
// 定义一些常量
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const inputUpload = document.getElementById('fileInput');
// 定义input的onchange事件,通过fileReader拿到图片的base64编码
window.onload = function() {
inputUpload.onchange = function uploadAvatar() {
const file = this.files[0];
if(file) {
// 读取图片资源
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = function () {
// 为了保证图片不失真,画布的高度是根据上传头像的宽高比
// 和options中定义好的绘制宽度计算出来的,
// 所以需要把国旗的绘制放在头像绘制完成后的回调函数中执行。
function cb() {
const param = {
sx: options.guoqiX,
sy: options.guoqiY,
x: canvas.width - options.guoqiWidth - options.radius * 2,
y: canvas.height - options.guoqiHeight - options.radius * 2,
width: options.guoqiWidth,
height: options.guoqiHeight
}
// 调用绘制图片的工具函数,传入所需参数
drawAvatar(options.guoqi,cb, param.sx,param.sy,param.x,param.y,
param.width,param.height, options.guoqiRadius)
}
// 调用绘制图片的工具函数,传入所需参数
drawAvatar(this.result,cb,options.avatarImgX,options.avatarImgY,
options.avatarX,options.avatarY,options.avatarWidth,
options.avatarHeight);
}
}
};
}
// 绘制图片
function drawAvatar(imgSrc,cb, ...args) {
let img = new Image();
img.src = imgSrc;
// 很多情况下的图片绘制的逻辑都会写在onload里面
// 是因为通常的图片资源都是网络请求获取的,
// 我们会要保证在资源加载完成后才能正确完成绘制。
// 我这里是直接使用的图片的base64编码,
// 绘制图片的逻辑也可不写在onload函数里
img.onload = function () {
args.splice(2, 0, img.width,img.height);
// 绘制的是头像,则计算并设置画布的高度,清空input的值
if(args[6] === options.avatarWidth) {
args[7] = parseInt(args[6] / (img.width / img.height));
canvas.width = args[6] + 20;
canvas.height = args[7] + 20;
inputUpload.value = '';
}
const r = args[8] || options.radius, x = args[4],
y = args[5], w = args[6], h=args[7];
ctx.drawImage(img, ...args);
// 绘制图像的圆角的函数
drawRadius(x, y, w, h, r);
// 绘制国旗
typeof cb === 'function' && cb();
}
}
// 绘制图片圆角
function drawRadius(x,y,w,h,r) {
ctx.beginPath();
ctx.strokeStyle='white';
ctx.fillStyle ='white'
ctx.lineWidth= r;
console.log(ctx.lineWidth);
ctx.moveTo(x+r, y);
ctx.arcTo(x+w, y, x+w, y+h, r);
ctx.arcTo(x+w, y+h, x, y+h, r);
ctx.arcTo(x, y+h, x, y, r);
ctx.arcTo(x, y, x+w, y, r);
ctx.stroke();
ctx.closePath();
}
// 图片下载,下载的原理就是创建a标签,指向图片的下载地址,
// 模拟触发a标签的点击完成下载
function downLoad(url){
let oA = document.createElement("a");
oA.download = 'avatar';
// 通过canvas.toDataURL方法生成图片的下载地址
oA.href = saveAsPNG();
document.body.appendChild(oA);
oA.click();
oA.remove(); // 下载之后把创建的元素删除
}
// 保存成png格式的图片,你这里可以选择其他格式的图片保存
function saveAsPNG() {
return canvas.toDataURL("image/png");
}
css代码:
.save-btn, .input-container{
border: none;
box-shadow: none;
width: 150px;
padding: 10px;
margin-bottom: 20px;
margin-right: 20px;
text-align: center;
color: #fff;
background-color: blue;
border-radius: 5px;
cursor: pointer;
}
.save-btn {
background-color: #67c23a;
}
.input-container {
background-color: #409eff;
position: relative;
.file-input {
cursor: pointer;
position: absolute;
display: inline-block;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0;
}
}
总结
到这儿,给头像添加国旗的功能就完成了,不知道你们有没有学会呢?代码写到这里其实还有很多待完善的地方,例如国旗的素材,我选的这个国旗图片很规矩,没有太多的设计感,还可以利用canvas的其他api,让生成的图片更有趣,更好看,更自然。我今天的分享就到这儿了,有什么疑惑或者建议可评论区留言给我奥~~,最后,祝我们的祖国生日快乐~,国泰民安,繁荣富强,这盛世,如你所愿!