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 相关文章推荐
jQuery 顺便学习下CSS选择器 奇偶匹配nth-child(even)
May 24 Javascript
js如何获取file控件的完整路径具体实现代码
May 15 Javascript
php与js的区别是什么
Aug 05 Javascript
不使用script导入js文件的几种方法
Oct 27 Javascript
vue自定义底部导航栏Tabbar的实现代码
Sep 03 Javascript
微信小程序动态增加按钮组件
Sep 14 Javascript
一百行JS代码实现一个校验工具
Apr 30 Javascript
js实现掷骰子小游戏
Oct 24 Javascript
Vue+Element-UI实现上传图片并压缩
Nov 26 Javascript
Vue-router 报错NavigationDuplicated的解决方法
Mar 31 Javascript
JavaScript中clientWidth,offsetWidth,scrollWidth的区别
Jan 25 Javascript
react中useState使用:如何实现在当前表格直接更改数据
Aug 05 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
CI(CodeIgniter)框架中的增删改查操作
2014/06/10 PHP
PHP屏蔽过滤指定关键字的方法
2014/11/03 PHP
php中分页及SqlHelper类用法实例
2017/01/12 PHP
PHP PDOStatement::getAttribute讲解
2019/02/01 PHP
PHP实现百度人脸识别
2019/05/06 PHP
Yii中特殊行为ActionFilter的使用方法示例
2020/10/18 PHP
浅谈JavaScript中面向对象技术的模拟
2006/09/25 Javascript
Prototype Hash对象 学习
2009/07/19 Javascript
浅析Js(Jquery)中,字符串与JSON格式互相转换的示例(直接运行实例)
2013/07/09 Javascript
js选择并转移导航菜单示例代码
2014/08/19 Javascript
jQuery多媒体插件jQuery Media Plugin使用详解
2014/12/19 Javascript
jQuery表格插件datatables用法汇总
2016/03/29 Javascript
javascript封装addLoadEvent实现页面同时加载执行多个函数的方法
2016/07/25 Javascript
jquery过滤特殊字符',防sql注入的实现方法
2016/08/17 Javascript
JavaScript表单即时验证 验证不成功不能提交
2017/08/31 Javascript
React diff算法的实现示例
2018/04/20 Javascript
vue 监听屏幕高度的实例
2018/09/05 Javascript
详解iframe跨域的几种常用方法(小结)
2019/04/29 Javascript
[00:32]2018DOTA2亚洲邀请赛Liquid出场
2018/04/03 DOTA
python使用cookielib库示例分享
2014/03/03 Python
Python利用前序和中序遍历结果重建二叉树的方法
2016/04/27 Python
Python入门_浅谈逻辑判断与运算符
2017/05/16 Python
Python中xrange与yield的用法实例分析
2017/12/26 Python
python对日志进行处理的实例代码
2018/10/06 Python
说说如何遍历Python列表的方法示例
2019/02/11 Python
python调用c++返回带成员指针的类指针实例
2019/12/12 Python
python列表返回重复数据的下标
2020/02/10 Python
jupyter notebook中美观显示矩阵实例
2020/04/17 Python
CSS3动画:5种预载动画效果实例
2017/04/05 HTML / CSS
移动端HTML5实现文件上传功能【附代码】
2016/03/25 HTML / CSS
中学门卫岗位职责
2013/12/26 职场文书
骨干教师培训感言
2014/01/16 职场文书
开门红主持词
2014/04/02 职场文书
教师年度考核评语
2014/04/28 职场文书
卫生院健康教育实施方案
2014/06/07 职场文书
Django中celery的使用项目实例
2022/07/07 Python