国庆节到了,实现一个生成国庆风头像小工具,详解实现过程!

简介: 明天就是国庆节了,最近看到好多好友换了国庆风的头像,感觉这个挺有意思,就找到了类似的源码研究了一番,并进行了改造(并非原创,只是进行了改造,只要想分享一下实现思路)。下面就来看看如何实现一键生成国庆风头像小工具。​

明天就是国庆节了,最近看到好多好友换了国庆风的头像,感觉这个挺有意思,就找到了类似的源码研究了一番,并进行了改造(并非原创,只是进行了改造,只要想分享一下实现思路)。下面就来看看如何实现一键生成国庆风头像小工具。

这里用到的技术:


  • 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!


相关文章
|
算法 定位技术
八爪鱼RPA在微信的十大高频场景,让你的工作事半功倍!
在微信中,rpa(机器人流程自动化)技术可以应用于各种情况,为用户提供更高效、便捷的工作体验。本文将介绍微信中的十大高频场景,并说明rpa可以如何应用于这些场景中,从而让工作事半功倍。
|
设计模式 SpringCloudAlibaba 负载均衡
每天打卡,跟冰河肝这些项目,技术能力嗖嗖往上提升
前几天,就有不少小伙伴问我,冰河,你星球有哪些项目呢?我想肝你星球的项目,可以吗?今天,我就给大家简单聊聊我星球里有哪些系统性的项目吧。其实,每一个项目的价值都会远超门票。
162 0
每天打卡,跟冰河肝这些项目,技术能力嗖嗖往上提升
|
4月前
|
运维 网络协议 测试技术
没有这5款好用的工具,那我真的不想打工了!
没有这5款好用的工具,那我真的不想打工了!
|
4月前
|
数据可视化 前端开发 JavaScript
自从做出了这款可视化大屏,再也不用担心裁员了!这波稳了~
自从做出了这款可视化大屏,再也不用担心裁员了!这波稳了~
|
4月前
|
测试技术 C++
🕒 笑cry!Debian:稳如老狗,时间见了都颤抖!宇宙级“时间管理大师”非它莫属 🚀
在数字时代的快车道上,系统们竞相提速与更新,唯有Debian宛如老僧入定,以“稳”字诀笑看风云变幻。当他人忙于应对崩溃与兼容问题时,Debian用户自在饮茶,体验着技术海洋中的避风塘。Debian以近乎苛刻的测试流程,成为服务器领域的定海神针,它似乎在说:“我虽慢,但却稳如泰山。”在这个追求极速的世界,Debian教会我们:稳定,不仅是一种技术标准,更是一种从容不迫的生活态度。🧠
45 0
|
JavaScript 前端开发 程序员
珍藏多年的神网站都捐了,你需要的我都有,有了这些网站工作生活不用愁(二)
珍藏多年的神网站都捐了,你需要的我都有,有了这些网站工作生活不用愁(二)
169 0
|
JSON 小程序 JavaScript
【小程序 | 启航篇】一文打通任督二脉
【小程序 | 启航篇】一文打通任督二脉
【小程序 | 启航篇】一文打通任督二脉
|
算法 测试技术
|
人工智能 前端开发 JavaScript
热饭的测开成果盘点第十期:测试平台OneKey(一)
!! 本期要分享的是一款集成各种功能的测试平台,所以功能非常之多。本期也属于超重量级的一期,请仔细观看。因为展示的是脱敏的测试环境,所以数据统计等不要在意。
热饭的测开成果盘点第十期:测试平台OneKey(一)
众筹十万美元搞火箭,搞研发全靠志愿者!这个“草根”航天组织已经开启载人测试
众筹十万美元搞火箭,搞研发全靠志愿者!这个“草根”航天组织已经开启载人测试
180 0
众筹十万美元搞火箭,搞研发全靠志愿者!这个“草根”航天组织已经开启载人测试