明天就是国庆节了,最近看到好多好友换了国庆风的头像,感觉这个挺有意思,就找到了类似的源码研究了一番,并进行了改造(并非原创,只是进行了改造,只要想分享一下实现思路)。下面就来看看如何实现一键生成国庆风头像小工具。
这里用到的技术:
- HTML+ CSS+ JavaScript;
- download.js库;
- fabric.js库;
先上体验链接:g.cuggz.com/。
注:可以点击上方的连接进行使用,不过我的域名被TX屏蔽了,还在申诉中,所以无法在QQ、微信中打开,需要复制链接到浏览器进行查看、使用。
下面是这个小工具的截图:
1. 页面布局
这部分不多说,直接上代码:
<div class="wrapper"> <!-- 选择框 --> <div class="main-box"> <a class="prev" onClick='changeHat()'></a> <div class="main-img"> <div id="content"> <canvas id='cvs'></canvas> </div> </div> <a class="next" onClick='changeHat()'></a> </div> <!-- 导出图 --> <img id='export-img' alt='国庆专属头像' src='' crossorigin="anonymous"/> <!-- 操作按钮 --> <div class="operation-btns"> <a class="upload-btn"> <input id='upload' type='file' onchange='viewer()' style='opacity: 0;'/> </a> <a class="export-btn" onClick='exportFunc()'></a> </div> </div> <!-- 模板 --> <div style='display: none'> <img id='img' src='' alt='' /> <img class='hide' id='hat0' src='img/1.png' /> <img class='hide' id='hat1' src='img/2.png' /> <img class='hide' id='hat2' src='img/3.png' /> <img class='hide' id='hat3' src='img/4.png' /> <img class='hide' id='hat4' src='img/5.png' /> <img class='hide' id='hat5' src='img/6.png' /> <img class='hide' id='hat6' src='img/7.png' /> </div> 复制代码
这个页面比较简单,外面就是一个大的背景图,中间就是一个头像的展示框以及模板的切换按钮,下面就是一个上传按钮和一个下载按钮。页面布局完之后,就是写样式了,CSS代码如下:
body, html { min-height: 100%; width: 100%; user-select: none; font-size: 18px; } .wrapper { width: 100%; height: 100%; max-width: 620px; max-height: 800px ; margin: 0 auto; background-image: url('../img/bg.png'); background-repeat: no-repeat; background-size: 100% 100%; padding-top: 13em; } #export-img { display:none; margin:0 auto; width:250px; height:250px; } .main-box { display: flex; align-items: center; justify-content: center; } .main-box .next, .main-box .prev { background-image: url('../img/next.png'); background-size: contain; border-radius: 50%; width: 2.275rem; height: 2.275rem } .main-box .prev { transform: rotate(180deg) } .main-box .main-img { margin: 0 .75rem; background: #fff; border: .25rem solid #fbe6b5; border-radius: .75rem; font-size: 0 } #content { border-radius: .5rem; position: relative; width: 9.5rem; height: 9.5rem; margin-left: 50%; transform: translateX(-50%); overflow: hidden } .operation-btns { display: flex; align-items: center; justify-content: center; flex-direction: column; margin-top: .75rem } .operation-btns .upload-btn { width: 11.6rem; height: 3.6rem; background-size: 100% 100%; background-image: url('../img/upload.png') } .operation-btns .export-btn { display: none; width: 11.6rem; height: 3.75rem; background-size: 100% 100%; background-image: url('../img/export.png') } 复制代码
这里只是简单的实现,仅供参考。还有很多可以优化的地方,这里不在修改,有兴趣的可以自己进行个性化定制。
2. 图片上传和展示
接下来就是逻辑部分的实现了。首先,有几个全局变量需要先定义,后面会用到:
let canvasFabric; // 画布实例 let hat = "hat0"; // 当前的模板class let hatInstance; // 模板图层实例 const screenWidth = document.getElementById("content").scrollHeight; // 内容框的高度 复制代码
之后就需要处理上传的图片并将他展示在页面上:
function viewer() { // 获取上传的图片文件 const file = document.getElementById("upload").files[0]; // 获取需要展示图片的标签 const img = document.getElementById("img"); // 创建文件读取文件对象 const reader = new FileReader(); if (file) { // 将文件转化为Base64 reader.readAsDataURL(file); // 当文件读取成功之后触发 reader.onload = () => { // 将base64的url赋值给要展示图片的标签 img.src = reader.result; // 图片加载完成触发 img.onload = () => img2Cvs(img); } } else { img.src = "" } } 复制代码
这里使用到了HTML5的FileReader对象,它提供了读取文件的方法和包含读取结果的事件模型。可以使用new来初始化对象,FileReader对象包含四个方法,其中 3 个用以读取文件,另一个用来中断读取。需要注意的是 ,无论读取成功或失败,方法并不会返回读取结果,这一结果存储在 result 属性中。这里用到了readAsDataURL方法,MDN对该方法的介绍如下:
readAsDataURL 方法会读取指定的 Blob 或 File 对象。读取操作完成的时候,readyState 会变成已完成DONE,并触发 loadend (en-US) 事件,同时 result 属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。
也就是说将上传的图片转化为了一个Base64格式的URL,并赋值给了展示图片的标签,这样这个标签就显示出现这个头像了,效果如下:
3. 初始化画布
在上面的代码的最后执行了 img.load,这里的 onload 事件会在图片加载完成后立即执行。在图片展示完成之后会执行img2Cvs
方法,这个方法主要用来初始化一块画布,顺便展示和隐藏页面的部分元素。
img2Cvs
方法中使用到了 fabric库,Fabric.js是一个可以简化Canvas程序编写的库。 Fabric.js为Canvas提供所缺少的对象模型, svg parser, 交互和一整套其他不可或缺的工具。Canvas提供一个好的画布能力, 但是Api不够友好。Fabric.js就是为此而开发,它主要就是用对象的方式去编写代码。Fabric.js能做的事情如下:
- 在Canvas上创建、填充图形(包括图片、文字、规则图形和复杂路径组成图形)。
- 给图形填充渐变颜色。
- 组合图形(包括组合图形、图形文字、图片等)。
- 设置图形动画集用户交互。
- 生成JSON, SVG数据等。
- 生成Canvas对象自带拖拉拽功能。
可以通过npm命令来安装fabric.js库:
npm install fabric --save 复制代码
也可以通过cdn来引用:
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.6/fabric.min.js"></script> 复制代码
下面就来看看img2Cvs
方法是如何实现的:
function img2Cvs(img) { // 获取并展示canvas画布,并将画布的大小设置为图片的大小 const cvs = document.getElementById("cvs"); cvs.width = img.width; cvs.height = img.height; cvs.style.display = "block"; // 创建一个画布,并设置其位置和背景图 canvasFabric = new fabric.Canvas("cvs", { width: screenWidth, height: screenWidth, backgroundImage: new fabric.Image(img, { scaleX: screenWidth / img.width, scaleY: screenWidth / img.height }) }); // 切换模板 changeHat(); // 隐藏上传图片按钮,展示下载图片按钮 document.getElementsByClassName("upload-btn")[0].style.display = "none"; document.getElementsByClassName("export-btn")[0].style.display = "block"; } 复制代码
这里面的fabric.Canvas()
方法有两个参数,第一个参数是canvas画布的id,第二个参数是初始化画布时的配置项,这里我们设置了初始的画布的大小以及背景图,使用我们上传的头像作为背景图。这里的背景图是实例化的fabric.Image对象,该对象初始化时的第一个参数是图片对象,第二个参数是图片的样式设置配置对象。
当创建好画布之后,就需要切换出第一个模板,并将上传图片按钮隐藏,以及将下载头像按钮显示出来。这样就完成了第一步的工作。下面就在来看看如何切换已有的模板。
4. 切换模板
接下来就来看看切换模板是如何实现的:
function changeHat() { // 隐藏当前的模板 document.getElementById(hat).style.display = "none"; // 获取所有的模板 const hats = document.getElementsByClassName("hide"); hat = "hat" + (+hat.replace("hat", "") + 1) % hats.length; // 获取当前的模板并展示出来 const hatImage = document.getElementById(hat); hatImage.style.display = "block"; // 如果当前存在图层,就将其移除 if (hatInstance) { canvasFabric.remove(hatInstance) } // 将当前的模板添加为图层对象 hatInstance = new fabric.Image(hatImage, { top: 0, left: 0, scaleX: screenWidth / hatImage.width, scaleY: screenWidth / hatImage.height, cornerColor: "#0b3a42", cornerStrokeColor: "#fff", cornerStyle: "circle", transparentCorners: false, rotatingPointOffset: 30 }); // 将图层对象设置为不可拉伸 hatInstance.setControlVisible({ mt: false, mb: false, ml: false, mr: false, bl: false, br: false, tl: false, tr: false, mtr: false, }) // 将图层添加到画布中 canvasFabric.add(hatInstance); } 复制代码
在默认情况下,fabric.js元素带有八个点来缩放任何对象,这里我们是不希望用户在水平或垂直方向对fabric对象进行拉伸的,可以通过setControlsVisibility()方法来设置其不可拉伸,该方法需要传入一个配置对象,该对象包含八个缩放点,都设置为false即可。
最后我们将使用模板生成的图层添加到了画布中,这里使用到了add方法,这是fabric提供的事件,以下为fabric.js官方提供的常用事件:
- object:added 添加图层
- object:modified 编辑图层
- object:removed 移除图层
- selection:created 初次选中图层
- selection:updated 图层选择变化
- selection:cleared 清空图层选中
5. 输出图片
经过上面的步骤,我们就初始化了一个画布,画布的背景是我们上传的图片,画布上还有一个图层,这个图层是我们自己选择的模板。最后一步就是输出合成之后的图片了。下面来看看点击下载图片按钮之后会执行哪些操作:
function exportFunc() { // 隐藏选择框、上传下载按钮、canvas画布 document.getElementsByClassName("main-box")[0].style.display = "none"; document.getElementsByClassName("operation-btns")[0].style.display = "none"; document.getElementById("cvs").style.display = "none"; // 将画布生成URL,并赋值给对应标签进行展示 const exportImage = document.getElementById("export-img"); exportImage.style.display = "block"; exportImage.src = canvasFabric.toDataURL({ width: screenWidth, height: screenWidth }); // 下载生成的图片 window.confirm("是否下载头像") ? download(exportImage.src, "国庆风头像", 'image/png') : void 0 } 复制代码
这里我们使用toDataURL方法来将画布实例对象生成图片,这是fabric对象的方法,可以将画布导出为图片,导出的一个Base64格式的URL。这样img标签获取到这个URL之后就能显示出来最终的图片了。
最后,还添加了一个可有可无的功能,就是下载生成的头像,这里使用到了download.js
库,该方法的第一个参数是图片的URL,第二个参数是下载图片的名称,第三个参数是图片的格式。
这就是这个小应用的所有功能了,只是一个简单的实现,还存在一个BUG,主要提供一个实现的思路。我之前是没有接触过canvas以及画布的概念的,这次涨知识了。以后有时间多学习了一下相关的使用,interesting!