( 业务代码 )uniapp中动态图片 合成分享海报 并分享

440 阅读3分钟

b74c869051b67b20039ff18784beedc.jpg

    <template>
	<view class="">
            <button @click="getBgImgCanvas1">绘制</button>
            <button @click="saveImage">保存</button>
            <canvas 
               style="width: 484rpx; height: 860rpx;"
               canvas-id="firstCanvas" id="firstCanvas">
            </canvas>
            <image class="iamge" v-for="(item, index) in imageList" :src="item.image"></image>
            <image :src="saveImg"></image>
	</view>
</template>
data(){
    return {
    // 图片如果有层级关系问题  必须先把最底层的东西排在最前面  
      imageList: [{
           type: 'image',
           // 背景图必须在一开始绘制
           image: 'https://images.pinduoduo.com/mrk/2020-05-31/f272d8eb-25c4-4142-aa8f-11987a4ddfbc.png',
           x: 0,
           y: 0,
           width: 484,
           height: 860
      }, {
           type: 'textcenter',
           textColor:'#333333',
           font: "bold 16px PingFangSC-Medium, PingFang SC",
           textAlign: "center",
           textBaseline: "middle",
           fillText: '上汇特惠 吃喝玩乐购',
           x: 85,
           y: 28,
           width: 310,
           height: 45
      }, {
           type:'image',
           image:'http://image.huikejie.com/group1/M00/03/B2/rB_yo2N0jeuALcGrAAXdKc64jLk147.jpg',
           x: 24,
           y: 87,
           width: 436,
           height: 436
      }, {
           type: 'text',
           fontSize: 14, //字体大小
           word: '瑶海区胡夫炸串】26.8元抢双人超值套餐,味道与颜值并存,超多菜品等你来打卡!', //需要处理的文字
           maxWidth: 436, //一行文字最大宽度
           x: 24, //文字在x轴要显示的位置
           y: 543, //文字在y轴要显示的位置
           maxLine: 3, //文字最多显示的行数
           fweight: 900
      }, {
           type: 'rect',
           fillStyle: '#f9f9f9',
           x: 24,
           y: 771,
           width: 248,
           height: 48,
           renderText:{
              textColor:'#A0A0A0',
              font: "12px PingFangSC-Medium, PingFang SC",
              textAlign: "center",
              textBaseline: "middle",
              fillText: '长按识别小程序购买',
              x: 24,
              y: 771,
              width: 248,
              height: 48
           }
      }, {
           type: 'image',
           image: 'https://commimg.pddpic.com/wxcodeurl/29f06b00-2aa1-44e1-afc4-4f74eddd3bb7.png',
           x: 308,
           y: 688,
           width: 148,
           height: 148
      }],
      canvasText:[
           {
              type: 'text',
              textColor:'#FD342A',
              fontSize: 12, //字体大小
              word: '¥', //需要处理的文字
              maxWidth: 50, //一行文字最大宽度
              x: 24, //文字在x轴要显示的位置
              y: 705, //文字在y轴要显示的位置
              maxLine: 1, //文字最多显示的行数
              fweight: 900,
           },
           {
              type: 'text',
              textColor:'#FD342A',
              fontSize: 18, //字体大小
              word: '12.01', //需要处理的文字
              maxWidth: 300, //一行文字最大宽度
              x: 24, //文字在x轴要显示的位置
              y: 705, //文字在y轴要显示的位置
              maxLine: 1, //文字最多显示的行数
              fweight: 900,
           }
      ],
      ctxCanvas: null,
      saveImg: ''
    }
},
    // px 转换成rpx
    px2upx(value){
	return   Number(value/(uni.upx2px(100)/100).toFixed(2)) ;
    },
    // 特殊的方法  文字在同一行获取   后面的文字的坐标 X 值 = 前文字的X值 + 前文字的宽度
    specialRender(ctx,arr){		
	for (let i = 0; i < arr.length; i++) {
        // 保存当前画笔的数据
            ctx.save()
            if(i == 0){
		arr[i].x = arr[i].x
		arr[i].left = arr[i].x + this.px2upx(ctx.measureText(arr[i].word).width) + 2 
            }else {
            //加2的间距 单位是px  后面会调用uni.upx2px进行单位转换
		arr[i].x = arr[i-1].left
		arr[i].left = arr[i-1].left + this.px2upx(ctx.measureText(arr[i].word).width) + 2
            }
            // 
            this.dealWords({ctx:ctx,...arr[i] })
            // 恢复画笔的数据
            ctx.restore()
	}
    },
    //  指定位置绘制文字 只设置了居中 和left绘制方式
    renderTextCenter(opt) {
	console.log(opt);
	opt.ctx.beginPath();
	opt.ctx.fillStyle =opt.textColor || "#000000";
	opt.ctx.textBaseline =opt.textBaseline ||  "top";
	opt.ctx.textAlign =opt.textAlign || "bottom";
	opt.ctx.font =opt.font || "bold 16px serif"
	// opt.ctx.strokeStyle = "#A0A0A0";
	let renderX = 0,renderY = 0;
	if(opt.textAlign=='center'){
		console.log(opt.textAlign);
                // 居中要找到中心点
		renderX = uni.upx2px(opt.x + Number((opt.width/2).toFixed(1)))
		renderY = uni.upx2px(opt.y + Number((opt.height/2).toFixed(1)))
	}else {
		renderX = uni.upx2px(opt.x + Number(0))
		renderY = uni.upx2px(opt.y + Number(0))
	}
	opt.ctx.fillText(opt.fillText, renderX, renderY);
	// opt.ctx.stroke();
    },
    //处理文字多出省略号显示 借鉴别人的 没有再去抽离整合了 时间有限
    dealWords(options) {
        console.log(options);
        options.ctx.font = `${options.fweight || 'bold' } ${options.fontSize || '14'}px PingFangSC-Medium, PingFang SC`
        if(options.textColor){
                options.ctx.fillStyle = options.textColor
        }
        options.textColor && (options.ctx.fillStyle = options.textColor )
        // options.ctx.setFontSize(28); //设置字体大小
        var allRow = Math.ceil(options.ctx.measureText(options.word).width / uni.upx2px(options
				.maxWidth)); //实际总共能分多少行
        var count = allRow >= options.maxLine ? options.maxLine : allRow; //实际能分多少行与设置的最大显示行数比,谁小就用谁做循环次数
        var endPos = 0; //当前字符串的截断点
        for (var j = 0; j < count; j++) {
            var nowStr = options.word.slice(endPos); //当前剩余的字符串
            var rowWid = 0; //每一行当前宽度  
            if (options.ctx.measureText(nowStr).width > uni.upx2px(options.maxWidth)) { //如果当前的字符串宽度大于最大宽度,然后开始截取
                for (var m = 0; m < nowStr.length; m++) {
                    rowWid += options.ctx.measureText(nowStr[m]).width; //当前字符串总宽度
                    if (rowWid > uni.upx2px(options.maxWidth)) {
                       if (j === options.maxLine - 1) { //如果是最后一行
                          options.ctx.fillText(nowStr.slice(0, m - 1) + '...',uni.upx2px(options.x), uni.upx2px(options.y) + (j + 1) *18); //(j+1)*18这是每一行的高度    
                        } else {
                           options.ctx.fillText(nowStr.slice(0,m),uni.upx2px(options.x),uni.upx2px(options.y) +(j + 1) * 18);
                        }
                        endPos += m; //下次截断点
                        break;
                     }
                }
            } else { //如果当前的字符串宽度小于最大宽度就直接输出
		options.ctx.fillText(nowStr.slice(0), uni.upx2px(options.x), uni.upx2px(options.y) + (j + 1) * 18);
            }
	}
    },
    async renderImageFn(options, index) {
    // 绘制图片  为保证图片完整绘制 必须等待图片下载完成
	const that = this;
	let renderImg0 = await uni.downloadFile({
            url: options.item.image,  
	});
        // 可以自行加异常处理
	that.ctxCanvas.drawImage(renderImg0[1].tempFilePath, uni.upx2px(options.item.x), uni.upx2px(options.item.y), uni.upx2px(options.item.width), uni.upx2px(options.item.height))
	return;
    },
    async getBgImgCanvas1() {
    // 开始绘制
	try {
	let that = this
	for (let i = 0; i < this.imageList.length; i++) {
        //  保存画笔状态
            this.ctxCanvas.save()
            if (this.imageList[i].type == 'image') {
                 console.log('index', i);
                 await that.renderImageFn({
                    item: this.imageList[i]
                 }, i)
                 console.log('index1', i);
            } else if (this.imageList[i].type == 'text') {
                 that.dealWords({
                    ctx: this.ctxCanvas,
                    ...this.imageList[i]
                 })
            } else if (this.imageList[i].type == 'rect') {
                 that.renderRect({
                    ctx: this.ctxCanvas,
                    ...this.imageList[i]
                 })
            } else if (this.imageList[i].type == "textcenter") {
                 that.renderTextCenter({
                    ctx: this.ctxCanvas,
                    ...this.imageList[i]
                 })
            }
            //  恢复画笔状态
            this.ctxCanvas.restore()
	}
	this.specialRender(this.ctxCanvas,this.canvasText)
	this.ctxCanvas.draw(false, () => {
            uni.canvasToTempFilePath({
                 canvasId: "firstCanvas",
                 success: res => {
                    this.saveImg = res.tempFilePath
                 },
                 fail: err => {
                    this.$queue.showToast("生成图片失败")
                 }
            }, this)
	})
	} catch (e) {
	//TODO handle the exception
	console.log('绘制失败,rnm退钱', e);
	}
}