UNPKG

sunzi-fabric

Version:

made by fabric@3.2.0

373 lines (329 loc) 12.9 kB
(function() { var degreesToRadians = fabric.util.degreesToRadians; fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ { /** * The object interactivity controls. * @private */ _controlsVisibility: null, /** * Determines which corner has been clicked * @private * @param {Object} pointer The pointer indicating the mouse position * @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found */ _findTargetCorner: function(pointer) { // objects in group, anykind, are not self modificable, // must not return an hovered corner. if (!this.hasControls || this.group || (!this.canvas || this.canvas._activeObject !== this)) { return false; } var ex = pointer.x, ey = pointer.y, xPoints, lines; this.__corner = 0; for (var i in this.oCoords) { if (!this.isControlVisible(i)) { continue; } if (i === 'mtr' && !this.hasRotatingPoint) { continue; } if (this.get('lockUniScaling') && (i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) { continue; } lines = this._getImageLines(this.oCoords[i].corner); // debugging // canvas.contextTop.fillRect(lines.bottomline.d.x, lines.bottomline.d.y, 2, 2); // canvas.contextTop.fillRect(lines.bottomline.o.x, lines.bottomline.o.y, 2, 2); // canvas.contextTop.fillRect(lines.leftline.d.x, lines.leftline.d.y, 2, 2); // canvas.contextTop.fillRect(lines.leftline.o.x, lines.leftline.o.y, 2, 2); // canvas.contextTop.fillRect(lines.topline.d.x, lines.topline.d.y, 2, 2); // canvas.contextTop.fillRect(lines.topline.o.x, lines.topline.o.y, 2, 2); // canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2); // canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2); xPoints = this._findCrossPoints({ x: ex, y: ey }, lines); if (xPoints !== 0 && xPoints % 2 === 1) { this.__corner = i; return i; } } return false; }, /** * Sets the coordinates of the draggable boxes in the corners of * the image used to scale/rotate it. * @private */ _setCornerCoords: function() { var coords = this.oCoords, newTheta = degreesToRadians(45 - this.angle), /* Math.sqrt(2 * Math.pow(this.cornerSize, 2)) / 2, */ /* 0.707106 stands for sqrt(2)/2 */ cornerHypotenuse = this.cornerSize * 0.707106, cosHalfOffset = cornerHypotenuse * fabric.util.cos(newTheta), sinHalfOffset = cornerHypotenuse * fabric.util.sin(newTheta), x, y; for (var point in coords) { x = coords[point].x; y = coords[point].y; coords[point].corner = { tl: { x: x - sinHalfOffset, y: y - cosHalfOffset }, tr: { x: x + cosHalfOffset, y: y - sinHalfOffset }, bl: { x: x - cosHalfOffset, y: y + sinHalfOffset }, br: { x: x + sinHalfOffset, y: y + cosHalfOffset } }; } }, /** * Draws a colored layer behind the object, inside its selection borders. * Requires public options: padding, selectionBackgroundColor * this function is called when the context is transformed * has checks to be skipped when the object is on a staticCanvas * @param {CanvasRenderingContext2D} ctx Context to draw on * @return {fabric.Object} thisArg * @chainable */ drawSelectionBackground: function(ctx) { if (!this.selectionBackgroundColor || (this.canvas && !this.canvas.interactive) || (this.canvas && this.canvas._activeObject !== this) ) { return this; } ctx.save(); var center = this.getCenterPoint(), wh = this._calculateCurrentDimensions(), vpt = this.canvas.viewportTransform; ctx.translate(center.x, center.y); ctx.scale(1 / vpt[0], 1 / vpt[3]); ctx.rotate(degreesToRadians(this.angle)); ctx.fillStyle = this.selectionBackgroundColor; ctx.fillRect(-wh.x / 2, -wh.y / 2, wh.x, wh.y); ctx.restore(); return this; }, /** * Draws borders of an object's bounding box. * Requires public properties: width, height * Requires public options: padding, borderColor * @param {CanvasRenderingContext2D} ctx Context to draw on * @param {Object} styleOverride object to override the object style * @return {fabric.Object} thisArg * @chainable */ drawBorders: function(ctx, styleOverride) { styleOverride = styleOverride || {}; var wh = this._calculateCurrentDimensions(), strokeWidth = 1 / this.borderScaleFactor, width = wh.x + strokeWidth, height = wh.y + strokeWidth; ctx.save(); ctx.strokeStyle = 'rgba(0,0,0,0.3)'; ctx.lineWidth = ctx.lineWidth + this.borderScaleFactor / 2; this._setLineDash(ctx, styleOverride.borderDashArray || this.borderDashArray, null); ctx.strokeRect( -width / 2, -height / 2, width, height ); ctx.restore(); ctx.save(); ctx.strokeStyle = styleOverride.borderColor || this.borderColor; this._setLineDash(ctx, styleOverride.borderDashArray || this.borderDashArray, null); ctx.strokeRect( -width / 2, -height / 2, width, height ); ctx.restore(); return this; }, /** * Draws borders of an object's bounding box when it is inside a group. * Requires public properties: width, height * Requires public options: padding, borderColor * @param {CanvasRenderingContext2D} ctx Context to draw on * @param {object} options object representing current object parameters * @param {Object} styleOverride object to override the object style * @return {fabric.Object} thisArg * @chainable */ drawBordersInGroup: function(ctx, options, styleOverride) { styleOverride = styleOverride || {}; var p = this._getNonTransformedDimensions(), matrix = fabric.util.customTransformMatrix(options.scaleX, options.scaleY, options.skewX), wh = fabric.util.transformPoint(p, matrix), strokeWidth = 1 / this.borderScaleFactor, width = wh.x + strokeWidth, height = wh.y + strokeWidth; ctx.save(); this._setLineDash(ctx, styleOverride.borderDashArray || this.borderDashArray, null); ctx.strokeStyle = styleOverride.borderColor || this.borderColor; ctx.strokeRect( -width / 2, -height / 2, width, height ); ctx.restore(); return this; }, /** * Draws corners of an object's bounding box. * Requires public properties: width, height * Requires public options: cornerSize, padding * @param {CanvasRenderingContext2D} ctx Context to draw on * @param {Object} styleOverride object to override the object style * @return {fabric.Object} thisArg * @chainable */ drawControls: function(ctx, styleOverride) { styleOverride = styleOverride || {}; var wh = this._calculateCurrentDimensions(), width = wh.x, height = wh.y, scaleOffset = styleOverride.cornerSize || this.cornerSize, left = -(width + scaleOffset) / 2, top = -(height + scaleOffset) / 2; ctx.save(); ctx.strokeStyle = ctx.fillStyle = styleOverride.cornerColor || this.cornerColor; if (!this.transparentCorners) { ctx.strokeStyle = styleOverride.cornerStrokeColor || this.cornerStrokeColor; } this._setLineDash(ctx, styleOverride.cornerDashArray || this.cornerDashArray, null); // top-left if (styleOverride.imageControlTL) this._drawControl('tl', ctx, styleOverride.imageControlTL, left, top); // top-right if (styleOverride.imageControlTR) this._drawControl('tr', ctx, styleOverride.imageControlTR, left + width, top); // bottom-left if (styleOverride.imageControlBL) this._drawControl('bl', ctx, styleOverride.imageControlBL, left, top + height); // bottom-right if (styleOverride.imageControlBR) this._drawControl('br', ctx, styleOverride.imageControlBR, left + width, top + height); ctx.restore(); return this; }, /** * @private */ // jindada update _drawControl: function(control, ctx, image, left, top) { if (!this.isControlVisible(control)) { return; } var size = this.cornerSize; ctx.beginPath(); ctx.arc(left + size / 2, top + size / 2, size / 2, 0, 2 * Math.PI, false); ctx.strokeStyle = this.borderColor; ctx.lineWidth = size / 8; ctx.stroke(); ctx.fillStyle = "#ff0136"; ctx.fill(); switch (control) { case 'tl': case 'tr': case 'bl': case 'br': ctx.drawImage(image, left + 1, top + 1, size - 2, size - 2); break; default: break; } }, /** * Returns true if the specified control is visible, false otherwise. * @param {String} controlName The name of the control. Possible values are 'tl', 'tr', 'br', 'bl', 'ml', 'mt', 'mr', 'mb', 'mtr'. * @returns {Boolean} true if the specified control is visible, false otherwise */ isControlVisible: function(controlName) { return this._getControlsVisibility()[controlName]; }, /** * Sets the visibility of the specified control. * @param {String} controlName The name of the control. Possible values are 'tl', 'tr', 'br', 'bl', 'ml', 'mt', 'mr', 'mb', 'mtr'. * @param {Boolean} visible true to set the specified control visible, false otherwise * @return {fabric.Object} thisArg * @chainable */ setControlVisible: function(controlName, visible) { this._getControlsVisibility()[controlName] = visible; return this; }, /** * Sets the visibility state of object controls. * @param {Object} [options] Options object * @param {Boolean} [options.bl] true to enable the bottom-left control, false to disable it * @param {Boolean} [options.br] true to enable the bottom-right control, false to disable it * @param {Boolean} [options.mb] true to enable the middle-bottom control, false to disable it * @param {Boolean} [options.ml] true to enable the middle-left control, false to disable it * @param {Boolean} [options.mr] true to enable the middle-right control, false to disable it * @param {Boolean} [options.mt] true to enable the middle-top control, false to disable it * @param {Boolean} [options.tl] true to enable the top-left control, false to disable it * @param {Boolean} [options.tr] true to enable the top-right control, false to disable it * @param {Boolean} [options.mtr] true to enable the middle-top-rotate control, false to disable it * @return {fabric.Object} thisArg * @chainable */ setControlsVisibility: function(options) { options || (options = { }); for (var p in options) { this.setControlVisible(p, options[p]); } return this; }, /** * Returns the instance of the control visibility set for this object. * @private * @returns {Object} */ _getControlsVisibility: function() { if (!this._controlsVisibility) { this._controlsVisibility = { tl: true, tr: true, br: true, bl: true, ml: true, mt: true, mr: true, mb: true, mtr: true }; } return this._controlsVisibility; }, /** * This callback function is called every time _discardActiveObject or _setActiveObject * try to to deselect this object. If the function returns true, the process is cancelled * @param {Object} [options] options sent from the upper functions * @param {Event} [options.e] event if the process is generated by an event */ onDeselect: function() { // implemented by sub-classes, as needed. }, /** * This callback function is called every time _discardActiveObject or _setActiveObject * try to to select this object. If the function returns true, the process is cancelled * @param {Object} [options] options sent from the upper functions * @param {Event} [options.e] event if the process is generated by an event */ onSelect: function() { // implemented by sub-classes, as needed. } }); })();