rubick/static/plugins/capture/capture-editor.js
2021-06-22 15:35:46 +08:00

412 lines
13 KiB
JavaScript

const Event = require('events')
const { getCurrentScreen } = require('./utils')
const CREATE_RECT = 1
const MOVING_RECT = 2
const RESIZE = 3
const ANCHORS = [
{ row: 'x', col: 'y', cursor: 'nwse-resize' },
{ row: '', col: 'y', cursor: 'ns-resize' },
{ row: 'r', col: 'y', cursor: 'nesw-resize' },
{ row: 'x', col: '', cursor: 'ew-resize' },
{ row: 'r', col: '', cursor: 'ew-resize' },
{ row: 'x', col: 'b', cursor: 'nesw-resize' },
{ row: '', col: 'b', cursor: 'ns-resize' },
{ row: 'r', col: 'b', cursor: 'nwse-resize' },
]
class CaptureEditor extends Event {
constructor($canvas, $bg, imageSrc) {
super()
this.$canvas = $canvas
this.imageSrc = imageSrc
this.disabled = false
let currentScreen;
(async () => {
currentScreen = await getCurrentScreen();
this.scaleFactor = currentScreen.scaleFactor
this.screenWidth = currentScreen.bounds.width
this.screenHeight = currentScreen.bounds.height
this.init().then(() => {
// console.log('init')
})
})();
this.$bg = $bg
this.ctx = $canvas.getContext('2d')
this.onMouseDown = this.onMouseDown.bind(this)
this.onMouseMove = this.onMouseMove.bind(this)
this.onMouseUp = this.onMouseUp.bind(this)
}
async init() {
this.$bg.style.backgroundImage = `url(${this.imageSrc})`
this.$bg.style.backgroundSize = `${this.screenWidth}px ${this.screenHeight}px`
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
let img = await new Promise((resolve) => {
let img = new Image()
img.src = this.imageSrc
if (img.complete) {
resolve(img)
} else {
img.onload = () => resolve(img)
}
})
canvas.width = img.width
canvas.height = img.height
ctx.drawImage(img, 0, 0)
this.bgCtx = ctx
document.addEventListener('mousedown', this.onMouseDown)
document.addEventListener('mousemove', this.onMouseMove)
document.addEventListener('mouseup', this.onMouseUp)
}
onMouseDown(e) {
if (this.disabled) {
return
}
this.mouseDown = true
const { pageX, pageY } = e
if (this.selectRect) {
const {
w, h, x, y, r, b,
} = this.selectRect
if (this.selectAnchorIndex !== -1) {
this.startPoint = {
x: pageX,
y: pageY,
moved: false,
selectRect: {
w, h, x, y, r, b,
},
rawRect: {
w, h, x, y, r, b,
},
}
this.action = RESIZE
return
}
this.startPoint = {
x: e.pageX,
y: e.pageY,
moved: false,
}
if (pageX > x && pageX < r && pageY > y && pageY < b) {
this.action = MOVING_RECT
this.startDragRect = {
x: pageX,
y: pageY,
selectRect: {
x, y, w, h, r, b,
},
}
} else {
this.action = CREATE_RECT
}
} else {
this.action = CREATE_RECT
this.startPoint = {
x: e.pageX,
y: e.pageY,
moved: false,
}
e.stopPropagation()
e.preventDefault()
}
}
onMouseDrag(e) {
if (this.disabled) {
return
}
e.stopPropagation()
e.preventDefault()
const { pageX, pageY } = e
let startDragging
let selectRect = this.selectRect
if (!this.startPoint.moved) {
if (Math.abs(this.startPoint.x - pageX) > 10 || Math.abs(this.startPoint.y - pageY) > 10) {
this.startPoint.moved = true
startDragging = true
}
}
if (!this.startPoint.moved) {
return
}
if (this.action === MOVING_RECT) {
// 移动选区
if (startDragging) {
this.emit('start-dragging', selectRect)
}
this.emit('dragging', selectRect)
const { w, h } = selectRect
const { x: startX, y: startY } = this.startPoint
let newX = this.startDragRect.selectRect.x + pageX - startX
let newY = this.startDragRect.selectRect.y + pageY - startY
let newR = newX + w
let newB = newY + h
if (newX < 0) {
newX = 0
newR = w
} else if (newR > this.screenWidth) {
newR = this.screenWidth
newX = newR - w
}
if (newY < 0) {
newY = 0
newB = h
} else if (newB > this.screenHeight) {
newB = this.screenHeight
newY = newB - h
}
this.selectRect = {
w,
h,
x: newX,
y: newY,
r: newR,
b: newB,
}
this.drawRect()
} else if (this.action === RESIZE) {
this.emit('dragging', selectRect)
let { row, col } = ANCHORS[this.selectAnchorIndex]
if (row) {
this.startPoint.rawRect[row] = this.startPoint.selectRect[row] + pageX - this.startPoint.x
selectRect.x = this.startPoint.rawRect.x
selectRect.r = this.startPoint.rawRect.r
if (selectRect.x > selectRect.r) {
let x = selectRect.r
selectRect.r = selectRect.x
selectRect.x = x
}
selectRect.w = selectRect.r - selectRect.x
this.startPoint.rawRect.w = selectRect.w
}
if (col) {
this.startPoint.rawRect[col] = this.startPoint.selectRect[col] + pageY - this.startPoint.y
selectRect.y = this.startPoint.rawRect.y
selectRect.b = this.startPoint.rawRect.b
if (selectRect.y > selectRect.b) {
let y = selectRect.b
selectRect.b = selectRect.y
selectRect.y = y
}
selectRect.h = selectRect.b - selectRect.y
this.startPoint.rawRect.h = selectRect.h
}
this.drawRect()
} else {
// 生成选区
const { pageX, pageY } = e
let x, y, w, h, r, b
if (this.startPoint.x > pageX) {
x = pageX
r = this.startPoint.x
} else {
r = pageX
x = this.startPoint.x
}
if (this.startPoint.y > pageY) {
y = pageY
b = this.startPoint.y
} else {
b = pageY
y = this.startPoint.y
}
w = r - x
h = b - y
this.selectRect = {
x, y, w, h, r, b,
}
selectRect = this.selectRect
if (startDragging) {
this.emit('start-dragging', selectRect)
}
this.emit('dragging', selectRect)
this.drawRect(x, y, w, h)
}
}
drawRect() {
if (this.disabled) {
return
}
if (!this.selectRect) {
this.$canvas.style.display = 'none'
return
}
const {
x, y, w, h,
} = this.selectRect
const scaleFactor = this.scaleFactor
let margin = 7
let radius = 5
this.$canvas.style.left = `${x - margin}px`
this.$canvas.style.top = `${y - margin}px`
this.$canvas.style.width = `${w + margin * 2}px`
this.$canvas.style.height = `${h + margin * 2}px`
this.$canvas.style.display = 'block'
this.$canvas.width = (w + margin * 2) * scaleFactor
this.$canvas.height = (h + margin * 2) * scaleFactor
if (w && h) {
let imageData = this.bgCtx.getImageData(x * scaleFactor, y * scaleFactor, w * scaleFactor, h * scaleFactor)
this.ctx.putImageData(imageData, margin * scaleFactor, margin * scaleFactor)
}
this.ctx.fillStyle = '#ffffff'
this.ctx.strokeStyle = '#67bade'
this.ctx.lineWidth = 2 * this.scaleFactor
this.ctx.strokeRect(margin * scaleFactor, margin * scaleFactor, w * scaleFactor, h * scaleFactor)
this.drawAnchors(w, h, margin, scaleFactor, radius)
}
drawAnchors(w, h, margin, scaleFactor, radius) {
if (this.disabled) {
return
}
if (this.mouseDown && this.action === CREATE_RECT) {
this.anchors = null
return
}
this.ctx.beginPath()
let anchors = [
[0, 0],
[w * this.scaleFactor / 2, 0],
[w * this.scaleFactor, 0],
[0, h * this.scaleFactor / 2],
[w * this.scaleFactor, h * this.scaleFactor / 2],
[0, h * this.scaleFactor],
[w * this.scaleFactor / 2, h * this.scaleFactor],
[w * this.scaleFactor, h * this.scaleFactor],
]
this.anchors = anchors.map(([x, y]) => [this.selectRect.x + x / scaleFactor, this.selectRect.y + y / scaleFactor])
anchors.forEach(([x, y], i) => {
this.ctx.arc(x + margin * scaleFactor, y + margin * scaleFactor, radius * scaleFactor, 0, 2 * Math.PI)
let next = anchors[(i + 1) % anchors.length]
this.ctx.moveTo(next[0] + margin * scaleFactor + radius * scaleFactor, next[1] + margin * scaleFactor)
})
this.ctx.closePath()
this.ctx.fill()
this.ctx.stroke()
}
onMouseMove(e) {
if (this.disabled) {
return
}
if (this.mouseDown) {
this.onMouseDrag(e)
return
}
this.selectAnchorIndex = -1
if (this.selectRect) {
const { pageX, pageY } = e
const {
x, y, r, b,
} = this.selectRect
let selectAnchor, selectIndex = -1
if (this.anchors) {
this.anchors.forEach(([x, y], i) => {
if (Math.abs(pageX - x) <= 10 && Math.abs(pageY - y) <= 10) {
selectAnchor = [x, y]
selectIndex = i
}
})
}
if (selectAnchor) {
this.selectAnchorIndex = selectIndex
document.body.style.cursor = ANCHORS[selectIndex].cursor
this.emit('moving')
return
}
if (pageX > x && pageX < r && pageY > y && pageY < b) {
document.body.style.cursor = 'move'
} else {
document.body.style.cursor = 'auto'
}
this.emit('moving')
}
}
onMouseUp(e) {
if (this.disabled) {
return
}
if (!this.mouseDown) {
return
}
this.mouseDown = false
e.stopPropagation()
e.preventDefault()
this.emit('mouse-up')
if (!this.startPoint.moved) {
this.emit('end-moving')
return
}
this.emit('end-dragging')
this.drawRect()
this.startPoint = null
}
getImageUrl() {
const scaleFactor = this.scaleFactor
const {
x, y, w, h,
} = this.selectRect
if (w && h) {
let imageData = this.bgCtx.getImageData(x * scaleFactor, y * scaleFactor, w * scaleFactor, h * scaleFactor)
let canvas = document.createElement('canvas')
canvas.width = w
canvas.height = h
let ctx = canvas.getContext('2d')
ctx.putImageData(imageData, 0, 0)
return canvas.toDataURL()
}
return ''
}
disable() {
this.disabled = true
}
enable() {
this.disabled = false
}
reset() {
this.anchors = null
this.startPoint = null
this.selectRect = null
this.startDragRect = null
this.selectAnchorIndex = -1
this.drawRect()
this.emit('reset')
}
}
exports.CaptureEditor = CaptureEditor
exports.CREATE_RECT = CREATE_RECT
exports.MOVING_RECT = MOVING_RECT
exports.RESIZE = RESIZE