canvas

源码https://github.com/zibuyu1/uni-app-weixin

<template>
  <canvas
    canvas-id="canvasdrawer"
    :style="{width: width + 'px', height: height + 'px', position: 'absolute', left: '-10000000px', top: '-10000000px'}"
  ></canvas>
</template>
<script>
export default {
  data() {
    return {
      tempFileList: [],
      width: 100, // 画布的宽度
      height: 100, // 画布的高度
      imageList: [], // 图片地址列表
      ctx: null // 上下文
    };
  },
  props: ["painting"],
  watch: {
    painting: {
      handler(newVal, oldVal) {
        if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
          this.readyPigment();
        }
      },
      deep: true
    }
  },
  onReady() {
    this.ctx = uni.createCanvasContext("canvasdrawer", this);
  },
  methods: {
    readyPigment() {
      const { width, height, views } = this.painting;
      this.width = width || 0;
      this.height = height || 0;
      const inter = setInterval(() => {
        if (this.ctx) {
          clearInterval(inter);
          this.ctx.clearActions();
          this.ctx.save();
          this.ctx.setFillStyle("#fff"); //这里是绘制白底,让图片有白色的背景
          this.ctx.fillRect(0, 0, this.width, this.height);
          this.getImagesInfo(views);
        }
      }, 100);
    },
    getImagesInfo(views) {
      const imageList = [];
      for (let i = 0; i < views.length; i++) {
        if (views[i].type === "image") {
          imageList.push(this.getImageInfo(views[i].url));
        }
      }
      const loadTask = [];
      for (let i = 0; i < Math.ceil(imageList.length / 8); i++) {
        loadTask.push(
          new Promise((resolve, reject) => {
            Promise.all(imageList.splice(i * 8, 8))
              .then(res => {
                resolve(res);
              })
              .catch(res => {
                reject(res);
              });
          })
        );
      }
      Promise.all(loadTask).then(res => {
        let tempFileList = [];
        for (let i = 0; i < res.length; i++) {
          tempFileList = tempFileList.concat(res[i]);
        }
        this.tempFileList = tempFileList;
        if (this.tempFileList.length) {
          this.startPainting();
        }
      });
    },
    startPainting() {
      const tempFileList = this.tempFileList;
      const views = this.painting.views;
      for (let i = 0, imageIndex = 0; i < views.length; i++) {
        if (views[i].type === "image") {
          this.drawImage({
            ...views[i],
            url: tempFileList[imageIndex]
          });
          imageIndex++;
        } else if (views[i].type === "text") {
          if (!this.ctx.measureText) {
            uni.showModal({
              title: "提示",
              content:
                "当前微信版本过低,无法使用 measureText 功能,请升级到最新微信版本后重试。"
            });
            this.$emit("getImage", { errMsg: "canvasdrawer:version too low" });
          } else {
            this.drawText(views[i]);
          }
        } else if (views[i].type === "rect") {
          this.drawRect(views[i]);
        }
      }
      this.ctx.draw(false, () => {
        const system = uni.getSystemInfoSync().system;
        if (/ios/i.test(system)) {
          this.saveImageToLocal();
        } else {
          // 延迟保存图片,解决安卓生成图片错位bug。
          setTimeout(() => {
            this.saveImageToLocal();
          }, 800);
        }
      });
    },
    drawImage(params) {
      this.ctx.save();
      const {
        url,
        top = 0,
        left = 0,
        width = 0,
        height = 0,
        borderRadius = 0,
        deg = 0
      } = params;
      if (borderRadius) {
        this.ctx.beginPath();
        this.ctx.arc(
          left + borderRadius,
          top + borderRadius,
          borderRadius,
          0,
          2 * Math.PI
        );
        this.ctx.clip();
        this.ctx.drawImage(url, left, top, width, height);
      } else {
        if (deg !== 0) {
          this.ctx.translate(left + width / 2, top + height / 2);
          this.ctx.rotate((deg * Math.PI) / 180);
          this.ctx.drawImage(url, -width / 2, -height / 2, width, height);
        } else {
          this.ctx.drawImage(url, left, top, width, height);
        }
      }
      this.ctx.restore();
    },
    drawText(params) {
      this.ctx.save();
      const {
        MaxLineNumber = 2,
        breakWord = false,
        color = "black",
        content = "",
        fontSize = 16,
        top = 0,
        left = 0,
        lineHeight = 20,
        textAlign = "left",
        width,
        bolder = false,
        textDecoration = "none"
      } = params;

      this.ctx.beginPath();
      this.ctx.setTextBaseline("top");
      this.ctx.setTextAlign(textAlign);
      this.ctx.setFillStyle(color);
      this.ctx.setFontSize(fontSize);

      if (!breakWord) {
        this.ctx.fillText(content, left, top);
        this.drawTextLine(left, top, textDecoration, color, fontSize, content);
      } else {
        let fillText = "";
        let fillTop = top;
        let lineNum = 1;
        for (let i = 0; i < content.length; i++) {
          fillText += [content[i]];
          if (this.ctx.measureText(fillText).width > width) {
            if (lineNum === MaxLineNumber) {
              if (i !== content.length) {
                fillText = fillText.substring(0, fillText.length - 1) + "...";
                this.ctx.fillText(fillText, left, fillTop);
                this.drawTextLine(
                  left,
                  fillTop,
                  textDecoration,
                  color,
                  fontSize,
                  fillText
                );
                fillText = "";
                break;
              }
            }
            this.ctx.fillText(fillText, left, fillTop);
            this.drawTextLine(
              left,
              fillTop,
              textDecoration,
              color,
              fontSize,
              fillText
            );
            fillText = "";
            fillTop += lineHeight;
            lineNum++;
          }
        }
        this.ctx.fillText(fillText, left, fillTop);
        this.drawTextLine(
          left,
          fillTop,
          textDecoration,
          color,
          fontSize,
          fillText
        );
      }

      this.ctx.restore();

      if (bolder) {
        this.drawText({
          ...params,
          left: left + 0.3,
          top: top + 0.3,
          bolder: false,
          textDecoration: "none"
        });
      }
    },
    drawTextLine(left, top, textDecoration, color, fontSize, content) {
      if (textDecoration === "underline") {
        this.drawRect({
          background: color,
          top: top + fontSize * 1.2,
          left: left - 1,
          width: this.ctx.measureText(content).width + 3,
          height: 1
        });
      } else if (textDecoration === "line-through") {
        this.drawRect({
          background: color,
          top: top + fontSize * 0.6,
          left: left - 1,
          width: this.ctx.measureText(content).width + 3,
          height: 1
        });
      }
    },
    drawRect(params) {
      this.ctx.save();
      const { background, top = 0, left = 0, width = 0, height = 0 } = params;
      this.ctx.setFillStyle(background);
      this.ctx.fillRect(left, top, width, height);
      this.ctx.restore();
    },
    getImageInfo(url) {
      return new Promise((resolve, reject) => {
        if (url) {
          const objExp = new RegExp(
            /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/
          );
          if (objExp.test(url)) {
            uni.getImageInfo({
              src: url,
              complete: res => {
                if (res.errMsg === "getImageInfo:ok") {
                  resolve(res.path);
                } else {
                  this.$emit("getImage", {
                    errMsg: "canvasdrawer:download fail"
                  });
                  reject(new Error("getImageInfo fail"));
                }
              },
              fail: err => {
                // console.log(err)
              }
            });
          } else {
            resolve(url);
          }
        }
      });
    },
    saveImageToLocal() {
      const width = this.width;
      const height = this.height;
      uni.canvasToTempFilePath(
        {
          x: 0,
          y: 0,
          width,
          height,
          canvasId: "canvasdrawer",
          complete: res => {
            if (res.errMsg === "canvasToTempFilePath:ok") {
              this.$emit("getImage", {
                tempFilePath: res.tempFilePath,
                errMsg: "canvasdrawer:ok"
              });
            } else {
              this.$emit("getImage", { errMsg: "canvasdrawer:fail" });
            }
          }
        },
        this
      );
    }
  }
};
</script>
share-personal.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,248评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,681评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,443评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,475评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,458评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,185评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,451评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,112评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,609评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,083评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,163评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,803评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,357评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,357评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,590评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,636评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,925评论 2 344