马上就要“十一”国庆节了,又恰逢公司已经三周岁了,所以市场部和产品共同策划了一个“正青春,共成长”的主题代言活动,准备在国庆节以及中秋节期间让公司员工和用户为公司代言,于是就有了技术部前端开发人员即本人为公司来开发这个主题代言活动页面。
开发要求:
1、用户可以从手机相册上传图片或拍照上传图片;
2、用户可以输入为公司代言的地点,如:我在:上海;
3、将用户输入的代言地点及活动二维码生成一张图片供用户保存到手机,以方便发送朋友圈或好友。
功能实现:
1、使用H5的input[type="file"]标签来上传图片,并使用其原生的js代码将其转换成base64字符串的图片;
2、使用canvas将用户上传的图片和输入的代言地点及活动二维码生成一张图片;
3、记录用户生成的图片信息。
看似简单的功能实现,实则麻烦多多。首先,自己本身对canvas不熟悉,平时基本不用,做的时候算是现学现用;其次,在使用canvas画图时,在IOS上竖拍的图片会被自动旋转90度,导致出来的图片不是用户拍时的效果,具体原因请参考iOS手机竖着拍的照片经过前端处理之后被旋转了90°的原因以及解决方案,这里才是本次开发的难点所在,这个问题折腾了我两三天的时间啊,不知道IOS为何要这么做,我也是查了好多资料,最终才得以解决。
代码实现:
1、上传代码使用到了H5的input[type="file"]:
- <input type="file" class="uploadfile" accept="image/*">
2、生成base64字符串的图片:
- /图片上传
- var file = {
- upload: function (e) {
- var file = e.target.files[0];
- var type = file.type.split('/')[0];
- if (type != 'image ') {
- alert('请上传图片');
- return;
- }
- //var size = Math.floor(file.size / 1024 / 1024);
- //if (size > 3) {
- // alert('图片大小不得超过3M ');
- // return;
- //};
- var reader = new FileReader();
- reader.readAsDataURL(file);
- var orientation = null;
- //获取照片方向角属性,用户旋转控制
- EXIF.getData(file, function () {
- EXIF.getAllTags(this);
- orientation = EXIF.getTag(this, 'Orientation ');
- });
- reader.onloadstart = function () {
- $(".ajaxLoading").show();
- };
- reader.onloadend = function (e) {
- var dataURL = reader.result;
- var imaged = new Image();
- imaged.src = dataURL;
- imaged.onload = function () {
- var canvas = document.createElement('canvas ');
- var ctx = canvas.getContext('2d ');
- //普通环境下设置canvas的宽高
- var w = 0,
- h = 0;
- if (this.width < 750) {
- w = this.width;
- h = this.height;
- canvas.width = w;
- canvas.height = h;
- } else {
- w = 750;
- canvas.width = w;
- var scale = this.width / this.height;
- w = w > this.width ? this.width : w
- h = w / scale;
- canvas.height = h;
- }
- if (navigator.userAgent.match(/iphone/i)) {
- if (orientation != "") {
- switch (orientation) {
- case 3:
- ctx.rotate(180 * Math.PI / 180);
- ctx.drawImage(this, -w, -h, w, h);
- break;
- case 6:
- //这里由于将图片纠正了回来,所以也要重新设置canvas的高已达到高度自适应
- canvas.width = 750;
- var scale = this.height / this.width;
- canvas.height = canvas.width / scale;
- h = 750 > this.height ? this.height : 750;
- w = h / scale;
- ctx.rotate(90 * Math.PI / 180);
- ctx.drawImage(this, 0, -h, w, h);
- break;
- case 8:
- ctx.rotate(270 * Math.PI / 180);
- ctx.drawImage(this, -h, 0, h, w);
- break;
- case 2:
- ctx.translate(w, 0);
- ctx.scale(-1, 1);
- ctx.drawImage(this, 0, 0, w, h);
- break;
- case 4:
- ctx.translate(w, 0);
- ctx.scale(-1, 1);
- ctx.rotate(180 * Math.PI / 180);
- ctx.drawImage(this, -w, -h, w, h);
- break;
- case 5:
- ctx.translate(w, 0);
- ctx.scale(-1, 1);
- ctx.rotate(90 * Math.PI / 180);
- ctx.drawImage(this, 0, -w, h, w);
- break;
- case 7:
- ctx.translate(w, 0);
- ctx.scale(-1, 1);
- ctx.rotate(270 * Math.PI / 180);
- ctx.drawImage(this, -h, 0, h, w);
- break;
- default:
- ctx.drawImage(this, 0, 0, w, h);
- }
- }
- } else {
- ctx.drawImage(this, 0, 0, w, h);
- }
- $.post(ulr, { base64_string: canvas.toDataURL('image / jpeg ') }, function (res) {
- if (res.success) {
- $("#daiyan_bg").attr("src", res.message);
- $(".ajaxLoading").hide();
- var qrCodeH = 0;
- if (navigator.userAgent.match(/iphone/i)) {
- if (orientation != "") {
- switch (orientation) {
- case 6:
- qrCodeH = w / 2 - $(".qrcode").height() - 105;
- break;
- default:
- qrCodeH = h / 2 - $(".qrcode").height() - 105;
- }
- }
- } else {
- var qrCodeH = h / 2 - $(".qrcode").height() - 105;
- }
- $(".qrcode").css("top", qrCodeH);
- $(".page1").hide().siblings(".page2").show();
- }
- }, "json");
- };
- };
- },
- event: function () {
- $(".uploadfile").change(function (e) {
- file.upload(e);
- });
- },
- init: function () {
- this.event();
- }
- };
- file.init();'
以上js代码中就解决了IOS将图片旋转90度的问题,使用到了
,解决代码如下:
- exif.js
首先获取照片方向角属性orientation :
- var orientation = null;
- //获取照片方向角属性,用户旋转控制
- EXIF.getData(file, function () {
- EXIF.getAllTags(this);
- orientation = EXIF.getTag(this, 'Orientation') ;
- });
其次根据orientation来旋转图片即可得到正确的图片:
- if (navigator.userAgent.match(/iphone/i)) {
- if (orientation != "") {
- switch (orientation) {
- case 3:
- ctx.rotate(180 * Math.PI / 180);
- ctx.drawImage(this, -w, -h, w, h);
- break;
- case 6:
- canvas.width = 750;
- var scale = this.height / this.width;
- canvas.height = canvas.width / scale;
- h = 750 > this.height ? this.height: 750;
- w = h / scale;
- ctx.rotate(90 * Math.PI / 180);
- ctx.drawImage(this, 0, -h, w, h);
- break;
- case 8:
- ctx.rotate(270 * Math.PI / 180);
- ctx.drawImage(this, -h, 0, h, w);
- break;
- case 2:
- ctx.translate(w, 0);
- ctx.scale( - 1, 1);
- ctx.drawImage(this, 0, 0, w, h);
- break;
- case 4:
- ctx.translate(w, 0);
- ctx.scale( - 1, 1);
- ctx.rotate(180 * Math.PI / 180);
- ctx.drawImage(this, -w, -h, w, h);
- break;
- case 5:
- ctx.translate(w, 0);
- ctx.scale( - 1, 1);
- ctx.rotate(90 * Math.PI / 180);
- ctx.drawImage(this, 0, -w, h, w);
- break;
- case 7:
- ctx.translate(w, 0);
- ctx.scale( - 1, 1);
- ctx.rotate(270 * Math.PI / 180);
- ctx.drawImage(this, -h, 0, h, w);
- break;
- default:
- ctx.drawImage(this, 0, 0, w, h);
- }
- }
- } else {
- ctx.drawImage(this, 0, 0, w, h);
- }
最后,由于已经使用
来对图片进行了纠正的处理,已经不再是iphone手机拍完照上传时的图片信息了,所以在第二次利用canvas将其生成海报时,就不会再被旋转了,至于第一次用canvas将拍的照片画成750px的图片是不想用户上传到图片服务器的图片过大从而导致加载过慢的问题,再者,如果不考虑上传到图片服务器的图片过大,那么将会生成base64格式的图片字符串,其宽高也是图片本来的宽高,但这里也还有一个隐藏的问题就是在有些iphone手机(我当时测的是iphone 6 plus)上不支持直接将base64格式的图片通过canvas画出来,所以为了兼容性,也只能通过后台将base64字符串格式的图片转换为普通的图片格式再来为最后的生成海报服务。 3、生成海报的代码如下:
- exif.js
- var pointMsg = $(".pointtxt").val(),
- daiyanMsg = $(".daiyan_msg").html();
- var imgbox = document.getElementById("daiyan_bg"),
- canvas = document.getElementById("myCanvas");
- var ctx = canvas.getContext("2d");
- canvas.width = 750;
- var imgUrl = new Image,
- qrCodeUrl = new Image,
- point = new Image;
- imgUrl.crossOrigin = "anonymous";
- imgUrl.src = imgbox.src;
- qrCodeUrl.src = "/Resource/4.0/images/201709daiyan/qrcode.jpg";
- point.src = "/Resource/4.0/images/201709daiyan/point.png";
- var daiyan_text = "我要代颜";
- var btm = document.getElementById("bottom");
- imgUrl.onload = function() {
- canvas.height = this.height + 50;
- ctx.fillStyle = "#fff";
- ctx.fillRect(0, 0, 750, 1334);
- var erWeiMaY = this.height - qrCodeUrl.height - 70;
- var wyDyY = this.height - 57;
- if (ctx.drawImage(imgUrl, 0, 0, 750, this.height), ctx.beginPath(), ctx.beginPath(), ctx.font = "normal 44px PingFangSC-Medium", ctx.textAlign = "start", ctx.textBaseline = "hanging", ctx.shadowOffsetX = 0, ctx.shadowOffsetY = 5, ctx.shadowColor = "rgba(0, 0, 0, 0.3)", ctx.shadowBlur = 5, "" != pointMsg && void 0 != pointMsg && null != pointMsg) {
- var pTxt = "我在:" + pointMsg;
- ctx.fillText(pTxt, 95, offy * 2 + 55);
- ctx.drawImage(point, 0, 0, 42, 55, 40, offy * 2 + 55, 42, 55);
- }
- ctx.beginPath();
- ctx.drawImage(qrCodeUrl, 0, 0, 140, 140, 570, erWeiMaY, 140, 140);
- ctx.drawImage(btm, 0, 0, 750, 50, 0, this.height, 750, 50);
- if (ctx.beginPath(), ctx.font = "normal 22px PingFangSC-Medium") {
- ctx.fillText(daiyan_text, 595, wyDyY);
- } ! [](http: //images2017.cnblogs.com/blog/688074/201709/688074-20170930143321965-1499864646.jpg)
- if (ctx.beginPath(), ctx.font = "normal 30px PingFangSC-Medium", ctx.shadowOffsetX = 0, ctx.shadowOffsetY = 5, ctx.shadowColor = "rgba(0, 0, 0, 0.3)", ctx.shadowBlur = 5) {
- ctx.fillText(daiyanMsg, 95, offy * 2 + 130);
- }
- document.getElementById("daiyan_bg1").src = canvas.toDataURL('image/jpeg');
- $(".page2").hide().siblings(".page3").show();
- $.post(url, {
- base64_string: canvas.toDataURL('image/jpeg')
- },
- function(res) {},
- "json"); //记录用户生成的海报信息
- var tipTop = this.height / 4 - 47; $(".save_tip").show().css("top", tipTop); setTimeout(function() {
- $(".save_tip").hide();
- },
- 3000);
- };
在这一步生成的海报其实也是base64字符串格式的图片,此时就可以用微信浏览器的长按保存功能将图片保存至手机上。至此,完成全部的开发。
案例代码下载:
H5上传图片并使用canvas制作海报
本案例中由于是纯静态页面,所以去掉了上传图片至图片服务器的代码,但在本博文贴出来的代码中加入了上传图片至图片服务器的代码,最后在案例代码中也加入了可以拖动用户输入文字那块的效果,那块的效果在做的时候其实也是折腾了两三个小时。原因如下:
产品要求用户可以输入文字,所以就加了一个input文本表单,但是又要求输入文字那里可以拖动,因此出现了新的问题:在拖动的touchstart事件里加入了
来禁止浏览器的默认行为,那么在触摸input文本表单时,input就无法获取焦点。 解决思路及办法:
- ev.preventDefault()
来源: http://www.cnblogs.com/tnnyang/p/7614905.html