H5上传图片并使用canvas制作海报
马上就要“十一”国庆节了,又恰逢公司已经三周岁了,所以市场部和产品共同策划了一个“正青春,共成长”的主题代言活动,准备在国庆节以及中秋节期间让公司员工和用户为公司代言,于是就有了技术部前端开发人员即本人为公司来开发这个主题代言活动页面。
开发要求:
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);
}
最后,由于已经使用exif.js来对图片进行了纠正的处理,已经不再是iphone手机拍完照上传时的图片信息了,所以在第二次利用canvas将其生成海报时,就不会再被旋转了,至于第一次用canvas将拍的照片画成750px的图片是不想用户上传到图片服务器的图片过大从而导致加载过慢的问题,再者,如果不考虑上传到图片服务器的图片过大,那么将会生成base64格式的图片字符串,但这里也还有一个隐藏的问题就是在有些iphone手机(我当时测的是iphone 6 plus)上不支持直接将base64格式的图片直接通过canvas画出来,所以为了兼容性,也只能通过后台将base64字符串格式的图片转换为普通的图片格式再来为最后的生成海报服务。
3、生成海报的代码如下:
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);
}

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事件里加入了ev.preventDefault()来禁止浏览器的默认行为,那么在触摸input文本表单时,input就无法获取焦点。
解决思路及办法:
刚开始想的是能不能在touchend的时候释放前边禁止的浏览器默认行为,结果就是然并卵,因为即使你在触摸结束时释放浏览器的默认行为,但触摸时又给禁止了,真该打自己。接着又想着能不能在页面加载完成后就禁止,结果还是然并卵,然后想到的是在触摸时就让input获取焦点focus,但是如果input中已经输入了文字,那么触摸时焦点就位于文字的最后而无法将焦点具体定位在文字中的某个地方,还是然并卵,于是就各种上网查资料也还是没有解决。最后想到的是获取手指触摸的元素,如果触摸的是input表单,那么就不禁止浏览器的默认行为,否则就禁止,这样带来的结果是虽然在触摸input时也可以拖动,但浏览器的默认行为没有被禁止,在微信中,如果向下拖动,浏览器的上边就会出现那个显示浏览器内核信息的黑色底,不过在触摸input表单边上的其他区域时是可以禁止浏览器的默认行为并拖动的,虽不完美,但实在是没有找到其他的解决办法了,如果有大神知道完美解决的办法,请及时告知,先在此谢过!!!
H5上传图片并使用canvas制作海报的更多相关文章
- Canvas 制作海报
HTML <template> <view class="content"> <view class="flex_row_c_c mod ...
- H5上传图片之canvas
H5上传图片之canvas,使用canvas处理压缩图片再上传 html代码: <form action="" method="post"> < ...
- angular下H5上传图片(可多张上传)
最近做的项目中用到了angular下上传图片功能,在做的过程中遇到了许多问题,最终都得以解决 angular上传时和普通上传时过程差不多,只不过是要不一些东西转化为angular的东西. 1.ng-f ...
- 使用canvas制作在线画板
canvas绘图的强大功能,让人前仆后继的去研究它.代码全部加起来不足百行.还用到了h5中的<input type="color"/>和<input type=& ...
- 利用canvas制作乱跑的小球
canvas制作乱跑的小球 说明:将下面的代码放到html的body就可以,键盘控制上(W)下(S)左(A)右(D) <body> <canvas id="canvas&q ...
- <canvas合成海报>所遇问题及解决方案总结
最近做了一个用canvas合成海报图片的移动端项目,由于一点canvas基础都没有,所以去网上搜了一位前辈的demo,但是开发过程中遇到了很多问题,现将所遇问题及解决方法总结如下: 1.移动端canv ...
- H5动效的常见制作手法
众所周知,一个元素,动往往比静更吸引眼球: 一套操作界面,合适的动态交互反馈能给用户带来更好的操作体验: 一个H5运营宣传页,炫酷的动画特效定能助力传播和品牌打造. 近两年,小到loading动画,表 ...
- 用Canvas制作简单的画图工具
今天用Canvas制作了一个画图工具,非常简单,功能也不是很多,主要有背景网格,画线,画圆,画矩形和画圆角矩形,也用到了canvas的一些基本知识,在这里一一列举. 1.线段的绘制: 如何绘制真正的1 ...
- 酷!使用 jQuery & Canvas 制作相机快门效果
在今天的教程中,我们将使用 HTML5 的 Canvas 元素来创建一个简单的摄影作品集,它显示了一组精选照片与相机快门的效果.此功能会以一个简单的 jQuery 插件形式使用,你可以很容易地整合到任 ...
随机推荐
- jquery选中radio或checkbox的正确姿势
jquery选中radio或checkbox的正确姿势 Intro 前几天突然遇到一个问题,没有任何征兆的..,jquery 选中radio button单选框时,一直没有办法选中,后来查了许多资料, ...
- 基于linux vim环境python代码自动补全
(一)简述 在使用vim编写python文件的过程中,默认的vim不会实现代码补全功能,在写程序或者是改程序的时候不是很方面,很容易出错,但是vim提供了各种插件,其中包括这个python文件的自动补 ...
- Vue相关(过渡动画)
Vue 过渡 && 动画 一.CSS过渡 1.transition标签可以用来封装需要过渡的元素,添加entering/leaving 过渡, 条件是: (1)使用条件渲染语句 v-i ...
- 【JCP模式实战--ferrous-framework】ferrous前端开发框架邀您初体验
一.简介 ferrous-framework是为了迎合微服务架构而封装的纯前端开发框架. 实现了一种介于单页面和多页面的开发模式,让大家根据自己的需要对单页面和多页面进行切换或者共存. 页面结构采用J ...
- keepalive之LVS-DR架构
author:JevonWei 版权声明:原创作品 Keepalive实战之LVS-DR 实验目的:构建LVS-DR架构,为了达到LVS的高可用目的,故在LVS-DR的Director端做Keepal ...
- Vuejs 页面的区域化与组件封装
组件的好处 当我用vue写页面的时候,大量的数据页面渲染,引入组件简化主页面的代码量,当代码区域块代码差不多相同时,组件封装会更加简化代码.组件是Vue.js最强大的功能之一. 组件可以扩展HTML元 ...
- 慕课网视频破解付费分享-前端开发-Python等
微信小程序 慕课网 BAT大牛经验总结全面深入解读Android面试 前端JS基础面试技巧 vue2.0+node.js+mongodb全栈打造商城 Vue.js高级实战-开发移动端音 ...
- 简单理解java中timer的schedule和scheduleAtFixedRate方法的区别
timer的schedule和scheduleAtFixedRate方法一般情况下是没什么区别的,只在某个情况出现时会有区别--当前任务没有来得及完成下次任务又交到手上. 我们来举个例子: 暑假到了老 ...
- JavaScript封装一个MyAlert弹出框
平时我们想要显示一些提示信息时会用到alert方法,alert是全局的一个方法,会短暂的中断程序,我们主要用来显示提示客户信息.但是这个方法有一定的局限性,而且本身样式也不够美观.于是我封装了一个实用 ...
- 201521123082 《Java程序设计》第12周学习总结
201521123082 <Java程序设计>第12周学习总结 标签(空格分隔): java 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. An ...