在上一篇的基础上,实现了一下另外一种方式。
上一篇地址:https://www.cnblogs.com/ljwsyt/p/9525290.html
首先,该方式也是有几种方法。
1.在上一篇的基础上,将生成的html转化成canvas,然后就可以直接对canvas进行打印和保存。
需要注意的是,canvas打印的时候是一片空白的,需要先转化为图片然后打印。而生成canvas之后可以直接右键保存了,也可以增加按钮进行保存,保存的时候也是先转化为base64图片然后再进行保存。
方法:使用html2canvas插件进行转化,只需引入就可以直接运行,
html2canvas(document.querySelector("#toPrint")).then(canvas => {
document.body.appendChild(canvas)
});
其源码应该也是根据元素的位置绘制的canvas。
2.直接绘制canvas。
html代码:增加了两个按钮
1 <div>
2 <div id="printArea">
3 <!--startprint-->
4 <canvas id="toPrint">
5 </canvas>
6 <!--endprint-->
7 </div>
8 <div id="bottom_btns">
9 <button onclick="printNotifier()" class="layui-btn">打印</button>
10 <button onclick="saveNotifier1()" class="layui-btn">保存</button>
11 </div>
12 </div>
css代码:
1 #toPrint {
2 position:absolute;
3 left: 50%;
4 top: 50%;
5 }
6
7 #bottom_btns {
8 position: absolute;
9 bottom: 10px;
10 left: 50%;
11 /* 按钮宽度加缩进 */
12 margin-left: -70px;
13 }
js代码:移动端兼容也很OK
1 myApp.controller('notifierController2', function ($rootScope, $scope, services, $sce, $stateParams, $state) {
2 $scope.services = services;
3
4 //查询录取通知书内容
5 services["getApplyStatus"] = function (param) {
6 return $rootScope.serverAction('/apply/queryDegreeApplyInfo', param, "GET");
7 };
8
9 $scope.mobile = /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent);
10 if(1 == $rootScope._USERINFO.role || 2 == $rootScope._USERINFO.role) {
11 if($scope.mobile) {
12 $rootScope._ALLMENU = [{
13 children: [{
14 res_name: "查看审核状态",
15 res_url: "#status_mobile",
16 res_id: "#status_mobile"
17 },{
18 res_name: "查看修改申请资料",
19 res_url: "#registerMsg#review",
20 res_id: "#registerMsg#review"
21 },{
22 res_name: "打印录取通知书",
23 res_url: "#notifier",
24 res_id: "#notifier"
25 }]
26 }];
27 //$rootScope.mobile_regstatus = true;
28 } else {
29 $rootScope._ALLMENU = [{
30 children: [{
31 res_name: "查看审核状态",
32 res_url: "#status",
33 res_id: "#status"
34 },{
35 res_name: "查看修改申请资料",
36 res_url: "#registerMsg#review",
37 res_id: "#registerMsg#review"
38 },{
39 res_name: "打印录取通知书",
40 res_url: "#notifier",
41 res_id: "#notifier"
42 }]
43 }];
44 }
45 }
46 $rootScope.curentSel = "#notifier";
47 $rootScope.setContent = function(url) {
48 if($scope.mobile) {
49 $('#main-layout').removeClass('hide-side');
50 if(-1 < url.indexOf("#registerMsg")) {
51 window.open(encodeURI(encodeURI('/pages/index_mobile.html#/registers#review?id=' + $rootScope._USERINFO.id)));
52 window.location.href = "/pages/index_mobile.html#/home";
53 return;
54 } else {
55 $rootScope.curentSel = url;
56 }
57 } else {
58 if(-1 < url.indexOf("#registerMsg")) {
59 window.open(encodeURI(encodeURI('/pages/index.html#/registers#review?id=' + $rootScope._USERINFO.id)));
60 window.location.href = "/pages/index.html#/home";
61 return;
62 } else {
63 $rootScope.curentSel = url;
64 }
65 }
66 }
67
68 //模板
69 $scope.printObj = {
70 notifierObj:{
71 "url": "/res/img/notifications.png",
72 "height": "631",
73 "width": "942"
74 },
75 paramList:[{
76 "objName":"黄大明",
77 "left":"133",
78 "top":"191",
79 "size": "28"
80 },{
81 "objName":"SXXX小学",
82 "left":"460",
83 "top":"272",
84 "size": "28"
85 },{
86 "objName":"2018",
87 "left":"195",
88 "top":"312",
89 "size": "28"
90 },{
91 "objName":"8",
92 "left":"325",
93 "top":"312",
94 "size": "28"
95 },{
96 "objName":"31",
97 "left":"405",
98 "top":"312",
99 "size": "28"
100 }]
101 }
102
103 services.getApplyStatus('token').success(function(res) {
104 if ('OK' == res.result) {
105 if(res.msg) {
106 userName = res.msg.studentName;
107 $scope.printObj.paramList[0].objName = res.msg.studentName;
108 $scope.printObj.paramList[1].objName = res.msg.applySchoolName;
109
110 //屏幕自适应
111 suitScreen($scope);
112 //画图
113 drawNotifier($scope);
114 //组装页面
115 //assembleHtml($scope);
116 //打印
117 //printNotifier();
118 }
119 } else {
120 layer.alert(res.msg);
121 }
122 });
123
124 });
125
126 var userName;
127
128 function saveNotifier1() {
129 //一样需要先转化为图片后保存
130 var type = 'png';//格式可以自定义
131 var imgData = $("#toPrint")[0].toDataURL(type);
132 // 加工image data,替换mime type
133 imgData = imgData.replace(_fixType(type),'image/octet-stream');
134 //可以直接用以下下载,但是下载的文件名没有后缀
135 //window.location.href=image; // it will save locally
136 //文件名可以自定义
137 var filename = '录取通知书_' + userName + '.' + type;
138 saveFile(imgData,filename);
139 }
140
141 function _fixType(type) {
142 //imgData是一串string,base64
143 type = type.toLowerCase().replace(/jpg/i, 'jpeg');
144 var r = type.match(/png|jpeg|bmp|gif/)[0];
145 return 'image/' + r;
146 }
147
148 function saveFile(data, filename) {
149 //命名空间
150 var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
151 save_link.href = data;
152 save_link.download = filename;
153
154 //window.location = save_link;//此方法可下载但是文件名无效
155 //下载
156 var event = document.createEvent('MouseEvents');
157 event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
158 save_link.dispatchEvent(event);
159 }
160
161 function printNotifier() {
162 try{
163 print.portrait = false;//横向打印 ,去掉页眉页脚
164 }catch(e){
165 //alert("不支持此方法");
166 }
167
168 //canvas无法直接打印,需先转换成img
169 $(convertCanvasToImage($("#toPrint")[0])).jqprint();
170 }
171
172 function convertCanvasToImage(canvas) {
173 var image = new Image();
174 image.src = canvas.toDataURL("image/png");
175 return image;
176 }
177
178 function suitScreen($scope) {
179 //下方留30放按钮
180 var effectiveHeight = findParam("#printArea", "height") - 30;
181 var effectiveWidth = findParam("#printArea","width");
182 if($scope.printObj.notifierObj.width/effectiveWidth > $scope.printObj.notifierObj.height/effectiveHeight) {
183 //取最接近的一个属性进行自适应,并适当调小一些
184 var suitTimes = $scope.printObj.notifierObj.width/effectiveWidth*1.2;
185 } else {
186 var suitTimes = $scope.printObj.notifierObj.height/effectiveHeight*1.2;
187 }
188 $scope.printObj.notifierObj.width = $scope.printObj.notifierObj.width/suitTimes;
189 $scope.printObj.notifierObj.height = $scope.printObj.notifierObj.height/suitTimes;
190 for(i=0;i<$scope.printObj.paramList.length;i++) {
191 $scope.printObj.paramList[i].size = $scope.printObj.paramList[i].size/suitTimes;
192 $scope.printObj.paramList[i].left = $scope.printObj.paramList[i].left/suitTimes;
193 $scope.printObj.paramList[i].top = $scope.printObj.paramList[i].top/suitTimes;
194 }
195 }
196
197 function drawNotifier($scope) {
198 //canvas需要先定位好,否则画好再动就清除了
199 $("#toPrint").css("margin-left", (0-$scope.printObj.notifierObj.width)/2+"px");
200 //上移30放按钮
201 $("#toPrint").css("margin-top", (0-$scope.printObj.notifierObj.height-60)/2+"px");
202 var canvas = document.getElementById("toPrint");
203 canvas.width = $scope.printObj.notifierObj.width;
204 canvas.height = $scope.printObj.notifierObj.height;
205 var ctx = canvas.getContext("2d");
206 var img=new Image();
207 img.src = $scope.printObj.notifierObj.url;
208 img.onload=function() {
209 //需要onload方法接收,否则画不出
210 ctx.drawImage(img, 0, 0, $scope.printObj.notifierObj.width, $scope.printObj.notifierObj.height);
211 //写文字,且要在画好图片之后写,否则会被图片覆盖
212 $.each($scope.printObj.paramList, function(index, e) {
213 //canvas的字体不会有12px的兼容性问题
214 ctx.font = "bold "+e.size+"px KaiTi";
215 //canvas写字以字体的左下角为基准,因而要再加一个字体大小的高度
216 ctx.fillText(e.objName,e.left, e.top+e.size);
217 });
218 }
219
220 }
221
222 function assembleHtml($scope) {
223 var htmlStr = "<img src='" + $scope.printObj.notifierObj.url+"' style='width:"+$scope.printObj.notifierObj.width+"px;height:"+
224 $scope.printObj.notifierObj.height+"px'>";
225 for(i=0;i<$scope.printObj.paramList.length;i++) {
226 var nowObj = $scope.printObj.paramList[i];
227 if(nowObj.size < 12) {
228 htmlStr += "<div style='font-size:"+nowObj.size+"px;top:"+nowObj.top+"px;left:"+nowObj.left+
229 //谷歌浏览器字体小于12px时会不再变小,使用-webkit-transform兼容,并设置已左上角作为变换原点
230 "px;-webkit-transform:scale("+nowObj.size/12+","+nowObj.size/12+");transform-origin:0 0'>"+nowObj.objName+"</div>";
231 } else {
232 htmlStr += "<div style='font-size:"+nowObj.size+"px;top:"+nowObj.top+"px;left:"+nowObj.left+
233 "px'>"+nowObj.objName+"</div>";
234 }
235 }
236 $("#toPrint").css("margin-left", (0-$scope.printObj.notifierObj.width)/2+"px");
237 //上移30放按钮
238 $("#toPrint").css("margin-top", (0-$scope.printObj.notifierObj.height-60)/2+"px");
239 $("#toPrint").css("height", $scope.printObj.notifierObj.height+"px");
240 $("#toPrint").css("width", $scope.printObj.notifierObj.width+"px");
241 $("#toPrint").append(htmlStr);
242 }
243
244 //获取有效区域
245 function findParam(targetObj, attribute) {
246 //取数字
247 if($(targetObj).css(attribute) && $(targetObj).css(attribute).replace(/[^0-9]/ig,"") != '0') {
248 return $(targetObj).css(attribute).replace(/[^0-9]/ig,"");
249 } else {
250 //递归
251 return findParam($(targetObj).parent(), attribute);
252 }
253 }
几个需要注意的点:
(1)由于需要留出30像素高的底部放按钮,所有在计算绘制区域的有效高度时应减去30;
(2)绘制顺序:先调整好画布的高宽和位置-->绘制图片-->绘制文字。否则绘制后再调画布会清空,而且先绘制图片再绘制文字时文字覆盖图片而不是反过来;
(3)绘制图片和文字要在图片的onload事件中进行,否则图片还未加载完成就绘制的话会是一片空白区域;
(4)canvas的字体大小不必考虑12px的兼容性问题;
(5)fillText和strokeText,前者是绘制实心文字,后者是空心文字;
(6)画布在未设置宽和高的情况下,会有默认100多的高宽,没有研究源码,但是调试的时候发现的,所有我们取有效区域的时候,就不能直接用toPrint这个canvas进行取了,而要根据其父元素进行取;
(7)createElementNS,下载时用到的,创建带有指定命名空间的元素节点,和createElement类似;
(8)在定义好字体后绘制之前,可以cxt.fillStyle = "blue";
进行设置颜色
(9)最后就是canvas转图片的方法了,
var image = new Image();
image.src = canvas.toDataURL("image/png");
其中
canvas.toDataURL("image/png")就可以用来进行图片转base64.首先绘制canvas,画图片进去,然后就可以生成了。
附另外一种图片转base64的方法
使用FileReader
1 var reader = new FileReader();
2 var AllowImgFileSize = 2100000; //上传图片最大值(单位字节)( 2 M = 2097152 B )超过2M上传失败
3 var file = $("#img1")[0].files[0];
4 var imgUrlBase64;
5 if (file) {
6 //将文件以Data URL形式读入页面
7 imgUrlBase64 = reader.readAsDataURL(file);
8 reader.onload = function (e) {
9 //var ImgFileSize = reader.result.substring(reader.result.indexOf(",") + 1).length;//截取base64码部分(可选可不选,需要与后台沟通)
10 if (AllowImgFileSize != 0 && AllowImgFileSize < reader.result.length) {
11 alert( '上传失败,请上传不大于2M的图片!');
12 return;
13 }else{
14 //执行上传操作
15 //alert(reader.result);
16 var tempPhoto;
17 for(var i=0;i<$scope.registerMsg.userPhotoInfos.length;i++) {
18 //其他允许多张,否则只允许一张
19 if($scope.img_url_code == $scope.registerMsg.userPhotoInfos[i].photoType
20 //&& 12 != $scope.registerMsg.userPhotoInfos[i].photoType
21 ) {
22 tempPhoto = $scope.registerMsg.userPhotoInfos[i];
23 $scope.registerMsg.userPhotoInfos.splice(i, 1);
24 break;
25 }
26 }
27 if(tempPhoto) {
28 /*if(tempPhoto.photoName) {
29 tempPhoto.photoName = $("#img1")[0].files[0].name;
30 } else if(!$scope.review) {
31 tempPhoto['photoName'] = $("#img1")[0].files[0].name;
32 }*/
33 tempPhoto.photoUrl = "";
34 tempPhoto.base64 = reader.result;
35 $scope.registerMsg.userPhotoInfos.push(tempPhoto);
36 } else {
37 $scope.registerMsg.userPhotoInfos.push({
38 "id": '', //记录的id(更新接口需要带上)
39 "extendProperty": null,
40 "photoPath": "",
41 "photoUrl": "", //照片的预览路径
42 "userId": $scope.userId, //对应的user的id
43 "createTime": 0,
44 "photoType": $scope.img_url_code,
45 "updateTime": 0,
46 //"photoName": $("#img1")[0].files[0].name,
47 "base64": reader.result //图片的base64编码
48 })
49 }
50
51 ......
52 }
53 }
54 }
手机端会有些模糊,原因是canvas在绘制后,进行手机端兼容的情况下会缩放
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=0">
因而,又对上一篇的方案1进行了修改,增加手动打印和保存,在保存时先html转canvas,再canvas转图片进行保存。
由于jquery的版本问题出现了一些兼容性,高一点版本的代码中已经没有$.browser对象了,所有与jqprint出现了不兼容,解决方法是再拼接进去。
代码:
1 (function(jQuery){
2
3 if(jQuery.browser) return;
4
5 jQuery.browser = {};
6 jQuery.browser.mozilla = false;
7 jQuery.browser.webkit = false;
8 jQuery.browser.opera = false;
9 jQuery.browser.msie = false;
10
11 var nAgt = navigator.userAgent;
12 jQuery.browser.name = navigator.appName;
13 jQuery.browser.fullVersion = ''+parseFloat(navigator.appVersion);
14 jQuery.browser.majorVersion = parseInt(navigator.appVersion,10);
15 var nameOffset,verOffset,ix;
16
17 // In Opera, the true version is after "Opera" or after "Version"
18 if ((verOffset=nAgt.indexOf("Opera"))!=-1) {
19 jQuery.browser.opera = true;
20 jQuery.browser.name = "Opera";
21 jQuery.browser.fullVersion = nAgt.substring(verOffset+6);
22 if ((verOffset=nAgt.indexOf("Version"))!=-1)
23 jQuery.browser.fullVersion = nAgt.substring(verOffset+8);
24 }
25 // In MSIE, the true version is after "MSIE" in userAgent
26 else if ((verOffset=nAgt.indexOf("MSIE"))!=-1) {
27 jQuery.browser.msie = true;
28 jQuery.browser.name = "Microsoft Internet Explorer";
29 jQuery.browser.fullVersion = nAgt.substring(verOffset+5);
30 }
31 // In Chrome, the true version is after "Chrome"
32 else if ((verOffset=nAgt.indexOf("Chrome"))!=-1) {
33 jQuery.browser.webkit = true;
34 jQuery.browser.name = "Chrome";
35 jQuery.browser.fullVersion = nAgt.substring(verOffset+7);
36 }
37 // In Safari, the true version is after "Safari" or after "Version"
38 else if ((verOffset=nAgt.indexOf("Safari"))!=-1) {
39 jQuery.browser.webkit = true;
40 jQuery.browser.name = "Safari";
41 jQuery.browser.fullVersion = nAgt.substring(verOffset+7);
42 if ((verOffset=nAgt.indexOf("Version"))!=-1)
43 jQuery.browser.fullVersion = nAgt.substring(verOffset+8);
44 }
45 // In Firefox, the true version is after "Firefox"
46 else if ((verOffset=nAgt.indexOf("Firefox"))!=-1) {
47 jQuery.browser.mozilla = true;
48 jQuery.browser.name = "Firefox";
49 jQuery.browser.fullVersion = nAgt.substring(verOffset+8);
50 }
51 // In most other browsers, "name/version" is at the end of userAgent
52 else if ( (nameOffset=nAgt.lastIndexOf(' ')+1) <
53 (verOffset=nAgt.lastIndexOf('/')) )
54 {
55 jQuery.browser.name = nAgt.substring(nameOffset,verOffset);
56 jQuery.browser.fullVersion = nAgt.substring(verOffset+1);
57 if (jQuery.browser.name.toLowerCase()==jQuery.browser.name.toUpperCase()) {
58 jQuery.browser.name = navigator.appName;
59 }
60 }
61 // trim the fullVersion string at semicolon/space if present
62 if ((ix=jQuery.browser.fullVersion.indexOf(";"))!=-1)
63 jQuery.browser.fullVersion=jQuery.browser.fullVersion.substring(0,ix);
64 if ((ix=jQuery.browser.fullVersion.indexOf(" "))!=-1)
65 jQuery.browser.fullVersion=jQuery.browser.fullVersion.substring(0,ix);
66
67 jQuery.browser.majorVersion = parseInt(''+jQuery.browser.fullVersion,10);
68 if (isNaN(jQuery.browser.majorVersion)) {
69 jQuery.browser.fullVersion = ''+parseFloat(navigator.appVersion);
70 jQuery.browser.majorVersion = parseInt(navigator.appVersion,10);
71 }
72 jQuery.browser.version = jQuery.browser.majorVersion;
73 })(jQuery);
整改项目的代码在
https://github.com/MRlijiawei/enroll
其他还有图片转化与保存及自定义文件名的方法,大家也可以作为参照。