在上一篇的基础上,实现了一下另外一种方式。
上一篇地址: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='"+$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
其他还有图片转化与保存及自定义文件名的方法,大家也可以作为参照。