|
@@ -0,0 +1,848 @@
|
|
|
+import Base from './base';
|
|
|
+import { ALIGN } from '../../constants'
|
|
|
+import { getActualPoint, getClickPoint, createSvg, keepLastIndex, getInitialPoint, getHtmlToText } from './utils';
|
|
|
+import { onClickOutside } from '../ui_utils'
|
|
|
+
|
|
|
+export default class Shape extends Base {
|
|
|
+
|
|
|
+ constructor ({
|
|
|
+ container,
|
|
|
+ annotation = null,
|
|
|
+ page,
|
|
|
+ viewport,
|
|
|
+ scale,
|
|
|
+ eventBus,
|
|
|
+ layer,
|
|
|
+ show = false
|
|
|
+ }) {
|
|
|
+ super({
|
|
|
+ container,
|
|
|
+ annotation,
|
|
|
+ page,
|
|
|
+ viewport,
|
|
|
+ scale,
|
|
|
+ eventBus,
|
|
|
+ show
|
|
|
+ })
|
|
|
+
|
|
|
+ this.layer = layer
|
|
|
+ this.hidden = true
|
|
|
+ this.initial = false
|
|
|
+ this.outline = null
|
|
|
+
|
|
|
+ this.leftTop = null
|
|
|
+ this.rightBottom = null
|
|
|
+
|
|
|
+ this.newLeftTop = null
|
|
|
+ this.newRightBottom = null
|
|
|
+
|
|
|
+ this.startCircle = null
|
|
|
+ this.endCircle = null
|
|
|
+
|
|
|
+ this.show = show
|
|
|
+
|
|
|
+ this.onContainerClick = this.handleContainerClick.bind(this)
|
|
|
+ this.onMousedown = this.handleMouseDown.bind(this)
|
|
|
+ this.onMousemove = this.handleMouseMove.bind(this)
|
|
|
+ this.onMouseup = this.handleMouseUp.bind(this)
|
|
|
+ this.onDelete = this.handleDelete.bind(this)
|
|
|
+ this.onBlur = this.handleFreetextEditElementBlur.bind(this)
|
|
|
+ this.onDbclick = this.handleElementDbClick.bind(this)
|
|
|
+ this.render()
|
|
|
+ }
|
|
|
+
|
|
|
+ setCss (ele, cssText) {
|
|
|
+ if (!ele) return
|
|
|
+ if (cssText) {
|
|
|
+ for (let key in cssText) {
|
|
|
+ ele.style[key] = cssText[key]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ render () {
|
|
|
+ const { width, height } = this.viewport
|
|
|
+ const annotation = this.annotation
|
|
|
+
|
|
|
+ const { leftTop, rightBottom } = this.getActualRect(
|
|
|
+ this.viewport,
|
|
|
+ this.viewport.scale
|
|
|
+ )
|
|
|
+ this.initialRect = {
|
|
|
+ width: Math.abs(leftTop.x - rightBottom.x),
|
|
|
+ height: Math.abs(leftTop.y - rightBottom.y)
|
|
|
+ }
|
|
|
+
|
|
|
+ this.leftTop = leftTop
|
|
|
+ this.rightBottom = rightBottom
|
|
|
+
|
|
|
+ const rect = this.calculate(this.leftTop, this.rightBottom)
|
|
|
+
|
|
|
+ const annotationContainer = document.createElement('div')
|
|
|
+ annotationContainer.className = 'annotation'
|
|
|
+ this.setCss(annotationContainer, {
|
|
|
+ top: rect.top - 2 + 'px',
|
|
|
+ left: rect.left - 4 + 'px',
|
|
|
+ })
|
|
|
+ this.annotationContainer = annotationContainer
|
|
|
+
|
|
|
+ let freetextEditElement = document.createElement('div')
|
|
|
+ freetextEditElement.setAttribute('role', 'textbox')
|
|
|
+ freetextEditElement.setAttribute('contenteditable', true)
|
|
|
+ freetextEditElement.setAttribute('spellcheck', false)
|
|
|
+ freetextEditElement.className = 'freetext'
|
|
|
+
|
|
|
+ const maxWidth = width - this.leftTop.x
|
|
|
+ const maxHeight = height - this.leftTop.y
|
|
|
+
|
|
|
+ const align = (annotation.justification && ALIGN[annotation.justification]) || 'left'
|
|
|
+
|
|
|
+ this.setCss(freetextEditElement, {
|
|
|
+ maxWidth: maxWidth+ 'px',
|
|
|
+ maxHeight: maxHeight + 'px',
|
|
|
+ fontSize: '16px',
|
|
|
+ color: '#000',
|
|
|
+ textAlign: align,
|
|
|
+ lineHeight: '1.2em',
|
|
|
+ padding: '2px 4px',
|
|
|
+ overflow: 'auto'
|
|
|
+ })
|
|
|
+
|
|
|
+ this.freetextEditElement = freetextEditElement
|
|
|
+
|
|
|
+ let freetextElement = document.createElement('div')
|
|
|
+ freetextElement.className = 'freetext'
|
|
|
+
|
|
|
+ this.setCss(freetextElement, {
|
|
|
+ width: rect.width+ 'px',
|
|
|
+ height: rect.height + 'px',
|
|
|
+ fontSize: '16px',
|
|
|
+ color: '#000',
|
|
|
+ textAlign: align,
|
|
|
+ lineHeight: '1.2em',
|
|
|
+ padding: '2px 4px',
|
|
|
+ overflow: 'hidden'
|
|
|
+ })
|
|
|
+ if (annotation.content) {
|
|
|
+ freetextElement.innerHTML = annotation.content
|
|
|
+ freetextEditElement.innerHTML = annotation.content
|
|
|
+ } else if (annotation.contents) {
|
|
|
+ freetextElement.innerHTML = annotation.contents
|
|
|
+ freetextEditElement.innerHTML = annotation.contents
|
|
|
+ } else if (annotation['contents-richtext']) {
|
|
|
+ const parser = new window.DOMParser();
|
|
|
+ const xmlDoc = parser.parseFromString(annotation['contents-richtext'], 'text/xml');
|
|
|
+ const error = xmlDoc.getElementsByTagName("parsererror")
|
|
|
+ if (error.length > 0) {
|
|
|
+ freetextElement.innerHTML = annotation['contents-richtext']
|
|
|
+ freetextEditElement.innerHTML = annotation['contents-richtext']
|
|
|
+ } else {
|
|
|
+ freetextElement.innerHTML = xmlDoc.firstElementChild.innerText
|
|
|
+ freetextEditElement.innerHTML = xmlDoc.firstElementChild.innerText
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.freetextElement = freetextElement
|
|
|
+
|
|
|
+ this.appendOrRemoveFreetextElement('append')
|
|
|
+
|
|
|
+ this.container.append(this.annotationContainer)
|
|
|
+
|
|
|
+ this.outerLineContainer = document.createElement('div')
|
|
|
+ this.outerLineContainer.className = 'outline-container'
|
|
|
+
|
|
|
+ this.deletetButton = createSvg(
|
|
|
+ "svg",
|
|
|
+ {
|
|
|
+ width: "30",
|
|
|
+ height: "30",
|
|
|
+ viewBox: "0 0 30 30",
|
|
|
+ fill: "none",
|
|
|
+ class: 'delete-button'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ width: '30px',
|
|
|
+ height: '30px'
|
|
|
+ }
|
|
|
+ );
|
|
|
+ this.deletetButton.addEventListener('click', this.onDelete)
|
|
|
+ this.deletetButton.innerHTML = this.deleteSvgStr
|
|
|
+
|
|
|
+ const left = (rect.left + rect.width - 30) + 'px'
|
|
|
+ const top = (rect.top + rect.height + 12) + 'px'
|
|
|
+ this.deletetButton.style.left = left
|
|
|
+ this.deletetButton.style.top = top
|
|
|
+
|
|
|
+ const outerLine = createSvg('svg', null, {
|
|
|
+ position: 'absolute',
|
|
|
+ zIndex: 1,
|
|
|
+ left: `${rect.left - 12}px`,
|
|
|
+ top: `${rect.top - 9}px`,
|
|
|
+ width: `${rect.width + 16}px`,
|
|
|
+ height: `${rect.height + 15}px`,
|
|
|
+ })
|
|
|
+
|
|
|
+ this.outerLine = outerLine
|
|
|
+
|
|
|
+ this.moveRect = createSvg(
|
|
|
+ "rect",
|
|
|
+ {
|
|
|
+ class: "move",
|
|
|
+ 'data-id': "move",
|
|
|
+ stroke: "#1460F3",
|
|
|
+ 'stroke-width': 1,
|
|
|
+ 'fill-opacity': 0,
|
|
|
+ width: rect.width + 10,
|
|
|
+ height: rect.height + 9,
|
|
|
+ x: 3,
|
|
|
+ y: 3
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ this.topLeftRect = createSvg(
|
|
|
+ "rect",
|
|
|
+ {
|
|
|
+ class: "nw-resize",
|
|
|
+ 'data-id': "nw-resize",
|
|
|
+ fill: "#1460F3",
|
|
|
+ stroke: "none",
|
|
|
+ width: 6,
|
|
|
+ height: 6,
|
|
|
+ x: 0,
|
|
|
+ y: 0
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ this.LeftRect = createSvg(
|
|
|
+ "rect",
|
|
|
+ {
|
|
|
+ class: "w-resize",
|
|
|
+ 'data-id': "w-resize",
|
|
|
+ fill: "#1460F3",
|
|
|
+ stroke: "none",
|
|
|
+ width: 6,
|
|
|
+ height: 6,
|
|
|
+ x: 0,
|
|
|
+ y: rect.height / 2 + 6
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ this.bottomLeftRect = createSvg(
|
|
|
+ "rect",
|
|
|
+ {
|
|
|
+ class: "sw-resize",
|
|
|
+ 'data-id': "sw-resize",
|
|
|
+ fill: "#1460F3",
|
|
|
+ stroke: "none",
|
|
|
+ width: 6,
|
|
|
+ height: 6,
|
|
|
+ x: 0,
|
|
|
+ y: rect.height + 9
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ this.bottomRect = createSvg(
|
|
|
+ "rect",
|
|
|
+ {
|
|
|
+ class: "s-resize",
|
|
|
+ 'data-id': "s-resize",
|
|
|
+ fill: "#1460F3",
|
|
|
+ stroke: "none",
|
|
|
+ width: 6,
|
|
|
+ height: 6,
|
|
|
+ x: rect.width / 2 + 9,
|
|
|
+ y: rect.height + 9
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ this.bottomRightRect = createSvg(
|
|
|
+ "rect",
|
|
|
+ {
|
|
|
+ class: "se-resize",
|
|
|
+ 'data-id': "se-resize",
|
|
|
+ fill: "#1460F3",
|
|
|
+ stroke: "none",
|
|
|
+ width: 6,
|
|
|
+ height: 6,
|
|
|
+ x: rect.width + 10,
|
|
|
+ y: rect.height + 9
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ this.rightRect = createSvg(
|
|
|
+ "rect",
|
|
|
+ {
|
|
|
+ class: "e-resize",
|
|
|
+ 'data-id': "e-resize",
|
|
|
+ fill: "#1460F3",
|
|
|
+ stroke: "none",
|
|
|
+ width: 6,
|
|
|
+ height: 6,
|
|
|
+ x: rect.width + 10,
|
|
|
+ y: rect.height / 2 + 6
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ this.topRightRect = createSvg(
|
|
|
+ "rect",
|
|
|
+ {
|
|
|
+ class: "ne-resize",
|
|
|
+ 'data-id': "ne-resize",
|
|
|
+ fill: "#1460F3",
|
|
|
+ stroke: "none",
|
|
|
+ width: 6,
|
|
|
+ height: 6,
|
|
|
+ x: rect.width + 10,
|
|
|
+ y: 0
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ this.topRect = createSvg(
|
|
|
+ "rect",
|
|
|
+ {
|
|
|
+ class: "n-resize",
|
|
|
+ 'data-id': "n-resize",
|
|
|
+ fill: "#1460F3",
|
|
|
+ stroke: "none",
|
|
|
+ width: 6,
|
|
|
+ height: 6,
|
|
|
+ x: rect.width / 2 + 9,
|
|
|
+ y: 0
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ this.outerLine.append(this.moveRect)
|
|
|
+ this.outerLine.append(this.topLeftRect)
|
|
|
+ this.outerLine.append(this.LeftRect)
|
|
|
+ this.outerLine.append(this.bottomLeftRect)
|
|
|
+ this.outerLine.append(this.bottomRect)
|
|
|
+ this.outerLine.append(this.bottomRightRect)
|
|
|
+ this.outerLine.append(this.rightRect)
|
|
|
+ this.outerLine.append(this.topRightRect)
|
|
|
+ this.outerLine.append(this.topRect)
|
|
|
+ this.outerLineContainer.append(this.outerLine)
|
|
|
+ this.outerLineContainer.append(this.deletetButton)
|
|
|
+ this.initial = true
|
|
|
+ this.handleElementSelect()
|
|
|
+ }
|
|
|
+
|
|
|
+ handleElementDbClick() {
|
|
|
+ const freetextEditElement = this.freetextEditElement
|
|
|
+ if (freetextEditElement) {
|
|
|
+ this.outerLine.removeEventListener('mousedown', this.onMousedown)
|
|
|
+ this.outerLine.removeEventListener('touchstart', this.onMousedown)
|
|
|
+ this.outerLine.removeEventListener('dblclick', this.onDbclick)
|
|
|
+ this.outerLineContainer.remove()
|
|
|
+
|
|
|
+ this.appendOrRemoveFreetextElement('remove')
|
|
|
+
|
|
|
+ this.appendOrRemoveFreetextEditElement('append')
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ getFreetextRectPoint () {
|
|
|
+ const rect = this.freetextEditElement.getBoundingClientRect()
|
|
|
+ const { width, height } = rect
|
|
|
+ const leftTop = this.leftTop
|
|
|
+ this.rightBottom = {
|
|
|
+ x: leftTop.x + width,
|
|
|
+ y: leftTop.y + height
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ getFreetextRectString () {
|
|
|
+ const viewport = this.viewport
|
|
|
+ const { scale } = this.viewport
|
|
|
+ this.getFreetextRectPoint()
|
|
|
+ const actualLeftTopPoint = getInitialPoint(this.leftTop, viewport, scale)
|
|
|
+ const actualRightBottomPoint = getInitialPoint(this.rightBottom, viewport, scale)
|
|
|
+ return `${actualLeftTopPoint.x},${actualRightBottomPoint.y},${actualRightBottomPoint.x},${actualLeftTopPoint.y}`
|
|
|
+ }
|
|
|
+
|
|
|
+ handleFreetextEditElementBlur () {
|
|
|
+ const freetextEditElement = this.freetextEditElement
|
|
|
+ if (freetextEditElement && freetextEditElement.innerText !== this.annotation.content) {
|
|
|
+ const freetextRectString = this.getFreetextRectString()
|
|
|
+
|
|
|
+ const text = getHtmlToText(freetextEditElement)
|
|
|
+ this.eventBus.dispatch('annotationChange', {
|
|
|
+ type: 'modify',
|
|
|
+ annotation: {
|
|
|
+ operate: "mod-annot",
|
|
|
+ name: this.annotation.name,
|
|
|
+ page: this.page,
|
|
|
+ content: text,
|
|
|
+ textColor: '#000000',
|
|
|
+ fillColor: '#FFFFFF',
|
|
|
+ fontSize: 16,
|
|
|
+ fillTransparency: 0,
|
|
|
+ alignment: 0,
|
|
|
+ color: 'transparent',
|
|
|
+ fontName: 'Helvetica',
|
|
|
+ transparency: 1,
|
|
|
+ fillTransparency: 0,
|
|
|
+ rect: freetextRectString
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.update({
|
|
|
+ leftTop: this.leftTop,
|
|
|
+ rightBottom: this.rightBottom
|
|
|
+ })
|
|
|
+ this.annotation.content = text
|
|
|
+ this.freetextElement.innerText = text
|
|
|
+ }
|
|
|
+
|
|
|
+ this.appendOrRemoveFreetextEditElement('remove')
|
|
|
+ this.appendOrRemoveFreetextElement('append')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理显示节点显示和隐藏
|
|
|
+ appendOrRemoveFreetextElement (type) {
|
|
|
+ const freetextElement = this.freetextElement
|
|
|
+ if (type === 'append') {
|
|
|
+ freetextElement.addEventListener('click', this.onContainerClick)
|
|
|
+
|
|
|
+ this.annotationContainer.append(freetextElement)
|
|
|
+ } else {
|
|
|
+ freetextElement.removeEventListener('click', this.onContainerClick)
|
|
|
+
|
|
|
+ freetextElement.remove()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理编辑框显示和隐藏
|
|
|
+ appendOrRemoveFreetextEditElement (type) {
|
|
|
+ const freetextEditElement = this.freetextEditElement
|
|
|
+ if (type === 'append') {
|
|
|
+ freetextEditElement.addEventListener('blur', this.onBlur)
|
|
|
+
|
|
|
+ this.annotationContainer.append(freetextEditElement)
|
|
|
+ freetextEditElement.focus()
|
|
|
+ keepLastIndex(freetextEditElement)
|
|
|
+ } else {
|
|
|
+ freetextEditElement.removeEventListener('blur', this.onBlur)
|
|
|
+
|
|
|
+ freetextEditElement.remove()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ handleElementSelect () {
|
|
|
+ if (this.show) {
|
|
|
+ this.show = false
|
|
|
+ this.hidden = false
|
|
|
+ this.updateTool()
|
|
|
+ onClickOutside([this.freetextElement, this.outerLine, this.deletetButton], this.handleOutside.bind(this))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ getActualRect (viewport, s,) {
|
|
|
+ const annotation = this.annotation
|
|
|
+ const [x1, y1, x2, y2] = annotation.rect.split(',')
|
|
|
+
|
|
|
+ const start = getActualPoint(
|
|
|
+ {
|
|
|
+ x: x1,
|
|
|
+ y: y1
|
|
|
+ },
|
|
|
+ viewport,
|
|
|
+ s
|
|
|
+ )
|
|
|
+ const end = getActualPoint(
|
|
|
+ {
|
|
|
+ x: x2,
|
|
|
+ y: y2
|
|
|
+ },
|
|
|
+ viewport,
|
|
|
+ s
|
|
|
+ )
|
|
|
+ return {
|
|
|
+ leftTop: {
|
|
|
+ x: Math.min(start.x, end.x),
|
|
|
+ y: Math.min(start.y, end.y)
|
|
|
+ },
|
|
|
+ rightBottom: {
|
|
|
+ x: Math.max(start.x, end.x),
|
|
|
+ y: Math.max(start.y, end.y)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ rectCalc ({ leftTop, rightBottom }) {
|
|
|
+ return {
|
|
|
+ top: leftTop.y < rightBottom.y ? leftTop.y : rightBottom.y,
|
|
|
+ left: leftTop.x < rightBottom.x ? leftTop.x : rightBottom.x,
|
|
|
+ width: Math.abs(rightBottom.x - leftTop.x),
|
|
|
+ height: Math.abs(rightBottom.y - leftTop.y),
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ calculate (leftTop, rightBottom) {
|
|
|
+ const initRect = this.rectCalc({ leftTop, rightBottom });
|
|
|
+
|
|
|
+ return {
|
|
|
+ top: initRect.top,
|
|
|
+ left: initRect.left,
|
|
|
+ width: initRect.width,
|
|
|
+ height: initRect.height,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ getInitialPoint () {
|
|
|
+ const left = this.leftTop.x
|
|
|
+ const top = this.leftTop.y
|
|
|
+ const right = this.rightBottom.x
|
|
|
+ const bottom = this.rightBottom.y
|
|
|
+
|
|
|
+ const { width, height, rotation, scale: s } = this.viewport;
|
|
|
+ let x1, y1, x2, y2;
|
|
|
+ if (rotation === 0) {
|
|
|
+ x1 = left / s;
|
|
|
+ y1 = (height - top) / s;
|
|
|
+ x2 = right / s;
|
|
|
+ y2 = (height - bottom) / s;
|
|
|
+ } else if (rotation === 90) {
|
|
|
+ x1 = bottom / s;
|
|
|
+ y1 = right / s;
|
|
|
+ x2 = top / s;
|
|
|
+ y2 = left / s;
|
|
|
+ } else if (rotation === 180) {
|
|
|
+ x1 = (width - left) / s;
|
|
|
+ y1 = top / s;
|
|
|
+ x2 = (width - right) / s;
|
|
|
+ y2 = bottom / s;
|
|
|
+ } else {
|
|
|
+ x1 = (height - bottom) / s
|
|
|
+ y1 = (width - left) / s
|
|
|
+ x2 = (height - top) / s
|
|
|
+ y2 = (width - right) / s
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ leftTop: {
|
|
|
+ x: x1,
|
|
|
+ y: y1,
|
|
|
+ },
|
|
|
+ rightBottom: {
|
|
|
+ x: x2,
|
|
|
+ y: y2,
|
|
|
+ },
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ updateTool () {
|
|
|
+ if (!this.initial) return
|
|
|
+
|
|
|
+ // debugger
|
|
|
+ if (this.hidden) {
|
|
|
+ this.outerLineContainer.remove()
|
|
|
+ if (this.layer.selectedElementName === this.annotation.name) {
|
|
|
+ this.layer.selectedElementName = null
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (this.layer.selectedElementName !== this.annotation.name) {
|
|
|
+ this.layer.selectedElementName = this.annotation.name
|
|
|
+ }
|
|
|
+ this.container.append(this.outerLineContainer)
|
|
|
+ this.outerLine.addEventListener('mousedown', this.onMousedown)
|
|
|
+ this.outerLine.addEventListener('dblclick', this.onDbclick)
|
|
|
+ this.outerLine.addEventListener('touchstart', this.onMousedown)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ handleContainerClick (event) {
|
|
|
+ if (!this.hidden) return
|
|
|
+ this.hidden = false
|
|
|
+ this.updateTool()
|
|
|
+ onClickOutside([this.outerLine, this.freetextElement, this.deletetButton], this.handleOutside.bind(this))
|
|
|
+ }
|
|
|
+
|
|
|
+ handleOutside () {
|
|
|
+ this.hidden = !this.hidden
|
|
|
+
|
|
|
+ if (this.layer.selectedElementName === this.annotation.name) {
|
|
|
+ this.layer.selectedElementName = null
|
|
|
+ }
|
|
|
+ this.outerLine.removeEventListener('mousedown', this.onMousedown)
|
|
|
+ this.outerLine.removeEventListener('touchstart', this.onMousedown)
|
|
|
+ this.outerLine.removeEventListener('dblclick', this.onDbclick)
|
|
|
+ this.outerLineContainer.remove()
|
|
|
+
|
|
|
+ if (this.layer.selectedElementName === this.annotation.name) {
|
|
|
+ this.layer.selectedElementName = null
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ handleMouseDown (event) {
|
|
|
+ if (this.layer.tool) {
|
|
|
+ event.stopPropagation()
|
|
|
+ }
|
|
|
+ const operatorId = event.target.getAttribute('data-id')
|
|
|
+ const { pageX, pageY } = getClickPoint(event)
|
|
|
+ this.startState = {
|
|
|
+ operator: operatorId,
|
|
|
+ clickX: pageX,
|
|
|
+ clickY: pageY,
|
|
|
+ }
|
|
|
+
|
|
|
+ document.addEventListener('mousemove', this.onMousemove)
|
|
|
+ document.addEventListener('mouseup', this.onMouseup)
|
|
|
+ document.addEventListener('touchmove', this.onMousemove)
|
|
|
+ document.addEventListener('touchend', this.onMouseup)
|
|
|
+ };
|
|
|
+
|
|
|
+ handleMouseMove (event) {
|
|
|
+ if (event.type === 'touchmove') {
|
|
|
+ document.body.style.overscrollBehavior = 'none';
|
|
|
+ }
|
|
|
+ this.moving = true
|
|
|
+ const { pageX, pageY } = getClickPoint(event)
|
|
|
+ const leftTop = this.leftTop
|
|
|
+ const rightBottom = this.rightBottom
|
|
|
+ const startState = this.startState
|
|
|
+
|
|
|
+ const { width, height } = this.viewport
|
|
|
+
|
|
|
+ const rect = {
|
|
|
+ width: 10,
|
|
|
+ height: 10
|
|
|
+ }
|
|
|
+ if (startState.operator === 'nw-resize') {
|
|
|
+ let left = pageX - (startState.clickX - leftTop.x);
|
|
|
+ let top = pageY - (startState.clickY - leftTop.y);
|
|
|
+ left = Math.min(rightBottom.x - rect.width, left)
|
|
|
+ left = Math.max(0, left)
|
|
|
+ top = Math.min(rightBottom.y - rect.height, top)
|
|
|
+ top = Math.max(0, top)
|
|
|
+
|
|
|
+ this.update({
|
|
|
+ leftTop: { x: left, y: top },
|
|
|
+ rightBottom,
|
|
|
+ });
|
|
|
+ } else if (startState.operator === 'w-resize') {
|
|
|
+ let left = pageX - (startState.clickX - leftTop.x);
|
|
|
+ left = Math.min(rightBottom.x - rect.width, left)
|
|
|
+ left = Math.max(0, left)
|
|
|
+
|
|
|
+ this.update({
|
|
|
+ leftTop: { x: left, y: leftTop.y },
|
|
|
+ rightBottom,
|
|
|
+ });
|
|
|
+ } else if (startState.operator === 'sw-resize') {
|
|
|
+ let left = pageX - (startState.clickX - leftTop.x);
|
|
|
+ let bottom = pageY - (startState.clickY - rightBottom.y);
|
|
|
+ left = Math.min(rightBottom.x - rect.width, left)
|
|
|
+ left = Math.max(0, left)
|
|
|
+ bottom = Math.min(height, bottom)
|
|
|
+ bottom = Math.max(leftTop.y + rect.height, bottom)
|
|
|
+
|
|
|
+ this.update({
|
|
|
+ leftTop: { x: left, y: leftTop.y },
|
|
|
+ rightBottom: { x: rightBottom.x, y: bottom },
|
|
|
+ });
|
|
|
+ } else if (startState.operator === 's-resize') {
|
|
|
+ let bottom = pageY - (startState.clickY - rightBottom.y);
|
|
|
+ bottom = Math.min(height, bottom)
|
|
|
+ bottom = Math.max(leftTop.y + rect.height, bottom)
|
|
|
+ this.update({
|
|
|
+ leftTop,
|
|
|
+ rightBottom: { x: rightBottom.x, y: bottom },
|
|
|
+ });
|
|
|
+ } else if (startState.operator === 'se-resize') {
|
|
|
+ let right = pageX - (startState.clickX - rightBottom.x);
|
|
|
+ let bottom = pageY - (startState.clickY - rightBottom.y);
|
|
|
+ right = Math.min(width, right)
|
|
|
+ right = Math.max(leftTop.x + rect.width, right)
|
|
|
+ bottom = Math.min(height, bottom)
|
|
|
+ bottom = Math.max(leftTop.y + rect.height, bottom)
|
|
|
+ this.update({
|
|
|
+ leftTop,
|
|
|
+ rightBottom: { x: right, y: bottom },
|
|
|
+ });
|
|
|
+ } else if (startState.operator === 'e-resize') {
|
|
|
+ let right = pageX - (startState.clickX - rightBottom.x);
|
|
|
+ right = Math.min(width, right)
|
|
|
+ right = Math.max(leftTop.x + rect.width, right)
|
|
|
+ this.update({
|
|
|
+ leftTop,
|
|
|
+ rightBottom: { x: right, y: rightBottom.y },
|
|
|
+ });
|
|
|
+ } else if (startState.operator === 'ne-resize') {
|
|
|
+ let right = pageX - (startState.clickX - rightBottom.x);
|
|
|
+ let top = pageY - (startState.clickY - leftTop.y);
|
|
|
+ right = Math.min(width, right)
|
|
|
+ right = Math.max(leftTop.x + rect.width, right)
|
|
|
+ top = Math.min(rightBottom.y - rect.height, top)
|
|
|
+ top = Math.max(0, top)
|
|
|
+ this.update({
|
|
|
+ leftTop: { x: leftTop.x, y: top },
|
|
|
+ rightBottom: { x: right, y: rightBottom.y },
|
|
|
+ });
|
|
|
+ } else if (startState.operator === 'n-resize') {
|
|
|
+ let top = pageY - (startState.clickY - leftTop.y);
|
|
|
+ top = Math.min(rightBottom.y - rect.height, top)
|
|
|
+ top = Math.max(0, top)
|
|
|
+ this.update({
|
|
|
+ leftTop: { x: leftTop.x, y: top },
|
|
|
+ rightBottom,
|
|
|
+ });
|
|
|
+ } else if (startState.operator === 'move') {
|
|
|
+ let left = pageX - (startState.clickX - leftTop.x);
|
|
|
+ let top = pageY - (startState.clickY - leftTop.y);
|
|
|
+ let right = pageX - (startState.clickX - rightBottom.x);
|
|
|
+ let bottom = pageY - (startState.clickY - rightBottom.y);
|
|
|
+
|
|
|
+ const rect = {
|
|
|
+ width: Math.abs(left - right),
|
|
|
+ height: Math.abs(bottom - top)
|
|
|
+ }
|
|
|
+ if (left < right) {
|
|
|
+ left = Math.max(0, left)
|
|
|
+ left = Math.min(width - rect.width, left)
|
|
|
+ right = Math.max(rect.width, right)
|
|
|
+ right = Math.min(width, right)
|
|
|
+ } else {
|
|
|
+ right = Math.max(0, right)
|
|
|
+ right = Math.min(width - rect.width, right)
|
|
|
+ left = Math.max(rect.width, left)
|
|
|
+ left = Math.min(width, left)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (bottom < top) {
|
|
|
+ bottom = Math.max(0, bottom)
|
|
|
+ bottom = Math.min(height - rect.height, bottom)
|
|
|
+ top = Math.max(rect.height, top)
|
|
|
+ top = Math.min(height, top)
|
|
|
+ } else {
|
|
|
+ top = Math.max(0, top)
|
|
|
+ top = Math.min(height - rect.height, top)
|
|
|
+ bottom = Math.max(rect.height, bottom)
|
|
|
+ bottom = Math.min(height, bottom)
|
|
|
+ }
|
|
|
+
|
|
|
+ this.update({
|
|
|
+ leftTop: { x: left, y: top },
|
|
|
+ rightBottom: { x: right, y: bottom },
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ handleMouseUp (event) {
|
|
|
+ if (this.layer.tool) {
|
|
|
+ event.stopPropagation()
|
|
|
+ }
|
|
|
+ if (event.type === 'touchend') {
|
|
|
+ document.body.style.overscrollBehavior = 'auto';
|
|
|
+ }
|
|
|
+ this.moving = false
|
|
|
+ document.removeEventListener('mousemove', this.onMousemove)
|
|
|
+ document.removeEventListener('mouseup', this.onMouseup)
|
|
|
+ document.removeEventListener('touchmove', this.onMousemove)
|
|
|
+ document.removeEventListener('touchend', this.onMouseup)
|
|
|
+
|
|
|
+ const { pageX, pageY } = getClickPoint(event)
|
|
|
+ if (pageX === this.startState.clickX && pageY === this.startState.clickY) return
|
|
|
+
|
|
|
+ this.leftTop = this.newStart
|
|
|
+ this.rightBottom = this.newEnd
|
|
|
+
|
|
|
+ const annotation = this.annotation
|
|
|
+ const { leftTop, rightBottom } = this.getInitialPoint()
|
|
|
+
|
|
|
+ const rect = leftTop.x + ',' + rightBottom.y + ',' + rightBottom.x + ',' + leftTop.y
|
|
|
+ annotation.rect = rect
|
|
|
+ this.eventBus.dispatch('annotationChange', {
|
|
|
+ type: 'modify',
|
|
|
+ annotation: {
|
|
|
+ operate: "mod-annot",
|
|
|
+ name: annotation.name,
|
|
|
+ page: this.page,
|
|
|
+ rect,
|
|
|
+ textColor: '#000000',
|
|
|
+ fillColor: '#FFFFFF',
|
|
|
+ fontSize: 16,
|
|
|
+ fillTransparency: 0,
|
|
|
+ alignment: 0,
|
|
|
+ color: 'transparent',
|
|
|
+ fontName: 'Helvetica',
|
|
|
+ transparency: 1,
|
|
|
+ fillTransparency: 0
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ update ({ leftTop, rightBottom }) {
|
|
|
+ const { width, height } = this.viewport
|
|
|
+ const rect = this.calculate(leftTop, rightBottom)
|
|
|
+
|
|
|
+ this.newStart = leftTop
|
|
|
+ this.newEnd = rightBottom
|
|
|
+
|
|
|
+ this.setCss(this.annotationContainer, {
|
|
|
+ top: rect.top - 2 + 'px',
|
|
|
+ left: rect.left - 4 + 'px',
|
|
|
+ })
|
|
|
+
|
|
|
+ const maxWidth = width - leftTop.x
|
|
|
+ const maxHeight = height - leftTop.y
|
|
|
+ this.setCss(this.freetextEditElement, {
|
|
|
+ maxWidth: maxWidth+ 'px',
|
|
|
+ maxHeight: maxHeight + 'px',
|
|
|
+ })
|
|
|
+
|
|
|
+ this.setCss(this.freetextElement, {
|
|
|
+ width: rect.width+ 'px',
|
|
|
+ height: rect.height + 'px'
|
|
|
+ })
|
|
|
+
|
|
|
+ this.setCss(this.outerLine, {
|
|
|
+ left: `${rect.left - 12}px`,
|
|
|
+ top: `${rect.top - 9}px`,
|
|
|
+ width: `${rect.width + 12 * 2}px`,
|
|
|
+ height: `${rect.height + 15}px`,
|
|
|
+ })
|
|
|
+
|
|
|
+ const left = (rect.left + rect.width - 30) + 'px'
|
|
|
+ const top = (rect.top + rect.height + 8) + 'px'
|
|
|
+ this.setCss(this.deletetButton, {
|
|
|
+ left,
|
|
|
+ top,
|
|
|
+ })
|
|
|
+
|
|
|
+
|
|
|
+ this.moveRect.setAttribute('width', rect.width + 10)
|
|
|
+ this.moveRect.setAttribute('height', rect.height + 9)
|
|
|
+
|
|
|
+ this.LeftRect.setAttribute("y", rect.height / 2 + 6)
|
|
|
+
|
|
|
+ this.bottomLeftRect.setAttribute("y", rect.height + 9)
|
|
|
+
|
|
|
+ this.bottomRect.setAttribute("x", rect.width / 2 + 9)
|
|
|
+ this.bottomRect.setAttribute("y", rect.height + 9)
|
|
|
+
|
|
|
+ this.bottomRightRect.setAttribute("x", rect.width + 10)
|
|
|
+ this.bottomRightRect.setAttribute("y", rect.height + 9)
|
|
|
+
|
|
|
+ this.rightRect.setAttribute("x", rect.width + 10,)
|
|
|
+ this.rightRect.setAttribute("y", rect.height / 2 + 6)
|
|
|
+
|
|
|
+ this.topRightRect.setAttribute("x", rect.width + 10)
|
|
|
+
|
|
|
+ this.topRect.setAttribute("x", rect.width / 2 + 9)
|
|
|
+ }
|
|
|
+
|
|
|
+ handleDelete (event) {
|
|
|
+ if (this.layer.tool) {
|
|
|
+ event.stopPropagation()
|
|
|
+ }
|
|
|
+ this.handleOutside()
|
|
|
+ this.annotationContainer.remove()
|
|
|
+ this.annotation.isDelete = true
|
|
|
+ this.eventBus.dispatch('annotationChange', {
|
|
|
+ type: 'delete',
|
|
|
+ annotation: {
|
|
|
+ operate: "del-annot",
|
|
|
+ name: this.annotation.name,
|
|
|
+ page: this.page,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|