2018-07-05 15:27:12 +08:00
|
|
|
import Pen from './lib/pen';
|
|
|
|
|
import Downloader from './lib/downloader';
|
|
|
|
|
|
2018-07-10 18:24:28 +08:00
|
|
|
const util = require('./lib/util');
|
|
|
|
|
|
2018-07-05 15:27:12 +08:00
|
|
|
const downloader = new Downloader();
|
|
|
|
|
|
|
|
|
|
// 最大尝试的绘制次数
|
|
|
|
|
const MAX_PAINT_COUNT = 5;
|
|
|
|
|
Component({
|
|
|
|
|
canvasWidthInPx: 0,
|
|
|
|
|
canvasHeightInPx: 0,
|
|
|
|
|
paintCount: 0,
|
2019-11-13 10:56:24 +08:00
|
|
|
currentPalette: {},
|
|
|
|
|
globalContext: {},
|
|
|
|
|
hasIdViews: [],
|
2018-07-05 15:27:12 +08:00
|
|
|
/**
|
|
|
|
|
* 组件的属性列表
|
|
|
|
|
*/
|
|
|
|
|
properties: {
|
|
|
|
|
customStyle: {
|
|
|
|
|
type: String,
|
|
|
|
|
},
|
|
|
|
|
palette: {
|
|
|
|
|
type: Object,
|
|
|
|
|
observer: function (newVal, oldVal) {
|
|
|
|
|
if (this.isNeedRefresh(newVal, oldVal)) {
|
|
|
|
|
this.paintCount = 0;
|
|
|
|
|
this.startPaint();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
2019-11-06 17:20:44 +08:00
|
|
|
widthPixels: {
|
|
|
|
|
type: Number,
|
|
|
|
|
value: 0
|
|
|
|
|
},
|
2018-08-15 19:32:07 +08:00
|
|
|
// 启用脏检查,默认 false
|
|
|
|
|
dirty: {
|
|
|
|
|
type: Boolean,
|
2018-08-17 12:24:44 +08:00
|
|
|
value: false,
|
|
|
|
|
},
|
2019-11-13 10:56:24 +08:00
|
|
|
actions: {
|
|
|
|
|
type: Object,
|
|
|
|
|
observer: function (newVal, oldVal) {
|
|
|
|
|
this.doAction(newVal)
|
|
|
|
|
},
|
|
|
|
|
}
|
2018-07-05 15:27:12 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
data: {
|
|
|
|
|
picURL: '',
|
|
|
|
|
showCanvas: true,
|
|
|
|
|
painterStyle: '',
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
methods: {
|
|
|
|
|
/**
|
|
|
|
|
* 判断一个 object 是否为 空
|
|
|
|
|
* @param {object} object
|
|
|
|
|
*/
|
|
|
|
|
isEmpty(object) {
|
|
|
|
|
for (const i in object) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
isNeedRefresh(newVal, oldVal) {
|
2018-08-15 19:32:07 +08:00
|
|
|
if (!newVal || this.isEmpty(newVal) || (this.data.dirty && util.equal(newVal, oldVal))) {
|
2018-07-05 15:27:12 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
2019-11-13 10:56:24 +08:00
|
|
|
doAction(newVal) {
|
2019-11-13 18:28:46 +08:00
|
|
|
if (newVal && newVal.css) {
|
|
|
|
|
if (Array.isArray(this.touchedView.css)) {
|
|
|
|
|
this.touchedView.css = [...this.touchedView.css, newVal.css]
|
|
|
|
|
} else {
|
|
|
|
|
this.touchedView.css = [this.touchedView.css, newVal.css]
|
2019-11-13 10:56:24 +08:00
|
|
|
}
|
2019-11-13 18:28:46 +08:00
|
|
|
}
|
|
|
|
|
const draw = {
|
|
|
|
|
width: this.currentPalette.width,
|
|
|
|
|
height: this.currentPalette.height,
|
|
|
|
|
views: [this.touchedView]
|
|
|
|
|
}
|
|
|
|
|
const pen = new Pen(this.globalContext, draw);
|
2019-11-13 10:56:24 +08:00
|
|
|
pen.paint();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
startX: 0,
|
|
|
|
|
startY: 0,
|
|
|
|
|
touchedView: {},
|
|
|
|
|
onTouchStart(event) {
|
|
|
|
|
const {
|
|
|
|
|
x,
|
|
|
|
|
y
|
|
|
|
|
} = event.touches[0]
|
|
|
|
|
this.startX = x
|
|
|
|
|
this.startY = y
|
2019-11-13 18:28:46 +08:00
|
|
|
const totalLayerCount = this.currentPalette.views.length
|
|
|
|
|
let findedIndex = -1;
|
|
|
|
|
this.touchedView = {}
|
|
|
|
|
for (let i = totalLayerCount - 1; i >= 0; i--) {
|
|
|
|
|
const view = this.currentPalette.views[i]
|
2019-11-13 10:56:24 +08:00
|
|
|
if (x > view.rect.left && y > view.rect.top && x < view.rect.right && y < view.rect.bottom) {
|
|
|
|
|
if (view.id) {
|
|
|
|
|
this.touchedView = view
|
2019-11-13 18:28:46 +08:00
|
|
|
this.triggerEvent('touchStart', {
|
2019-11-13 10:56:24 +08:00
|
|
|
id: view.id,
|
2019-11-13 18:28:46 +08:00
|
|
|
css: view.css,
|
|
|
|
|
rect: view.rect
|
2019-11-13 10:56:24 +08:00
|
|
|
})
|
|
|
|
|
}
|
2019-11-13 18:28:46 +08:00
|
|
|
findedIndex = i;
|
2019-11-13 10:56:24 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-13 18:28:46 +08:00
|
|
|
|
|
|
|
|
if (findedIndex < 0) {
|
|
|
|
|
// 证明点击了背景
|
|
|
|
|
this.triggerEvent('touchStart', {})
|
|
|
|
|
} else if (this.touchedView.id) {
|
|
|
|
|
const bottomLayers = this.currentPalette.views.slice(0, findedIndex)
|
|
|
|
|
const topLayers = this.currentPalette.views.slice(findedIndex + 1)
|
|
|
|
|
const bottomDraw = {
|
|
|
|
|
width: this.currentPalette.width,
|
|
|
|
|
height: this.currentPalette.height,
|
|
|
|
|
background: this.currentPalette.background,
|
|
|
|
|
views: bottomLayers
|
|
|
|
|
}
|
|
|
|
|
const topDraw = {
|
|
|
|
|
width: this.currentPalette.width,
|
|
|
|
|
height: this.currentPalette.height,
|
|
|
|
|
views: topLayers
|
|
|
|
|
}
|
2019-11-13 19:03:20 +08:00
|
|
|
if (this.prevFindedIndex < findedIndex) {
|
|
|
|
|
new Pen(wx.createCanvasContext('bottom', this), bottomDraw).paint();
|
|
|
|
|
new Pen(wx.createCanvasContext('top', this), topDraw).paint();
|
|
|
|
|
} else {
|
|
|
|
|
new Pen(wx.createCanvasContext('top', this), topDraw).paint();
|
|
|
|
|
new Pen(wx.createCanvasContext('bottom', this), bottomDraw).paint();
|
|
|
|
|
}
|
2019-11-13 18:28:46 +08:00
|
|
|
|
|
|
|
|
this.doAction()
|
|
|
|
|
}
|
2019-11-13 19:03:20 +08:00
|
|
|
this.prevFindedIndex = findedIndex
|
2019-11-13 10:56:24 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onTouchEnd() {
|
|
|
|
|
this.touchedView = {}
|
2019-11-13 18:28:46 +08:00
|
|
|
this.triggerEvent('touchEnd')
|
2019-11-13 10:56:24 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onTouchCancel() {
|
|
|
|
|
this.touchedView = {}
|
2019-11-13 18:28:46 +08:00
|
|
|
this.triggerEvent('touchCancel')
|
2019-11-13 10:56:24 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onTouchMove(event) {
|
|
|
|
|
if (!this.touchedView.id) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const {
|
|
|
|
|
x,
|
|
|
|
|
y
|
|
|
|
|
} = event.touches[0]
|
|
|
|
|
const offsetX = x - this.startX
|
|
|
|
|
const offsetY = y - this.startY
|
|
|
|
|
this.startX = x
|
|
|
|
|
this.startY = y
|
|
|
|
|
const css = {
|
|
|
|
|
left: `${this.touchedView.rect.x + offsetX}px`,
|
|
|
|
|
top: `${this.touchedView.rect.y + offsetY}px`,
|
|
|
|
|
right: undefined,
|
|
|
|
|
bottom: undefined
|
|
|
|
|
}
|
|
|
|
|
this.doAction({
|
|
|
|
|
id: this.touchedView.id,
|
|
|
|
|
css
|
|
|
|
|
})
|
2019-11-13 18:28:46 +08:00
|
|
|
this.triggerEvent('touchMove',{
|
|
|
|
|
id: this.touchedView.id,
|
|
|
|
|
css
|
|
|
|
|
})
|
2019-11-13 10:56:24 +08:00
|
|
|
},
|
|
|
|
|
|
2018-07-05 15:27:12 +08:00
|
|
|
startPaint() {
|
|
|
|
|
if (this.isEmpty(this.properties.palette)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-16 15:59:18 +08:00
|
|
|
if (!(getApp().systemInfo && getApp().systemInfo.screenWidth)) {
|
2018-07-05 15:27:12 +08:00
|
|
|
try {
|
2018-07-16 15:59:18 +08:00
|
|
|
getApp().systemInfo = wx.getSystemInfoSync();
|
2018-07-05 15:27:12 +08:00
|
|
|
} catch (e) {
|
2018-07-10 18:24:28 +08:00
|
|
|
const error = `Painter get system info failed, ${JSON.stringify(e)}`;
|
2019-11-13 10:56:24 +08:00
|
|
|
this.triggerEvent('imgErr', {
|
2019-11-06 17:20:44 +08:00
|
|
|
error: error
|
|
|
|
|
});
|
2018-07-10 18:24:28 +08:00
|
|
|
console.error(error);
|
|
|
|
|
return;
|
2018-07-05 15:27:12 +08:00
|
|
|
}
|
|
|
|
|
}
|
2019-11-06 17:20:44 +08:00
|
|
|
let screenK = getApp().systemInfo.screenWidth / 750;
|
|
|
|
|
setStringPrototype(screenK, 1);
|
2019-11-13 10:56:24 +08:00
|
|
|
|
2018-07-05 15:27:12 +08:00
|
|
|
this.downloadImages().then((palette) => {
|
2019-11-13 10:56:24 +08:00
|
|
|
this.currentPalette = palette
|
|
|
|
|
this.hasIdViews = []
|
|
|
|
|
palette.views && palette.views.map(view => {
|
|
|
|
|
if (view.id) {
|
|
|
|
|
this.hasIdViews.push(view)
|
|
|
|
|
}
|
|
|
|
|
})
|
2019-11-06 17:20:44 +08:00
|
|
|
const {
|
|
|
|
|
width,
|
|
|
|
|
height
|
|
|
|
|
} = palette;
|
2019-11-13 10:56:24 +08:00
|
|
|
|
2018-07-05 15:27:12 +08:00
|
|
|
if (!width || !height) {
|
|
|
|
|
console.error(`You should set width and height correctly for painter, width: ${width}, height: ${height}`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-11-06 17:20:44 +08:00
|
|
|
this.canvasWidthInPx = width.toPx();
|
|
|
|
|
if (this.properties.widthPixels) {
|
|
|
|
|
// 如果重新设置过像素宽度,则重新设置比例
|
|
|
|
|
setStringPrototype(screenK, this.properties.widthPixels / this.canvasWidthInPx)
|
|
|
|
|
this.canvasWidthInPx = this.properties.widthPixels
|
|
|
|
|
}
|
2019-11-13 10:56:24 +08:00
|
|
|
|
2019-11-06 17:20:44 +08:00
|
|
|
this.canvasHeightInPx = height.toPx();
|
2018-07-05 15:27:12 +08:00
|
|
|
this.setData({
|
2019-11-06 17:20:44 +08:00
|
|
|
painterStyle: `width:${this.canvasWidthInPx}px;height:${this.canvasHeightInPx}px;`,
|
2018-07-05 15:27:12 +08:00
|
|
|
});
|
2019-11-13 10:56:24 +08:00
|
|
|
this.globalContext = wx.createCanvasContext('k-canvas', this);
|
|
|
|
|
const pen = new Pen(this.globalContext, palette);
|
2018-07-05 15:27:12 +08:00
|
|
|
pen.paint(() => {
|
2018-07-18 18:26:58 +08:00
|
|
|
this.saveImgToLocal();
|
2018-07-05 15:27:12 +08:00
|
|
|
});
|
2018-07-10 18:24:28 +08:00
|
|
|
});
|
2018-07-05 15:27:12 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
downloadImages() {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
let preCount = 0;
|
|
|
|
|
let completeCount = 0;
|
|
|
|
|
const paletteCopy = JSON.parse(JSON.stringify(this.properties.palette));
|
2018-07-27 14:39:36 +08:00
|
|
|
if (paletteCopy.background) {
|
2018-07-10 18:24:28 +08:00
|
|
|
preCount++;
|
2018-07-05 15:27:12 +08:00
|
|
|
downloader.download(paletteCopy.background).then((path) => {
|
|
|
|
|
paletteCopy.background = path;
|
2018-07-10 18:24:28 +08:00
|
|
|
completeCount++;
|
2018-07-05 15:27:12 +08:00
|
|
|
if (preCount === completeCount) {
|
|
|
|
|
resolve(paletteCopy);
|
|
|
|
|
}
|
|
|
|
|
}, () => {
|
2018-07-10 18:24:28 +08:00
|
|
|
completeCount++;
|
2018-07-05 15:27:12 +08:00
|
|
|
if (preCount === completeCount) {
|
|
|
|
|
resolve(paletteCopy);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (paletteCopy.views) {
|
|
|
|
|
for (const view of paletteCopy.views) {
|
2018-07-27 14:39:36 +08:00
|
|
|
if (view && view.type === 'image' && view.url) {
|
2018-07-10 18:24:28 +08:00
|
|
|
preCount++;
|
|
|
|
|
/* eslint-disable no-loop-func */
|
2018-07-05 15:27:12 +08:00
|
|
|
downloader.download(view.url).then((path) => {
|
|
|
|
|
view.url = path;
|
2018-07-27 14:39:36 +08:00
|
|
|
wx.getImageInfo({
|
|
|
|
|
src: view.url,
|
|
|
|
|
success: (res) => {
|
|
|
|
|
// 获得一下图片信息,供后续裁减使用
|
|
|
|
|
view.sWidth = res.width;
|
|
|
|
|
view.sHeight = res.height;
|
|
|
|
|
},
|
|
|
|
|
fail: (error) => {
|
2018-09-21 15:27:06 +08:00
|
|
|
// 如果图片坏了,则直接置空,防止坑爹的 canvas 画崩溃了
|
|
|
|
|
view.url = "";
|
|
|
|
|
console.error(`getImageInfo ${view.url} failed, ${JSON.stringify(error)}`);
|
2018-07-27 14:39:36 +08:00
|
|
|
},
|
|
|
|
|
complete: () => {
|
|
|
|
|
completeCount++;
|
|
|
|
|
if (preCount === completeCount) {
|
|
|
|
|
resolve(paletteCopy);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
2018-07-05 15:27:12 +08:00
|
|
|
}, () => {
|
2018-07-10 18:24:28 +08:00
|
|
|
completeCount++;
|
2018-07-05 15:27:12 +08:00
|
|
|
if (preCount === completeCount) {
|
|
|
|
|
resolve(paletteCopy);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (preCount === 0) {
|
|
|
|
|
resolve(paletteCopy);
|
|
|
|
|
}
|
2018-07-10 18:24:28 +08:00
|
|
|
});
|
2018-07-05 15:27:12 +08:00
|
|
|
},
|
|
|
|
|
|
2018-07-18 18:26:58 +08:00
|
|
|
saveImgToLocal() {
|
2018-07-05 15:27:12 +08:00
|
|
|
const that = this;
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
wx.canvasToTempFilePath({
|
|
|
|
|
canvasId: 'k-canvas',
|
|
|
|
|
success: function (res) {
|
2018-07-18 18:26:58 +08:00
|
|
|
that.getImageInfo(res.tempFilePath);
|
2018-07-05 15:27:12 +08:00
|
|
|
},
|
|
|
|
|
fail: function (error) {
|
|
|
|
|
console.error(`canvasToTempFilePath failed, ${JSON.stringify(error)}`);
|
2019-11-06 17:20:44 +08:00
|
|
|
that.triggerEvent('imgErr', {
|
|
|
|
|
error: error
|
|
|
|
|
});
|
2018-07-05 15:27:12 +08:00
|
|
|
},
|
|
|
|
|
}, this);
|
|
|
|
|
}, 300);
|
|
|
|
|
},
|
2018-07-18 18:26:58 +08:00
|
|
|
|
|
|
|
|
getImageInfo(filePath) {
|
|
|
|
|
const that = this;
|
|
|
|
|
wx.getImageInfo({
|
|
|
|
|
src: filePath,
|
|
|
|
|
success: (infoRes) => {
|
|
|
|
|
if (that.paintCount > MAX_PAINT_COUNT) {
|
|
|
|
|
const error = `The result is always fault, even we tried ${MAX_PAINT_COUNT} times`;
|
|
|
|
|
console.error(error);
|
2019-11-06 17:20:44 +08:00
|
|
|
that.triggerEvent('imgErr', {
|
|
|
|
|
error: error
|
|
|
|
|
});
|
2018-07-18 18:26:58 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 比例相符时才证明绘制成功,否则进行强制重绘制
|
|
|
|
|
if (Math.abs((infoRes.width * that.canvasHeightInPx - that.canvasWidthInPx * infoRes.height) / (infoRes.height * that.canvasHeightInPx)) < 0.01) {
|
2019-11-06 17:20:44 +08:00
|
|
|
that.triggerEvent('imgOK', {
|
|
|
|
|
path: filePath
|
|
|
|
|
});
|
2018-07-18 18:26:58 +08:00
|
|
|
} else {
|
|
|
|
|
that.startPaint();
|
|
|
|
|
}
|
|
|
|
|
that.paintCount++;
|
|
|
|
|
},
|
|
|
|
|
fail: (error) => {
|
|
|
|
|
console.error(`getImageInfo failed, ${JSON.stringify(error)}`);
|
2019-11-06 17:20:44 +08:00
|
|
|
that.triggerEvent('imgErr', {
|
|
|
|
|
error: error
|
|
|
|
|
});
|
2018-07-18 18:26:58 +08:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
},
|
2018-07-05 15:27:12 +08:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2019-11-06 17:20:44 +08:00
|
|
|
function setStringPrototype(screenK, scale) {
|
2018-07-10 18:24:28 +08:00
|
|
|
/* eslint-disable no-extend-native */
|
2018-07-20 15:57:33 +08:00
|
|
|
/**
|
|
|
|
|
* 是否支持负数
|
|
|
|
|
* @param {Boolean} minus 是否支持负数
|
|
|
|
|
*/
|
|
|
|
|
String.prototype.toPx = function toPx(minus) {
|
|
|
|
|
let reg;
|
|
|
|
|
if (minus) {
|
|
|
|
|
reg = /^-?[0-9]+([.]{1}[0-9]+){0,1}(rpx|px)$/g;
|
|
|
|
|
} else {
|
|
|
|
|
reg = /^[0-9]+([.]{1}[0-9]+){0,1}(rpx|px)$/g;
|
|
|
|
|
}
|
2018-07-10 18:24:28 +08:00
|
|
|
const results = reg.exec(this);
|
|
|
|
|
if (!this || !results) {
|
|
|
|
|
console.error(`The size: ${this} is illegal`);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
const unit = results[2];
|
|
|
|
|
const value = parseFloat(this);
|
2018-07-05 15:27:12 +08:00
|
|
|
|
2018-07-10 18:24:28 +08:00
|
|
|
let res = 0;
|
|
|
|
|
if (unit === 'rpx') {
|
2019-11-06 17:20:44 +08:00
|
|
|
res = Math.round(value * screenK * (scale || 1));
|
2018-07-10 18:24:28 +08:00
|
|
|
} else if (unit === 'px') {
|
2019-11-06 17:20:44 +08:00
|
|
|
res = Math.round(value * (scale || 1));
|
2018-07-10 18:24:28 +08:00
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
};
|
2019-11-06 17:20:44 +08:00
|
|
|
}
|