uni-app寫微信小程序分享海報

需求:有用戶頭像,用戶暱稱,活動標題,商品主圖,商品標題,商品價格,小程序二維碼

思路:寫成一個js文件,就可以在需要的組件中混入使用(mixins),調用繪製函數:createCanvasImage('posterCanvas'),保存圖片函數:saveCanvasImage。

方法:canvas的dom元素的top寫很大,相當於隱藏。canvas畫好後,調用getCanvasImage函數得到圖片(posterImg)顯示。

調用的組件dom代碼:<canvas class="canvas" canvas-id="posterCanvas"></canvas>  <image :src="posterImg" class="img" />

註釋:<!-- canvas 要放在調用的組件裏(外面),不能放在封裝組件裏面 會找不到 canvas-->

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);

    },

  },

};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章