uniapp 开发微信小程序中 canvas 绘制海报保存到本地( 绘制文字设置不超过几行,多余文字…显示 )

  • Post author:
  • Post category:uniapp




微信小程序中 canvas 绘制海报保存本地( 绘制文字设置不超过几行,多余文字—显示… )

本人开发中也碰到了保存图片到本地,打开图片为黑色的bug —— 已解决 ;

在这里插入图片描述

效果 :

<template>
	<view class="mask" v-if="isShowArea">
		<view class="out-modal slot-content">
			<view class="poster">
				<view class="top-title uni-center">
					<image class="logo" src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3566088443,3713209594&fm=26&gp=0.jpg" mode=""></image>
					<text class="title">搞笑天团</text>
					<image class="close-icon" @tap="closeModal" src="/static/images/message/close-share-icon.png" mode=""></image>
				</view>
				<canvas :style="{ width: canvasW + 'px', height: canvasH + 'px' }" canvas-id="myCanvas"></canvas>
			</view>
			<view class="save-pic" @tap="saveImageToPhotosAlbum">保存图片</view>
		</view>
	</view>
</template>
<script>
export default {
  components: {},
  data() {
    return {
      canvasW: 0,
      canvasH: 0,
      userName: "小小东林",
      area: "深圳",
      year: "20",
      tempFilePath: "",
      goodPlace: "擅长领域:",
      introduce: "自我介绍:",
      goodPlaceValue: "合同风控与账款清收合同风控与账款清收合同风控",
      introduceValue:"小小东林2010年取得搞笑资格,2011年就任于深圳年就任于深圳搞笑集团就任于深圳搞笑集团就任于搞笑使者",
      discernMsg: "长按识别小程序",
      slogan: "为你的企业保驾护航",
      goodsImg: {},
      codeImg: {},
      ctx: null,
      isShowArea: false
    };
  },

  onLoad(option) {},
  onShow() {},
  onReady() {
    this.ctx = uni.createCanvasContext("myCanvas", this);
    this.canvasW = (580 / 750) * wx.getSystemInfoSync().windowWidth;
    this.canvasH = (665 / 750) * wx.getSystemInfoSync().windowWidth;
  },
  methods: {
    closeModal() {
      this.isShowArea = false;
    },
    async creatPoster() {
      this.isShowArea = true;
      let ctx = this.ctx;
      uni.showToast({
        icon: "loading",
        mask: true,
        duration: 10000,
        title: "海报绘制中"
      });
      this.goodsImg = await this.getImageInfo(
        "https://img1.baidu.com/it/u=2233916703,3773857652&fm=26&fmt=auto&gp=0.jpg"
      );
      this.codeImg = await this.getImageInfo(
        "https://img1.baidu.com/it/u=1956268585,3986895079&fm=26&fmt=auto&gp=0.jpg"
      );

      if (
        this.goodsImg.errMsg == "getImageInfo:ok" &&
        this.codeImg.errMsg == "getImageInfo:ok"
      ) {
        setTimeout(() => {
          ctx.save();
          ctx.beginPath(); //开始绘制
          ctx.setFillStyle("#FFFFFF"); // 默认白色
          ctx.fillRect(0, 0, this.canvasW, this.canvasH); // fillRec	t(x,y,宽度,高度)
          // 绘制头像
          ctx.arc(44, 44, 44, 0, Math.PI * 2, true); //画出圆
          ctx.translate(this.canvasW / 2 - 44, 10);
          ctx.clip();
          ctx.drawImage(this.goodsImg.path, 0, 0, 88, 88); // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度)
          ctx.restore();

          // 绘制名字
          ctx.beginPath();
          ctx.setFontSize(17); // 字号
          ctx.setFillStyle("#222222"); // 颜色
          let userNameX =
            this.canvasW / 2 - ctx.measureText(this.userName).width / 2;
          ctx.fillText(this.userName, userNameX + 3, 124); // (文字,x,y)
          // 绘制地区
          ctx.setFontSize(12); // 字号
          ctx.setFillStyle("#666666"); // 颜色
          let areaX = ctx.measureText(this.userName).width + 26;
          ctx.fillText(this.area, userNameX + areaX, 124); // (文字,x,y)
          // 绘制年龄
          ctx.setFontSize(12); // 字号
          ctx.setFillStyle("#666666"); // 颜色
          let yearX = ctx.measureText(this.year).width + 20;
          ctx.fillText(this.year, userNameX + areaX + yearX, 124); // (文字,x,y)

          // 绘制擅长领域
          ctx.setFontSize(12); // 字号
          ctx.setFillStyle("#666666"); // 颜色
          ctx.fillText(this.goodPlace, 12, 154); // (文字,x,y)
          ctx.setFontSize(12); // 字号
          ctx.setFillStyle("#666666"); // 颜色
          let textWitdh =
            this.canvasW - ctx.measureText(this.goodPlace).width - 24;
          this.drawText(ctx, this.goodPlaceValue, 72, 154, 0, textWitdh);

          // 绘制自我介绍
          ctx.setFontSize(12); // 字号
          ctx.setFillStyle("#666666"); // 颜色
          ctx.fillText(this.introduce, 12, 188); // (文字,x,y)
          ctx.setFontSize(12); // 字号
          ctx.setFillStyle("#666666"); // 颜色
          let textWitdh2 =
            this.canvasW - ctx.measureText(this.introduce).width - 24;
          this.drawText(ctx, this.introduceValue, 72, 188, 0, textWitdh2);

          // 绘制线条
          ctx.moveTo(0, 230);
          ctx.lineTo(this.canvasW, 230);
          ctx.strokeStyle = "#E7E7E7";
          ctx.stroke();
          ctx.restore();
          // 绘制长按识别
          ctx.setFontSize(14); // 字号
          ctx.setFillStyle("#222222"); // 颜色
          ctx.fillText(this.discernMsg, this.canvasW / 2 - 44 + 16, 278); // (文字,x,y)
          // 绘制保驾护航
          ctx.setFontSize(14); // 字号
          ctx.setFillStyle("#222222"); // 颜色
          ctx.fillText(this.slogan, this.canvasW / 2 - 44 + 16, 302); // (文字,x,y)

          // 绘制二维码
          ctx.translate(this.canvasW / 2 - 44 - 60, 252);
          ctx.drawImage(this.codeImg.path, 0, 0, 64, 64); // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度)
          ctx.restore();

          // --------第一种通过计时器拿到生成的临时图片路劲
          ctx.draw();
          ctx.save();
          setTimeout(() => {
            wx.canvasToTempFilePath(
              {
                canvasId: "myCanvas",
                success: res => {
                  this.tempFilePath = res.tempFilePath;
                  wx.showToast({
                    title: "生成成功",
                    icon: "success",
                    duration: 1500
                  });
                },
                fail: res => {
                  console.log(res);
                  wx.showToast({
                    title: "生成失败",
                    icon: "none",
                    duration: 1500
                  });
                }
              },
              this
            );
          }, 2000);
          // ----------第二种在ctx的draw中执行拿到生成的临时图片路劲
          //   ctx.draw(true, () => {
          //     wx.canvasToTempFilePath(
          //       {
          //         canvasId: "myCanvas",
          //         success: (res)=> {
          //           this.tempFilePath= res.tempFilePath;
          //           wx.showToast({
          //             title: "生成成功",
          //             icon: "success",
          //             duration: 3000
          //           });
          //         },
          //         fail:(res)=> {
          //           console.log(res);
          //         }
          //       },
          //       this
          //     );
          //   });
        }, 1000);
      }
    },
    saveImageToPhotosAlbum() {
      wx.saveImageToPhotosAlbum({
        filePath: this.tempFilePath,
        success: res => {
          wx.showToast({
            title: "保存成功",
            icon: "success",
            duration: 1500
          });
        },
        fail: err => {
          wx.showToast({
            title: "保存失败",
            icon: "none",
            duration: 1500
          });
        }
      });
    },
    /**
     * 绘制多行文本
	 * @params
     * ctx canvas对象
     * str 文本
     * leftWidth 距离左侧的距离
     * initHeight 距离顶部的距离
     * titleHeight 文本的高度
     * canvasWidth 文本的宽度
     * @returns {*}
     */
    drawText(ctx, str, leftWidth, initHeight, titleHeight, canvasWidth) {
      let itemWidth = 0;
	//   measureText:计算字体的宽度
      if (ctx.measureText(str).width > canvasWidth * 2) {
        for (var i = 0; i < str.length; i++) {
          itemWidth += ctx.measureText(str[i]).width;
          if (itemWidth >= canvasWidth * 2) {
            str = str.substring(0, i - 2) + "...";
            i = str.length;
          }
        }
      }
      let lineWidth = 0;
      let lastSubStrIndex = 0; //每次开始截取的字符串的索引
      for (let i = 0; i < str.length; i++) {
        lineWidth += ctx.measureText(str[i]).width;
        if (lineWidth > canvasWidth) {
          ctx.fillText(
            str.substring(lastSubStrIndex, i),
            leftWidth,
            initHeight
          ); //绘制截取部分
          initHeight += 15;
          lineWidth = 0;
          lastSubStrIndex = i;
          titleHeight += 5;
        }
        if (i == str.length - 1) {
          //绘制剩余部分
          var otherStr = str.substring(lastSubStrIndex, i + 1);
          ctx.fillText(otherStr, leftWidth, initHeight);
        }
      }
      // 标题border-bottom 线距顶部距离
      titleHeight = titleHeight + 5;
      return titleHeight;
    },
    // 获取图片信息
    getImageInfo(image) {
      return new Promise((req, rej) => {
        uni.getImageInfo({
          src: image,
          success: function(res) {
            req(res);
          },
          fail: function(res) {
            uni.showToast({
              icon: "success",
              mask: true,
              title: "图片加载失败"
            });
          }
        });
      });
    }
  }
};
</script>

<style lang="scss" scoped>
.mask {
  width: 100%;
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: rgba(0, 0, 0, 0.4);
  z-index: 99999999999;
}
.out-modal {
  width: 640rpx;
  height: 900rpx;
}
.poster {
  width: 640rpx;
  height: 790rpx;
  border-radius: 16rpx;
  background-color: #f5f5f5;
  padding: 32rpx;
  box-sizing: border-box;
  .top-title {
    width: 100%;
    height: 48rpx;
    position: relative;
    line-height: 48rpx;
    margin-bottom: 20rpx;
    .logo {
      width: 48rpx;
      height: 48rpx;
      position: absolute;
      border-radius: 50%;
      left: 27%;
    }
    .title {
      font-size: 34rpx;
      color: #000000;
      font-weight: bold;
    }
    .close-icon {
      width: 48rpx;
      height: 48rpx;
      position: absolute;
      right: 32rpx;
      top: 2rpx;
    }
  }
}
.save-pic {
  width: 640rpx;
  height: 96rpx;
  background-color: #f13539;
  color: #ffffff;
  font-size: 30rpx;
  text-align: center;
  line-height: 96rpx;
  border-radius: 45rpx;
  margin-top: 30rpx;
}
</style>



版权声明:本文为weixin_45416252原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。