PHP Classes

File: toastui/src/js/extension/cropzone.js

Recommend this page to a friend!
  Classes of Mark de Leon   PHP Document Scanner using SANE or eSCL AirPrint   toastui/src/js/extension/cropzone.js   Download  
File: toastui/src/js/extension/cropzone.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: PHP Document Scanner using SANE or eSCL AirPrint
Web interface to scan printed documents
Author: By
Last change:
Date: 4 years ago
Size: 13,150 bytes
 

Contents

Class file image Download
/** * @author NHN Ent. FE Development Team <dl_javascript@nhn.com> * @fileoverview Cropzone extending fabric.Rect */ import snippet from 'tui-code-snippet'; import fabric from 'fabric/dist/fabric.require'; import {clamp} from '../util'; const CORNER_TYPE_TOP_LEFT = 'tl'; const CORNER_TYPE_TOP_RIGHT = 'tr'; const CORNER_TYPE_MIDDLE_TOP = 'mt'; const CORNER_TYPE_MIDDLE_LEFT = 'ml'; const CORNER_TYPE_MIDDLE_RIGHT = 'mr'; const CORNER_TYPE_MIDDLE_BOTTOM = 'mb'; const CORNER_TYPE_BOTTOM_LEFT = 'bl'; const CORNER_TYPE_BOTTOM_RIGHT = 'br'; /** * Cropzone object * Issue: IE7, 8(with excanvas) * - Cropzone is a black zone without transparency. * @class Cropzone * @extends {fabric.Rect} * @ignore */ const Cropzone = fabric.util.createClass(fabric.Rect, /** @lends Cropzone.prototype */{ /** * Constructor * @param {Object} options Options object * @override */ initialize(options, extendsOptions) { options = snippet.extend(options, extendsOptions); options.type = 'cropzone'; this.callSuper('initialize', options); this.options = options; this.on({ 'moving': this._onMoving, 'scaling': this._onScaling }); }, /** * Render Crop-zone * @param {CanvasRenderingContext2D} ctx - Context * @private * @override */ _render(ctx) { const cropzoneDashLineWidth = 7; const cropzoneDashLineOffset = 7; this.callSuper('_render', ctx); // Calc original scale const originalFlipX = this.flipX ? -1 : 1; const originalFlipY = this.flipY ? -1 : 1; const originalScaleX = originalFlipX / this.scaleX; const originalScaleY = originalFlipY / this.scaleY; // Set original scale ctx.scale(originalScaleX, originalScaleY); // Render outer rect this._fillOuterRect(ctx, 'rgba(0, 0, 0, 0.55)'); if (this.options.lineWidth) { this._fillInnerRect(ctx); this._strokeBorder(ctx, 'rgb(255, 255, 255)', { lineWidth: this.options.lineWidth }); } else { // Black dash line this._strokeBorder(ctx, 'rgb(0, 0, 0)', { lineDashWidth: cropzoneDashLineWidth }); // White dash line this._strokeBorder(ctx, 'rgb(255, 255, 255)', { lineDashWidth: cropzoneDashLineWidth, lineDashOffset: cropzoneDashLineOffset }); } // Reset scale ctx.scale(1 / originalScaleX, 1 / originalScaleY); }, /** * Cropzone-coordinates with outer rectangle * * x0 x1 x2 x3 * y0 +--------------------------+ * |///////|//////////|///////| // <--- "Outer-rectangle" * |///////|//////////|///////| * y1 +-------+----------+-------+ * |///////| Cropzone |///////| Cropzone is the "Inner-rectangle" * |///////| (0, 0) |///////| Center point (0, 0) * y2 +-------+----------+-------+ * |///////|//////////|///////| * |///////|//////////|///////| * y3 +--------------------------+ * * @typedef {{x: Array<number>, y: Array<number>}} cropzoneCoordinates * @ignore */ /** * Fill outer rectangle * @param {CanvasRenderingContext2D} ctx - Context * @param {string|CanvasGradient|CanvasPattern} fillStyle - Fill-style * @private */ _fillOuterRect(ctx, fillStyle) { const {x, y} = this._getCoordinates(ctx); ctx.save(); ctx.fillStyle = fillStyle; ctx.beginPath(); // Outer rectangle // Numbers are +/-1 so that overlay edges don't get blurry. ctx.moveTo(x[0] - 1, y[0] - 1); ctx.lineTo(x[3] + 1, y[0] - 1); ctx.lineTo(x[3] + 1, y[3] + 1); ctx.lineTo(x[0] - 1, y[3] + 1); ctx.lineTo(x[0] - 1, y[0] - 1); ctx.closePath(); // Inner rectangle ctx.moveTo(x[1], y[1]); ctx.lineTo(x[1], y[2]); ctx.lineTo(x[2], y[2]); ctx.lineTo(x[2], y[1]); ctx.lineTo(x[1], y[1]); ctx.closePath(); ctx.fill(); ctx.restore(); }, /** * Draw Inner grid line * @param {CanvasRenderingContext2D} ctx - Context * @private */ _fillInnerRect(ctx) { const {x: outerX, y: outerY} = this._getCoordinates(ctx); const x = this._caculateInnerPosition(outerX, (outerX[2] - outerX[1]) / 3); const y = this._caculateInnerPosition(outerY, (outerY[2] - outerY[1]) / 3); ctx.save(); ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)'; ctx.lineWidth = this.options.lineWidth; ctx.beginPath(); ctx.moveTo(x[0], y[1]); ctx.lineTo(x[3], y[1]); ctx.moveTo(x[0], y[2]); ctx.lineTo(x[3], y[2]); ctx.moveTo(x[1], y[0]); ctx.lineTo(x[1], y[3]); ctx.moveTo(x[2], y[0]); ctx.lineTo(x[2], y[3]); ctx.stroke(); ctx.closePath(); ctx.restore(); }, /** * Calculate Inner Position * @param {Array} outer - outer position * @param {number} size - interval for calcaulate * @returns {Array} - inner position * @private */ _caculateInnerPosition(outer, size) { const position = []; position[0] = outer[1]; position[1] = outer[1] + size; position[2] = outer[1] + (size * 2); position[3] = outer[2]; return position; }, /** * Get coordinates * @param {CanvasRenderingContext2D} ctx - Context * @returns {cropzoneCoordinates} - {@link cropzoneCoordinates} * @private */ _getCoordinates(ctx) { const width = this.getWidth(), height = this.getHeight(), halfWidth = width / 2, halfHeight = height / 2, left = this.getLeft(), top = this.getTop(), canvasEl = ctx.canvas; // canvas element, not fabric object return { x: snippet.map([ -(halfWidth + left), // x0 -(halfWidth), // x1 halfWidth, // x2 halfWidth + (canvasEl.width - left - width) // x3 ], Math.ceil), y: snippet.map([ -(halfHeight + top), // y0 -(halfHeight), // y1 halfHeight, // y2 halfHeight + (canvasEl.height - top - height) // y3 ], Math.ceil) }; }, /** * Stroke border * @param {CanvasRenderingContext2D} ctx - Context * @param {string|CanvasGradient|CanvasPattern} strokeStyle - Stroke-style * @param {number} lineDashWidth - Dash width * @param {number} [lineDashOffset] - Dash offset * @private */ _strokeBorder(ctx, strokeStyle, {lineDashWidth, lineDashOffset, lineWidth}) { const halfWidth = this.getWidth() / 2, halfHeight = this.getHeight() / 2; ctx.save(); ctx.strokeStyle = strokeStyle; if (ctx.setLineDash) { ctx.setLineDash([lineDashWidth, lineDashWidth]); } if (lineDashOffset) { ctx.lineDashOffset = lineDashOffset; } if (lineWidth) { ctx.lineWidth = lineWidth; } ctx.beginPath(); ctx.moveTo(-halfWidth, -halfHeight); ctx.lineTo(halfWidth, -halfHeight); ctx.lineTo(halfWidth, halfHeight); ctx.lineTo(-halfWidth, halfHeight); ctx.lineTo(-halfWidth, -halfHeight); ctx.stroke(); ctx.restore(); }, /** * onMoving event listener * @private */ _onMoving() { const left = this.getLeft(), top = this.getTop(), width = this.getWidth(), height = this.getHeight(), maxLeft = this.canvas.getWidth() - width, maxTop = this.canvas.getHeight() - height; this.setLeft(clamp(left, 0, maxLeft)); this.setTop(clamp(top, 0, maxTop)); }, /** * onScaling event listener * @param {{e: MouseEvent}} fEvent - Fabric event * @private */ _onScaling(fEvent) { const pointer = this.canvas.getPointer(fEvent.e), settings = this._calcScalingSizeFromPointer(pointer); // On scaling cropzone, // change real width and height and fix scaleFactor to 1 this.scale(1).set(settings); }, /** * Calc scaled size from mouse pointer with selected corner * @param {{x: number, y: number}} pointer - Mouse position * @returns {Object} Having left or(and) top or(and) width or(and) height. * @private */ _calcScalingSizeFromPointer(pointer) { const pointerX = pointer.x, pointerY = pointer.y, tlScalingSize = this._calcTopLeftScalingSizeFromPointer(pointerX, pointerY), brScalingSize = this._calcBottomRightScalingSizeFromPointer(pointerX, pointerY); /* * @todo: ?? ???? shift ???? ??? free size scaling? ? --> ????? * canvas.class.js // _scaleObject: function(...){...} */ return this._makeScalingSettings(tlScalingSize, brScalingSize); }, /** * Calc scaling size(position + dimension) from left-top corner * @param {number} x - Mouse position X * @param {number} y - Mouse position Y * @returns {{top: number, left: number, width: number, height: number}} * @private */ _calcTopLeftScalingSizeFromPointer(x, y) { const bottom = this.getHeight() + this.top, right = this.getWidth() + this.left, top = clamp(y, 0, bottom - 1), // 0 <= top <= (bottom - 1) left = clamp(x, 0, right - 1); // 0 <= left <= (right - 1) // When scaling "Top-Left corner": It fixes right and bottom coordinates return { top, left, width: right - left, height: bottom - top }; }, /** * Calc scaling size from right-bottom corner * @param {number} x - Mouse position X * @param {number} y - Mouse position Y * @returns {{width: number, height: number}} * @private */ _calcBottomRightScalingSizeFromPointer(x, y) { const {width: maxX, height: maxY} = this.canvas; const {left, top} = this; // When scaling "Bottom-Right corner": It fixes left and top coordinates return { width: clamp(x, (left + 1), maxX) - left, // (width = x - left), (left + 1 <= x <= maxX) height: clamp(y, (top + 1), maxY) - top // (height = y - top), (top + 1 <= y <= maxY) }; }, /* eslint-disable complexity */ /** * Make scaling settings * @param {{width: number, height: number, left: number, top: number}} tl - Top-Left setting * @param {{width: number, height: number}} br - Bottom-Right setting * @returns {{width: ?number, height: ?number, left: ?number, top: ?number}} Position setting * @private */ _makeScalingSettings(tl, br) { const tlWidth = tl.width; const tlHeight = tl.height; const brHeight = br.height; const brWidth = br.width; const tlLeft = tl.left; const tlTop = tl.top; let settings; switch (this.__corner) { case CORNER_TYPE_TOP_LEFT: settings = tl; break; case CORNER_TYPE_TOP_RIGHT: settings = { width: brWidth, height: tlHeight, top: tlTop }; break; case CORNER_TYPE_BOTTOM_LEFT: settings = { width: tlWidth, height: brHeight, left: tlLeft }; break; case CORNER_TYPE_BOTTOM_RIGHT: settings = br; break; case CORNER_TYPE_MIDDLE_LEFT: settings = { width: tlWidth, left: tlLeft }; break; case CORNER_TYPE_MIDDLE_TOP: settings = { height: tlHeight, top: tlTop }; break; case CORNER_TYPE_MIDDLE_RIGHT: settings = { width: brWidth }; break; case CORNER_TYPE_MIDDLE_BOTTOM: settings = { height: brHeight }; break; default: break; } return settings; }, /* eslint-enable complexity */ /** * Return the whether this cropzone is valid * @returns {boolean} */ isValid() { return ( this.left >= 0 && this.top >= 0 && this.width > 0 && this.height > 0 ); } }); module.exports = Cropzone;