资讯专栏INFORMATION COLUMN

使用 JavaScript 根据用户照片和姓名生成海报

rickchen / 2333人阅读

摘要:前言最近在为公司的一个比赛制作专题页,碰到一个使用参赛者上传的照片生成专属海报的需求,实现过程中用到了一些以前没用过的,也踩了一些坑,于是将其记录下来。

前言

最近在为公司的一个比赛制作专题页,碰到一个使用参赛者上传的照片生成专属海报的需求,实现过程中用到了一些以前没用过的 api,也踩了一些坑,于是将其记录下来。

需求描述

用户点击按钮进行照片上传

照片上传完成后,将照片进行裁剪,并和海报背景、姓名等组合得到海报

将生成的海报上传

效果大概如下:

海报背景:

成品:

实现过程 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、绘制照片

海报中放置照片的区域为正方形,但是用户上传的照片却不一定,因此需要对照片进行裁剪,裁剪的原则为取照片中间部分。然后将裁剪参数传进 canvasdrawImage 方法,进行绘制:

$("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 支持 canvastoBlob 方法,可以直接使用:

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) {
    
  }
});

至此,整个流程完结。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86919.html

相关文章

  • 手对手的教你用canvas画一个简单的海报

    摘要:啦啦啦,首先说下需求,产品想让用户在我们内,分享一张图片到微信等平台。图片中包含用户的姓名头像和带着自己信息的二维码。然后,如何生成这张海报呢首先我们老大告诉我有一个插件叫其作用就是可以将节点转化成图片,是个不错的东西。 啦啦啦,首先说下需求,产品想让用户在我们app内,分享一张图片到微信、qq等平台。图片中包含用户的姓名、头像、和带着自己信息的二维码。然后,如何生成这张海报呢~~~首...

    verano 评论0 收藏0
  • 手对手的教你用canvas画一个简单的海报

    摘要:啦啦啦,首先说下需求,产品想让用户在我们内,分享一张图片到微信等平台。图片中包含用户的姓名头像和带着自己信息的二维码。然后,如何生成这张海报呢首先我们老大告诉我有一个插件叫其作用就是可以将节点转化成图片,是个不错的东西。 啦啦啦,首先说下需求,产品想让用户在我们app内,分享一张图片到微信、qq等平台。图片中包含用户的姓名、头像、和带着自己信息的二维码。然后,如何生成这张海报呢~~~首...

    BlackHole1 评论0 收藏0
  • 手对手的教你用canvas画一个简单的海报

    摘要:啦啦啦,首先说下需求,产品想让用户在我们内,分享一张图片到微信等平台。图片中包含用户的姓名头像和带着自己信息的二维码。然后,如何生成这张海报呢首先我们老大告诉我有一个插件叫其作用就是可以将节点转化成图片,是个不错的东西。 啦啦啦,首先说下需求,产品想让用户在我们app内,分享一张图片到微信、qq等平台。图片中包含用户的姓名、头像、和带着自己信息的二维码。然后,如何生成这张海报呢~~~首...

    cyixlq 评论0 收藏0

发表评论

0条评论

rickchen

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<