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 相关文章推荐
iframe 异步加载技术及性能分析
Jul 19 Javascript
深入领悟JavaScript中的面向对象
Nov 18 Javascript
超链接的禁用属性Disabled使用示例
Jul 31 Javascript
JS倒计时代码汇总
Nov 25 Javascript
jquery实现的动态回到顶部特效代码
Oct 28 Javascript
自己动手写的javascript前端等待控件
Oct 30 Javascript
JS实现自定义简单网页软键盘效果代码
Nov 05 Javascript
vuejs+element-ui+laravel5.4上传文件的示例代码
Aug 12 Javascript
老生常谈javascript的面向对象思想
Aug 22 Javascript
VUE2.0+ElementUI2.0表格el-table实现表头扩展el-tooltip
Nov 30 Javascript
JS实现音乐钢琴特效
Jan 06 Javascript
vue router 动态路由清除方式
May 25 Vue.js
微信小程序实现上拉加载功能示例【加载更多数据/触底加载/点击加载更多数据】
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文件上传实例详解!!!
2007/01/02 PHP
深入解析PHP中SESSION反序列化机制
2017/03/01 PHP
Javascript实例教程(19) 使用HoTMetal(5)
2006/12/23 Javascript
JavaScript confirm选择判断
2008/10/18 Javascript
JQuery拖拽元素改变大小尺寸实现代码
2012/12/10 Javascript
详解AngularJS中自定义指令的使用
2015/06/17 Javascript
jquery实现实时改变网页字体大小、字体背景色和颜色的方法
2015/08/05 Javascript
angularjs的select使用及默认选中设置
2017/04/08 Javascript
Angular如何在应用初始化时运行代码详解
2018/06/11 Javascript
JavaScript对象的特性与实践应用深入详解
2018/12/30 Javascript
vue实现百度下拉列表交互操作示例
2019/03/12 Javascript
微信小程序--特定区域滚动到顶部时固定的方法
2019/04/28 Javascript
JS实现基本的网页计算器功能示例
2020/01/16 Javascript
vue滑动吸顶及锚点定位的示例代码
2020/05/10 Javascript
js实现QQ邮箱邮件拖拽删除功能
2020/08/27 Javascript
[10:54]Team Spirit vs Navi
2018/06/07 DOTA
[54:47]Liquid vs VP Supermajor决赛 BO 第五场 6.10
2018/07/05 DOTA
python抓取网页中的图片示例
2014/02/28 Python
详解在Python程序中使用Cookie的教程
2015/04/30 Python
Python3中简单的文件操作及两个简单小实例分享
2017/06/18 Python
python模块smtplib实现纯文本邮件发送功能
2018/05/22 Python
对python中for、if、while的区别与比较方法
2018/06/25 Python
python3.7环境下安装Anaconda的教程图解
2019/09/10 Python
利用python读取YUV文件 转RGB 8bit/10bit通用
2019/12/09 Python
python实现实时视频流播放代码实例
2020/01/11 Python
Python 使用 environs 库定义环境变量的方法
2020/02/25 Python
python opencv图像处理(素描、怀旧、光照、流年、滤镜 原理及实现)
2020/12/10 Python
h5页面唤起app如果没安装就跳转下载(iOS和Android)
2020/06/03 HTML / CSS
美国户外服装和装备购物网站:Outland USA
2020/03/22 全球购物
生产部厂长职位说明书
2014/03/03 职场文书
小学老师对学生的评语
2014/12/29 职场文书
硕士论文致谢范文
2015/05/14 职场文书
2015年新教师个人工作总结
2015/10/14 职场文书
大学优秀学生主要事迹材料
2015/11/04 职场文书
销售口号霸气押韵
2015/12/24 职场文书
python如何查找列表中元素的位置
2022/05/30 Python