【愚公系列】2022年08月 微信小程序-view生成分享图片(原生封装版)

举报
愚公搬代码 发表于 2022/08/31 21:52:33 2022/08/31
【摘要】 一、view生成分享图片 1.组件的封装项目结构图:部分代码如下1、dialogModalvar app = getApp()Component({ data: { }, properties: { isShow: { type: Boolean, value: false }, title: { type: String, ...

一、view生成分享图片

1.组件的封装

项目结构图:
在这里插入图片描述

部分代码如下

1、dialogModal

var app = getApp()
Component({
  data: {

  },
  properties: {
    isShow: {
      type: Boolean,
      value: false
    },
    title: {
      type: String,
      value: '提示'
    },
    content: {
      type: String,
      value: ''
    },
    cancelText: {
      type: String,
      value: '取消'
    },
    confirmText: {
      type: String,
      value: '确定'
    },
    isNeedAuth: {
      type: Boolean,
      value: false
    },
    cancelType: {
      type: String,
      value: ''
    },
    confirmType: {
      type: String,
      value: ''
    }
  },
  methods: {
    preventTouchMove() { },
    cancel() {
      this.setData({
        isShow: false
      })
      this.triggerEvent('cancel')
    },
    confirm() {
      this.setData({
        isShow: false
      })
      this.triggerEvent('confirm')
    }
  }
})


```go
<view class="container" wx:if="{{isShow}}" catchtouchmove="preventTouchMove">
  <view class="back-model"></view>
  <view class="conent-model">
    <text class="title">{{title}}</text>
    <text class="content">{{content}}</text>
    <view class="quickBtn">
      <button class="cancel-btn" open-type="{{cancelType}}" bindtap="cancel">{{cancelText}}</button>
      <button class="confirm-btn" open-type="{{confirmType}}" bindtap="confirm">{{confirmText}}</button>
    </view>
  </view>
</view>

2、painter
lib


const util = require('./util');

const SAVED_FILES_KEY = 'savedFiles';
const KEY_TOTAL_SIZE = 'totalSize';
const KEY_PATH = 'path';
const KEY_TIME = 'time';
const KEY_SIZE = 'size';

// 可存储总共为 6M,目前小程序可允许的最大本地存储为 10M
let MAX_SPACE_IN_B = 6 * 1024 * 1024;
let savedFiles = {};

export default class Dowloader {
  constructor() {
    // app 如果设置了最大存储空间,则使用 app 中的
    if (getApp().PAINTER_MAX_LRU_SPACE) {
      MAX_SPACE_IN_B = getApp().PAINTER_MAX_LRU_SPACE;
    }
    wx.getStorage({
      key: SAVED_FILES_KEY,
      success: function (res) {
        if (res.data) {
          savedFiles = res.data;
        }
      },
    });
  }

  /**
   * 下载文件,会用 lru 方式来缓存文件到本地
   * @param {String} url 文件的 url
   */
  download(url) {
    return new Promise((resolve, reject) => {
      if (!(url && util.isValidUrl(url))) {
        resolve(url);
        return;
      }
      const file = getFile(url);

      if (file) {
        // 检查文件是否正常,不正常需要重新下载
        wx.getSavedFileInfo({
          filePath: file[KEY_PATH],
          success: (res) => {
            resolve(file[KEY_PATH]);
          },
          fail: (error) => {
            console.error(`the file is broken, redownload it, ${JSON.stringify(error)}`);
            downloadFile(url).then((path) => {
              resolve(path);
            }, () => {
              reject();
            });
          },
        });
      } else {
        downloadFile(url).then((path) => {
          resolve(path);
        }, () => {
          reject();
        });
      }
    });
  }
}

function downloadFile(url) {
  return new Promise((resolve, reject) => {
    wx.downloadFile({
      url: url,
      success: function (res) {
        if (res.statusCode !== 200) {
          console.error(`downloadFile ${url} failed res.statusCode is not 200`);
          reject();
          return;
        }
        const { tempFilePath } = res;
        wx.getFileInfo({
          filePath: tempFilePath,
          success: (tmpRes) => {
            const newFileSize = tmpRes.size;
            doLru(newFileSize).then(() => {
              saveFile(url, newFileSize, tempFilePath).then((filePath) => {
                resolve(filePath);
              });
            }, () => {
              resolve(tempFilePath);
            });
          },
          fail: (error) => {
          // 文件大小信息获取失败,则此文件也不要进行存储
            console.error(`getFileInfo ${res.tempFilePath} failed, ${JSON.stringify(error)}`);
            resolve(res.tempFilePath);
          },
        });
      },
      fail: function (error) {
        console.error(`downloadFile failed, ${JSON.stringify(error)} `);
        reject();
      },
    });
  });
}

function saveFile(key, newFileSize, tempFilePath) {
  return new Promise((resolve, reject) => {
    wx.saveFile({
      tempFilePath: tempFilePath,
      success: (fileRes) => {
        const totalSize = savedFiles[KEY_TOTAL_SIZE] ? savedFiles[KEY_TOTAL_SIZE] : 0;
        savedFiles[key] = {};
        savedFiles[key][KEY_PATH] = fileRes.savedFilePath;
        savedFiles[key][KEY_TIME] = new Date().getTime();
        savedFiles[key][KEY_SIZE] = newFileSize;
        savedFiles['totalSize'] = newFileSize + totalSize;
        wx.setStorage({
          key: SAVED_FILES_KEY,
          data: savedFiles,
        });
        resolve(fileRes.savedFilePath);
      },
      fail: (error) => {
        console.error(`saveFile ${key} failed, then we delete all files, ${JSON.stringify(error)}`);
        // 由于 saveFile 成功后,res.tempFilePath 处的文件会被移除,所以在存储未成功时,我们还是继续使用临时文件
        resolve(tempFilePath);
        // 如果出现错误,就直接情况本地的所有文件,因为你不知道是不是因为哪次lru的某个文件未删除成功
        reset();
      },
    });
  });
}

/**
 * 清空所有下载相关内容
 */
function reset() {
  wx.removeStorage({
    key: SAVED_FILES_KEY,
    success: () => {
      wx.getSavedFileList({
        success: (listRes) => {
          removeFiles(listRes.fileList);
        },
        fail: (getError) => {
          console.error(`getSavedFileList failed, ${JSON.stringify(getError)}`);
        },
      });
    },
  });
}

function doLru(size) {
  return new Promise((resolve, reject) => {
    let totalSize = savedFiles[KEY_TOTAL_SIZE] ? savedFiles[KEY_TOTAL_SIZE] : 0;

    if (size + totalSize <= MAX_SPACE_IN_B) {
      resolve();
      return;
    }
    // 如果加上新文件后大小超过最大限制,则进行 lru
    const pathsShouldDelete = [];
    // 按照最后一次的访问时间,从小到大排序
    const allFiles = JSON.parse(JSON.stringify(savedFiles));
    delete allFiles[KEY_TOTAL_SIZE];
    const sortedKeys = Object.keys(allFiles).sort((a, b) => {
      return allFiles[a][KEY_TIME] - allFiles[b][KEY_TIME];
    });

    for (const sortedKey of sortedKeys) {
      totalSize -= savedFiles[sortedKey].size;
      pathsShouldDelete.push(savedFiles[sortedKey][KEY_PATH]);
      delete savedFiles[sortedKey];
      if (totalSize + size < MAX_SPACE_IN_B) {
        break;
      }
    }

    savedFiles['totalSize'] = totalSize;

    wx.setStorage({
      key: SAVED_FILES_KEY,
      data: savedFiles,
      success: () => {
      // 保证 storage 中不会存在不存在的文件数据
        if (pathsShouldDelete.length > 0) {
          removeFiles(pathsShouldDelete);
        }
        resolve();
      },
      fail: (error) => {
        console.error(`doLru setStorage failed, ${JSON.stringify(error)}`);
        reject();
      },
    });
  });
}

function removeFiles(pathsShouldDelete) {
  for (const pathDel of pathsShouldDelete) {
    let delPath = pathDel;
    if (typeof pathDel === 'object') {
      delPath = pathDel.filePath;
    }
    wx.removeSavedFile({
      filePath: delPath,
      fail: (error) => {
        console.error(`removeSavedFile ${pathDel} failed, ${JSON.stringify(error)}`);
      },
    });
  }
}

function getFile(key) {
  if (!savedFiles[key]) {
    return;
  }
  savedFiles[key]['time'] = new Date().getTime();
  wx.setStorage({
    key: SAVED_FILES_KEY,
    data: savedFiles,
  });
  return savedFiles[key];
}

const QR = require('./qrcode.js');

export default class Painter {
  constructor(ctx, data) {
    this.ctx = ctx;
    this.data = data;
  }

  paint(callback) {
    this.style = {
      width: this.data.width.toPx(),
      height: this.data.height.toPx(),
    };
    this._background();
    for (const view of this.data.views) {
      this._drawAbsolute(view);
    }
    this.ctx.draw(false, () => {
      callback();
    });
  }

  _background() {
    this.ctx.save();
    const {
      width,
      height,
    } = this.style;
    const bg = this.data.background;
    this.ctx.translate(width / 2, height / 2);

    this._doClip(this.data.borderRadius, width, height);
    if (!bg) {
      // 如果未设置背景,则默认使用白色
      this.ctx.fillStyle = '#fff';
      this.ctx.fillRect(-(width / 2), -(height / 2), width, height);
    } else if (bg.startsWith('#') || bg.startsWith('rgba') || bg.toLowerCase() === 'transparent') {
      // 背景填充颜色
      this.ctx.fillStyle = bg;
      this.ctx.fillRect(-(width / 2), -(height / 2), width, height);
    } else {
      // 背景填充图片
      this.ctx.drawImage(bg, -(width / 2), -(height / 2), width, height);
    }
    this.ctx.restore();
  }

  _drawAbsolute(view) {
    // 证明 css 为数组形式,需要合并
    if (view.css && view.css.length) {
      /* eslint-disable no-param-reassign */
      view.css = Object.assign(...view.css);
    }
    switch (view.type) {
      case 'image':
        this._drawAbsImage(view);
        break;
      case 'text':
        this._fillAbsText(view);
        break;
      case 'rect':
        this._drawAbsRect(view);
        break;
      case 'qrcode':
        this._drawQRCode(view);
        break;
      default:
        break;
    }
  }

  /**
   * 根据 borderRadius 进行裁减
   */
  _doClip(borderRadius, width, height) {
    if (borderRadius && width && height) {
      const r = Math.min(borderRadius.toPx(), width / 2, height / 2);
      // 防止在某些机型上周边有黑框现象,此处如果直接设置 setFillStyle 为透明,在 Android 机型上会导致被裁减的图片也变为透明, iOS 和 IDE 上不会
      // setGlobalAlpha 在 1.9.90 起支持,低版本下无效,但把 setFillStyle 设为了 white,相对默认的 black 要好点
      this.ctx.globalAlpha = 0;
      this.ctx.fillStyle = 'white';
      this.ctx.beginPath();
      this.ctx.arc(-width / 2 + r, -height / 2 + r, r, 1 * Math.PI, 1.5 * Math.PI);
      this.ctx.lineTo(width / 2 - r, -height / 2);
      this.ctx.arc(width / 2 - r, -height / 2 + r, r, 1.5 * Math.PI, 2 * Math.PI);
      this.ctx.lineTo(width / 2, height / 2 - r);
      this.ctx.arc(width / 2 - r, height / 2 - r, r, 0, 0.5 * Math.PI);
      this.ctx.lineTo(-width / 2 + r, height / 2);
      this.ctx.arc(-width / 2 + r, height / 2 - r, r, 0.5 * Math.PI, 1 * Math.PI);
      this.ctx.closePath();
      this.ctx.fill();
      // 在 ios 的 6.6.6 版本上 clip 有 bug,禁掉此类型上的 clip,也就意味着,在此版本微信的 ios 设备下无法使用 border 属性
      if (!(getApp().systemInfo &&
          getApp().systemInfo.version <= '6.6.6' &&
          getApp().systemInfo.platform === 'ios')) {
        this.ctx.clip();
      }
      this.ctx.globalAlpha = 1;
    }
  }

  /**
   * 画边框
   */
  _doBorder(view, width, height) {
    if (!view.css) {
      return;
    }
    const {
      borderRadius,
      borderWidth,
      borderColor,
    } = view.css;
    if (!borderWidth) {
      return;
    }
    this.ctx.save();
    this._preProcess(view, true);
    let r;
    if (borderRadius) {
      r = Math.min(borderRadius.toPx(), width / 2, height / 2);
    } else {
      r = 0;
    }
    const lineWidth = borderWidth.toPx();
    this.ctx.lineWidth = lineWidth;
    this.ctx.strokeStyle = borderColor || 'black';
    this.ctx.beginPath();
    this.ctx.arc(-width / 2 + r, -height / 2 + r, r + lineWidth / 2, 1 * Math.PI, 1.5 * Math.PI);
    this.ctx.lineTo(width / 2 - r, -height / 2 - lineWidth / 2);
    this.ctx.arc(width / 2 - r, -height / 2 + r, r + lineWidth / 2, 1.5 * Math.PI, 2 * Math.PI);
    this.ctx.lineTo(width / 2 + lineWidth / 2, height / 2 - r);
    this.ctx.arc(width / 2 - r, height / 2 - r, r + lineWidth / 2, 0, 0.5 * Math.PI);
    this.ctx.lineTo(-width / 2 + r, height / 2 + lineWidth / 2);
    this.ctx.arc(-width / 2 + r, height / 2 - r, r + lineWidth / 2, 0.5 * Math.PI, 1 * Math.PI);
    this.ctx.closePath();
    this.ctx.stroke();
    this.ctx.restore();
  }

  _preProcess(view, notClip) {
    let width;
    let height;
    let extra;
    switch (view.type) {
      case 'text': {
        const fontWeight = view.css.fontWeight === 'bold' ? 'bold' : 'normal';
        view.css.fontSize = view.css.fontSize ? view.css.fontSize : '20rpx';
        this.ctx.font = `normal ${fontWeight} ${view.css.fontSize.toPx()}px sans-serif`;
        // this.ctx.setFontSize(view.css.fontSize.toPx());
        const textLength = this.ctx.measureText(view.text).width;
        width = view.css.width ? view.css.width.toPx() : textLength;
        // 计算行数
        const calLines = Math.ceil(textLength / width);
        const lines = view.css.maxLines < calLines ? view.css.maxLines : calLines;
        const lineHeight = view.css.lineHeight ? view.css.lineHeight.toPx() : view.css.fontSize.toPx();
        height = lineHeight * lines;
        extra = { lines: lines, lineHeight: lineHeight };
        break;
      }
      case 'image': {
        // image 如果未设置长宽,则使用图片本身的长宽
        const ratio = getApp().systemInfo.pixelRatio ? getApp().systemInfo.pixelRatio : 2;
        width = view.css && view.css.width ? view.css.width.toPx() : Math.round(view.sWidth / ratio);
        height = view.css && view.css.height ? view.css.height.toPx() : Math.round(view.sHeight / ratio);
        break;
      }
      default: {
        if (!(view.css.width && view.css.height)) {
          console.error('You should set width and height');
          return;
        }
        width = view.css.width.toPx();
        height = view.css.height.toPx();
      }
    }
    const x = view.css && view.css.right ? this.style.width - view.css.right.toPx(true) : (view.css && view.css.left ? view.css.left.toPx(true) : 0);
    const y = view.css && view.css.bottom ? this.style.height - height - view.css.bottom.toPx(true) : (view.css && view.css.top ? view.css.top.toPx(true) : 0);

    const angle = view.css && view.css.rotate ? this._getAngle(view.css.rotate) : 0;
    // 当设置了 right 时,默认 align 用 right,反之用 left
    const align = view.css && view.css.align ? view.css.align : (view.css && view.css.right ? 'right' : 'left');
    switch (align) {
      case 'center':
        this.ctx.translate(x, y + height / 2);
        break;
      case 'right':
        this.ctx.translate(x - width / 2, y + height / 2);
        break;
      default:
        this.ctx.translate(x + width / 2, y + height / 2);
        break;
    }
    this.ctx.rotate(angle);
    if (!notClip && view.css && view.css.borderRadius) {
      this._doClip(view.css.borderRadius, width, height);
    }

    return {
      width: width,
      height: height,
      x: x,
      y: y,
      extra: extra,
    };
  }

  _drawQRCode(view) {
    this.ctx.save();
    const {
      width,
      height,
    } = this._preProcess(view);
    QR.api.draw(view.content, this.ctx, -width / 2, -height / 2, width, height, view.css.background, view.css.color);
    this.ctx.restore();
    this._doBorder(view, width, height);
  }

  _drawAbsImage(view) {
    if (!view.url) {
      return;
    }
    this.ctx.save();
    const {
      width,
      height,
    } = this._preProcess(view);
    // 获得缩放到图片大小级别的裁减框
    let rWidth;
    let rHeight;
    let startX = 0;
    let startY = 0;
    if (width > height) {
      rHeight = Math.round((view.sWidth / width) * height);
      rWidth = view.sWidth;
    } else {
      rWidth = Math.round((view.sHeight / height) * width);
      rHeight = view.sHeight;
    }
    if (view.sWidth > rWidth) {
      startX = Math.round((view.sWidth - rWidth) / 2);
    }
    if (view.sHeight > rHeight) {
      startY = Math.round((view.sHeight - rHeight) / 2);
    }
    if (view.css && view.css.mode === 'scaleToFill') {
      this.ctx.drawImage(view.url, -(width / 2), -(height / 2), width, height);
    } else {
      this.ctx.drawImage(view.url, startX, startY, rWidth, rHeight, -(width / 2), -(height / 2), width, height);
    }
    this.ctx.restore();
    this._doBorder(view, width, height);
  }

  _fillAbsText(view) {
    if (!view.text) {
      return;
    }
    this.ctx.save();
    const {
      width,
      height,
      extra,
    } = this._preProcess(view);
    this.ctx.fillStyle = view.css.color || 'black';
    const { lines, lineHeight } = extra;
    const preLineLength = Math.round(view.text.length / lines);
    let start = 0;
    let alreadyCount = 0;
    for (let i = 0; i < lines; ++i) {
      alreadyCount = preLineLength;
      let text = view.text.substr(start, alreadyCount);
      let measuredWith = this.ctx.measureText(text).width;
      // 如果测量大小小于width一个字符的大小,则进行补齐,如果测量大小超出 width,则进行减除
      // 如果已经到文本末尾,也不要进行该循环
      while ((start + alreadyCount <= view.text.length) && (width - measuredWith > view.css.fontSize.toPx() || measuredWith > width)) {
        if (measuredWith < width) {
          text = view.text.substr(start, ++alreadyCount);
        } else {
          if (text.length <= 1) {
            // 如果只有一个字符时,直接跳出循环
            break;
          }
          text = view.text.substr(start, --alreadyCount);
        }
        measuredWith = this.ctx.measureText(text).width;
      }
      start += text.length;
      // 如果是最后一行了,发现还有未绘制完的内容,则加...
      if (i === lines - 1 && start < view.text.length) {
        while (this.ctx.measureText(`${text}...`).width > width) {
          if (text.length <= 1) {
            // 如果只有一个字符时,直接跳出循环
            break;
          }
          text = text.substring(0, text.length - 1);
        }
        text += '...';
        measuredWith = this.ctx.measureText(text).width;
      }
      this.ctx.setTextAlign(view.css.align ? view.css.align : 'left');
      let x;
      switch (view.css.align) {
        case 'center':
          x = 0;
          break;
        case 'right':
          x = (width / 2);
          break;
        default:
          x = -(width / 2);
          break;
      }
      const y = -(height / 2) + (i === 0 ? view.css.fontSize.toPx() : (view.css.fontSize.toPx() + i * lineHeight));
      if (view.css.textStyle === 'stroke') {
        this.ctx.strokeText(text, x, y, measuredWith);
      } else {
        this.ctx.fillText(text, x, y, measuredWith);
      }
      const fontSize = view.css.fontSize.toPx();
      if (view.css.textDecoration) {
        this.ctx.beginPath();
        if (/\bunderline\b/.test(view.css.textDecoration)) {
          this.ctx.moveTo(x, y);
          this.ctx.lineTo(x + measuredWith, y);
        }
        if (/\boverline\b/.test(view.css.textDecoration)) {
          this.ctx.moveTo(x, y - fontSize);
          this.ctx.lineTo(x + measuredWith, y - fontSize);
        }
        if (/\bline-through\b/.test(view.css.textDecoration)) {
          this.ctx.moveTo(x, y - fontSize / 3);
          this.ctx.lineTo(x + measuredWith, y - fontSize / 3);
        }
        this.ctx.closePath();
        this.ctx.strokeStyle = view.css.color;
        this.ctx.stroke();
      }
    }

    this.ctx.restore();
    this._doBorder(view, width, height);
  }

  _drawAbsRect(view) {
    this.ctx.save();
    const {
      width,
      height,
    } = this._preProcess(view);
    this.ctx.fillStyle = view.css.color;
    this.ctx.fillRect(-(width / 2), -(height / 2), width, height);
    this.ctx.restore();
    this._doBorder(view, width, height);
  }

  _getAngle(angle) {
    return Number(angle) * Math.PI / 180;
  }
}

3、shareBox

Component({
  properties: {
    //属性值可以在组件使用时指定
    isCanDraw: {
      type: Boolean,
      value: false,
      observer(newVal, oldVal) {
        newVal && this.drawPic()
      }
    }
  },
  data: {
    isModal: false, //是否显示拒绝保存图片后的弹窗
    imgDraw: {}, //绘制图片的大对象
    sharePath: '', //生成的分享图
    visible: false
  },
  methods: {
    handlePhotoSaved() {
      this.savePhoto(this.data.sharePath)
    },
    handleClose() {
      this.setData({
        visible: false
      })
    },
    drawPic() {
      if (this.data.sharePath) { //如果已经绘制过了本地保存有图片不需要重新绘制
        this.setData({
          visible: true
        })
        this.triggerEvent('initData') 
        return
      }
      wx.showLoading({
        title: '生成中'
      })
      this.setData({
        imgDraw: {
          width: '750rpx',
          height: '1334rpx',
          background: 'https://cdn.nlark.com/yuque/0/2020/png/1252071/1586359267074-3d9d4922-d473-49db-8b14-e43d1d7d3e1a.png',
          views: [
            {
              type: 'image',
              url: 'https://cdn.nlark.com/yuque/0/2020/jpeg/1252071/1586359160786-8d5b7738-3ad3-43e7-bf0a-738c58365645.jpeg',//头图
              css: {
                top: '32rpx',
                left: '30rpx',
                right: '32rpx',
                width: '688rpx',
                height: '420rpx',
                borderRadius: '16rpx'
              },
            },
            {
              type: 'image',
              url: wx.getStorageSync('avatarUrl') || 'https://cdn.nlark.com/yuque/0/2020/png/1252071/1586358984220-88e904c6-345e-4d21-9960-6f26aaa85043.png',//用户头像
              css: {
                top: '404rpx',
                left: '328rpx',
                width: '96rpx',
                height: '96rpx',
                borderWidth: '6rpx',
                borderColor: '#FFF',
                borderRadius: '96rpx'
              }
            },
            {
              type: 'text',
              text: wx.getStorageSync('nickName') || '匿名用户',
              css: {
                top: '532rpx',
                fontSize: '28rpx',
                left: '375rpx',
                align: 'center',
                color: '#3c3c3c'
              }
            },
            {
              type: 'text',
              text: `邀请您参与助力活动`,
              css: {
                top: '576rpx',
                left: '375rpx',
                align: 'center',
                fontSize: '28rpx',
                color: '#3c3c3c'
              }
            },
            {
              type: 'text',
              text: `这是一个view转canvas绘制的示例`,
              css: {
                top: '644rpx',
                left: '375rpx',
                maxLines: 1,
                align: 'center',
                fontWeight: 'bold',
                fontSize: '44rpx',
                color: '#3c3c3c'
              }
            },
            {
              type: 'image',
              url: 'https://cdn.nlark.com/yuque/0/2020/jpeg/1252071/1586358913137-22be603f-99b1-4349-98bd-961d369b89e7.jpeg',//小程序码
              css: {
                top: '834rpx',
                left: '470rpx',
                width: '200rpx',
                height: '200rpx'
              }
            }
          ]
        }
      })
    },
    onImgErr(e) {
      wx.hideLoading()
      wx.showToast({
        title: '生成分享图失败,请刷新页面重试'
      })
    },
    onImgOK(e) {
      wx.hideLoading()
      this.setData({
        sharePath: e.detail.path,
        visible: true,
      })
      //通知外部绘制完成,重置isCanDraw为false
      this.triggerEvent('initData') 
    },
    preventDefault() { },
    // 保存图片
    savePhoto(path) {
      wx.showLoading({
        title: '正在保存...',
        mask: true
      })
      this.setData({
        isDrawImage: false
      })
      wx.saveImageToPhotosAlbum({
        filePath: path,
        success: (res) => {
          wx.showToast({
            title: '保存成功',
            icon: 'none'
          })
          setTimeout(() => {
            this.setData({
              visible: false
            })
          }, 300)
        },
        fail: (res) => {
          wx.getSetting({
            success: res => {
              let authSetting = res.authSetting
              if (!authSetting['scope.writePhotosAlbum']) {
                this.setData({
                  isModal: true
                })
              }
            }
          })
          setTimeout(() => {
            wx.hideLoading()
            this.setData({
              visible: false
            })
          }, 300)
        }
      })
    }
  }
})

<view class="share-wrap" wx:if="{{visible}}" catchtouchmove="preventDefault">
  <view class="share-back"></view>
  <view class="share-container">
    <view class="close" bindtap="handleClose" data-ptpid="ebe9-1656-ad6a-462e"></view>
    <image mode="widthFix" src="{{sharePath}}" class="share-image" />
    <view class="share-tips">保存图片,叫伙伴们来参与吧</view>
    <view class="save-btn" bindtap="handlePhotoSaved" data-ptpid="4095-16fd-bc97-4868"></view>
  </view>
</view>
<painter style="position: absolute; top: -9999rpx;" palette="{{imgDraw}}" bind:imgOK="onImgOK" />
<dialog-modal isShow="{{isModal}}" title="提示" content="您未开启保存图片到相册的权限,请点击确定去开启权限!" confirmType="openSetting" />

2.组件的使用

const app = getApp()

Page({
  data: {
    nickName: '',
    avatarUrl: '',
    isCanDraw: false
  },
  onLoad() {
    this.setData({
      nickName: wx.getStorageSync('nickName') || '',
      avatarUrl: wx.getStorageSync('avatarUrl') || ''
    })
  },
  getUserInfo(e) {
    this.setData({
      nickName: e.detail.userInfo.nickName,
      avatarUrl: e.detail.userInfo.avatarUrl
    })
    wx.setStorageSync('avatarUrl', e.detail.userInfo.avatarUrl)
    wx.setStorageSync('nickName', e.detail.userInfo.nickName)
  },
  createShareImage() {
    this.setData({
      isCanDraw: !this.data.isCanDraw
    })
  },
})

<view class="section">
	<!-- 生成分享图,将view转绘为图片 -->
	<button type="primary" class="intro" open-type="getUserInfo" bindgetuserinfo="getUserInfo" wx:if="{{!nickName}}">获取分享图头像昵称</button>
	<button type="primary" class="intro" bindtap="createShareImage" wx:else>点我生成分享图</button>
	<share-box isCanDraw="{{isCanDraw}}" bind:initData="createShareImage" />
</view>

3.效果

在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。