JavaScript基于用户照片姓名生成海报


Posted in Javascript onMay 29, 2020

前言

最近在为公司的一个比赛制作专题页,碰到一个使用参赛者上传的照片生成专属海报的需求,实现过程中用到了一些以前没用过的 api,也踩了一些坑,于是将其记录下来。

需求描述

  • 用户点击按钮进行照片上传
  • 照片上传完成后,将照片进行裁剪,并和海报背景、姓名等组合得到海报
  • 将生成的海报上传

效果大概如下:

海报背景:

JavaScript基于用户照片姓名生成海报

成品:

JavaScript基于用户照片姓名生成海报

实现过程

1、初始化 canvas

canvas#poster-canvas(width='960' height='1280')
function initCanvas() {
canvasCtx = document.getElementById("poster-canvas").getContext('2d');
}

2、绘制海报背景

海报背景为预先提供的一张照片,将其设置到一个隐藏的 img 标签里面,并且预留一个 canvas 元素用于绘制海报:

img.poster-background(src='/assets/xxx/poster-background.jpeg')

页面加载完成后,将海报背景绘制到 canvas 内:

$('img.poster-background').on('load', function () {
 var backgroundImg = $('img.poster-background')[0];
 canvasCtx.drawImage(backgroundImg, 0, 0, 960, 1280);
 renderName();
});

海报背景绘制完成之后,需要将用户姓名绘制到特定位置。由于用户姓名长度不一,因此需要进行计算确定字体大小:

function renderName() {
 var name = $('input[name="chName"]').val();
 var fontSize;
 if (name.length < 3) {
  fontSize = 100;
 } else {
  fontSize = parseInt(320 / name.length);
 }
 canvasCtx.font = "bold " + fontSize + "px Courier New";
 canvasCtx.fillStyle = "#de071b";
 canvasCtx.fillText(name, 20, 1066);
}

3、上传照片

使用 file 类型的 input 元素,因为页面上表现为点击按钮,因此使用经典的将 input 元素透明化并覆盖按钮的方法:

a.upload-btn 
 input#photo(type='file' name='photo' accept='image/jpeg, image/png')
 | 上传自己的照片生成专属海报
.upload-btn input {
 position: absolute;
 left: 0;
 top: 0;
 opacity: 0;
 width: 100%;
 height: 68px;
 cursor: pointer;
}

然后监听 input 元素的 change 事件,然后使用 FormData API 构造表单数据,使用 ajax 进行异步上传,照片上传完成之后。得到一个地址,将这个地址设置到页面上预留的一个 img 标签里面:

$('#photo').on('change', function (e) {
 var file = e.target.files[0];
 var type = file.type;
 if (type !== 'image/jpeg' && type !== 'image/png') {
  window.toastr.error('请上传 jpg 或 png 格式的图片');
 } else {
  var formData = new FormData();
  formData.append('avatar', file);
  $.ajax({
   type: 'POST',
   url: '/upload_url',
   data: formData,
   contentType: false,
   processData: false,
   success: function(result) {
    var avatarUrl = result.data.url;
    $('img.avatar').attr('src', avatarUrl);
   },
   error: function(err) {
    
   }
  });
 }
});

4、绘制照片

海报中放置照片的区域为正方形,但是用户上传的照片却不一定,因此需要对照片进行裁剪,裁剪的原则为取照片中间部分。然后将裁剪参数传进 canvas 的 drawImage 方法,进行绘制:

$('img.avatar').on('load', function () {
 var avatarImg = $('img.avatar')[0];
 var originWidth = avatarImg.width;
 var originHeight = avatarImg.height;
 var newWidth, cutStartX, cutStartY;

 if (originWidth < originHeight) {
  newWidth = originWidth;
  cutStartX = 0;
  cutStartY = (originHeight - originWidth) / 2;
 } else if (originWidth > originHeight) {
  newWidth = originHeight;
  cutStartX = (originWidth - originHeight) / 2;
  cutStartY = 0;
 } else {
  newWidth = originWidth;
  cutStartX = 0;
  cutStartY = 0;
 }
 
 canvasCtx.drawImage(avatarImg, cutStartX, cutStartY, newWidth, newWidth, 0, 0, 960, 960);

 uploadPoster();
   
});

前面绘制海报背景和这里绘制照片,调用的是同一个方法,只不过后者多传进去了裁剪参数。但是需要注意的是,裁剪参数是在绘制位置之前传进去的,而不是简单的补在后面:

canvasCtx.drawImage(backgroundImg, 0, 0, 960, 1280);

canvasCtx.drawImage(avatarImg, cutStartX, cutStartY, newWidth, newWidth, 0, 0, 960, 960);

5、上传海报

依然使用 FormData API,因此需要先用 canvas 构造一个 Blob 对象。新版本的 Chrome 和 Firefox 支持 canvas 的 toBlob 方法,可以直接使用:

document.getElementById("poster-canvas").toBlob(function (blob) {});

其它浏览器里,可以先用 toDataURL方法得到 base64 格式的图片数据,再转为 Blob:

var blob = dataURLtoBlob(document.getElementById("poster-canvas").toDataURL());

function dataURLtoBlob(dataurl) {
 if (dataurl.indexOf('base64') < 0) {
  dataurl = 'data:image/jpeg;base64,' + dataurl;
 }
 var arr = dataurl.split(',');
 var mime = arr[0].match(/:(.*?);/)[1];
 var bstr = atob(arr[1]);
 var n = bstr.length;
 var u8arr = new Uint8Array(n);
 while (n --) {
  u8arr[n] = bstr.charCodeAt(n);
 }
 return new Blob([u8arr], {type: mime});
}

然后进行上传,步骤和前面上传照片一致:

var formData = new FormData();
formData.append('poster', blob);
$.ajax({
 type: 'POST',
 url: '/upload_poster_url',
 data: formdata,
 contentType: false,
 processData: false,
 success: function(result) {
  
 },
 error: function(err) {
  
 }
});

至此,整个流程完结。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js限制文本框只能输入数字(正则表达式)
Jul 15 Javascript
左侧是表头的JS表格控件(自写,网上没有的)
Jun 04 Javascript
Json序列化和反序列化方法解析
Dec 19 Javascript
jQuery中ajax的load()方法用法实例
Dec 26 Javascript
简单模拟node.js中require的加载机制
Oct 27 Javascript
jQuery中的on与bind绑定事件区别实例详解
Feb 28 Javascript
JavaScript订单操作小程序完整版
Jun 23 Javascript
基于JS实现移动端左滑删除功能
Jul 28 Javascript
浅谈js的解析顺序 作用域 严格模式
Oct 23 Javascript
JS数组求和的常用方法实例小结
Jan 07 Javascript
vue使用swiper.js重叠轮播组建样式
Nov 14 Javascript
vue自定义组件(通过Vue.use()来使用)即install的用法说明
Aug 11 Javascript
微信小程序实现上拉加载功能示例【加载更多数据/触底加载/点击加载更多数据】
May 29 #Javascript
JavaScript设计模式之策略模式实现原理详解
May 29 #Javascript
JavaScript隐式类型转换代码实例
May 29 #Javascript
vue实现编辑器键盘抬起时内容跟随光标距顶位置向上滚动效果
May 28 #Javascript
node+vue实现文件上传功能
May 28 #Javascript
vue中实现图片压缩 file文件的方法
May 28 #Javascript
详解Vue 数据更新了但页面没有更新的 7 种情况汇总及延伸总结
May 28 #Javascript
You might like
Phpbean路由转发的php代码
2008/01/10 PHP
php 保留小数点
2009/04/21 PHP
PHP中操作ini配置文件的方法
2013/04/25 PHP
php简单实现无限分类树形列表的方法
2015/03/27 PHP
基于jquery自定义图片热区效果
2012/07/21 Javascript
js 字符串转换成数字的三种方法
2013/03/23 Javascript
js使用eval解析json(js中使用json)
2014/01/17 Javascript
使用typeof方法判断undefined类型
2014/09/09 Javascript
JS实现适合于后台使用的动画折叠菜单效果
2015/09/21 Javascript
简单实现JavaScript图片切换效果
2016/11/28 Javascript
微信小程序 引入es6 promise
2017/04/12 Javascript
jQuery实现可编辑表格并生成json结果(实例代码)
2017/07/19 jQuery
详解Angular6.0使用路由步骤(共7步)
2018/06/29 Javascript
JavaScript JSON数据处理全集(小结)
2019/08/15 Javascript
Node.js Domain 模块实例详解
2020/03/18 Javascript
Python实现的二维码生成小软件
2014/07/11 Python
Python入门篇之字符串
2014/10/17 Python
Python实现二叉搜索树
2016/02/03 Python
Python3实战之爬虫抓取网易云音乐的热门评论
2017/10/09 Python
python制作抖音代码舞
2019/04/07 Python
Python字符串的常见操作实例小结
2019/04/08 Python
基于python监控程序是否关闭
2020/01/14 Python
Django中文件上传和文件访问微项目的方法
2020/04/27 Python
详解用Python爬虫获取百度企业信用中企业基本信息
2020/07/02 Python
基于HTML5的WebSocket的实例代码
2018/08/15 HTML / CSS
美国美妆网站:B-Glowing
2016/10/12 全球购物
The North Face官方旗舰店:美国著名户外品牌
2020/09/28 全球购物
数组越界问题
2015/10/21 面试题
业务主管岗位职责范本
2013/12/25 职场文书
三分钟演讲稿事例
2014/03/03 职场文书
竞选村长演讲稿
2014/04/28 职场文书
办公室主任岗位承诺书
2014/05/29 职场文书
音乐教师求职信
2014/06/28 职场文书
关于有小孩的离婚协议书
2014/10/26 职场文书
心理学培训心得体会
2016/01/22 职场文书
Java并发编程之Executor接口的使用
2021/06/21 Java/Android