js开发打印证书功能

时间:2024-05-28 12:05:38

最近突然被加了要打印证书的功能的需求。其实打印功能很简单,直接调用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代码:

 myApp.controller('notifierController', function ($rootScope, $scope, services, $sce, $stateParams, $state) {
$scope.services = services; //查询录取通知书内容
services["getApplyStatus"] = function (param) {
return $rootScope.serverAction('/apply/queryDegreeApplyInfo', param, "GET");
}; //模板
$scope.printObj = {
notifierObj:{
"url": "/res/img/notifications.png",
"height": "631",
"width": "942"
},
paramList:[{
"objName":"黄大明",
"left":"133",
"top":"189",
"size": "28"
},{
"objName":"SXXX小学",
"left":"460",
"top":"270",
"size": "28"
},{
"objName":"2018",
"left":"195",
"top":"314",
"size": "28"
},{
"objName":"8",
"left":"325",
"top":"314",
"size": "28"
},{
"objName":"31",
"left":"405",
"top":"314",
"size": "28"
}]
} services.getApplyStatus('token').success(function(res) {
if ('OK' == res.result) {
if(res.msg) {
$scope.printObj.paramList[0].objName = res.msg.studentName;
$scope.printObj.paramList[1].objName = res.msg.applySchoolName; //屏幕自适应
suitScreen($scope);
//组装页面
assembleHtml($scope);
//打印
printNotifier();
}
} else {
layer.alert(res.msg);
}
}); //拖动
/*$(document).on("mousedown","#toPrint",function(e){
//webkit内核和火狐禁止文字被选中
$('body').addClass('select')
//ie浏览器禁止文字选中
document.body.onselectstart=document.body.ondrag=function(){
return false;
}
$scope.moveBegin = true;
$scope.mouseStartPoint = {"left":e.pageX,"top": e.pageY};
});
$(document).on("mouseup",function(e){
$scope.moveBegin = false;
});
$(document).on("mousemove",function(e){
if(!$scope.moveBegin) {
return;
}
$("#toPrint").css("margin-left", e.pageX - $scope.mouseStartPoint.left - 454 + "px");
$("#toPrint").css("margin-top", e.pageY - $scope.mouseStartPoint.top - 315 + "px");
});*/
}); function printNotifier() {
try{
print.portrait = false;//横向打印
}catch(e){
//alert("不支持此方法");
}
var HKEY_RootPath="HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\PageSetup\\";
/*try{
var WSc=new ActiveXObject("WScript.Shell");
HKEY_Key="header";
WSc.RegWrite(HKEY_RootPath+HKEY_Key,"");
HKEY_Key="footer";
WSc.RegWrite(HKEY_RootPath+HKEY_Key,"");
}catch(e){ }*/
$("#printArea").jqprint();
} function suitScreen($scope) {
var effectiveHeight = findParam("#toPrint", "height");
var effectiveWidth = findParam("#toPrint","width");
if($scope.printObj.notifierObj.width/effectiveWidth > $scope.printObj.notifierObj.height/effectiveHeight) {
//取最接近的一个属性进行自适应,并适当调小一些
var suitTimes = $scope.printObj.notifierObj.width/effectiveWidth*1.2;
} else {
var suitTimes = $scope.printObj.notifierObj.height/effectiveHeight*1.2;
}
$scope.printObj.notifierObj.width = $scope.printObj.notifierObj.width/suitTimes;
$scope.printObj.notifierObj.height = $scope.printObj.notifierObj.height/suitTimes;
for(i=0;i<$scope.printObj.paramList.length;i++) {
$scope.printObj.paramList[i].size = $scope.printObj.paramList[i].size/suitTimes;
$scope.printObj.paramList[i].left = $scope.printObj.paramList[i].left/suitTimes;
$scope.printObj.paramList[i].top = $scope.printObj.paramList[i].top/suitTimes;
}
} function assembleHtml($scope) {
var htmlStr = "<img src='" + $scope.printObj.notifierObj.url+"' style='width:"+$scope.printObj.notifierObj.width+"px;height:"+
$scope.printObj.notifierObj.height+"px'>";
for(i=0;i<$scope.printObj.paramList.length;i++) {
var nowObj = $scope.printObj.paramList[i];
htmlStr += "<div style='font-size:"+nowObj.size+"px;top:"+nowObj.top+"px;left:"+nowObj.left+"px'>"+nowObj.objName+"</div>";
}
$("#toPrint").css("margin-left", (0-$scope.printObj.notifierObj.width)/2+"px");
$("#toPrint").css("margin-top", (0-$scope.printObj.notifierObj.height)/2+"px");
$("#toPrint").css("height", $scope.printObj.notifierObj.height+"px");
$("#toPrint").css("width", $scope.printObj.notifierObj.width+"px");
$("#toPrint").append(htmlStr);
} //获取有效区域
function findParam(targetObj, attribute) {
//取数字
if($(targetObj).css(attribute) && $(targetObj).css(attribute).replace(/[^0-9]/ig,"") != '0') {
return $(targetObj).css(attribute).replace(/[^0-9]/ig,"");
} else {
//递归
return findParam($(targetObj).parent(), attribute);
}
}

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作为临界点。

代码如下:

 function assembleHtml($scope) {
var htmlStr = "<img src='" + $scope.printObj.notifierObj.url+"' style='width:"+$scope.printObj.notifierObj.width+"px;height:"+
$scope.printObj.notifierObj.height+"px'>";
for(i=0;i<$scope.printObj.paramList.length;i++) {
var nowObj = $scope.printObj.paramList[i];
if(nowObj.size < 12) {
htmlStr += "<div style='font-size:"+nowObj.size+"px;top:"+nowObj.top+"px;left:"+nowObj.left+
//谷歌浏览器字体小于12px时会不再变小,使用-webkit-transform兼容,并设置已左上角作为变换原点
"px;-webkit-transform:scale("+nowObj.size/12+","+nowObj.size/12+");transform-origin:0 0'>"+nowObj.objName+"</div>";
} else {
htmlStr += "<div style='font-size:"+nowObj.size+"px;top:"+nowObj.top+"px;left:"+nowObj.left+
"px'>"+nowObj.objName+"</div>";
}
}
$("#toPrint").css("margin-left", (0-$scope.printObj.notifierObj.width)/2+"px");
$("#toPrint").css("margin-top", (0-$scope.printObj.notifierObj.height)/2+"px");
$("#toPrint").css("height", $scope.printObj.notifierObj.height+"px");
$("#toPrint").css("width", $scope.printObj.notifierObj.width+"px");
$("#toPrint").append(htmlStr);
}

附上效果对比图,更容易理解些

js开发打印证书功能

其中“类好啊”是未变换未设置原点的最终效果,“北门小学”是都设置后的效果,下边的日期是设置了缩放但是没有设置原点的效果。

还有一个很关键的步骤就是定义好模板之后获取数据并替换掉模板中的值。原本设计要做一个通用的,但是就要存库,而且一个模板一张图片就要存一次,而且对于值的获取后台也不好处理,所有暂时就直接js中定义模板了。

整个流程很简洁也很实用,希望大家喜欢。感谢大家支持,求关注,求红包奖励金。