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


Posted in Javascript onOctober 05, 2021

这里用到的技术:

  • HTML+ CSS+ JavaScript;
  • download.js库;
  • fabric.js库;

先上体验链接:g.cuggz.com/ 。​

注:可以点击上方的连接进行使用,不过我的域名被TX屏蔽了,还在申诉中,所以无法在QQ、微信中打开,需要复制链接到浏览器进行查看、使用。​

下面是这个小工具的截图:

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

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 = ""
  }
}

这里使用到了HTML5FileReader对象,它提供了读取文件的方法和包含读取结果的事件模型。可以使用new来初始化对象,FileReader对象包含四个方法,其中 3 个用以读取文件,另一个用来中断读取。需要注意的是 ,无论读取成功或失败,方法并不会返回读取结果,这一结果存储在 result 属性中。这里用到了readAsDataURL方法,MDN对该方法的介绍如下:

readAsDataURL 方法会读取指定的 Blob 或 File 对象。读取操作完成的时候,readyState 会变成已完成DONE,并触发 loadend (en-US) 事件,同时 result 属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。

也就是说将上传的图片转化为了一个Base64格式的URL,并赋值给了展示图片的标签,这样这个标签就显示出现这个头像了,效果如下:

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

这样就完成了图片的上传和展示,接下来就该初始化一个画布了。

3. 初始化画布

在上面的代码的最后执行了 img.load,这里的 onload 事件会在图片加载完成后立即执行。在图片展示完成之后会执行img2Cvs方法,这个方法主要用来初始化一块画布,顺便展示和隐藏页面的部分元素。

img2Cvs方法中使用到了 fabric库,Fabric.js是一个可以简化Canvas程序编写的库。 Fabric.jsCanvas提供所缺少的对象模型, 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!​

到此这篇关于国庆节到了,利用JS实现一个生成国庆风头像的小工具 详解实现过程的文章就介绍到这了,更多相关利用JS实现一个生成国庆风头像内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
filemanage功能中用到的common.js
Apr 08 Javascript
使用JQUERY Tabs插件宿主IFRAMES
Jan 01 Javascript
js滚动条回到顶部的代码
Dec 06 Javascript
Javascript的各种节点操作实例演示代码
Jun 27 Javascript
JavaScript中的细节分析
Jun 30 Javascript
javascript 四十条常用技巧大全
Sep 09 Javascript
关于javascript事件响应的基础语法总结(必看篇)
Dec 26 Javascript
深入理解Javascript中的作用域链和闭包
Apr 25 Javascript
解决bootstrap-select 动态加载数据不显示的问题
Aug 10 Javascript
vue 表单验证按钮事件交由父组件触发的方法
Dec 17 Javascript
详解Vue中使用插槽(slot)、聚类插槽
Apr 12 Javascript
详解Vue的七种传值方式
Feb 08 Vue.js
Javascript设计模式之原型模式详细
JS数组方法some、every和find的使用详情
8个JS的reduce使用实例和reduce操作方式
Oct 05 #Javascript
JS 4个超级实用的小技巧 提升开发效率
Oct 05 #Javascript
JavaScript实现简单拖拽效果
Sep 15 #Javascript
一小时迅速入门Mybatis之bind与多数据源支持 Java API
Sep 15 #Javascript
Javascript之datagrid查询详解
Sep 15 #Javascript
You might like
获取URL文件名后缀
2013/10/24 PHP
PHP中的函数声明与使用详解
2017/05/27 PHP
利用 fsockopen() 函数开放端口扫描器的实例
2017/08/19 PHP
PHP一致性hash分布式算法封装类定义与用法示例
2018/08/04 PHP
Js 订制自己的AlertBox(信息提示框)
2009/01/09 Javascript
自己的js工具_Form 封装
2009/08/21 Javascript
在Ajax中使用Flash实现跨域数据读取的实现方法
2010/12/02 Javascript
js查错流程归纳
2012/05/04 Javascript
jquery实现焦点图片随机切换效果的方法
2015/03/12 Javascript
高效利用Angular中内置服务$http、$location等
2016/03/22 Javascript
封装获取dom元素的简单实例
2016/07/08 Javascript
jQuery插件ajaxFileUpload使用实例解析
2016/10/19 Javascript
基于jQuery实现表格的排序
2016/12/02 Javascript
老生常谈ES6中的类
2017/07/31 Javascript
利用Vue构造器创建Form组件的通用解决方法
2018/12/03 Javascript
浅谈对于react-thunk中间件的简单理解
2019/05/01 Javascript
[01:11:15]VGJ.S vs Secret 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
Python threading多线程编程实例
2014/09/18 Python
Python基于checksum计算文件是否相同的方法
2015/07/09 Python
解决python中导入win32com.client出错的问题
2019/07/26 Python
Python generator生成器和yield表达式详解
2019/08/08 Python
浅谈html5与APP混合开发遇到的问题总结
2018/03/20 HTML / CSS
会计电算化应届生求职信
2013/11/03 职场文书
职工运动会邀请函
2014/01/19 职场文书
经销商订货会主持词
2014/03/27 职场文书
大队委竞选演讲稿
2014/04/28 职场文书
总经理人事任命书
2014/06/05 职场文书
中学教师暑期培训方案
2014/08/27 职场文书
关于安全的广播稿
2014/10/23 职场文书
优秀党员先进事迹材料
2014/12/18 职场文书
2015年感恩母亲节的演讲稿
2015/03/18 职场文书
2015年专项整治工作总结
2015/04/03 职场文书
求职意向书范本
2015/05/11 职场文书
导游词之神仙居景区
2019/11/15 职场文书
Python竟然能剪辑视频
2021/05/25 Python
vue使用localStorage持久性存储实现评论列表
2022/04/14 Vue.js