forked from integrated_whb/integrated_whb_vue
290 lines
8.1 KiB
Vue
290 lines
8.1 KiB
Vue
<template>
|
|
<canvas ref="canvas" @mousedown="mouseDown" @mousemove="mouseMove" @mouseup="mouseUp"
|
|
@touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd"></canvas>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
props: {
|
|
width: {
|
|
type: Number,
|
|
default: 800
|
|
},
|
|
height: {
|
|
type: Number,
|
|
default: 300
|
|
},
|
|
lineWidth: {
|
|
type: Number,
|
|
default: 4
|
|
},
|
|
lineColor: {
|
|
type: String,
|
|
default: '#000000'
|
|
},
|
|
bgColor: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
isCrop: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
isClearBgColor: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
format: {
|
|
type: String,
|
|
default: 'image/png'
|
|
},
|
|
quality: {
|
|
type: Number,
|
|
default: 1
|
|
}
|
|
},
|
|
data () {
|
|
return {
|
|
hasDrew: false,
|
|
resultImg: '',
|
|
points: [],
|
|
canvasTxt: null,
|
|
startX: 0,
|
|
startY: 0,
|
|
isDrawing: false,
|
|
sratio: 1
|
|
}
|
|
},
|
|
computed: {
|
|
ratio () {
|
|
return this.height / this.width
|
|
},
|
|
stageInfo () {
|
|
return this.$refs.canvas.getBoundingClientRect()
|
|
},
|
|
myBg () {
|
|
return this.bgColor ? this.bgColor : 'rgba(255, 255, 255, 0)'
|
|
}
|
|
},
|
|
watch: {
|
|
'myBg': function (newVal) {
|
|
this.$refs.canvas.style.background = newVal
|
|
}
|
|
},
|
|
beforeMount () {
|
|
window.addEventListener('resize', this.$_resizeHandler)
|
|
},
|
|
beforeDestroy () {
|
|
window.removeEventListener('resize', this.$_resizeHandler)
|
|
},
|
|
mounted () {
|
|
const canvas = this.$refs.canvas
|
|
canvas.height = this.height
|
|
canvas.width = this.width
|
|
canvas.style.background = this.myBg
|
|
this.$_resizeHandler()
|
|
// 在画板以外松开鼠标后冻结画笔
|
|
document.onmouseup = () => {
|
|
this.isDrawing = false
|
|
}
|
|
},
|
|
methods: {
|
|
$_resizeHandler () {
|
|
const canvas = this.$refs.canvas
|
|
canvas.style.width = this.width + "px"
|
|
const realw = parseFloat(window.getComputedStyle(canvas).width)
|
|
canvas.style.height = this.ratio * realw + "px";
|
|
this.canvasTxt = canvas.getContext('2d')
|
|
this.canvasTxt.scale(1 * this.sratio, 1 * this.sratio)
|
|
this.sratio = realw / this.width
|
|
this.canvasTxt.scale(1 / this.sratio, 1 / this.sratio)
|
|
},
|
|
// pc
|
|
mouseDown (e) {
|
|
e = e || event
|
|
e.preventDefault()
|
|
this.isDrawing = true
|
|
this.hasDrew = true
|
|
let obj = {
|
|
x: e.offsetX,
|
|
y: e.offsetY
|
|
}
|
|
this.drawStart(obj)
|
|
},
|
|
mouseMove (e) {
|
|
e = e || event
|
|
e.preventDefault()
|
|
if (this.isDrawing) {
|
|
let obj = {
|
|
x: e.offsetX,
|
|
y: e.offsetY
|
|
}
|
|
this.drawMove(obj)
|
|
}
|
|
},
|
|
mouseUp (e) {
|
|
e = e || event
|
|
e.preventDefault()
|
|
let obj = {
|
|
x: e.offsetX,
|
|
y: e.offsetY
|
|
}
|
|
this.drawEnd(obj)
|
|
this.isDrawing = false
|
|
},
|
|
// mobile
|
|
touchStart (e) {
|
|
e = e || event
|
|
e.preventDefault()
|
|
this.hasDrew = true
|
|
if (e.touches.length === 1) {
|
|
let obj = {
|
|
x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
|
|
y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top
|
|
}
|
|
this.drawStart(obj)
|
|
}
|
|
},
|
|
touchMove (e) {
|
|
e = e || event
|
|
e.preventDefault()
|
|
if (e.touches.length === 1) {
|
|
let obj = {
|
|
x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
|
|
y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top
|
|
}
|
|
this.drawMove(obj)
|
|
}
|
|
},
|
|
touchEnd (e) {
|
|
e = e || event
|
|
e.preventDefault()
|
|
if (e.touches.length === 1) {
|
|
let obj = {
|
|
x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
|
|
y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top
|
|
}
|
|
this.drawEnd(obj)
|
|
}
|
|
},
|
|
// 绘制
|
|
drawStart (obj) {
|
|
this.startX = obj.x
|
|
this.startY = obj.y
|
|
this.canvasTxt.beginPath()
|
|
this.canvasTxt.moveTo(this.startX, this.startY)
|
|
this.canvasTxt.lineTo(obj.x, obj.y)
|
|
this.canvasTxt.lineCap = 'round'
|
|
this.canvasTxt.lineJoin = 'round'
|
|
this.canvasTxt.lineWidth = this.lineWidth * this.sratio
|
|
this.canvasTxt.stroke()
|
|
this.canvasTxt.closePath()
|
|
this.points.push(obj)
|
|
},
|
|
drawMove (obj) {
|
|
this.canvasTxt.beginPath()
|
|
this.canvasTxt.moveTo(this.startX, this.startY)
|
|
this.canvasTxt.lineTo(obj.x, obj.y)
|
|
this.canvasTxt.strokeStyle = this.lineColor
|
|
this.canvasTxt.lineWidth = this.lineWidth * this.sratio
|
|
this.canvasTxt.lineCap = 'round'
|
|
this.canvasTxt.lineJoin = 'round'
|
|
this.canvasTxt.stroke()
|
|
this.canvasTxt.closePath()
|
|
this.startY = obj.y
|
|
this.startX = obj.x
|
|
this.points.push(obj)
|
|
},
|
|
drawEnd (obj) {
|
|
this.canvasTxt.beginPath()
|
|
this.canvasTxt.moveTo(this.startX, this.startY)
|
|
this.canvasTxt.lineTo(obj.x, obj.y)
|
|
this.canvasTxt.lineCap = 'round'
|
|
this.canvasTxt.lineJoin = 'round'
|
|
this.canvasTxt.stroke()
|
|
this.canvasTxt.closePath()
|
|
this.points.push(obj)
|
|
this.points.push({x: -1, y: -1})
|
|
},
|
|
// 操作
|
|
generate (options) {
|
|
let imgFormat = options && options.format ? options.format: this.format
|
|
let imgQuality = options && options.quality ? options.quality: this.quality
|
|
const pm = new Promise((resolve, reject) => {
|
|
if (!this.hasDrew) {
|
|
reject(`Warning: Not Signned!`)
|
|
return
|
|
}
|
|
var resImgData = this.canvasTxt.getImageData(0, 0, this.$refs.canvas.width, this.$refs.canvas.height)
|
|
this.canvasTxt.globalCompositeOperation = "destination-over"
|
|
this.canvasTxt.fillStyle = this.myBg
|
|
this.canvasTxt.fillRect(0,0,this.$refs.canvas.width ,this.$refs.canvas.height)
|
|
this.resultImg = this.$refs.canvas.toDataURL(imgFormat, imgQuality)
|
|
var resultImg = this.resultImg
|
|
this.canvasTxt.clearRect(0, 0, this.$refs.canvas.width ,this.$refs.canvas.height)
|
|
this.canvasTxt.putImageData(resImgData, 0, 0)
|
|
this.canvasTxt.globalCompositeOperation = "source-over"
|
|
if (this.isCrop) {
|
|
const crop_area = this.getCropArea(resImgData.data)
|
|
var crop_canvas = document.createElement('canvas')
|
|
const crop_ctx = crop_canvas.getContext('2d')
|
|
crop_canvas.width = crop_area[2] - crop_area[0]
|
|
crop_canvas.height = crop_area[3] - crop_area[1]
|
|
const crop_imgData = this.canvasTxt.getImageData(...crop_area)
|
|
crop_ctx.globalCompositeOperation = "destination-over"
|
|
crop_ctx.putImageData(crop_imgData, 0, 0)
|
|
crop_ctx.fillStyle = this.myBg
|
|
crop_ctx.fillRect(0, 0, crop_canvas.width , crop_canvas.height)
|
|
resultImg = crop_canvas.toDataURL(imgFormat, imgQuality)
|
|
crop_canvas = null
|
|
}
|
|
resolve(resultImg)
|
|
})
|
|
return pm
|
|
},
|
|
reset () {
|
|
this.canvasTxt.clearRect(
|
|
0,
|
|
0,
|
|
this.$refs.canvas.width,
|
|
this.$refs.canvas.height
|
|
)
|
|
if (this.isClearBgColor) {
|
|
this.$emit('update:bgColor', '')
|
|
this.$refs.canvas.style.background = 'rgba(255, 255, 255, 0)'
|
|
}
|
|
this.points = []
|
|
this.hasDrew = false
|
|
this.resultImg = ''
|
|
},
|
|
getCropArea (imgData) {
|
|
var topX = this.$refs.canvas.width; var btmX = 0; var topY = this.$refs.canvas.height; var btnY = 0
|
|
for (var i = 0; i < this.$refs.canvas.width; i++) {
|
|
for (var j = 0; j < this.$refs.canvas.height; j++) {
|
|
var pos = (i + this.$refs.canvas.width * j) * 4
|
|
if (imgData[pos] > 0 || imgData[pos + 1] > 0 || imgData[pos + 2] || imgData[pos + 3] > 0) {
|
|
btnY = Math.max(j, btnY)
|
|
btmX = Math.max(i, btmX)
|
|
topY = Math.min(j, topY)
|
|
topX = Math.min(i, topX)
|
|
}
|
|
}
|
|
}
|
|
topX++
|
|
btmX++
|
|
topY++
|
|
btnY++
|
|
const data = [topX, topY, btmX, btnY]
|
|
return data
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
canvas {
|
|
max-width: 100%;
|
|
display: block;
|
|
}
|
|
</style>
|