一、在 components 中新建 canvasCom 文件夹并新建 canvasCom.vue 文件
1、写入 html 代码
<template>
<view class="wrap" :style="'width:' + width + 'px;height: ' + height + 'px;'">
<image class="img" :src="imageSrc"></image>
<canvas class="canvas" canvas-id="myCanvas" id="myCanvas"
:class="isPointerEvents ? 'pointerEvents' : 'noPointerEvents'" @longtap="longtap"></canvas>
</view>
</template>
2、写入 css 代码
.wrap {
position: relative;
.img,
.canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.pointerEvents {
pointer-events: all;
}
.noPointerEvents {
pointer-events: none;
}
}
3、写入 js 代码
export default {
name: "canvasCom",
props: {
// 宽 --- 数值
width: {
type: Number,
default: 300
},
// 高 --- 数值
height: {
type: Number,
default: 300
},
// 元素 --- 数组对象
info: {
type: Array,
default: []
}
},
data() {
return {
// 图片
imageSrc: '',
// 是否可触摸
isPointerEvents: false,
// 判断平台
type: uni.getSystemInfoSync(),
};
},
mounted() {
// 绘制
this.drawImage();
// 判断平台
if (this.type.uniPlatform == 'mp-weixin') {
this.isPointerEvents = true;
} else if (this.type.uniPlatform == 'web') {
// 开启后 h5 可以使用系统长按功能
this.isPointerEvents = false;
}
},
methods: {
// 绘制图片
async drawImage() {
const ctx = uni.createCanvasContext('myCanvas', this);
ctx.drawImage(await this.baseFun(this.info[0].src), this.info[0].x == 'center' ? await this.centerImg(
this.width, this
.info[0].width) : this.info[0].x, this.info[0].y, this.info[0].width, this.info[0].height);
ctx.drawImage(await this.baseFun(this.info[1].src), this.info[1].x == 'center' ? await this.centerImg(this.width, this
.info[1].width) : this.info[1].x, this.info[1].y, this.info[1].width, this.info[1].height);
ctx.drawImage(await this.baseFun(this.info[4].src), this.info[4].x == 'center' ? await this.centerImg(
this.width, this
.info[4].width) : this.info[4].x, this.info[4].y, this.info[4].width, this.info[4].height);
// 设置字体样式
ctx.font = this.info[2].font;
// 设置文本颜色
ctx.fillStyle = this.info[2].fillStyle;
// 绘制文本
let text = this.info[2].content;
ctx.fillText(text, await this.centerText(text, ctx), 50);
ctx.font = this.info[3].font;
ctx.fillStyle = this.info[3].fillStyle;
let text_ = this.info[3].content;
ctx.fillText(text_, await this.centerText(text_, ctx), 100);
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'myCanvas',
success: (res) => {
this.imageSrc = res.tempFilePath;
},
fail: (err) => {
console.error('Canvas to temp file path failed: ', err);
}
}, this);
});
},
// 将图片转换为 base64
baseFun(url) {
let isImg = /^https?:\/\//i;
return new Promise((resolve, reject) => {
if (isImg.test(url)) {
// 网络图片
uni.request({
url: url,
responseType: 'arraybuffer',
success: (res) => {
let base64Img = 'data:image/jpeg;base64,' + uni.arrayBufferToBase64(res
.data);
resolve(base64Img);
},
fail: (err) => {
reject(err);
}
});
} else {
// 本地图片
resolve(url);
}
})
},
// 计算文字居中
centerText(str, ctx) {
return new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(this);
query.select('#myCanvas').boundingClientRect(data => {
let textWidth = ctx.measureText(str).width;
let width = data.width / 2 - textWidth / 2;
if (width) {
resolve(width);
} else {
reject('获取失败')
}
}).exec();
})
},
// 计算图片居中
centerImg(domWidth, imgWidth) {
return new Promise((resolve, reject) => {
let width = domWidth / 2 - imgWidth / 2;
if (width) {
resolve(width);
} else {
reject('获取失败')
}
})
},
// 长按事件
longtap() {
console.log('长按');
if (this.type.uniPlatform == 'mp-weixin') {
// 微信小程序长按保存
uni.saveImageToPhotosAlbum({
filePath: this.imageSrc,
success: function() {
console.log('save success');
}
});
}
},
}
}
注:经过尝试,目前还没有更好的方案将 info 自动化绘制,各位大佬有好的方案可评论或私信分享,感谢!
二、父组件调用
1、html 代码
<canvas-com :width="width" :height="height" :info="info"></canvas-com>
2、js 代码
import canvasCom from '@/components/canvasCom/canvasCom.vue';
export default {
data() {
return {
width: 300,
height: 300,
info: [{
type: 'img',
src: '/static/logo.png',
width: 300,
height: 300,
x: 0, // center --- 居中 其他值可以为数值
y: 0
},
{
type: 'img',
src: '/static/logo.png',
width: 100,
height: 100,
x: 'center', // center --- 居中 其他值可以为数值
y: 100
},
{
type: 'text',
content: '主标题',
font: 'bold 20px Arial',
fillStyle: '#000000',
x: 'center', // center --- 居中 其他值可以为数值
y: 50
},
{
type: 'text',
content: '副标题',
font: 'bold 15px Arial',
fillStyle: '#666666',
x: 'center', // center --- 居中 其他值可以为数值
y: 100
},
{
type: 'img',
src: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/logo.png',
width: 161,
height: 56,
x: 'center', // center --- 居中 其他值可以为数值
y: 240
}
]
};
},
components: { canvasCom },
}