import { v4 as uuidv4 } from 'uuid' import { faCircleCheck, faCircleXmark, faCircle } from "@fortawesome/free-solid-svg-icons"; import {icon, library} from "@fortawesome/fontawesome-svg-core"; library.add(faCircleCheck, faCircleXmark, faCircle) export class ScreenshotEditor { static resolve: Function static reject: Function static canvas: any static ctx: CanvasRenderingContext2D static originalImage: any static isDown: boolean static startX: number static startY: number static offsetX: number static offsetY: number static mouseX: number static mouseY: number static x1?: number static x2?: number static y1?: number static y2?: number static rects: [number, number, number, number][] = [] static originalScrollY: number = 0 static async destruct(): Promise { const self = this $('#' + self.canvas.id + '_textarea').remove() $('#screenshot_controller_block').remove() self.canvas.removeEventListener('mousedown', function (e: MouseEvent) { self.handleMouseDown(e) }) self.canvas.removeEventListener('mousemove', function (e: MouseEvent) { self.handleMouseMove(e) }) self.canvas.removeEventListener('mouseup', function (e: MouseEvent) { self.handleMouseUp(e) }) self.canvas.removeEventListener('mouseout', function (e: MouseEvent) { self.handleMouseOut(e) }) self.canvas.remove() self.rects = [] self.originalImage = undefined window.scrollTo(0, self.originalScrollY) } static async run(image: string): Promise { const self = this const width = window.innerWidth const height = window.innerHeight self.originalScrollY = window.scrollY window.scrollTo(0, 0) self.noteEditor(image, width, height) return new Promise((resolve, reject) => { self.resolve = resolve self.reject = reject }) } private static removeLastRectangle() { const self = this // clear the canvas self.ctx.clearRect(0, 0, self.canvas.width, self.canvas.height) self.ctx.drawImage(self.originalImage, 0, 0) self.rects.pop() self.displayOpacity() self.displayRectangles() } private static displayOpacity() { const self = this self.ctx.fillStyle = "rgba(128,128,128,0.3)" self.ctx.fillRect(0, 0, self.canvas.width, self.canvas.height) } private static displayRectangles() { const self = this self.rects.forEach((rect, i) => { self.ctx.strokeStyle = "red" self.ctx.lineWidth = 2 self.ctx.fillStyle = "transparent" self.ctx.strokeRect(rect[0], rect[1], rect[2], rect[3]) self.ctx.strokeStyle = "red" self.ctx.lineWidth = 2 self.ctx.fillStyle = "white" // self.ctx.fillRect(rect[0] -20, rect[1] -10, 20, 20) self.ctx.fillRect(rect[0] -7, rect[1] -10, 25, 20) self.ctx.strokeRect(rect[0] -7, rect[1] -10, 25, 20) self.ctx.font = 'bold 16px Arial' self.ctx.fillStyle = "red" self.ctx.fillText(`${i + 1}`, rect[0], rect[1] + 7) }) } private static displayControllers() { const self = this const innerWidth = (self.canvas.width - 40) const padding = 20 const blockWidth = 36 + 36 + 300 + 2 * padding const startX = (innerWidth - blockWidth) / 2 let bg_attributes = [] let bg_style = [] bg_attributes.push('id="screenshot_controller_block"') bg_style.push('width:' + blockWidth + 'px') bg_style.push('height:' + 40 + 'px') bg_style.push('position:absolute') bg_style.push('left:' + startX + 'px') bg_style.push('top:20px') bg_style.push('z-index:10003') bg_attributes.push('style="' + bg_style.join(";") + '"') const iconCircleXmark = icon( faCircleXmark, { styles: { position: "absolute", left: "23px", top: "0px", color: "red", cursor: "pointer", }, attributes: { id: "screenshot_controller_back" }, transform: { size: 46 } }) const iconCircleCheck = icon( faCircleCheck, { styles: { position: "absolute", left: "73px", top: "0px", color: "green", cursor: "pointer", }, attributes: { id: "screenshot_controller_go" }, transform: { size: 46 } }) let html = [] html.push('
') html.push(iconCircleXmark.html.join("")) html.push(iconCircleCheck.html.join("")) html.push('označte jednotlivé sektory zájmu') html.push('
') $('body').append(html.join("")) $('#screenshot_controller_back').on('click', function (e: JQuery.TriggeredEvent) { console.log('#screenshot_controller_back') if (self.rects.length) { self.removeLastRectangle() } else { self.destruct() self.reject() } }) $('#screenshot_controller_go').on('click', async function (e: JQuery.TriggeredEvent) { console.log('#screenshot_controller_go') // clear the canvas self.ctx.clearRect(0, 0, self.canvas.width, self.canvas.height) self.ctx.drawImage(self.originalImage, 0, 0) self.displayRectangles() let data: any = { image: self.canvas.toDataURL('image/jpeg', 0.75), } self.canvas.toBlob(async (blob: Blob) => { data.blob = blob await self.destruct() self.resolve(data) }, 'image/jpeg') }) } static noteEditor(image: string, width: number, height: number) { const self = this self.canvas = document.createElement("canvas") self.canvas.width = width self.canvas.height = height self.canvas.id = "screenshot_edit_" + uuidv4() self.canvas.style = "z-index:10002;position:absolute;top:0px;left:0px;" $('body').append(self.canvas) self.ctx = self.canvas.getContext("2d") if (self.ctx === null) { throw new Error("cannot create canvas: ctx") } const img = new Image() img.onload = () => { self.ctx.drawImage(img, 0, 0) self.originalImage = img self.displayOpacity() self.displayControllers() const canvasOffset = self.canvas.getBoundingClientRect() self.offsetX = canvasOffset.left; self.offsetY = canvasOffset.top; // this flage is true when the user is dragging the mouse self.isDown = false self.canvas.addEventListener('mousedown', function (e: MouseEvent) { self.handleMouseDown(e) }) self.canvas.addEventListener('touchstart', function (e: TouchEvent) { self.handleMouseDown(e) }) self.canvas.addEventListener('mousemove', function (e: MouseEvent) { self.handleMouseMove(e) }) self.canvas.addEventListener('touchmove', function (e: TouchEvent) { self.handleMouseMove(e) }) self.canvas.addEventListener('mouseup', function (e: MouseEvent) { self.handleMouseUp(e) }) self.canvas.addEventListener('touchend', function (e: TouchEvent) { self.handleMouseUp(e) }) self.canvas.addEventListener('mouseout', function (e: MouseEvent) { self.handleMouseOut(e) }) self.canvas.addEventListener('touchcancel', function (e: TouchEvent) { self.handleMouseOut(e) }) } img.width = width img.height = height img.src = image } private static handleMouseDown(e: MouseEvent | TouchEvent) { const self = this e.preventDefault() e.stopPropagation() if ('touches' in e) { const touch = e.touches[0] // get the current mouse position self.startX = (touch.clientX || 0) - self.offsetX self.startY = (touch.clientY || 0) - self.offsetY } else { // get the current mouse position self.startX = (e.clientX || 0) - self.offsetX self.startY = (e.clientY || 0) - self.offsetY } console.log("isDown") self.isDown = true } private static handleMouseUp(e: MouseEvent | TouchEvent) { const self = this e.preventDefault(); e.stopPropagation(); if (self.isDown) { if (self.x1 !== undefined && self.x2 !== undefined && self.y1 !== undefined && self.y2 !== undefined) { self.rects.push([self.x1, self.y1, self.x2, self.y2]) self.displayRectangles() } } // the drag is over, clear the dragging flag self.isDown = false self.x1 = undefined self.x2 = undefined self.y1 = undefined self.y2 = undefined // console.log(x1, x2, y1, y2) } private static handleMouseOut(e: MouseEvent | TouchEvent) { const self = this e.preventDefault() e.stopPropagation() // the drag is over, clear the dragging flag self.isDown = false } private static handleMouseMove(e: MouseEvent | TouchEvent) { const self = this e.preventDefault(); e.stopPropagation(); // if we're not dragging, just return if (!self.isDown) { return } if ('touches' in e) { const touch = e.touches[0] // get the current mouse position self.mouseX = (touch.clientX || 0) - self.offsetX self.mouseY = (touch.clientY || 0) - self.offsetY } else { // get the current mouse position self.mouseX = (e.clientX || 0) - self.offsetX self.mouseY = (e.clientY || 0) - self.offsetY } // clear the canvas self.ctx.clearRect(0, 0, self.canvas.width, self.canvas.height) self.ctx.drawImage(self.originalImage, 0, 0) self.displayOpacity() self.displayRectangles() // calculate the rectangle width/height based // on starting vs current mouse position const width = self.mouseX - self.startX const height = self.mouseY - self.startY // draw a new rect from the start position // to the current mouse position // style the context self.ctx.strokeStyle = "red" self.ctx.lineWidth = 2 self.ctx.fillStyle = "transparent" self.ctx.strokeRect(self.startX, self.startY, width, height) self.x1 = self.startX self.y1 = self.startY self.x2 = width self.y2 = height } }