219 lines
5.7 KiB
Vue
219 lines
5.7 KiB
Vue
<script>
|
|
import Painter from './painter'
|
|
import { equal } from './util'
|
|
export default {
|
|
name: 'VueCanvasPoster',
|
|
props: {
|
|
painting: {
|
|
type: Object,
|
|
default: function() {
|
|
return {}
|
|
}
|
|
},
|
|
dirty: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
widthPixels: {
|
|
type: Number,
|
|
default: 750
|
|
}
|
|
},
|
|
watch: {
|
|
painting: {
|
|
handler(newVal, oldVal) {
|
|
if (this.isNeedRefresh(newVal, oldVal)) {
|
|
this.paintCount = 0
|
|
this.startPaint()
|
|
}
|
|
},
|
|
deep: true,
|
|
immediate: true
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
paintCount: 0,
|
|
painterStyle: '',
|
|
canvasWidthInPx: 375,
|
|
canvasHeightInPx: 375,
|
|
width: 100,
|
|
height: 100,
|
|
canvas: null,
|
|
ctx: null
|
|
}
|
|
},
|
|
render(h) {
|
|
return h('div', [
|
|
h('canvas', {
|
|
ref: 'canvas',
|
|
class: 'canvas',
|
|
style: this.painterStyle
|
|
})
|
|
])
|
|
},
|
|
mounted() {
|
|
this.$nextTick(() => {
|
|
this.canvas = this.$refs.canvas // 指定canvas
|
|
this.ctx = this.canvas.getContext('2d') //设置2D渲染区域
|
|
})
|
|
},
|
|
methods: {
|
|
/**
|
|
* 判断一个 object 是否为 空
|
|
* @param {object} object
|
|
*/
|
|
isEmpty(object) {
|
|
for (const i in object) {
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
isNeedRefresh(newVal, oldVal) {
|
|
if (!newVal || this.isEmpty(newVal) || (this.dirty && equal(newVal, oldVal))) {
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
// 开始绘画
|
|
startPaint() {
|
|
if (this.isEmpty(this.painting)) {
|
|
return
|
|
}
|
|
setStringPrototype(1)
|
|
// 下载图片
|
|
this.downloadImages()
|
|
.then(res => {
|
|
const { width, height } = res
|
|
if (!width || !height) {
|
|
console.error(`You should set width and height correctly for painter, width: ${width}, height: ${height}`)
|
|
return
|
|
}
|
|
this.canvasWidthInPx = width.toPx()
|
|
if (this.widthPixels) {
|
|
// 重设宽度,高度
|
|
setStringPrototype(this.widthPixels / this.canvasWidthInPx)
|
|
this.canvasWidthInPx = this.widthPixels
|
|
}
|
|
this.canvasHeightInPx = height.toPx()
|
|
this.painterStyle = `width:${this.canvasWidthInPx}px;height:${this.canvasHeightInPx}px;`
|
|
this.canvas = this.$refs.canvas // 指定canvas
|
|
this.canvas.width = this.canvasWidthInPx
|
|
this.canvas.height = this.canvasHeightInPx
|
|
const ctx = this.canvas.getContext('2d') //设置2D渲染区域
|
|
const pen = new Painter(ctx, res)
|
|
pen.paint(() => {
|
|
var imageBase64 = this.canvas.toDataURL('image/png')
|
|
this.$emit('success', imageBase64)
|
|
})
|
|
})
|
|
.catch(err => {
|
|
this.$emit('fail', err)
|
|
})
|
|
},
|
|
// 下载所有图片
|
|
downloadImages() {
|
|
return new Promise((resolve) => {
|
|
let preCount = 0
|
|
let completeCount = 0
|
|
const paintCopy = JSON.parse(JSON.stringify(this.painting))
|
|
if (paintCopy.background) {
|
|
preCount++
|
|
this.loadImage(paintCopy.background).then(
|
|
image => {
|
|
paintCopy.background = image
|
|
completeCount++
|
|
preCount === completeCount && resolve(paintCopy)
|
|
},
|
|
err => {
|
|
completeCount++
|
|
preCount === completeCount && resolve(paintCopy)
|
|
console.log(err)
|
|
}
|
|
)
|
|
}
|
|
if (paintCopy.views) {
|
|
for (const view of paintCopy.views) {
|
|
if (view && view.type === 'image' && view.url) {
|
|
preCount++
|
|
/* eslint-disable no-loop-func */
|
|
this.loadImage(view.url).then(
|
|
image => {
|
|
completeCount++
|
|
view.url = image
|
|
// 获得一下图片信息,供后续裁减使用
|
|
view.sWidth = image.width
|
|
view.sHeight = image.height
|
|
preCount === completeCount && resolve(paintCopy)
|
|
},
|
|
err => {
|
|
completeCount++
|
|
preCount === completeCount && resolve(paintCopy)
|
|
console.log(err)
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
preCount === 0 && resolve(paintCopy)
|
|
// if (preCount !== completeCount) {
|
|
// reject('paintCopy');
|
|
// }
|
|
})
|
|
},
|
|
// 下载图片
|
|
loadImage(src) {
|
|
return new Promise((resolve, reject) => {
|
|
if (src.startsWith('#')) {
|
|
resolve(src)
|
|
return
|
|
}
|
|
const img = new Image()
|
|
img.onload = () => resolve(img)
|
|
img.onerror = () => reject(`下载图片失败 ${src}`)
|
|
img.crossOrigin = 'anonymous'
|
|
img.src = src
|
|
if (img.complete === true) {
|
|
// Inline XML images may fail to parse, throwing an Error later on
|
|
setTimeout(() => resolve(img), 500)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
function setStringPrototype(scale) {
|
|
/* eslint-disable no-extend-native */
|
|
/**
|
|
* 是否支持负数
|
|
* @param {Boolean} minus 是否支持负数
|
|
*/
|
|
String.prototype.toPx = function toPx(minus, baseSize) {
|
|
if (this === '0') {
|
|
return 0
|
|
}
|
|
let reg
|
|
if (minus) {
|
|
reg = /^-?[0-9]+([.]{1}[0-9]+){0,1}(px|%)$/g
|
|
} else {
|
|
reg = /^[0-9]+([.]{1}[0-9]+){0,1}(px|%)$/g
|
|
}
|
|
const results = reg.exec(this)
|
|
const unit = results[2]
|
|
const value = parseFloat(this)
|
|
let res = 0
|
|
if (unit === 'px') {
|
|
res = Math.round(value * (scale || 1))
|
|
} else if (unit === '%') {
|
|
res = Math.round((value * baseSize) / 100)
|
|
}
|
|
return res
|
|
}
|
|
}
|
|
</script>
|
|
<style scoped>
|
|
.canvas {
|
|
position: fixed;
|
|
top: 2000px;
|
|
}
|
|
</style>
|