乐趣区

关于canvas:canvas-绘制海报or价签下载单张图片下载多张图片zip压缩包

需要:批量下载价签,点击批量下载,if 抉择一条数据则绘制一张 canvas,并且间接下载。else 多条数据则循环绘制多张 canvas,并且下载 zip,zip 名字设置“标签”+ 以后日期(zip 名字可自行设置)
此性能我用的 vue 实现的,然而 canvas 绘制海报的原理都是一样的。
知识点:革除画布,画矩形、画边框、截取图片画图、文字超出换行并显示省略号、字体大小色彩加粗、1px 线条、循环生成 canvas 并且下载、下载 zip
留神的点:

  1. 图片加载的时候会提早绘图的过程 Canvas 期待所有图片加载实现才开始绘图, 此处用的是 promise.all() 解决,留神同步异步得问题。(https://www.cnblogs.com/jannr…
<template>
    <div>
         <el-button type="text" @click="handleLabelDownload""> 批量下载 </el-button>
         <canvas ref="canvas" width="400" height="916" style="display:none"></canvas>
         <a ref='aImg' href=""></a>
    </div>
</template>
<script>
    import JSZip from "jszip";
    import FileSaver from 'file-saver'
    const imgName = require('@/../static/img/ 图片名字.png')
    export default {
      methods:{
         // 点击批量下载按钮 事件 events
         async handleLabelDownload(){if(this.items.length !== 0){let zip = new JSZip()
               if(this.items.length === 1){await this.drawCanvas(0)
                }else{for(let u in this.items){await this.drawCanvas(u,zip)
                 }
              }
             if(this.items.length > 1){zip.generateAsync({ type: 'blob'}).then(function (content) {let date = new Date(),month = 0;
                   if(date.getMonth() + 1 < 10){month = '0'+(date.getMonth()+1)
                   }else{month = date.getMonth() +1
                   }
                   FileSaver.saveAs(content, '标签 -'+month+'-'+date.getDate()+'.zip');
               });
            }
          }
        }, 
    // 加载图片  预加载图片,最初返回一个 promise 对象
    loadImage(url) {return new Promise((resolve)=>{const img = new Image();
        img.onload = ()=>resolve(img);
        img.src = url;
      })
    },

    getTrueLength(str) {
      let len = 0, trueLen = 0;
      if(str){
        len = str.length
        for (let x = 0; x < len; x++) {if (str.charCodeAt(x) > 128) {trueLen += 2;} else {trueLen += 1;}
        }
      }
      return trueLen;
    },
    cutString(str, leng) {
      let len = str.length, tlen = len, nlen = 0;
      for (let x = 0; x < len; x++) {if (str.charCodeAt(x) > 128) {if (nlen + 2 < leng) {nlen += 2;} else {
            tlen = x;
            break;
          }
        } else {if (nlen + 1 < leng) {nlen += 1;} else {
            tlen = x;
            break;
          }
        }
      }
      return tlen;
    },
     // 绘制 价签
    async drawCanvas(u,zip){const {price,itemId,itemName,itemBn,distributor_id,label_name,label_bn,label_spec,label_model,label_texture} = this.items[u]
         // 掉接口 去获取二维码 url
          const rest = await goodsCode({itemId, itemName,itemBn,distributor_id})
          const iconUrl = rest.data.data.qrcode_url || ''
          await Promise.all([this.loadImage(this.imgName),
              this.loadImage(iconUrl)
          ]).then((imgs) => {
              // 获取画布画笔
              const context = this.$refs.canvas.getContext('2d')

              // 清理矩形办法:clearRect(x,y,w,h)
              context.clearRect(0,0,400,916)

              // 背景 框
              context.fillStyle = '#ffffff'
              context.fillRect(0,0,400,916);

              // 描边矩形办法:strokeRect(x,y,w,h)
              context.strokeStyle='#32323e';
              context.lineWidth=2;
              context.strokeRect(0, 0, 400, 916);

              // 填充矩形办法:fillRect(x,y,w,h)
              context.fillStyle='#32323e';
              context.fillRect(1,1, 400,140);
              // title 字
              context.font = '42px 微软雅黑';
              context.textAlign ='center';
              context.fillStyle ='#fff';
              context.fillText(title 名字,224,84);
              //  logo
              context.drawImage(imgs[0],0,1000,1500,1500,90,49,50,50);
              // 商品名称
              let text = label_name
              if(text){context.beginPath() // 开始绘画的申明
                context.font = '40px 微软雅黑';
                context.fillStyle ='#373737';
                if(this.getTrueLength(text) <= 18){
                    context.textAlign ='center';
                    context.fillText(text,200,240);
                    context.fillText(text,201,240);
                 }else{for (let i = 1; this.getTrueLength(text) > 0; i++) {let tl = this.cutString(text, 18);
                        context.textAlign ='left';
                         if(i <= 2){
                            let t = 0,s='';
                            if(i == 2){if(this.getTrueLength(text.substr(0, tl)) >= 16){
                                    t = 8
                                    s='...'
                                }else{t = tl}
                           }else{t = tl}
                          context.fillText(text.substr(0, t).replace(/^\s+|\s+$/, "")+s, 20, i * 45 + 190);
                          context.fillText(text.substr(0, t).replace(/^\s+|\s+$/, "")+s, 21, i * 45 + 190);
                        }
                        text = text.substr(tl);
                     }
                  }
                 context.closePath()}
               // 货号
               if(label_bn){
                  context.font = '24px 微软雅黑';
                  context.fillStyle ='#373737';
                  context.textAlign ='center';
                   context.fillText(label_bn,200,323,360);
               }
               // 线条
               context.moveTo(20.5,370.5); // 定义终点, 能够了解为将画笔挪动到一个地位
               context.lineTo(20.5,370.5) // 定义一个线条一端的终点
               context.lineTo(380.5,370.5) // 定义一个线条一端的起点
               context.lineWidth = 1 // 定义线条宽度
               context.strokeStyle='#d0d0d0'; // 定义线条色彩
               // context.lineCap='round' // 定义线帽(含圆角、尖角、斜角)context.stroke() // 给线条上色,即进行绘制
               
               context.font = '22px 微软雅黑';
               context.fillStyle ='#373737';// 文字色彩
               context.textAlign ='center';
               context.fillText('规格:',53,412); // 文本程度对齐形式
               context.fillText('规格:',54,412); // 文字加粗
                // 规格值
               if(label_spec){
                  context.font = '18px 微软雅黑';
                  context.fillStyle ='#373737';
                  context.textAlign ='left';
                  context.fillText(label_spec,82,413,300);
               }

               context.moveTo(20.5,434.5);
               context.lineTo(20.5,434.5);
               context.lineTo(380.5,434.5);
               context.lineWidth = 1
               context.strokeStyle='#d0d0d0';
               context.stroke();

               context.font = '22px 微软雅黑';
               context.fillStyle ='#373737';
               context.textAlign ='center';
               context.fillText('型号:',53,476);
               context.fillText('型号:',54,476);
              
               if(label_model){
                   context.font = '18px 微软雅黑';
                   context.fillStyle ='#373737';
                   context.textAlign ='left';
                   context.fillText(label_model,82,477,300);
               }

              context.moveTo(20.5,498.5);
              context.lineTo(20.5,498.5);
              context.lineTo(380.5,498.5);
              context.lineWidth = 1
              context.strokeStyle='#d0d0d0';
              context.stroke();

              context.font = '22px 微软雅黑';
              context.fillStyle ='#373737';
              context.textAlign ='center';
              context.fillText('材质:',53,540);
              context.fillText('材质:',54,540);

              if(label_texture){
                  context.font = '18px 微软雅黑';
                  context.fillStyle ='#373737';
                  context.textAlign ='left';
                  context.fillText(label_texture,82,541,300);
              }

              context.moveTo(20.5,562.5);
              context.lineTo(20.5,562.5);
              context.lineTo(380.5,562.5);
              context.lineWidth = 1
              context.strokeStyle='#d0d0d0';
              context.stroke();
              // 价格
              context.font = '30px 微软雅黑';
              context.fillStyle ='#373737';
              context.textAlign ='center';
              const prices = '¥' + price
              context.fillText(prices,200,662);
              context.fillText(prices,201,662);

              context.moveTo(20,692);
              context.lineTo(20,692);
              context.lineTo(380,692);
              context.lineWidth = 1
              context.strokeStyle='#a3a3a3';
              context.stroke();

             context.moveTo(20,697);
             context.lineTo(20,697);
             context.lineTo(380,697);
             context.lineWidth = 1
             context.strokeStyle='#a3a3a3';
             context.stroke();

             context.drawImage(imgs[1],140,730,130,130);
             context.font = '14px 微软雅黑';
             context.textAlign ='center';
             context.fillStyle ='#373737';
             context.fillText('扫 码 了 解 更 多',200,880);
             if(!zip){
                 // 如果是一张图片的话,就间接主动触发 a 标签的 click 事件,主动下载文件
                let dataURL = this.$refs.canvas.toDataURL();
                let oA = this.$refs.aImg;
                oA.href = dataURL;
                oA.download = name + '.png'; // 下载的文件名能够此处批改
                oA.click()}
         })
         if(zip){await this.addToZip(this.$refs.canvas, zip, name+'.png');
         }
      },
      addToZip(canvas, zip, name){return new Promise((resolve, reject) => {canvas.toBlob(function (blob) {zip.file(name, blob);
                  resolve();});
          })
      },
                                     
      }
    }
</script>

实际效果:

退出移动版