前端 canvas 在微信小程序、uniapp、vue及jsp中的用法

发布于:2023-10-24 ⋅ 阅读:(124) ⋅ 点赞:(0)

一、微信小程序端使用canvas渲染商品海报

   1、canvas合成主函数。
  //合成海报(商品)
  getProductPosterTest() {
    var that = this
    const iamgeRate = that.data.iamgeRate //图片宽高比
    wx.createSelectorQuery()
      .select('#myCanvas') // 在 WXML 中填入的 id
      .fields({
        node: true,
        size: true
      })
      .exec((res) => {
        // Canvas 对象
        const canvas = res[0].node
        // 渲染上下文
        const ctx = canvas.getContext('2d')
        // Canvas 画布的实际绘制宽高
        const width = res[0].width
        const height = res[0].height
        console.log('width', width);
        console.log('height', height);
        // 初始化画布大小
        console.log('dpr', wx.getWindowInfo());
        const dpr = wx.getWindowInfo().pixelRatio
        canvas.width = width * dpr
        canvas.height = height * dpr
        ctx.scale(dpr, dpr)
        // 设置背景为白色
        ctx.fillStyle = "#fff";
        ctx.fillRect(0, 0, canvas.width, canvas.height)

        // 使用Promise 写成链式调用,顺序渲染图片
        that.canvasCover(canvas, ctx, iamgeRate, width).then(cov => {
          return that.canvasAvatar(canvas, ctx, iamgeRate, width)
        }).then(res => {
          return that.canvasCode(canvas, ctx, iamgeRate, width)
        })

        //不加定时器容易出现空白图
        setTimeout(() => {
          wx.canvasToTempFilePath({
            canvas: canvas,
            success: res => {
              // 生成的图片临时文件路径
              that.setData({
                img: res.tempFilePath
              })
            },
          })
        }, 500)
      })
  },
2、设置海报背景图渲染,同时渲染海报文字内容。商品名称只显示两行,超出的用'...'替代
  // 设置封面图
  canvasCover(canvas, ctx, iamgeRate, width) {
    let that = this;
    return new Promise(cov => {
      //商品信息文案
      let str = that.data.productInfo.productName
      if (str.length > 15) {
        str = str.slice(0, 15) + '...'
      }
      ctx.font = "16px Arial";
      ctx.textAlgin = "left"
      ctx.fillStyle = "#000000";
      ctx.fillText(str, 10, width / iamgeRate + 30, width - 20);

      ctx.font = "20px Arial";
      ctx.textAlgin = "left"
      ctx.fillStyle = "#FF0202";
      ctx.fillText('¥'+that.data.productInfo.productPrice, 10, width / iamgeRate + 60, width - 20);

      //分享人信息文案
      ctx.font = "15px Arial";
      ctx.textAlgin = "left"
      ctx.fillStyle = "#000000";
      ctx.fillText(wx.getStorageSync('memberInfo').memberNickname, 80, width / iamgeRate + 100);

      ctx.font = "14px Arial";
      ctx.fillStyle = "#939393"; 
      ctx.fillText('邀请您来XXXX', 80, width / iamgeRate + 130);

      const image = canvas.createImage()
      image.src = that.data.localBgImg
      image.onload = () => {
        ctx.drawImage(image, 0, 0, width, width / iamgeRate)
      }
      cov(true)
    })
  },
3、渲染头像,渲染时,将头像裁成圆形
  // 设置 头像 设置头像 (裁剪圆形)
  canvasAvatar(canvas, ctx, iamgeRate, width) {
    var that = this
    return new Promise(res => {
      const logo = canvas.createImage()
      logo.src = that.data.localLogoImg
      logo.onload = () => {
        //头像裁圆
        ctx.save()
        ctx.beginPath()
        ctx.arc(40, width / iamgeRate + 80 + 30, 30, 0, 2 * Math.PI)
        // ctx.fill()
        ctx.clip()
        ctx.drawImage(logo, 10, width / iamgeRate + 80, 60, 60)
        ctx.restore()
        console.log('头像完成')
        res(true)
      }
    })
  },
4、最后将小程序码渲染在画布上
  // 设置小程序码
  canvasCode(canvas, ctx, iamgeRate, width) {
    let that = this
    return new Promise(cod => {
      const imgs = canvas.createImage()
      imgs.src = that.data.localQrcodeImg //二维码图片
      imgs.onload = () => {
        ctx.drawImage(imgs, width - 110, width / iamgeRate + 55, 100, 100)
      }
    })
  },
5、效果

二、uniapp 使用canvas 渲染合成图片

     1、uniapp 小程序端合成证书
           设置canvas标签
<canvas id="myCanvas" canvas-id="myCanvas" style="width: 391px;height: 496px;" ></canvas>
let ctx = wx.createContext('myCanvas', this),

           渲染canvas
			//渲染canvas
			drawCertificate(){
				var that = this;
				 that.imgurl = that.renderData.url
				 that.getSystemInfo(); //获取设备信息				 
				 wx.getImageInfo({
				 	src: that.imgurl,
				 	success: function(ress) {
				 that.ctx.drawImage(ress.path, 0, 0, that.w, that.h, 0, 0, 391, 496);
				 		
				  that.ctx.font = '8px sen-serif'
				  that.ctx.fillStyle  = "#000"
				  that.ctx.fillText('证书编号:' + that.renderData.recordCode, 50, 50)
	  
				  that.ctx.font = '25px sen-serif'	  
				  var text = that.renderData.certificateName;
				  var textWidth = that.ctx.measureText(text).width;
				  var canvasWidth = that.w;
				  var xPosition = canvasWidth / 2 - textWidth / 2;
				  that.ctx.fillText(text, xPosition, 75);

				  that.ctx.font = '11px sen-serif'
				 let text1 = that.renderData.certificateText
				 text1 = text1.replace(/#company#/g, that.renderData.companyName)
				 text1 = text1.replace(/#name#/g,'  ' + that.renderData.name)
				 let level = that.renderData.levelName.substring(0,that.renderData.levelName.lastIndexOf('考试'))
				 text1 = text1.replace(/#level#/g, ' ' + level + ' ')
				 text1 = "        " + text1
                  //超长文版按行渲染
				 that.fillTextWrapSelf(this.ctx, {
				 	text: text1,
				 	x: 40,
				 	y: 112,
				 	size: 11,
				 	color: '#000000',
				 	indent: 1,
				 	lineHeight: 28,
				 	maxWidth: 305,
				 	maxHeight: 180,
				 	vertical: 'center'
				 });
				 
				 let text2 = "       经考核合格,能够对该机器进行装机,保养和维修工作。"
				 //超长文版按行渲染
				 that.fillTextWrapSelf(this.ctx, {
				 	text: text2,
				 	x: 55,
				 	y: 200,
				 	size: 11,
				 	color: '#000000',
				 	indent: 1,
				 	lineHeight: 28,
				 	maxWidth: 305,
				 	maxHeight: 180,
				 	vertical: 'center'
				 });
				 	 
				 that.ctx.fillText(`特发此证`, 300, 300)
				 	 
				 that.ctx.fillText(`培训讲师:`, 240, 350)
				 that.ctx.fillText(`部门负责人:`, 240, 380)

				  //名称下横线	 
				 // that.ctx.beginPath()
				 // that.ctx.moveTo(219, 381)
				 // that.ctx.lineTo(320, 381)
				 // that.ctx.stroke()
				 	 
				 // that.ctx.beginPath()
				 // that.ctx.moveTo(219, 422)
				 // that.ctx.lineTo(320, 422)
				 // that.ctx.stroke()
				 	
				 let text3 = that.renderData.traner	 	
				 that.ctx.fillText(text3, 310, 348)
				 
				 let text4 = that.renderData.manager	 
				 that.ctx.fillText(text4, 309, 380)
				 	 
				 let text5 = that.renderData.issuingUnit
				 that.ctx.fillText(text5, 195, 428)
				 	 
				 that.ctx.fillText(that.renderData.departmentName, 290, 455)
				 	 
				 that.ctx.restore()
				 		
				 wx.drawCanvas({
				 	canvasId: 'myCanvas',
				 	reserve: true,
				 	actions: that.ctx.getActions() // 获取绘图动作数组
				 });
				 

                 //将canvas 画布内容导出图片
				 setTimeout(() => {
				 	wx.canvasToTempFilePath(
				 		 {
				 			 width: that.w,
				 			 height: that.h,
				 			 canvasId: 'myCanvas',
				 			 success: function success(res) {
				 				 console.log('保存图片', res);
				 				 that.showUrl = res.tempFilePath;
				 				 console.log('showUrl',that.showUrl);
				 			 },
				 		   fail: function(res){
				 			 console.log("err", res);
				 		   },
				 			complete: function(){
				   
				 			 }
				 		 },
				 		 this
				 	);
				 },500)
				 
				 },
				 fail(err) {
				 	console.log('err', err);
				 }
				 })
			},
长文本逐行渲染方法
			  fillTextWrapSelf (ctx, obj) {
			    let {
			      text,
			      x, y,
			      size,
			      color,
			      bold,
			      indent,
			      maxWidth,
			      maxHeight,
			      lineHeight,
			      vertical} = obj
			    // 默认值
			    bold = bold || false
			    indent = indent || 0
			    lineHeight = lineHeight || 24
			    vertical = vertical || 'top'
			    this.ctx.save()
			    this.ctx.setTextAlign('left')
			    this.ctx.setTextBaseline('normal')
			    this.ctx.setFillStyle(color)
			    if (bold) {
			      this.ctx.font = 'normal bold ' + Math.round(size) + 'px sans-serif'
			    } else {
			      this.ctx.setFontSize(size)
			    }
			    let textArr = text.split('')
			    let rowArr = [] // 每行文本数组
			    let rowText = ''// 当前行文本
			    let rowWid = 0 // 当前行宽度
			    let lastText = text// 最后一行文本
			    for (let i = 0; i < textArr.length; i++) {
			      let oMaxWidth = maxWidth
			      if (rowArr.length === 0 && indent) {
			        oMaxWidth = maxWidth - indent * size
			      }
			      rowText = rowText + textArr[i]
			      rowWid = parseInt(this.ctx.measureText(rowText + '').width)
			      if (rowWid >= oMaxWidth) {
			        rowArr.push(rowText)
			        lastText = lastText.substr(rowText.length)
			        // 重置参数
			        rowText = ''
			        rowWid = 0
			      }
			    }
			    if (lastText) rowArr.push(lastText)
			    let rows = rowArr.length // 行数
			    let bY = y
			    if (vertical === 'center' && maxHeight > rows * lineHeight) {
			      bY = y + Math.ceil((maxHeight - rows * lineHeight) / 2)
			    }
			    for (let i = 0; i < rowArr.length; i++) {
			      let cY = bY + i * lineHeight
			      // if (cY + lineHeight > maxHeight + y) {
			      //   this.ctx.fillText(rowArr[i], x, cY)
			      //   break
			      // }
			      if (i === 0 && indent) { // 首行缩进
			        this.ctx.fillText(rowArr[i], x + indent * size, cY)
			      } else {
			        this.ctx.fillText(rowArr[i], x, cY)
			      }
			    }
			    this.ctx.restore() 
			  }
2、uniapp app端合成图片
     设置Canvas dom
<canvas id="myCanvas" canvas-id="myCanvas" type="2d" class="canvasBox"/>
     初始化canvas
			//初始化Canvas
			drawImage() {
				var that = this
				const query = uni.createSelectorQuery().in(that);
				query.select('#myCanvas').boundingClientRect(data => {
					that.canvasHeigth = data.height
					that.canvasWidth = data.width
					that.context = uni.createCanvasContext('myCanvas')
					console.log('coverImage', that.mainImage);
					var mainImageTmp = this.checkDownloadImgUrl(that.mainImage)
					that.bgImage = mainImageTmp
					that.context.drawImage(mainImageTmp, 0, 0, that.canvasWidth, that
						.canvasHeigth)
					//画
					that.context.draw()

				}).exec()

			},
画图
drawImage(){
   this.bgImage = showImageTmp
   this.context.drawImage(bgImage , 0, 0, this.canvasWidth, this.canvasHeigth)
   //画
    this.context.draw()

   //转换图片步骤同小程序版
}

三、vue端实现canvas绘图

 <canvas id="effect" style="width: 391px;height: 496px;">
            您的浏览器暂不支持此功能,请升级后重试。
  </canvas>

        

    //合成证书
    toViewEffect() {
      let canvas = document.getElementById('effect'); //获得画布
      let ctx = canvas.getContext('2d');
      canvas.width = 391;
      canvas.height = 496;
      // 背景图片
      let img = new Image();
      // img.src = $('#smallImg').val();
      img.src = this.info.baseCode;
      img.alt = '';
      img.onload = () => {
        //图片加载完成后,执行此方法
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height)

        // 编号
        ctx.font = "8px Arial";
        ctx.textAlgin = "left"
        ctx.fillStyle = "#000000";
        ctx.fillText(`证书编号:${this.info.recordCode}`, 45, 45);

        // 标题
        ctx.font = "25px Arial";
        ctx.textAlgin = "center"
        ctx.fillStyle = "#000000";
        // let text = document.getElementById("certificateName").value
        let text = this.info.certificateName
        let textWidth = ctx.measureText(text).width;
        let canvasWidth = canvas.width;
        let xPosition = canvasWidth / 2 - textWidth / 2;
        ctx.fillText(text, xPosition, 75);

        // 正文
        // let member = JSON.parse(localStorage.getItem("member"))
        let str = this.info.certificateText;
        str = str.replace(/#company#/g, this.info.companyName)
        str = str.replace(/#name#/g, '  ' + this.info.name + '  ')
        let level = this.info.levelName.substring(0,this.info.levelName.lastIndexOf('考试'))
        str = str.replace(/#level#/g, ' ' + level + ' ')
        let mainText = '       ' + str;

        ctx.font = "11px Arial";
        ctx.textAlgin = "left"
        ctx.fillStyle = "#000000";
        //截取固定长度,逐行渲染
        let temp = '', row = [], maxwidth = 305
        for (let a = 0; a < mainText.length; a++) {  // #将文本按最大宽度换行
          // #判断文本是否超出,是则换行
          (ctx.measureText(temp).width >= maxwidth) && (row.push(temp), temp = "")
          temp += mainText[a];
        }
        row.push(temp);
        for (let b = 0; b < row.length; b++) { // #按行写入文本
          ctx.fillText(row[b], 45, 190 + (b - row.length / 2) * 25, maxwidth); // #40是行高
        }

        //结论
        let conclusionText = '       经考核合格,能够对该机器进行装机,保养和维修工作。'
        ctx.font = "11px Arial";
        ctx.textAlgin = "left"
        ctx.fillStyle = "#000000";
        ctx.fillText(conclusionText, 55, 290, maxwidth);

        ctx.font = "11px Arial";
        ctx.textAlgin = "right"
        ctx.fillStyle = "#000000";
        ctx.fillText('特发此证。', 275, 310);

        //培训讲师
        ctx.font = "11px Arial";
        ctx.textAlgin = "right"
        ctx.fillStyle = "#000000";
        ctx.fillText('培训讲师:       ' + this.info.traner, 220, 365);

        //部门负责人
        ctx.font = "11px Arial";
        ctx.textAlgin = "right"
        ctx.fillStyle = "#000000";
        ctx.fillText('部门负责人:    ' + this.info.manager, 220, 395);

        // 落款1
        ctx.font = "11px Arial";
        ctx.textAlgin = "right"
        ctx.fillStyle = "#000000";
        ctx.fillText(this.info.issuingUnit, 190, 435);

        // 落款2
        ctx.font = "11px Arial";
        ctx.textAlgin = "right"
        ctx.fillStyle = "#000000";
        ctx.fillText(this.info.departmentName, 290, 460);

        //讲师下划线
        //绘制直线
        ctx.moveTo(290, 370)
        ctx.lineTo(350, 370)

        ctx.moveTo(290, 400)
        ctx.lineTo(350, 400)
        ctx.lineWidth = '.4'; //线的宽度
        ctx.strokeStyle = '#000000'; //线的颜色
        ctx.stroke()

        this.base64 = canvas.toDataURL('image/jpg');
      }
    },

需要注意的是,vue端要渲染的背景图片容易出现跨域并且不容易解决,能渲染在页面展示,但是导出图片报错或者空白图片,最后让后端同事将图片转成base64格式的返回给我。同时,vue端导出的图片格式也是base64格式的,需要处理。

四、jsp端同vue端使用


网站公告

今日签到

点亮在社区的每一天
去签到