最近突然被加了要打印證書的功能的需求。其實打印功能很簡單,直接調用window.print()就可以打印,只是這是最基本的打印,會打印當前頁面的所有元素,而我們要的是局部打印,實現方法:
1.設置好開頭與結尾,然后進行識別和打印(前邊有介紹),見:https://www.cnblogs.com/ljwsyt/p/9511546.html;
2.使用jqPrint進行打印。jqPrint是對window的打印進行了一些簡單便捷的封裝,其源代碼很少。
地址:https://github.com/MRlijiawei/enroll/blob/master/enroll-webapp/src/main/resources/static/plugins/print/jquery.jqprint-0.3.js
項目要求寫一個公共插件,在任何系統都可以使用。但是局限性太大,而且和應用場景有很大關聯。如果是做一個拖拽組件,那就只能管理員的角色進行一一拖拽,比較費時;最終暫時寫了一個用戶自助打印的組件。
思路是使用絕對布局把文字定位到證書圖片上相應的位置,然后打印。
html代碼:
<div> <div id="printArea"> <!--startprint942,631--> <div id="toPrint"> </div> <!--endprint--> </div> <div style="display:none"> <button onclick="printNotifier()">打印</button> </div> </div>
有效區域只有一個空的div
css代碼:
#toPrint { position:absolute; /* background:url('/res/img/notifications.png'); */ left: 50%; top: 50%; } #toPrint div { position:absolute; } #toPrint img { position:absolute; }
可以看到只是設置了一個居中和絕對布局。其余樣式在js中動態控制,因為做了自適應和公共化。
js代碼:
1 myApp.controller('notifierController', 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 //模板 10 $scope.printObj = { 11 notifierObj:{ 12 "url": "/res/img/notifications.png", 13 "height": "631", 14 "width": "942" 15 }, 16 paramList:[{ 17 "objName":"黃大明", 18 "left":"133", 19 "top":"189", 20 "size": "28" 21 },{ 22 "objName":"SXXX小學", 23 "left":"460", 24 "top":"270", 25 "size": "28" 26 },{ 27 "objName":"2018", 28 "left":"195", 29 "top":"314", 30 "size": "28" 31 },{ 32 "objName":"8", 33 "left":"325", 34 "top":"314", 35 "size": "28" 36 },{ 37 "objName":"31", 38 "left":"405", 39 "top":"314", 40 "size": "28" 41 }] 42 } 43 44 services.getApplyStatus('token').success(function(res) { 45 if ('OK' == res.result) { 46 if(res.msg) { 47 $scope.printObj.paramList[0].objName = res.msg.studentName; 48 $scope.printObj.paramList[1].objName = res.msg.applySchoolName; 49 50 //屏幕自適應 51 suitScreen($scope); 52 //組裝頁面 53 assembleHtml($scope); 54 //打印 55 printNotifier(); 56 } 57 } else { 58 layer.alert(res.msg); 59 } 60 }); 61 62 //拖動 63 /*$(document).on("mousedown","#toPrint",function(e){ 64 //webkit內核和火狐禁止文字被選中 65 $('body').addClass('select') 66 //ie瀏覽器禁止文字選中 67 document.body.onselectstart=document.body.ondrag=function(){ 68 return false; 69 } 70 $scope.moveBegin = true; 71 $scope.mouseStartPoint = {"left":e.pageX,"top": e.pageY}; 72 }); 73 $(document).on("mouseup",function(e){ 74 $scope.moveBegin = false; 75 }); 76 $(document).on("mousemove",function(e){ 77 if(!$scope.moveBegin) { 78 return; 79 } 80 $("#toPrint").css("margin-left", e.pageX - $scope.mouseStartPoint.left - 454 + "px"); 81 $("#toPrint").css("margin-top", e.pageY - $scope.mouseStartPoint.top - 315 + "px"); 82 });*/ 83 }); 84 85 function printNotifier() { 86 try{ 87 print.portrait = false;//橫向打印 88 }catch(e){ 89 //alert("不支持此方法"); 90 } 91 var HKEY_RootPath="HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\PageSetup\\"; 92 /*try{ 93 var WSc=new ActiveXObject("WScript.Shell"); 94 HKEY_Key="header"; 95 WSc.RegWrite(HKEY_RootPath+HKEY_Key,""); 96 HKEY_Key="footer"; 97 WSc.RegWrite(HKEY_RootPath+HKEY_Key,""); 98 }catch(e){ 99 100 }*/ 101 $("#printArea").jqprint(); 102 } 103 104 function suitScreen($scope) { 105 var effectiveHeight = findParam("#toPrint", "height"); 106 var effectiveWidth = findParam("#toPrint","width"); 107 if($scope.printObj.notifierObj.width/effectiveWidth > $scope.printObj.notifierObj.height/effectiveHeight) { 108 //取最接近的一個屬性進行自適應,並適當調小一些 109 var suitTimes = $scope.printObj.notifierObj.width/effectiveWidth*1.2; 110 } else { 111 var suitTimes = $scope.printObj.notifierObj.height/effectiveHeight*1.2; 112 } 113 $scope.printObj.notifierObj.width = $scope.printObj.notifierObj.width/suitTimes; 114 $scope.printObj.notifierObj.height = $scope.printObj.notifierObj.height/suitTimes; 115 for(i=0;i<$scope.printObj.paramList.length;i++) { 116 $scope.printObj.paramList[i].size = $scope.printObj.paramList[i].size/suitTimes; 117 $scope.printObj.paramList[i].left = $scope.printObj.paramList[i].left/suitTimes; 118 $scope.printObj.paramList[i].top = $scope.printObj.paramList[i].top/suitTimes; 119 } 120 } 121 122 function assembleHtml($scope) { 123 var htmlStr = "<img src='" + $scope.printObj.notifierObj.url+"' style='width:"+$scope.printObj.notifierObj.width+"px;height:"+ 124 $scope.printObj.notifierObj.height+"px'>"; 125 for(i=0;i<$scope.printObj.paramList.length;i++) { 126 var nowObj = $scope.printObj.paramList[i]; 127 htmlStr += "<div style='font-size:"+nowObj.size+"px;top:"+nowObj.top+"px;left:"+nowObj.left+"px'>"+nowObj.objName+"</div>"; 128 } 129 $("#toPrint").css("margin-left", (0-$scope.printObj.notifierObj.width)/2+"px"); 130 $("#toPrint").css("margin-top", (0-$scope.printObj.notifierObj.height)/2+"px"); 131 $("#toPrint").css("height", $scope.printObj.notifierObj.height+"px"); 132 $("#toPrint").css("width", $scope.printObj.notifierObj.width+"px"); 133 $("#toPrint").append(htmlStr); 134 } 135 136 //獲取有效區域 137 function findParam(targetObj, attribute) { 138 //取數字 139 if($(targetObj).css(attribute) && $(targetObj).css(attribute).replace(/[^0-9]/ig,"") != '0') { 140 return $(targetObj).css(attribute).replace(/[^0-9]/ig,""); 141 } else { 142 //遞歸 143 return findParam($(targetObj).parent(), attribute); 144 } 145 }
angular1的框架。
其中有幾個關鍵思路:
(1)模板結構定義,及根據參數長度動態生成頁面。
(2)屏幕自適應,選取比較接近的(寬或高),然后計算倍數,並做了1.2倍的縮小,使其不至於都挨着頁面邊界不美觀。取有效寬度時,從需要打印的元素往上層parent一層層遞歸取,直到取到有效的寬和高。遞歸時,由於已開始沒有return,導致一直取不到最終值
return findParam($(targetObj).parent(), attribute);
(3)拖動實現,已注掉的那段mouse事件,思路是點擊目標元素時開始計算坐標,放開時停止,中間移動的動作根據坐標偏移量來調整目標的left和top。有個小瑕疵就是有時候放開之后再點擊就回到頁面中間去了,由於后來做了頁面大小的自適應就沒有管了。
(4)打印的時候一些設置。由於是自動打印,不能要求用戶手動我改設置,主要是兩個,一個是基本上證書都是橫向,所有打印要改成默認橫向,然后去掉頁眉頁腳,實現的代碼是
try{
print.portrait = false;//橫向打印
}catch(e){
//alert("不支持此方法");
}
下邊注釋掉的網上說的是去掉頁眉頁腳用的,但是實測加上之后反而去不掉了。
開發完成之后,基本功能也都OK了,然后就是考慮兼容性的問題了。自適應解決了很大程度上的兼容性問題,但是對於瀏覽器的兼容性和移動端的兼容性還需要再進行測試。
經測試,在移動端的時候,如果屏幕分辨率較小,那么就會出現子題比較小的情況。而谷歌對於小於12px的字體,大小始終固定在12不會再變小,這就出現了兼容性問題。
解決思路是按比例縮放,使用屬性是-webkit-transform。而縮放默認是以中心為原點進行縮放,而我們畫圖填充是以左上角為原點的,所有縮放的同時還必須使用transform-origin:0 0設置以左上角為基准進行縮放。
最后就是,如果大於12px而又進行了縮放,那么就會變得更大,所有我們給這兩個樣式設置一個條件,那就是以12px作為臨界點。
代碼如下:
1 function assembleHtml($scope) { 2 var htmlStr = "<img src='" + $scope.printObj.notifierObj.url+"' style='width:"+$scope.printObj.notifierObj.width+"px;height:"+ 3 $scope.printObj.notifierObj.height+"px'>"; 4 for(i=0;i<$scope.printObj.paramList.length;i++) { 5 var nowObj = $scope.printObj.paramList[i]; 6 if(nowObj.size < 12) { 7 htmlStr += "<div style='font-size:"+nowObj.size+"px;top:"+nowObj.top+"px;left:"+nowObj.left+ 8 //谷歌瀏覽器字體小於12px時會不再變小,使用-webkit-transform兼容,並設置已左上角作為變換原點 9 "px;-webkit-transform:scale("+nowObj.size/12+","+nowObj.size/12+");transform-origin:0 0'>"+nowObj.objName+"</div>"; 10 } else { 11 htmlStr += "<div style='font-size:"+nowObj.size+"px;top:"+nowObj.top+"px;left:"+nowObj.left+ 12 "px'>"+nowObj.objName+"</div>"; 13 } 14 } 15 $("#toPrint").css("margin-left", (0-$scope.printObj.notifierObj.width)/2+"px"); 16 $("#toPrint").css("margin-top", (0-$scope.printObj.notifierObj.height)/2+"px"); 17 $("#toPrint").css("height", $scope.printObj.notifierObj.height+"px"); 18 $("#toPrint").css("width", $scope.printObj.notifierObj.width+"px"); 19 $("#toPrint").append(htmlStr); 20 }
附上效果對比圖,更容易理解些

其中“類好啊”是未變換未設置原點的最終效果,“北門小學”是都設置后的效果,下邊的日期是設置了縮放但是沒有設置原點的效果。
還有一個很關鍵的步驟就是定義好模板之后獲取數據並替換掉模板中的值。原本設計要做一個通用的,但是就要存庫,而且一個模板一張圖片就要存一次,而且對於值的獲取后台也不好處理,所有暫時就直接js中定義模板了。
整個流程很簡潔也很實用,希望大家喜歡。感謝大家支持,求關注,求紅包獎勵金。
