欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-09 16:13:32
前言最近在为公司的一个比赛制作专题页,碰到一个使用参赛者上传的照片生成专属海报的需求,实现过程中用到了一些以前没用过的 api,也踩了一些坑,于是将其记录下来。需求描述 用户点击按钮进行照片上传...

前言

最近在为公司的一个比赛制作专题页,碰到一个使用参赛者上传的照片生成专属海报的需求,实现过程中用到了一些以前没用过的 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) {
  
 }
});

至此,整个流程完结。

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