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 相关文章推荐
lib.utf.js
Aug 21 Javascript
javaScript parseInt字符转化为数字函数使用小结
Nov 05 Javascript
基于jQuery的树控件实现代码(asp.net+json)
Jul 11 Javascript
jquery获取table中的某行全部td的内容方法
Mar 08 Javascript
jQuery基本选择器选择元素使用介绍
Apr 18 Javascript
通过length属性判断jquery对象是否存在
Oct 18 Javascript
js中top的作用深入剖析
Mar 04 Javascript
bootstrapValidator自定验证方法写法
Dec 01 Javascript
vue组件实例解析
Jan 10 Javascript
jquery tmpl模板(实例讲解)
Sep 02 jQuery
通过js示例讲解时间复杂度与空间复杂度
Aug 06 Javascript
ES6中Symbol、Set和Map用法详解
Aug 20 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脚本[带参数]的方法
2010/01/22 PHP
PHP Socket 编程
2010/04/09 PHP
PHP浮点比较大小的方法
2016/02/14 PHP
Laravel框架FormRequest中重写错误处理的方法
2019/02/18 PHP
php使用pecl方式安装扩展操作示例
2019/08/12 PHP
Javascript----文件操作
2007/01/18 Javascript
jQuery选中select控件 无法设置selected的解决方法
2010/09/01 Javascript
Javascript Ajax异步读取RSS文档具体实现
2013/12/12 Javascript
jQuery实现长按按钮触发事件的方法
2015/02/02 Javascript
使用jQuery实现更改默认alert框体
2015/04/13 Javascript
JavaScript中的call方法和apply方法使用对比
2015/08/12 Javascript
JavaScript的函数式编程基础指南
2016/03/19 Javascript
js精准的倒计时函数分享
2016/06/29 Javascript
jQuery实现自动输入email、时间和域名的方法
2016/08/24 Javascript
node+experss实现爬取电影天堂爬虫
2016/11/20 Javascript
JS常用知识点整理
2017/01/21 Javascript
判断滚动条滑到底部触发事件(实例讲解)
2017/11/15 Javascript
原生js实现移动端触摸轮播的示例代码
2017/12/22 Javascript
vue2.0模拟锚点的实例
2018/03/14 Javascript
javascript实现鼠标点击生成文字特效
2019/12/24 Javascript
[01:52]2014DOTA2西雅图邀请赛 V社开大会你不知道的小秘密
2014/07/08 DOTA
Zabbix实现微信报警功能
2016/10/09 Python
Python线程指南详细介绍
2017/01/05 Python
python3.4爬虫demo
2019/01/22 Python
Pytorch技巧:DataLoader的collate_fn参数使用详解
2020/01/08 Python
Python confluent kafka客户端配置kerberos认证流程详解
2020/10/12 Python
python 爬虫之selenium可视化爬虫的实现
2020/12/04 Python
俄罗斯设计师家具购物网站:The Furnish
2019/12/01 全球购物
说说你所熟悉或听说过的j2ee中的几种常用模式?及对设计模式的一些看法
2012/05/24 面试题
十八届三中全会学习方案
2014/02/16 职场文书
后勤管理员岗位职责
2014/08/27 职场文书
小学语文教师竞聘演讲稿范文
2019/08/09 职场文书
Mysql MVCC机制原理详解
2021/04/20 MySQL
Java基础之详解HashSet的使用方法
2021/06/30 Java/Android
利用Python实现模拟登录知乎
2022/05/25 Python
windows11选中自动复制怎么开启? Win11自动复制所选内容的方法
2022/07/23 数码科技