使用canvasdrawer组件来进行绘制,根据原有canvasdrawer改写了一个uniapp-cli版本的组件,使用时只需要和vue组件一样引入即可使用。十分简单,易用。
下为改写canvasdrawer组件代码
<template>
<canvas canvas-id="canvasdrawer"
:style="{width:width+'px',height:height+'px'}"
class="board"
v-if="showCanvas">
</canvas>
</template>
<script>
export default {
props: {
painting: {
type: Object,
default: {}
}
},
data(){
return {
showCanvas: false,
width: 100,
height: 100,
index: 0,
imageList: [],
tempFileList: [],
isPainting: false,
ctx: null
}
},
watch: {
painting(newVal, oldVal) {
if (!this.isPainting) {
if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
if (newVal && newVal.width && newVal.height) {
this.showCanvas = true;
this.isPainting = true;
this.readyPigment();
}
} else {
if (newVal && newVal.mode !== "same") {
this.$emit("getImage", {
errMsg: "canvasdrawer:samme params"
});
}
}
}
}
},
methods : {
readyPigment() {
const {
width,
height,
views
} = this.painting;
this.width = width;
this.height = height;
const inter = setInterval(() => {
if (this.ctx) {
clearInterval(inter);
this.ctx.clearActions();
this.ctx.save();
this.getImageList(views);
this.downLoadImages(0);
}
}, 100);
},
getImageList(views) {
const imageList = [];
for (let i = 0; i < views.length; i++) {
if (views[i].type === "image") {
imageList.push(views[i].url);
}
}
this.imageList = imageList;
},
downLoadImages(index) {
const {
imageList,
tempFileList
} = this;
console.log(index,imageList.length)
if (index < imageList.length) {
this.getImageInfo(imageList[index]).then(file => {
tempFileList.push(file);
this.tempFileList = tempFileList;
this.downLoadImages(index + 1);
});
} else {
this.startPainting();
}
},
startPainting() {
const {
tempFileList,
painting: {
views
}
} = this;
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) {
wx.showModal({
title: "提示",
content: "当前微信版本过低,无法使用 measureText 功能,请升级到最新微信版本后重试。"
});
this.$emit("getImage", {
errMsg: "canvasdrawer:version too low"
});
return;
} else {
this.drawText(views[i]);
}
} else if (views[i].type === "rect") {
this.drawRect(views[i]);
}
}
this.ctx.draw(false, () => {
const system = wx.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
} = params;
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();
},
async getImageInfo(url) {
const res = await wx.getImageInfo({
src: url
});
if (res.errMsg === "getImageInfo:ok") {
return res.path;
} else {
this.$emit("getImage", {
errMsg: "canvasdrawer:download fail"
});
return new Error("getImageInfo fail");
}
},
async saveImageToLocal() {
const {
width,
height
} = this;
const res = await wx.canvasToTempFilePath({
x: 0,
y: 0,
width,
height,
canvasId: "canvasdrawer"
}, this);
if (res.errMsg === "canvasToTempFilePath:ok") {
this.showCanvas = false;
this.isPainting = false;
this.imageList = [];
this.tempFileList = [];
this.$emit("getImage", {
tempFilePath: res.tempFilePath,
errMsg: "canvasdrawer:ok"
});
} else {
this.$emit("getImage", {
errMsg: "canvasdrawer:fail"
});
}
}
},
onReady() {
this.ctx = wx.createCanvasContext("canvasdrawer", this);
}
}
</script>
<style lang="less">
.board {
position: fixed;
top: 2000rpx;
}
</style>
下为使用时代码
<template>
<view class="content">
<view class="draw-img">
<image :src="shareImage" show-menu-by-longpress class="share-image" :style="{width: '100%',height:canvasHeight+'px'}" @click="preImg"></image>
<canvasdrawer :painting="painting" @getImage="eventGetImage"></canvasdrawer>
</view>
<view class="btn">
<view @click="eventSave">保存至手机相册</view>
<view class="cancel">重新生成</view>
</view>
</view>
</template>
<script>
import canvasdrawer from "@/components/canvasdrawer/canvasdrawer";
export default {
data() {
return {
canvasHeight:'',
shareImage:'',
painting:{},
paintinged:{
"width": 375,
"height": 555,
"clear": true,
"views": [
{
"type": "image",
"url": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1602478808861&di=2dfd90b69ef34fb417913b2822aabe6e&imgtype=0&src=http%3A%2F%2Fa0.att.hudong.com%2F56%2F12%2F01300000164151121576126282411.jpg",
"top": 0,
"left": 0,
"width": 375,
"height": 555
},
{
"type": "image",
"url": "https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1906469856,4113625838&fm=26&gp=0.jpg",
"top": 200,
"left": 200,
"width": 100,
"height": 100
},
{
"type": "text",
"content": "文字1",
"fontSize": 14,
"color": "red",
"textAlign": "left",
"top": 208,
"left": 50
},
{
"type": "text",
"content": "文字2",
"fontSize": 16,
"color": "yellow",
"textAlign": "left",
"top": 300,
"left": 50
},
{
"type": "text",
"content": "文字三",
"fontSize": 18,
"color": "blue",
"textAlign": "left",
"top": 422,
"left": 163
}
]
},
}
},
components: {
canvasdrawer
},
async onLoad(option){
this.getSystem()
this.eventDraw()
},
methods: {
async getSystem(){
let that=this
let res=await uni.getSystemInfo()
wx.createSelectorQuery().select('.btn').boundingClientRect(function (rect) {
that.canvasHeight=res[1].windowHeight-rect.height
}).exec()
},
eventDraw() {
wx.showLoading({
title: "请稍后",
mask: true
});
this.painting = this.paintinged;
},
async eventSave() {
// 保存图片至本地
const res = await wx.saveImageToPhotosAlbum({
filePath: this.shareImage
});
if (res.errMsg === "saveImageToPhotosAlbum:ok") {
wx.showToast({
title: "保存图片成功",
icon: "success",
duration: 2000
});
}
},
eventGetImage(event) {
wx.hideLoading();
this.shareImage = event.tempFilePath;
},
preImg(){
let that=this
wx.previewImage({
urls: [that.shareImage] // 需要预览的图片http链接列表
})
}
}
}
</script>
<style lang="less" scoped>
.content{
width: 100%;
height: 100vh;
.btn{
width: 100%;
height: 98rpx;
line-height: 98rpx;
position: fixed;
left: 0;
bottom: 0;
view{
float: left;
width: 50%;
height: 100%;
background-color: #00A578;
color: #fff;
text-align: center;
font-size: 30rpx;
font-family: PingFang SC;
font-weight: 400;
}
.cancel{
background-color: #fff;
color: #656565;
}
}
.draw-img{
width: 100%;
}
}
</style>
使用方式与canvasdrawer方法一致,本文只是将canvasdrawer整理成vue组件。
实例项目-----传送门思密达