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 相关文章推荐
在JavaScript中使用inline函数的问题
Mar 08 Javascript
Use Word to Search for Files
Jun 15 Javascript
如何将JS的变量值传递给ASP变量
Dec 10 Javascript
JS解决url传值出现中文乱码的另类办法
Apr 08 Javascript
js判断浏览器版本以及浏览器内核的方法
Jan 20 Javascript
js中split和replace的用法实例
Feb 28 Javascript
js支持键盘控制的左右切换立体式图片轮播效果代码分享
Aug 26 Javascript
详解webpack分包及异步加载套路
Jun 29 Javascript
vue-quill-editor富文本编辑器简单使用方法
Sep 21 Javascript
mpvue开发音频类小程序踩坑和建议详解
Mar 12 Javascript
浅谈JS中几种轻松处理'this'指向方式
Sep 16 Javascript
vue实现购物车列表
Jun 30 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
php批量修改表结构实例
2017/05/24 PHP
PHP实现动态添加XML中数据的方法
2018/03/30 PHP
js利用与或运算符优先级实现if else条件判断表达式
2010/04/15 Javascript
js获取单选框或复选框值及操作
2012/12/18 Javascript
jquery eval解析JSON中的注意点介绍
2013/08/23 Javascript
js函数内变量的作用域分析
2015/01/12 Javascript
javascript中日期函数new Date()的浏览器兼容性问题
2015/09/05 Javascript
关于webpack2和模块打包的新手指南(小结)
2017/08/07 Javascript
如何利用@angular/cli V6.0直接开发PWA应用详解
2018/05/06 Javascript
JS+HTML实现的圆形可点击区域示例【3种方法】
2018/08/01 Javascript
vue.js项目 el-input 组件 监听回车键实现搜索功能示例
2018/08/25 Javascript
解决vue的变量在settimeout内部效果失效的问题
2018/08/30 Javascript
JS函数节流和防抖之间的区分和实现详解
2019/01/11 Javascript
Layui点击图片弹框预览的实现方法
2019/09/16 Javascript
vue选项卡切换登录方式小案例
2019/09/27 Javascript
[03:23]我的刀塔你不可能这么可爱 第一期金萌萌的故事
2014/06/20 DOTA
Linux下使用python调用top命令获得CPU利用率
2015/03/10 Python
python django 访问静态文件出现404或500错误
2017/01/20 Python
python编程测试电脑开启最大线程数实例代码
2018/02/09 Python
numpy.transpose对三维数组的转置方法
2018/04/17 Python
对numpy数据写入文件的方法讲解
2018/07/09 Python
python调用百度地图WEB服务API获取地点对应坐标值
2019/01/16 Python
opencv与numpy的图像基本操作
2019/03/08 Python
Python 保持登录状态进行接口测试的方法示例
2019/08/06 Python
python3 selenium自动化 下拉框定位的例子
2019/08/23 Python
Python装饰器原理与基本用法分析
2020/01/07 Python
美的官方商城:Midea
2016/09/14 全球购物
以下为Windows NT 下的32 位C++程序,请计算sizeof 的值
2016/12/07 面试题
电钳专业个人求职信
2014/01/04 职场文书
中考冲刺决心书
2014/03/11 职场文书
公司周年庆典策划方案
2014/05/17 职场文书
关于运动会广播稿200字
2014/10/08 职场文书
中秋客户感谢信
2015/01/22 职场文书
八达岭长城导游词
2015/01/30 职场文书
个人借条范本
2015/05/25 职场文书
Vue Element UI自定义描述列表组件
2021/05/18 Vue.js