【愚公系列】2022年08月 微信小程序-view生成分享图片(原生封装版)
【摘要】 一、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)