//components/canvans.wpy
<template>
<canvas canvas-id="canvasdrawer"
style="width:px; height:px;"
class="board"
wx:if="">
</canvas>
</template>
<script>
import wepy from "wepy";
export default class Drawer extends wepy.component {
/**
* @argument
* painting -- json数据
* @event
* getImage 渲染图片完成后的回调
**/
props = {
painting: {
type: Object,
default: {}
}
};
data = {
showCanvas: false,
width: 100,
height: 100,
index: 0,
imageList: [],
tempFileList: [],
isPainting: false,
ctx: null,
cache: {}
};
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.$apply();
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;
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) {
wepy.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, () => {
wx.setStorageSync('canvasdrawer_pic_cache', this.cache)
const system = wx.getSystemInfoSync().system
if (/ios/i.test(system)) {
this.saveImageToLocal()
} else {
// 延迟保存图片,解决安卓生成图片错位bug。
setTimeout(() => {
this.saveImageToLocal()
}, 100);
}
});
}
drawImage(params) {
this.ctx.save();
const {
url,
top = 0,
left = 0,
width = 0,
height = 0,
radius = 0
} = params;
if(radius != 0){
this.ctx.arc(left + radius, top + radius, radius, 0, 2 * Math.PI)
this.ctx.clip();
}
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 (this.cache[url]) {
resolve(this.cache[url])
} else {
const objExp = new RegExp(/^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/)
if (objExp.test(url)) {
wx.getImageInfo({
src: url,
complete: res => {
if (res.errMsg === 'getImageInfo:ok') {
this.cache[url] = res.path
resolve(res.path)
} else {
this.triggerEvent('getImage', {errMsg: 'canvasdrawer:download fail'})
reject(new Error('getImageInfo fail'))
}
}
})
} else {
this.cache[url] = url
resolve(url)
}
}
})
}
async saveImageToLocal() {
const {
width,
height
} = this;
const res = await wepy.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.$apply();
this.$emit("getImage", {
tempFilePath: res.tempFilePath,
errMsg: "canvasdrawer:ok"
});
} else {
this.$emit("getImage", {
errMsg: "canvasdrawer:fail"
});
}
}
onLoad() {
wepy.removeStorageSync("canvasdrawer_pic_cache");
this.cache = wepy.getStorageSync("canvasdrawer_pic_cache") || {};
this.ctx = wepy.createCanvasContext("canvasdrawer", this);
}
}
</script>
<style lang="less">
.board {
position: fixed;
top: 2000rpx;
}
</style>
<view>
<image src="" class="down"></image>
<canvasdrawer :painting.sync="painting" @getImage.user="eventGetImage"></canvasdrawer>
<button type='primary' class='openSetting' open-type="openSetting" wx:if="">去授权</button>
<view class="longtap" wx:if="" @tap="eventSave">保存至手机相册</view>
</view>
import canvasdrawer from '@/components/canvas';
export default class Index extends wepy.page {
components = {
canvasdrawer,
};
data = {
shareImage: '',//生成的图片地址
painting: null,//canvas图片信息
show: false,
userInfo: {},//用户信息
openSetBtn: false,//授权相册
};
watch = {
shareImage: function(newVal) {
if (newVal) {
this.show = true;
wx.hideLoading();
}
}
};
methods = {
async eventSave() {
// 保存图片至本地
let that = this;
try {
const res = await wepy.saveImageToPhotosAlbum({
filePath: this.shareImage
});
that.openSetBtn = false;
that.$apply();
if (res.errMsg === 'saveImageToPhotosAlbum:ok') {
wepy.showToast({
title: '保存图片成功',
icon: 'success',
duration: 2000
});
}
} catch (err) {
console.log(err);
if (err.errMsg != 'saveImageToPhotosAlbum:fail cancel') {
wx.showModal({
title: '警告',
content: '若不打开授权,则无法将图片保存在相册中!',
showCancel: false
});
that.openSetBtn = true;
that.$apply();
} else {
that.openSetBtn = false;
that.$apply();
}
}
},
//生成图片
eventGetImage(event) {
const { tempFilePath, errMsg } = event;
if (errMsg === 'canvasdrawer:ok') this.shareImage = tempFilePath;
},
};
getData(){
let that = this;
let gameName = '游戏名字-5';
let num = 55; //礼包个数
let pre = 0;
if (gameName) {
gameName.split('').forEach((val, i) => {
if (val.charCodeAt(0) > 127 || val.charCodeAt(0) == 94) {
pre++;
} else {
pre += 0.5;
}
});
}
this.painting = {
width: 586,
height: 742,
views: [
{
type: 'image',
url: 'https://dl.gamdream.com/activity/douwan/20181108/share2.png',
top: 0,
left: 0,
width: 586,
height: 742
},
{
type: 'image',
url: 'https://dl.gamdream.com/activity/douwan/20181108/share2.png',
top: 152,
left: 222,
width: 149,
height: 149,
radius: Math.floor(149 / 2)
},
{
type: 'image',
url: 'https://dl.gamdream.com/activity/douwan/20181108/radius.png',
top: 152,
left: 222,
width: 149,
height: 149
},
{
type: 'text',
content: '微信名字',
fontSize: 26,
color: '#333333',
textAlign: 'center',
top: 307,
left: 298
},
{
type: 'text',
content: '我在逗玩城已经领取',
fontSize: 28,
color: '#333333',
textAlign: 'center',
top: 350,
left: 298
},
{
type: 'text',
content: num,
fontSize: 48,
color: '#333333',
textAlign: 'left',
top: 397,
left: Math.floor((600 - String(num).length * 48 * 0.586 - 30) / 2)
},
{
type: 'text',
content: '个',
fontSize: 30,
color: '#333333',
textAlign: 'left',
top: 412,
left: Math.floor(
(600 - String(num).length * 48 * 0.586 - 30) / 2 +
String(num).length * 48 * 0.586
)
},
{
type: 'text',
content: gameName,
fontSize: 30,
color: '#8A2C1B',
textAlign: 'left',
top: 465,
left: Math.floor((600 - (30 * pre + 20 + 60)) / 2)
},
{
type: 'text',
content: '礼包',
fontSize: 30,
color: '#333333',
textAlign: 'left',
top: 465,
left: Math.floor((600 - (30 * pre + 20 + 60)) / 2 + (30 * pre + 20))
},
]
};
that.$apply();
}
onLoad(){
this.getData()
}
}