uni-app写微信小程序分享海报

需求:有用户头像,用户昵称,活动标题,商品主图,商品标题,商品价格,小程序二维码

思路:写成一个js文件,就可以在需要的组件中混入使用(mixins),调用绘制函数:createCanvasImage('posterCanvas'),保存图片函数:saveCanvasImage。

方法:canvas的dom元素的top写很大,相当于隐藏。canvas画好后,调用getCanvasImage函数得到图片(posterImg)显示。

调用的组件dom代码:  

注释:

js代码:

export default {

  data() {

    return {

      posterObj: {

        url: "", //商品主图

        userImg: "", //用户头像

        nickname: "", //用户昵称

        dec: "", //介绍

        title: "", //标题

        discountPrice: "", //折后价格

        orignPrice: "", //原价

        code: "", //小程序码

      },

      canvasFlag: true, // 是否显示canvas

      posterImg: "",//canvas绘制后得到的图片

      x: 25, // 绘制起点x

      y: 0, // 绘制起点y

      w: 270, // 绘制宽度

      h: 345, // 绘制高度

      radius: 10, // 圆角

      padding: 10, // 边距

      imgW: 187.5, // 主图片宽

      imgH: 187.5, //主图片高

      imgBg: "#f7f7f7", //主图片背景色

    };

  },

  methods: {

    // 画圆角矩形 ctx、x起点、y起点、w宽度、h高度、r圆角半径、fillColor填充颜色、strokeColor边框颜色

    roundRect(ctx, x, y, w, h, r, fillColor, strokeColor, btn) {

      // 开始绘制

      ctx.beginPath();

      // 绘制左上角圆弧 Math.PI = 180度

      // 圆心x起点、圆心y起点、半径、以3点钟方向顺时针旋转后确认的起始弧度、以3点钟方向顺时针旋转后确认的终止弧度

      ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);

      // 绘制border-top

      // 移动起点位置 x终点、y终点

      ctx.moveTo(x + r, y);

      // 画一条线 x终点、y终点

      ctx.lineTo(x + w - r, y);

      // 绘制右上角圆弧

      ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);

      // 绘制border-right

      ctx.lineTo(x + w, y + h - r);

      // 绘制右下角圆弧



      ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);



      // 绘制左下角圆弧



      ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);



      // 绘制border-left

      ctx.lineTo(x, y + r);

      if (btn == "btn") {

        const grd = ctx.createLinearGradient(0, 0, 200, 0); //渐变色

        grd.addColorStop(0, fillColor);

        grd.addColorStop(1, strokeColor);

        // 因为边缘描边存在锯齿,最好指定使用 transparent 填充

        ctx.setFillStyle(grd);

        // 对绘画区域填充

        ctx.fill();

      } else {

        if (fillColor) {

          // 因为边缘描边存在锯齿,最好指定使用 transparent 填充

          ctx.setFillStyle(fillColor);

          // 对绘画区域填充

          ctx.fill();

        }

        if (strokeColor) {

          // 因为边缘描边存在锯齿,最好指定使用 transparent 填充

          ctx.setStrokeStyle(strokeColor);

          // 画出当前路径的边框

          ctx.stroke();

        }

      }

      // 关闭一个路径

      ctx.closePath();

      // 剪切,剪切之后的绘画绘制剪切区域内进行,需要save与restore 这个很重要 不然没办法保存

      ctx.clip();

      // console.log("画圆角");

    },



    /**

     * canvas绘图相关,把文字转化成只能行数,多余显示省略号

     * ctx: 当前的canvas

     * text: 文本

     * contentWidth: 文本最大宽度

     * lineNumber: 显示几行

     */

    canvasMultiLineText(ctx, text, contentWidth, lineNumber) {

      var textArray = text.split(""); // 分割成字符串数组

      var temp = "";

      var row = [];

      for (let i = 0; i < textArray.length; i++) {

        if (ctx.measureText(temp).width < contentWidth) {

          temp += textArray[i];

        } else {

          i--; // 这里添加i--是为了防止字符丢失

          row.push(temp);

          temp = "";

        }

      }

      row.push(temp);



      // 如果数组长度大于2,则截取前两个

      if (row.length > lineNumber) {

        var rowCut = row.slice(0, lineNumber);

        var rowPart = rowCut[1];

        var test = "";

        var empty = [];

        for (var a = 0; a < rowPart.length; a++) {

          if (ctx.measureText(test).width < contentWidth) {

            test += rowPart[a];

          } else {

            break;

          }

        }

        empty.push(test); // 处理后面加省略号

        var group = empty[0] + "...";

        rowCut.splice(lineNumber - 1, 1, group);

        row = rowCut;

      }

      return row;

    },

    // 绘制图片 : 当前canvas 图 x起点 y起点 w宽度 h高度 r圆角 c1填充颜色 c2边框颜色

    drawImg(ctx, img, x, y, w, h, r, c1, c2) {

      let _this = this;

      //将网络图片转成本地路径

      ctx.restore();

      uni.getImageInfo({

        src: img,

        success(res) {

          ctx.save();

          //覆盖绘制

          //问题:在微信小程序使用canvas绘制圆角图片时,微信调试工具正常显示,android真机都不显示。

          // 原因:因为ctx.clip()剪切区域使用的填充颜色是透明的,所以图片没出来。

          // 解决方案:将剪切区域设置成实体颜色就好了。

          _this.roundRect(ctx, x, y, w, h, r, c1, c2); //绘制图片圆角背景

          ctx.drawImage(res.path, x, y, w, h, r); //绘制图

          ctx.restore(); //恢复之前保存的绘图上下文 恢复之前保存的绘图上下午即状态 可以继续绘制

          ctx.draw(true);

        },

        fail() {

          _this.canvasFlag = true;

          uni.showToast({ title: "img生成失败", duration: 2000, icon: "none" });

        },

      });

    },

    // 绘制文本 : 当前canvas 文本 x起点 y起点 c填充颜色 s样式

    drawText(ctx, text, x, y, c, s) {

      ctx.setFillStyle(c);

      ctx.font = s;

      ctx.fillText(text, x, y);

      ctx.draw(true);

    },

    // 绘制两行标题 : 当前canvas 文本 x起点 y起点 c填充颜色 s样式

    drawTitle(ctx, text, x, y, c, s) {

      ctx.setGlobalAlpha(1); //不透明

      ctx.setFillStyle(c); //文字颜色:默认黑色

      ctx.font = s;

      let row = this.canvasMultiLineText(ctx, text, 180, 2); //计算绘制的2行文本

      let leftSpace = x + 10; // 这段文字起始的X位置

      let textLineHeight = 18; // 一行文字加一行行间距

      for (let b = 0; b < row.length; b++) {

        //一行一行绘制文本

        ctx.fillText(row[b], leftSpace, y + textLineHeight * b - 15, 180);

        ctx.draw(true);

      }

    },

    // 绘制价格 : 当前canvas 现价 原价 x起点 y起点

    drawPrice(ctx, zpPrice, orignPrice, x, y) {

      ctx.setFillStyle("#FF354D"); //文字颜色:默认黑色

      ctx.setFontSize(21); //设置字体大小,默认10

      let zpPriceW = ctx.measureText(zpPrice).width; //文本的宽度

      ctx.fillText(`${zpPrice}`, x, y + 30, zpPriceW);

      ctx.beginPath(); //开始一个新的路径

      ctx.setFontSize(14); //设置字体大小,默认10

      ctx.setFillStyle("#999"); //文字颜色:默认黑色

      let orignPriceW = ctx.measureText(orignPrice).width; //去掉市场价

      ctx.fillText(`¥${orignPrice}`, x + zpPriceW, y + 30, orignPriceW); //5价格间距

      ctx.moveTo(x + zpPriceW, y + 25); //设置线条的起始路径坐标

      ctx.lineTo(x + zpPriceW + orignPriceW, y + 25); //设置线条的终点路径坐标

      ctx.setStrokeStyle("#999");

      ctx.stroke(); //对当前路径进行描边

      ctx.closePath(); //关闭当前路径

    },

    // 生成海报

    createCanvasImage(id) {

      // console.log(this.posterObj, "posterObj");

      uni.showLoading({

        title: "海报生成中...",

      });

      let _this = this;

      const ctx = uni.createCanvasContext(id);



      ctx.draw(); //清空原来的画图内容

      ctx.save();

      this.roundRect(ctx, this.x, this.y, this.w, this.h, this.radius, "#fff", "#fff"); //绘制海报圆角背景白色的

      ctx.restore(); //恢复之前保存的绘图上下文 恢复之前保存的绘图上下午即状态 可以继续绘制

      ctx.save();

      //将网络图片转成本地路径 用户头像

      this.drawImg(

        ctx,

        this.posterObj.userImg,

        this.x + this.padding,

        this.y + this.padding,

        50,

        50,

        25,

        this.imgBg,

        this.imgBg

      );



      // 用户昵称

      this.drawText(ctx, this.posterObj.nickname, this.x + 70, this.y + 30, "#333", "normal bold 18px sans-serif");



      // dec介绍

      this.drawText(ctx, this.posterObj.dec, this.x + 70, this.y + 55, "#666", "normal 14px sans-serif");



      //将网络图片转成本地路径 商品图片

      this.drawImg(

        ctx,

        this.posterObj.url,

        this.x + 41,

        this.y + 70,

        this.imgW,

        this.imgH,

        this.radius,

        this.imgBg,

        this.imgBg

      );



      // 海报商品title

      let contentTextY = this.y + 295; // 这段文字起始的y位置

      _this.drawTitle(ctx, this.posterObj.title, this.x, contentTextY, "#333", "normal bold 14px sans-serif");



      //绘制价格

      this.drawPrice(ctx, this.posterObj.discountPrice, this.posterObj.orignPrice, this.x + this.padding, contentTextY);



      // 二维码 图标

      this.drawImg(ctx, this.posterObj.code, this.x + 200, this.y + 271, 60, 60, 0, "#ffffff", this.imgBg);

      setTimeout(() => {

        this.getCanvasImage(id);

        setTimeout(() => {

          uni.hideLoading();

        }, 300);

      }, 300);

    },

    getCanvasImage(id) {

      let _this = this;

      // 把画布转化成临时文件

      uni.canvasToTempFilePath({

        x: _this.x,

        y: _this.y,

        quality: 1,

        width: this.w,

        height: this.h,

        destWidth: this.w * 10,

        destHeight: this.h * 10,

        canvasId: id,

        success(res) {

          _this.posterImg = res.tempFilePath;

        },

        fail() {

          uni.showToast({ title: "生成失败,稍后再试", duration: 2000, icon: "none" });

        },

      });

    },

    // 保存到系统相册

    saveCanvasImage() {

      uni.showLoading({

        title: "保存中...",

      });

      let _this = this;

      // 保存图片至相册

      uni.saveImageToPhotosAlbum({

        filePath: _this.posterImg,

        success(res2) {

          uni.hideLoading();

          uni.showToast({ title: "图片保存成功,可以去分享啦~", icon: "none", duration: 2000 });

          _this.canvasCancelEvn();

        },

        fail() {

          uni.showToast({ title: "保存失败,稍后再试", duration: 2000, icon: "none" });

          uni.hideLoading();

        },

      });

    },

    // 取消海报

    canvasCancelEvn() {

      // console.log();

      this.$emit("cancel", true);

    },

  },

};

 

你可能感兴趣的:(uni-app)