js证书批量生成与打包下载

时间:2024-01-21 11:34:08

前边有提到最近的一个证书生成保存下载打印的需求。

之前实现的是一个单个操作的页面,现在把实现的批量效果和进度效果的代码展示出来。

html

 1 <button class="btn btn-primary" ng-click="derive()" style="margin-top: 20px;">生成证书(方案1)</button>
 2                     <button class="btn btn-primary" ng-click="derive2()" style="margin-top: 20px;">生成证书(方案2)</button>
 3                     <button class="btn btn-primary" ng-click="derive3()" style="margin-top: 20px;">生成证书(方案3)</button>
 4 <grid-table data-control="tableControl"></grid-table>
 5         <!-- 进度 -->
 6         <div class="progress_cls" ng-if="progressShow">
 7             <div class="layui-progress layui-progress-big" lay-filter="notifierProgress" lay-showpercent="yes">
 8                 <div class="layui-progress-bar" lay-percent="0%">
 9                 <!-- 解决数字不出来问题 -->
10                     <span class="layui-progress-text">0%</span>
11                 </div>
12             </div>
13             <div class="progress_stitistics_cls">
14                 <div>总数:<span>{{progressTotal}}</span></div>
15                 <div>成功:<span>{{doneNum}}</span></div>
16                 <div>失败:<span>{{failedNum}}</span></div>
17             </div>
18             <button class="btn btn-primary" ng-click="closeProgress()" ng-if="progressDone">确定</button>
19         </div>
20         <!-- 进度2 -->
21         <div class="progress_cls" ng-if="progressShow2">
22             <div class="layui-progress layui-progress-big" lay-filter="notifierProgress2" lay-showpercent="yes">
23                 <div class="layui-progress-bar" lay-percent="0%">
24                 <!-- 解决数字不出来问题 -->
25                     <span class="layui-progress-text">0%</span>
26                 </div>
27             </div>
28             <div class="progress_stitistics_cls">
29                 <div>总数:<span>{{progressTotal}}</span></div>
30                 <div>当前完成:<span>{{doneNum}}</span></div>
31             </div>
32             <button class="btn btn-primary" ng-click="closeProgress()" ng-if="progressDone2">确定</button>
33         </div>
34         <!-- 进度3 -->
35         <div class="progress_cls" ng-if="progressShow3">
36             <div class="layui-progress layui-progress-big" lay-filter="notifierProgress3" lay-showpercent="yes">
37                 <div class="layui-progress-bar" lay-percent="0%">
38                 <!-- 解决数字不出来问题 -->
39                     <span class="layui-progress-text">0%</span>
40                 </div>
41             </div>
42             <div class="progress_stitistics_cls">
43                 <div>总数:<span>{{progressTotal}}</span></div>
44                 <div>当前完成:<span>{{doneNum}}</span></div>
45             </div>
46             <button class="btn btn-primary" ng-click="closeProgress()" ng-if="progressDone3">确定</button>
47         </div>

css

 1 #toPrint {
 2     position:absolute;
 3     left: 10000px;
 4     top: 50%;
 5 }
 6 
 7 #toPrint div {
 8     position:absolute;
 9     font-weight: bold;
10 }
11 
12 #toPrint img {
13     position:absolute;
14 }
15 
16 #textArea {
17     width: 100%;
18     height: 100%;
19 }
20 
21 .printCanvas {
22     display:inline-block;
23 }
24 
25 #toPrint3 {
26     position:absolute;
27     left: 10000px;
28     top: 50%;
29     display: inline-flex;
30 }

js

  1  /*
  2      导出数据
  3      */
  4     $scope.derive = function () {
  5         var toSendList = [];
  6         var passList = [];
  7         for(var i=0;i<$scope.tableControl.rows.length;i++) {
  8             if($scope.tableControl.rows[i].select) {
  9                 toSendList.push($scope.tableControl.allData[i]);
 10                 //没有数据,原5,现先1
 11                 if(1 == $scope.tableControl.allData[i].applyStatus) {
 12                     passList.push($scope.tableControl.allData[i]);
 13                 }
 14             }
 15         }
 16         if(1 > toSendList.length) {
 17             layer.alert("请选择需要生成证书的记录");
 18             return;
 19         }
 20         if(toSendList.length != passList.length) {
 21             layer.alert("只能对审核通过的记录进行证书生成");
 22             return;
 23         }
 24         layer.confirm("是否确认生成证书?", {
 25             btn: [\'确定\', \'取消\'] 
 26         }, function () {
 27             $scope.progressTotal = toSendList.length;
 28             $scope.doneNum = 0;
 29             $scope.failedNum = 0;
 30 
 31             layer.closeAll();
 32             var maskLoad = layer.load(1, {shade: [0.8, \'#393D49\']});
 33             //打开进度
 34             $scope.curProgress = "0%";
 35             $scope.progressShow = true;
 36             $.each(toSendList, function(index, e){
 37                 var url = location.origin+"/pages/print/printBatch.html?"+encodeURIComponent(e.studentName)+"&&"+encodeURIComponent(e.applySchoolName);
 38                 //services.save_notifier(url).success(function (res) {
 39                 $.ajax({
 40                     url: location.origin+\'/basic/school\',
 41                     headers: {\'token\': $rootScope.token},
 42                     type: \'get\',
 43                     dataType: \'json\',
 44                     contentType: \'application/json;charset=UTF-8\',
 45                     async: true,
 46                 }).success(function (res) {
 47                     if (\'OK\' == res.result) {
 48                         $scope.doneNum++;
 49                     } else {
 50                         $scope.failedNum++;
 51                     }
 52                 }).error(function (res) {
 53                     $scope.failedNum++;
 54                 }).always(function () {
 55                     //刷新成功和失败数量
 56                     if(($scope.doneNum + $scope.failedNum) < $scope.progressTotal) {
 57                         //更新进度
 58                         //$scope.curProgress = Math.round(($scope.doneNum + $scope.failedNum) / $scope.progressTotal * 100) + "%";
 59                         element.progress(\'notifierProgress\', Math.round(($scope.doneNum + $scope.failedNum) / $scope.progressTotal * 100) + "%")
 60                     } else {
 61                         //完成
 62                         $scope.curProgress = "100%";
 63                         ////进度条渲染需要时间??数据较少时执行完成还没渲染出来--改用定时器
 64                         var finishInterval = setInterval(function() {
 65                             if($(".layui-progress")[0]) {
 66                                 element.progress(\'notifierProgress\', "100%");
 67                                 clearInterval(finishInterval);
 68                             }
 69                         }, 200);
 70                         $scope.progressDone = true;
 71                         //去掉转圈
 72                         $(".layui-layer-loading").hide();
 73                         //layer.closeAll();
 74                     }
 75                 });
 76             });
 77             
 78         });
 79     }
 80     
 81     //方案2
 82     $scope.derive2 = function () {
 83         //先单线程
 84         var toSendList = [];
 85         var passList = [];
 86         for(var i=0;i<$scope.tableControl.rows.length;i++) {
 87             if($scope.tableControl.rows[i].select) {
 88                 toSendList.push($scope.tableControl.allData[i]);
 89                 //没有数据,原5,现先1
 90                 if(1 == $scope.tableControl.allData[i].applyStatus) {
 91                     passList.push($scope.tableControl.allData[i]);
 92                 }
 93             }
 94         }
 95         if(1 > toSendList.length) {
 96             layer.alert("请选择需要生成证书的记录");
 97             return;
 98         }
 99         if(toSendList.length != passList.length) {
100             layer.alert("只能对审核通过的记录进行证书生成");
101             return;
102         }
103         layer.confirm("是否确认生成证书?", {
104             btn: [\'确定\', \'取消\'] 
105         }, function () {
106             $scope.progressTotal = toSendList.length;
107             $scope.doneNum = 0;
108             $scope.failedNum = 0;
109 
110             layer.closeAll();
111             var maskLoad = layer.load(1, {shade: [0.8, \'#393D49\']});
112             //打开进度
113             $scope.curProgress = "0%";
114             $scope.progressShow2 = true;
115             
116             //弹出隐藏绘图层
117             $("#printArea").remove();
118             $("body").append("<div id=\'printArea\'><div id=\'toPrint\'></div></div>");
119             
120             //绘制证书
121             suitScreen($scope);
122             var imgStr = "<img src=\'" + $scope.printObj.notifierObj.url+"\' style=\'width:"+$scope.printObj.notifierObj.width+"px;height:"+
123             $scope.printObj.notifierObj.height+"px\'><div id=\'textArea\'></div>";
124             $("#toPrint").append(imgStr);
125             $("#toPrint").css("margin-top", (0-$scope.printObj.notifierObj.height-60)/2+"px");
126             $("#toPrint").css("height", $scope.printObj.notifierObj.height+"px");
127             $("#toPrint").css("width", $scope.printObj.notifierObj.width+"px");
128             
129             //填充文字
130             $.each(toSendList, function(index, e){
131                 $("#textArea").empty();
132                 $scope.printObj.paramList[0].objName = e.studentName;
133                 $scope.printObj.paramList[1].objName = e.applySchoolName;
134                 var htmlStr = "";
135                 for(i=0;i<$scope.printObj.paramList.length;i++) {
136                     var nowObj = $scope.printObj.paramList[i];
137                     if(nowObj.fontSize < 12) {
138                         htmlStr += "<div style=\'font-family:"+nowObj.fontFamily+";font-size:"+nowObj.fontSize+"px;top:"+nowObj.top+"px;left:"+nowObj.left+
139                         //谷歌浏览器字体小于12px时会不再变小,使用-webkit-transform兼容,并设置已左上角作为变换原点
140                             "px;-webkit-transform:scale("+nowObj.fontSize/12+","+nowObj.fontSize/12+");transform-origin:0 0\'>"+nowObj.objName+"</div>";
141                     } else {
142                         htmlStr += "<div style=\'font-family:"+nowObj.fontFamily+";font-size:"+nowObj.fontSize+"px;top:"+nowObj.top+"px;left:"+nowObj.left+
143                             "px\'>"+nowObj.objName+"</div>";
144                     }
145                 }
146                 //$("#toPrint").css("margin-left", (0-$scope.printObj.notifierObj.width)/2+"px");
147                 $("#textArea").append(htmlStr);
148                 
149                 //保存
150                 html2canvas(document.querySelector("#toPrint")).then(function(canvas) {
151                     var type = \'png\';//格式可以自定义
152                     var imgData = canvas.toDataURL(type);
153                     imgData = imgData.replace(_fixType(type),\'image/octet-stream\');
154                     //文件名可以自定义
155                     var filename = \'录取通知书_\' + e.studentName + \'.\' + type;
156                     saveFile(imgData,filename);
157                     $scope.doneNum++;
158                     //刷新成功和失败数量
159                     if(($scope.doneNum + $scope.failedNum) < $scope.progressTotal) {
160                         //更新进度
161                         //$scope.curProgress = Math.round(($scope.doneNum + $scope.failedNum) / $scope.progressTotal * 100) + "%";
162                         element.progress(\'notifierProgress2\', Math.round(($scope.doneNum + $scope.failedNum) / $scope.progressTotal * 100) + "%")
163                     } else {
164                         //完成
165                         $scope.curProgress = "100%";
166                         ////进度条渲染需要时间??数据较少时执行完成还没渲染出来--改用定时器
167                         var finishInterval = setInterval(function() {
168                             if($(".layui-progress")[0]) {
169                                 element.progress(\'notifierProgress2\', "100%");
170                                 clearInterval(finishInterval);
171                             }
172                         }, 200);
173                         $scope.progressDone2 = true;
174                         //去掉转圈
175                         $(".layui-layer-loading").hide();
176                     }
177                 });
178             });
179             
180         });
181     }
182     
183     //方案3
184     $scope.derive3 = function () {
185         //先单线程
186         var toSendList = [];
187         var passList = [];
188         for(var i=0;i<$scope.tableControl.rows.length;i++) {
189             if($scope.tableControl.rows[i].select) {
190                 toSendList.push($scope.tableControl.allData[i]);
191                 //没有数据,原5,现先1
192                 if(1 == $scope.tableControl.allData[i].applyStatus) {
193                     passList.push($scope.tableControl.allData[i]);
194                 }
195             }
196         }
197         if(1 > toSendList.length) {
198             layer.alert("请选择需要生成证书的记录");
199             return;
200         }
201         if(toSendList.length != passList.length) {
202             layer.alert("只能对审核通过的记录进行证书生成");
203             return;
204         }
205         layer.confirm("是否确认生成证书?", {
206             btn: [\'确定\', \'取消\'] 
207         }, function () {
208             $scope.progressTotal = toSendList.length;
209             $scope.doneNum = 0;
210             $scope.failedNum = 0;
211 
212             layer.closeAll();
213             var maskLoad = layer.load(1, {shade: [0.8, \'#393D49\']});
214             //打开进度
215             $scope.curProgress = "0%";
216             $scope.progressShow3 = true;
217             
218             //弹出隐藏绘图层
219             $("#toPrint3").remove();
220             $("body").append("<div id=\'toPrint3\'></div>");
221             
222             suitScreen($scope);
223 
224             var allCanvas = $("canvas");
225             var zip = new JSZip();
226             //zip.file("readme.txt", "证书\n");
227             var img = zip.folder("images");
228 
229             //图片加载是异步,所有用递归来做,否则前边生成的都会被最后一个覆盖
230             (function loop(n) {
231                 if (n>=toSendList.length) return;
232                 
233                 var image = new Image();
234                 image.src = $scope.printObj.notifierObj.url;
235                 image.onload = function () { //为异步函数,所以将创建canvas放在onload中.
236 
237                     $("#toPrint3").empty();
238                     $("#toPrint3").append("<canvas id=\'toPrint_\' class=\'printCanvas\'></canvas>");
239                     $scope.printObj.paramList[0].objName = toSendList[n].studentName;
240                     $scope.printObj.paramList[1].objName = toSendList[n].applySchoolName;
241                     
242                     
243                     $("#toPrint_").css("margin-top", (0-$scope.printObj.notifierObj.height)/2+"px");
244                     var canvas = document.getElementById("toPrint_");
245                     canvas.width = $scope.printObj.notifierObj.width;
246                     canvas.height = $scope.printObj.notifierObj.height;
247                     var ctx = canvas.getContext("2d");
248                     ctx.drawImage(image, 0, 0, $scope.printObj.notifierObj.width, $scope.printObj.notifierObj.height);
249                     $.each($scope.printObj.paramList, function(index, e) {
250                         //canvas的字体不会有12px的兼容性问题
251                         ctx.font = "bold "+e.fontSize+"px "+e.fontFamily;
252                         //canvas写字以字体的左下角为基准,因而要再加一个字体大小的高度
253                         ctx.fillText(e.objName, e.left, e.top+e.fontSize);
254                     });
255                     img.file(\'录取通知书_\' + toSendList[n].studentName + \'.png\', canvas.toDataURL().substring(22), {base64: true});
256                     $scope.doneNum++;
257                     //刷新成功和失败数量
258                     if(($scope.doneNum + $scope.failedNum) < $scope.progressTotal) {
259                         //更新进度
260                         //$scope.curProgress = Math.round(($scope.doneNum + $scope.failedNum) / $scope.progressTotal * 100) + "%";
261                         element.progress(\'notifierProgress3\', Math.round(($scope.doneNum + $scope.failedNum) / $scope.progressTotal * 100) + "%")
262                     } else {
263                         //完成
264                         $scope.curProgress = "100%";
265                         var finishInterval = setInterval(function() {
266                             if($(".layui-progress")[0]) {
267                                 element.progress(\'notifierProgress3\', "100%");
268                                 clearInterval(finishInterval);
269                             }
270                         }, 200);
271                         $scope.progressDone3 = true;
272                         //去掉转圈
273                         $(".layui-layer-loading").hide();
274                         
275                         zip.generateAsync({type:"blob"}).then(function(content) {
276                             saveAs(content, "证书.zip");
277                         });
278                     }
279                     
280                     loop(n+1);
281                 }
282           })(0);
283 
284 
285         });
286     }
287     
288     $scope.closeProgress = function () {
289         $scope.progressShow = false;
290         $scope.progressDone = false;
291         $scope.progressShow2 = false;
292         $scope.progressDone2 = false;
293         $scope.progressShow3 = false;
294         $scope.progressDone3 = false;
295         layer.closeAll();
296     }
297     
298   //模板
299     $scope.printObj = {
300         notifierObj:{
301             "url": "/res/img/notifications.png",
302             "height": "631",
303             "width": "942"
304         },
305         paramList:[{
306                 "objName":"黄大明",
307                 "left":"133",
308                 "top":"191",
309                 "fontSize": "28",
310                 "fontFamily": "KaiTi"
311             },{
312                 "objName":"SXXX小学",
313                 "left":"460",
314                 "top":"272",
315                 "fontSize": "28",
316                 "fontFamily": "KaiTi"
317             },{
318                 "objName":"2018",
319                 "left":"195",
320                 "top":"312",
321                 "fontSize": "28",
322                 "fontFamily": "KaiTi"
323             },{
324                 "objName":"8",
325                 "left":"325",
326                 "top":"312",
327                 "fontSize": "28",
328                 "fontFamily": "KaiTi"
329             },{
330                 "objName":"31",
331                 "left":"405",
332                 "top":"312",
333                 "fontSize": "28",
334                 "fontFamily": "KaiTi"
335             }]
336     }
337     
338     function suitScreen($scope) {
339         //A4横向标准
340         var effectiveHeight = 1240;
341         var effectiveWidth = 1754;
342         if($scope.printObj.notifierObj.width/effectiveWidth > $scope.printObj.notifierObj.height/effectiveHeight) {
343             //取最接近的一个属性进行自适应,并适当调小一些
344             var suitTimes = $scope.printObj.notifierObj.width/effectiveWidth*1.2;
345         } else {
346             var suitTimes = $scope.printObj.notifierObj.height/effectiveHeight*1.2;
347         }
348         $scope.printObj.notifierObj.width = $scope.printObj.notifierObj.width/suitTimes;
349         $scope.printObj.notifierObj.height = $scope.printObj.notifierObj.height/suitTimes;
350         for(i=0;i<$scope.printObj.paramList.length;i++) {
351             $scope.printObj.paramList[i].fontSize = $scope.printObj.paramList[i].fontSize/suitTimes;
352             $scope.printObj.paramList[i].left = $scope.printObj.paramList[i].left/suitTimes;
353             $scope.printObj.paramList[i].top = $scope.printObj.paramList[i].top/suitTimes;
354         }
355     }
356     
357     function _fixType(type) {
358         //imgData是一串string,base64
359         type = type.toLowerCase().replace(/jpg/i, \'jpeg\');
360         var r = type.match(/png|jpeg|bmp|gif/)[0];
361         return \'image/\' + r;
362     }
363 
364     function saveFile(data, filename) {
365         var save_link = document.createElementNS(\'http://www.w3.org/1999/xhtml\', \'a\');
366         save_link.href = data;
367         save_link.download = filename;
368        
369         //下载
370         var event = document.createEvent(\'MouseEvents\');
371         event.initMouseEvent(\'click\', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
372         save_link.dispatchEvent(event);
373     }
374     
375     //方案3使用canvas先画好,然后批量打包保存
376     function drawNotifier($scope, id, img) {
377         //canvas需要先定位好,否则画好再动就清除了
378         //$("#toPrint").css("margin-left", (0-$scope.printObj.notifierObj.width)/2+"px");不可见元素,同样不考虑其左右缩进
379         $(id).css("margin-top", (0-$scope.printObj.notifierObj.height)/2+"px");
380         var canvas = document.getElementById(id.substring(1));
381         canvas.width = $scope.printObj.notifierObj.width;
382         canvas.height = $scope.printObj.notifierObj.height;
383         var ctx = canvas.getContext("2d");
384         var img=new Image();
385         img.src = $scope.printObj.notifierObj.url;
386         var deferred=$.Deferred();
387         var thisObj = $scope.printObj;
388         //img.onload=function() {
389         requestAnimationFrame(function() {
390             //需要onload方法接收,否则画不出
391             ctx.drawImage(img, 0, 0, thisObj.notifierObj.width, thisObj.notifierObj.height);
392             //写文字,且要在画好图片之后写,否则会被图片覆盖
393             $.each(thisObj.paramList, function(index, e) {
394                 //canvas的字体不会有12px的兼容性问题
395                 ctx.font = "bold "+e.fontSize+"px "+e.fontFamily;
396                 //canvas写字以字体的左下角为基准,因而要再加一个字体大小的高度
397                 ctx.fillText(e.objName, e.left, e.top+e.fontSize);
398             });
399             deferred.resolve(canvas.toDataURL().substring(22));
400         })
401         //}
402         return deferred.promise();
403         
404     }

以上是三种实现方案,第一种需要一个接口,后两种不需要接口,直接前端生成。

说一下中间遇到的问题:

1.进度条里边的文字显示不出来:

用的是layui的进度条,很简单,就几行代码。

<div class="layui-progress layui-progress-big" lay-filter="notifierProgress" lay-showpercent="yes">
        <div class="layui-progress-bar" lay-percent="0%">
        </div>
</div>

但是进度百分比文字就是显示不出来,查看元素,官方api里是有span标签的,但是自己的一直没有,可见layui官网也是有点坑。也许一些版本或者新的版本支持吧,但是有个直接有效的解决办法就是,在内部直接写一个span标签。

      <div class="layui-progress layui-progress-big" lay-filter="notifierProgress" lay-showpercent="yes">
                <div class="layui-progress-bar" lay-percent="0%">
                <!-- 解决数字不出来问题 -->
                    <span class="layui-progress-text">0%</span>
                </div>
            </div>

2.进度动态更新:

当前使用的是angular框架,我使用了一个变量来动态刷新,但是实际并没有效果。最终不得不使用组件的方法:

element.progress(\'notifierProgress\', Math.round(($scope.doneNum + $scope.failedNum) / $scope.progressTotal * 100) + "%")

其中第一个参数就是前边的lay-filter

3.对于需要进行绘制又不想让用户看到:

很简单的方法,直接给你需要操作的元素绝对定位,然后给他一个很大很大的left(当然上下左右都可以),这样他就飘到屏幕的十万八千里外了,用户除非自己F12看,否则永远也看不到。这个方法之前在上一家公司有使用过。

4.对于canvas画图片异步加载的问题:

这个问题困扰了我很久,因为我要做批量绘制,使用同一个dom,绘制完一个然后清空再绘制下一个。早早就写完了逻辑,一运行也没有报错,燃鹅,打开文件一看,全都是最后一条数据生成的图片。已经使用了image.onload进行绘制了还出现这种问题。然后打断点调试,发现each遍历完了,才走进onload事件。我就又尝试着用定时器、用回调,都没有成功,而且这样比较容易出现问题。最终找到了一个解决办法,那就是用递归。代码如下

 1 //图片加载是异步,所有用递归来做,否则前边生成的都会被最后一个覆盖
 2             (function loop(n) {
 3                 if (n>=toSendList.length) return;
 4                 
 5                 var image = new Image();
 6                 image.src = $scope.printObj.notifierObj.url;
 7                 image.onload = function () { //为异步函数,所以将创建canvas放在onload中.
 8 
 9                     $("#toPrint3").empty();
10                     $("#toPrint3").append("<canvas id=\'toPrint_\' class=\'printCanvas\'></canvas>");
11                     $scope.printObj.paramList[0].objName = toSendList[n].studentName;
12                     $scope.printObj.paramList[1].objName = toSendList[n].applySchoolName;
13                     
14                     
15                     $("#toPrint_").css("margin-top", (0-$scope.printObj.notifierObj.height)/2+"px");
16                     var canvas = document.getElementById("toPrint_");
17                     canvas.width = $scope.printObj.notifierObj.width;
18                     canvas.height = $scope.printObj.notifierObj.height;
19                     var ctx = canvas.getContext("2d");
20                     ctx.drawImage(image, 0, 0, $scope.printObj.notifierObj.width, $scope.printObj.notifierObj.height);
21                     $.each($scope.printObj.paramList, function(index, e) {
22                         //canvas的字体不会有12px的兼容性问题
23                         ctx.font = "bold "+e.fontSize+"px "+e.fontFamily;
24                         //canvas写字以字体的左下角为基准,因而要再加一个字体大小的高度
25                         ctx.fillText(e.objName, e.left, e.top+e.fontSize);
26                     });
27                     img.file(\'录取通知书_\' + toSendList[n].studentName + \'.png\', canvas.toDataURL().substring(22), {base64: true});
28                     $scope.doneNum++;
29                     //刷新成功和失败数量
30                     if(($scope.doneNum + $scope.failedNum) < $scope.progressTotal) {
31                         //更新进度
32                         //$scope.curProgress = Math.round(($scope.doneNum + $scope.failedNum) / $scope.progressTotal * 100) + "%";
33                         element.progress(\'notifierProgress3\', Math.round(($scope.doneNum + $scope.failedNum) / $scope.progressTotal * 100) + "%")
34                     } else {
35                         //完成
36                         $scope.curProgress = "100%";
37                         var finishInterval = setInterval(function() {
38                             if($(".layui-progress")[0]) {
39                                 element.progress(\'notifierProgress3\', "100%");
40                                 clearInterval(finishInterval);
41                             }
42                         }, 200);
43                         $scope.progressDone3 = true;
44                         //去掉转圈
45                         $(".layui-layer-loading").hide();
46                         
47                         zip.generateAsync({type:"blob"}).then(function(content) {
48                             saveAs(content, "证书.zip");
49                         });
50                     }
51                     
52                     loop(n+1);
53                 }
54           })(0);

还有在调试中使用的一些方法,其中:

 1 var deferred=$.Deferred();
 2         var thisObj = $scope.printObj;
 3         //img.onload=function() {
 4         requestAnimationFrame(function() {
 5             //需要onload方法接收,否则画不出
 6             ctx.drawImage(img, 0, 0, thisObj.notifierObj.width, thisObj.notifierObj.height);
 7             //写文字,且要在画好图片之后写,否则会被图片覆盖
 8             $.each(thisObj.paramList, function(index, e) {
 9                 //canvas的字体不会有12px的兼容性问题
10                 ctx.font = "bold "+e.fontSize+"px "+e.fontFamily;
11                 //canvas写字以字体的左下角为基准,因而要再加一个字体大小的高度
12                 ctx.fillText(e.objName, e.left, e.top+e.fontSize);
13             });
14             deferred.resolve(canvas.toDataURL().substring(22));
15         })
16         //}
17         return deferred.promise();

给方法添加回调,使用promise,这样在调用这个方法时就可以用then进行接收了。

requestAnimationFrame,一个用的好比较有意思的window方法,类似timeout和interval,但是不需要设置时间,此处可以代替onload使用。

5.canvas转base64与文件操作:

 1 //canvas转图片
 2 
 3 canvas.toDataURL().substring(22)
 4 
 5 //js新建文件和文件填充
 6 
 7 var zip = new JSZip();
 8 //zip.file("readme.txt", "证书\n");
 9 var img = zip.folder("images");
10 
11 img.file(\'录取通知书_\' + toSendList[n].studentName + \'.png\', canvas.toDataURL().substring(22), {base64: true});
12 
13 //js文件打包下载
14 
15 zip.generateAsync({type:"blob"}).then(function(content) {
16 saveAs(content, "证书.zip");
17 });

用到的插件:FileSaver.js, jszip.min.js。用法也比较简单。

6.数据较少时,进度条无法刷新到100%的问题:

数据较少,用时较少,然后进度条渲染出来了,但是一直停在0%。没有研究源码,不知道是因为异步加载的问题还是因为css动画的样式问题还是因为渲染组件异步的问题,总之就是在执行完之后,打断点审查元素并没有生成组件,所有此时更新进度100%也没用。

解决方法:使用定时器

1     var finishInterval = setInterval(function() {
2           if($(".layui-progress")[0]) {
3                   element.progress(\'notifierProgress3\', "100%");
4                        clearInterval(finishInterval);
5            }
6      }, 200);

一般遇到这种异步造成还没渲染完成就做了处理的问题,使用定时器或者timeout都是一种解决办法。