国庆节到了,利用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 相关文章推荐
ASP.NET jQuery 实例10 动态修改hyperlink的URL值
Feb 03 Javascript
javascript仿qq界面的折叠菜单实现代码
Dec 12 Javascript
js实现身份证号码验证的简单实例
Feb 19 Javascript
给angular加上动画效遇到的问题总结
Feb 17 Javascript
JavaScript中setTimeout的那些事儿
Nov 14 Javascript
ReactNative之FlatList的具体使用方法
Nov 29 Javascript
给localStorage设置一个过期时间的方法分享
Nov 06 Javascript
浅谈webpack 四个核心概念之Entry
Jun 12 Javascript
JS数组方法slice()用法实例分析
Jan 18 Javascript
小程序分享链接onShareAppMessage的具体用法
May 22 Javascript
Vue实现背景更换颜色操作
Jul 17 Javascript
原生js实现无缝轮播图效果
Jan 28 Javascript
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
提升PHP执行速度全攻略(上)
2006/10/09 PHP
php从数组中随机抽取一些元素的代码
2012/11/05 PHP
ajax取消挂起请求的处理方法
2013/03/18 PHP
PHP JSON出错:Cannot use object of type stdClass as array解决方法
2014/08/16 PHP
php中memcache 基本操作实例
2015/05/17 PHP
ThinkPHP安装和设置
2015/07/27 PHP
yii2分页之实现跳转到具体某页的实例代码
2016/06/02 PHP
php源码之将图片转化为data/base64数据流实例详解
2016/11/27 PHP
详解PHP防止直接访问.php 文件的实现方法
2017/07/28 PHP
PHP实现权限管理功能示例
2017/09/22 PHP
Javascript----文件操作
2007/01/18 Javascript
javascript 循环读取JSON数据的代码
2010/07/17 Javascript
JavaScript 匿名函数(anonymous function)与闭包(closure)
2011/10/04 Javascript
javascript几个易错点记录
2014/11/26 Javascript
vue2.0中click点击当前li实现动态切换class
2017/06/21 Javascript
VUE axios上传图片到七牛的实例代码
2017/07/28 Javascript
vue-cli V3.0版本的使用详解
2018/10/24 Javascript
Vue+Typescript中在Vue上挂载axios使用时报错问题
2019/08/07 Javascript
JS实现贪吃蛇游戏
2019/11/15 Javascript
JavaScript实现PC端横向轮播图
2020/02/07 Javascript
viewer.js实现图片预览功能
2020/06/24 Javascript
Vue 组件的挂载与父子组件的传值实例
2020/09/02 Javascript
小程序实现点击tab切换左右滑动
2020/11/16 Javascript
Python生成随机数的方法
2014/01/14 Python
Python的Tornado框架异步编程入门实例
2015/04/24 Python
Python的字典和列表的使用中一些需要注意的地方
2015/04/24 Python
Python 保存矩阵为Excel的实现方法
2019/01/28 Python
python列表每个元素同增同减和列表元素去空格的实例
2019/07/20 Python
Python 多线程,threading模块,创建子线程的两种方式示例
2019/09/29 Python
8段用于数据清洗Python代码(小结)
2019/10/31 Python
python3实现网页版raspberry pi(树莓派)小车控制
2020/02/12 Python
Python小整数对象池和字符串intern实例解析
2020/03/21 Python
德国最大的婴儿用品网上商店:Kidsroom.de(支持中文)
2020/09/02 全球购物
酒店员工职业生涯规划
2014/02/25 职场文书
社区母亲节活动方案
2014/03/05 职场文书
理发店策划方案
2014/06/05 职场文书