2020-04-07 21:46:50 +08:00
import { Util , Collection , Transform } from '../Util' ;
2019-02-25 01:06:04 +08:00
import { Factory } from '../Factory' ;
2019-01-02 04:59:27 +08:00
import { Node } from '../Node' ;
import { Shape } from '../Shape' ;
import { Rect } from './Rect' ;
import { Group } from '../Group' ;
2019-03-10 23:31:13 +08:00
import { ContainerConfig } from '../Container' ;
2019-03-07 11:19:32 +08:00
import { Konva } from '../Global' ;
2019-02-25 01:06:04 +08:00
import { getNumberValidator } from '../Validators' ;
2019-02-27 21:06:04 +08:00
import { _registerNode } from '../Global' ;
2019-01-02 04:59:27 +08:00
2020-04-07 21:46:50 +08:00
import { GetSet , IRect , Vector2d } from '../types' ;
2019-01-02 04:59:27 +08:00
2019-03-10 23:31:13 +08:00
interface Box extends IRect {
rotation : number ;
}
export interface TransformerConfig extends ContainerConfig {
resizeEnabled? : boolean ;
rotateEnabled? : boolean ;
rotationSnaps? : Array < number > ;
2020-02-29 05:38:18 +08:00
rotationSnapTolerance? : number ;
2019-03-10 23:31:13 +08:00
rotateAnchorOffset? : number ;
borderEnabled? : boolean ;
borderStroke? : string ;
borderStrokeWidth? : number ;
borderDash? : Array < number > ;
anchorFill? : string ;
anchorStroke? : string ;
anchorStrokeWidth? : number ;
anchorSize? : number ;
keepRatio? : boolean ;
centeredScaling? : boolean ;
enabledAnchors? : Array < string > ;
node? : Rect ;
boundBoxFunc ? : ( oldBox : Box , newBox : Box ) = > Box ;
}
2019-08-10 17:57:53 +08:00
var EVENTS_NAME = 'tr-konva' ;
2019-01-02 04:59:27 +08:00
var ATTR_CHANGE_LIST = [
'resizeEnabledChange' ,
'rotateAnchorOffsetChange' ,
'rotateEnabledChange' ,
'enabledAnchorsChange' ,
'anchorSizeChange' ,
'borderEnabledChange' ,
'borderStrokeChange' ,
'borderStrokeWidthChange' ,
'borderDashChange' ,
'anchorStrokeChange' ,
'anchorStrokeWidthChange' ,
'anchorFillChange' ,
'anchorCornerRadiusChange' ,
'ignoreStrokeChange'
2019-08-10 17:57:53 +08:00
]
. map ( e = > e + ` . ${ EVENTS_NAME } ` )
. join ( ' ' ) ;
2019-01-02 04:59:27 +08:00
2020-04-02 23:01:31 +08:00
var NODES_RECT = 'nodesRect' ;
2019-01-02 04:59:27 +08:00
var TRANSFORM_CHANGE_STR = [
2019-08-10 17:57:53 +08:00
'widthChange' ,
'heightChange' ,
'scaleXChange' ,
'scaleYChange' ,
'skewXChange' ,
'skewYChange' ,
'rotationChange' ,
'offsetXChange' ,
'offsetYChange' ,
'transformsEnabledChange' ,
2020-04-11 05:00:25 +08:00
'strokeWidthChange'
2019-08-10 17:57:53 +08:00
]
. map ( e = > e + ` . ${ EVENTS_NAME } ` )
. join ( ' ' ) ;
2019-01-02 04:59:27 +08:00
var ANGLES = {
'top-left' : - 45 ,
'top-center' : 0 ,
'top-right' : 45 ,
'middle-right' : - 90 ,
'middle-left' : 90 ,
'bottom-left' : - 135 ,
'bottom-center' : 180 ,
'bottom-right' : 135
} ;
2019-12-23 23:52:49 +08:00
const TOUCH_DEVICE = 'ontouchstart' in Konva . _global ;
2019-01-02 04:59:27 +08:00
function getCursor ( anchorName , rad , isMirrored ) {
if ( anchorName === 'rotater' ) {
return 'crosshair' ;
}
rad += Util . _degToRad ( ANGLES [ anchorName ] || 0 ) ;
// If we are mirrored, we need to mirror the angle (this is not the same as
// rotate).
if ( isMirrored ) {
rad *= - 1 ;
}
var angle = ( ( Util . _radToDeg ( rad ) % 360 ) + 360 ) % 360 ;
if ( Util . _inRange ( angle , 315 + 22.5 , 360 ) || Util . _inRange ( angle , 0 , 22.5 ) ) {
// TOP
return 'ns-resize' ;
} else if ( Util . _inRange ( angle , 45 - 22.5 , 45 + 22.5 ) ) {
// TOP - RIGHT
return 'nesw-resize' ;
} else if ( Util . _inRange ( angle , 90 - 22.5 , 90 + 22.5 ) ) {
// RIGHT
return 'ew-resize' ;
} else if ( Util . _inRange ( angle , 135 - 22.5 , 135 + 22.5 ) ) {
// BOTTOM - RIGHT
return 'nwse-resize' ;
} else if ( Util . _inRange ( angle , 180 - 22.5 , 180 + 22.5 ) ) {
// BOTTOM
return 'ns-resize' ;
} else if ( Util . _inRange ( angle , 225 - 22.5 , 225 + 22.5 ) ) {
// BOTTOM - LEFT
return 'nesw-resize' ;
} else if ( Util . _inRange ( angle , 270 - 22.5 , 270 + 22.5 ) ) {
// RIGHT
return 'ew-resize' ;
} else if ( Util . _inRange ( angle , 315 - 22.5 , 315 + 22.5 ) ) {
// BOTTOM - RIGHT
return 'nwse-resize' ;
} else {
// how can we can there?
Util . error ( 'Transformer has unknown angle for cursor detection: ' + angle ) ;
return 'pointer' ;
}
}
var ANCHORS_NAMES = [
'top-left' ,
'top-center' ,
'top-right' ,
'middle-right' ,
'middle-left' ,
'bottom-left' ,
'bottom-center' ,
'bottom-right'
] ;
var MAX_SAFE_INTEGER = 100000000 ;
2020-04-07 21:46:50 +08:00
function getCenter ( shape : Box ) {
2020-04-02 23:01:31 +08:00
return {
x :
shape . x +
( shape . width / 2 ) * Math . cos ( shape . rotation ) +
( shape . height / 2 ) * Math . sin ( - shape . rotation ) ,
y :
shape . y +
( shape . height / 2 ) * Math . cos ( shape . rotation ) +
( shape . width / 2 ) * Math . sin ( shape . rotation )
} ;
}
2020-04-07 21:46:50 +08:00
function rotateAroundPoint ( shape : Box , angleRad : number , point : Vector2d ) {
2020-04-02 23:01:31 +08:00
const x =
point . x +
( shape . x - point . x ) * Math . cos ( angleRad ) -
( shape . y - point . y ) * Math . sin ( angleRad ) ;
const y =
point . y +
( shape . x - point . x ) * Math . sin ( angleRad ) +
( shape . y - point . y ) * Math . cos ( angleRad ) ;
return {
. . . shape ,
rotation : shape.rotation + angleRad ,
x ,
y
} ;
}
2020-04-07 21:46:50 +08:00
function rotateAroundCenter ( shape : Box , deltaRad : number ) {
2020-04-02 23:01:31 +08:00
const center = getCenter ( shape ) ;
return rotateAroundPoint ( shape , deltaRad , center ) ;
}
2020-04-07 21:46:50 +08:00
function getShapeRect ( shape : Box ) {
2020-04-02 23:01:31 +08:00
const angleRad = shape . rotation ;
const x1 = shape . x ;
const y1 = shape . y ;
const x2 = x1 + shape . width * Math . cos ( angleRad ) ;
const y2 = y1 + shape . width * Math . sin ( angleRad ) ;
const x3 =
shape . x +
shape . width * Math . cos ( angleRad ) +
shape . height * Math . sin ( - angleRad ) ;
const y3 =
shape . y +
shape . height * Math . cos ( angleRad ) +
shape . width * Math . sin ( angleRad ) ;
const x4 = shape . x + shape . height * Math . sin ( - angleRad ) ;
const y4 = shape . y + shape . height * Math . cos ( angleRad ) ;
const leftX = Math . min ( x1 , x2 , x3 , x4 ) ;
const rightX = Math . max ( x1 , x2 , x3 , x4 ) ;
const topY = Math . min ( y1 , y2 , y3 , y4 ) ;
const bottomY = Math . max ( y1 , y2 , y3 , y4 ) ;
return {
x : leftX ,
y : topY ,
width : rightX - leftX ,
height : bottomY - topY
} ;
}
2020-04-07 21:46:50 +08:00
function getShapesRect ( shapes : Array < Box > ) {
2020-04-02 23:01:31 +08:00
// if (shapes.length === 1) {
// const shape = shapes[0];
// return {
// x: shape.x,
// y: shape.y,
// width: shape.width,
// height: shape.height,
// rotation: shape.rotation
// };
// }
let x1 = 9999999999 ;
let y1 = 9999999999 ;
let x2 = - 999999999 ;
let y2 = - 999999999 ;
shapes . forEach ( shape = > {
const rect = getShapeRect ( shape ) ;
x1 = Math . min ( x1 , rect . x ) ;
y1 = Math . min ( y1 , rect . y ) ;
x2 = Math . max ( x2 , rect . x + rect . width ) ;
y2 = Math . max ( y2 , rect . y + rect . height ) ;
} ) ;
return {
x : x1 ,
y : y1 ,
width : x2 - x1 ,
height : y2 - y1 ,
rotation : 0
} ;
}
2020-04-07 10:51:03 +08:00
function transformShape (
2020-04-07 21:46:50 +08:00
shape : Box ,
oldSelection : Box ,
newSelection : Box ,
2020-04-07 10:51:03 +08:00
keepOffset = 1
) {
const offset = rotateAroundPoint ( shape , - oldSelection . rotation , {
x : oldSelection.x ,
y : oldSelection.y
} ) ;
2020-04-02 23:01:31 +08:00
const offsetX = offset . x - oldSelection . x ;
const offsetY = offset . y - oldSelection . y ;
const angle = oldSelection . rotation ;
const scaleX = shape . width ? newSelection . width / oldSelection.width : 1 ;
const scaleY = shape . height ? newSelection . height / oldSelection.height : 1 ;
return {
x :
keepOffset * newSelection . x +
offsetX * scaleX * Math . cos ( angle ) +
offsetY * scaleY * Math . sin ( - angle ) ,
y :
keepOffset * newSelection . y +
offsetX * scaleX * Math . sin ( angle ) +
offsetY * scaleY * Math . cos ( angle ) ,
width : shape.width * scaleX ,
2020-04-07 10:51:03 +08:00
height : shape.height * scaleY ,
rotation : shape.rotation
2020-04-02 23:01:31 +08:00
} ;
}
2020-04-07 10:51:03 +08:00
function transformAndRotateShape (
2020-04-07 21:46:50 +08:00
shape : Box ,
oldSelection : Box ,
newSelection : Box
2020-04-07 10:51:03 +08:00
) {
2020-04-02 23:01:31 +08:00
const updated = transformShape ( shape , oldSelection , newSelection ) ;
return rotateAroundPoint (
2020-04-07 10:51:03 +08:00
updated ,
2020-04-02 23:01:31 +08:00
newSelection . rotation - oldSelection . rotation ,
newSelection
) ;
}
2020-04-13 23:12:39 +08:00
function getSnap ( snaps : Array < number > , newRotationRad : number , tol : number ) {
let snapped = newRotationRad ;
for ( let i = 0 ; i < snaps . length ; i ++ ) {
const angle = Konva . getAngle ( snaps [ i ] ) ;
const absDiff = Math . abs ( angle - newRotationRad ) % ( Math . PI * 2 ) ;
const dif = Math . min ( absDiff , Math . PI * 2 - absDiff ) ;
if ( dif < tol ) {
snapped = angle ;
}
}
return snapped ;
}
2019-01-02 04:59:27 +08:00
/ * *
* Transformer constructor . Transformer is a special type of group that allow you transform Konva
* primitives and shapes . Transforming tool is not changing ` width ` and ` height ` properties of nodes
* when you resize them . Instead it changes ` scaleX ` and ` scaleY ` properties .
* @constructor
* @memberof Konva
* @param { Object } config
* @param { Boolean } [ config . resizeEnabled ] Default is true
* @param { Boolean } [ config . rotateEnabled ] Default is true
* @param { Array } [ config . rotationSnaps ] Array of angles for rotation snaps . Default is [ ]
2020-02-29 05:38:18 +08:00
* @param { Number } [ config . rotationSnapTolerance ] Snapping tolerance . If closer than this it will snap . Default is 5
2019-01-02 04:59:27 +08:00
* @param { Number } [ config . rotateAnchorOffset ] Default is 50
* @param { Number } [ config . padding ] Default is 0
* @param { Boolean } [ config . borderEnabled ] Should we draw border ? Default is true
* @param { String } [ config . borderStroke ] Border stroke color
* @param { Number } [ config . borderStrokeWidth ] Border stroke size
* @param { Array } [ config . borderDash ] Array for border dash .
* @param { String } [ config . anchorFill ] Anchor fill color
* @param { String } [ config . anchorStroke ] Anchor stroke color
* @param { String } [ config . anchorCornerRadius ] Anchor corner radius
* @param { Number } [ config . anchorStrokeWidth ] Anchor stroke size
* @param { Number } [ config . anchorSize ] Default is 10
* @param { Boolean } [ config . keepRatio ] Should we keep ratio when we are moving edges ? Default is true
* @param { Boolean } [ config . centeredScaling ] Should we resize relative to node ' s center ? Default is false
* @param { Array } [ config . enabledAnchors ] Array of names of enabled handles
* @param { Function } [ config . boundBoxFunc ] Bounding box function
* @param { Function } [ config . ignoreStroke ] Should we ignore stroke size ? Default is false
*
* @example
* var transformer = new Konva . Transformer ( {
* node : rectangle ,
* rotateAnchorOffset : 60 ,
* enabledAnchors : [ 'top-left' , 'top-right' , 'bottom-left' , 'bottom-right' ]
* } ) ;
* layer . add ( transformer ) ;
* /
export class Transformer extends Group {
2020-04-02 23:01:31 +08:00
_nodes : Array < Node > ;
2019-09-07 03:15:07 +08:00
_movingAnchorName : string ;
2019-01-02 04:59:27 +08:00
_transforming = false ;
2020-04-07 21:46:50 +08:00
_anchorDragOffset : Vector2d ;
2019-01-02 04:59:27 +08:00
sin : number ;
cos : number ;
_cursorChange : boolean ;
2019-03-10 23:31:13 +08:00
constructor ( config? : TransformerConfig ) {
2019-01-02 04:59:27 +08:00
// call super constructor
super ( config ) ;
this . _createElements ( ) ;
// bindings
this . _handleMouseMove = this . _handleMouseMove . bind ( this ) ;
this . _handleMouseUp = this . _handleMouseUp . bind ( this ) ;
this . update = this . update . bind ( this ) ;
// update transformer data for certain attr changes
this . on ( ATTR_CHANGE_LIST , this . update ) ;
if ( this . getNode ( ) ) {
this . update ( ) ;
}
}
/ * *
2019-02-20 09:17:49 +08:00
* alias to ` tr.node(shape) `
2019-01-02 04:59:27 +08:00
* @method
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # attachTo
2019-01-02 04:59:27 +08:00
* @returns { Konva . Transformer }
* @example
* transformer . attachTo ( shape ) ;
* /
attachTo ( node ) {
this . setNode ( node ) ;
2019-01-06 16:01:20 +08:00
return this ;
2019-01-02 04:59:27 +08:00
}
setNode ( node ) {
2020-04-02 23:01:31 +08:00
return this . setNodes ( [ node ] ) ;
// if (this._node) {
// this.detach();
// }
// this._node = node;
// this._resetTransformCache();
// const additionalEvents = node._attrsAffectingSize
// .map(prop => prop + 'Change.' + EVENTS_NAME)
// .join(' ');
// const onChange = () => {
// this._resetTransformCache();
// if (!this._transforming) {
// this.update();
// }
// };
// node.on(additionalEvents, onChange);
// node.on(TRANSFORM_CHANGE_STR, onChange);
// node.on(`xChange.${EVENTS_NAME} yChange.${EVENTS_NAME}`, () =>
// this._resetTransformCache()
// );
// // we may need it if we set node in initial props
// // so elements are not defined yet
// var elementsCreated = !!this.findOne('.top-left');
// if (elementsCreated) {
// this.update();
// }
// return this;
}
getNode() {
return this . _nodes && this . _nodes [ 0 ] ;
}
drawScene ( can ? , top ? , caching ? ) {
return super . drawScene ( can , top , caching ) ;
}
// _attachTo(node) => {
// }
setNodes ( nodes : Array < Node > = [ ] ) {
if ( this . _nodes && this . _nodes . length ) {
2019-01-02 04:59:27 +08:00
this . detach ( ) ;
}
2020-04-02 23:01:31 +08:00
this . _nodes = nodes ;
if ( nodes . length === 1 ) {
this . rotation ( nodes [ 0 ] . rotation ( ) ) ;
} else {
this . rotation ( 0 ) ;
}
this . _nodes . forEach ( node = > {
const additionalEvents = node . _attrsAffectingSize
. map ( prop = > prop + 'Change.' + EVENTS_NAME )
. join ( ' ' ) ;
const onChange = ( ) = > {
this . _resetTransformCache ( ) ;
if ( ! this . _transforming ) {
this . update ( ) ;
}
} ;
node . on ( additionalEvents , onChange ) ;
node . on ( TRANSFORM_CHANGE_STR , onChange ) ;
2020-04-11 05:00:25 +08:00
node . on ( ` clearCache. ${ EVENTS_NAME } ` , ( ) = > {
2020-04-02 23:01:31 +08:00
this . _resetTransformCache ( ) ;
} ) ;
2020-04-11 05:00:25 +08:00
node . on ( ` xChange. ${ EVENTS_NAME } yChange. ${ EVENTS_NAME } ` , onChange ) ;
2020-04-02 23:01:31 +08:00
} ) ;
2019-01-02 04:59:27 +08:00
this . _resetTransformCache ( ) ;
2019-02-20 09:17:49 +08:00
// we may need it if we set node in initial props
2019-01-28 04:43:50 +08:00
// so elements are not defined yet
2019-01-02 04:59:27 +08:00
var elementsCreated = ! ! this . findOne ( '.top-left' ) ;
if ( elementsCreated ) {
this . update ( ) ;
}
2019-02-20 09:17:49 +08:00
return this ;
2019-01-02 04:59:27 +08:00
}
2020-04-02 23:01:31 +08:00
getNodes() {
return this . _nodes ;
2019-01-02 04:59:27 +08:00
}
2020-03-03 22:09:21 +08:00
/ * *
* return the name of current active anchor
* @method
2020-04-02 23:01:31 +08:00
* @name Konva . Transformer # getActiveAnchor
2020-03-03 22:09:21 +08:00
* @returns { String | Null }
* @example
2020-04-02 23:01:31 +08:00
* transformer . getActiveAnchor ( ) ;
2020-03-03 22:09:21 +08:00
* /
getActiveAnchor() {
return this . _movingAnchorName ;
}
2019-01-02 04:59:27 +08:00
/ * *
2019-01-06 16:01:20 +08:00
* detach transformer from an attached node
2019-01-02 04:59:27 +08:00
* @method
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # detach
2019-01-02 04:59:27 +08:00
* @returns { Konva . Transformer }
* @example
* transformer . detach ( ) ;
* /
detach() {
if ( this . getNode ( ) ) {
2019-08-10 17:57:53 +08:00
this . getNode ( ) . off ( '.' + EVENTS_NAME ) ;
2020-04-02 23:01:31 +08:00
this . _nodes = [ ] ;
2019-01-02 04:59:27 +08:00
}
this . _resetTransformCache ( ) ;
}
_resetTransformCache() {
2020-04-02 23:01:31 +08:00
this . _clearCache ( NODES_RECT ) ;
2019-01-02 04:59:27 +08:00
this . _clearCache ( 'transform' ) ;
this . _clearSelfAndDescendantCache ( 'absoluteTransform' ) ;
}
_getNodeRect() {
2020-04-02 23:01:31 +08:00
return this . _getCache ( NODES_RECT , this . __getNodeRect ) ;
2019-01-02 04:59:27 +08:00
}
2020-04-02 23:01:31 +08:00
__getNodeShape ( node , rot = this . rotation ( ) ) {
var rect = node . getClientRect ( {
skipTransform : true ,
skipShadow : true ,
skipStroke : this.ignoreStroke ( )
} ) ;
var absScale = node . getAbsoluteScale ( ) ;
var absPos = node . getAbsolutePosition ( ) ;
var dx = rect . x * absScale . x - node . offsetX ( ) * absScale . x ;
var dy = rect . y * absScale . y - node . offsetY ( ) * absScale . y ;
2020-04-13 23:12:39 +08:00
const rotation =
( Konva . getAngle ( node . getAbsoluteRotation ( ) ) + Math . PI * 2 ) %
( Math . PI * 2 ) ;
2020-04-02 23:01:31 +08:00
const box = {
x : absPos.x + dx * Math . cos ( rotation ) + dy * Math . sin ( - rotation ) ,
y : absPos.y + dy * Math . cos ( rotation ) + dx * Math . sin ( rotation ) ,
width : rect.width * absScale . x ,
height : rect.height * absScale . y ,
rotation : rotation
} ;
return rotateAroundPoint ( box , - Konva . getAngle ( rot ) , {
x : 0 ,
y : 0
} ) ;
}
// returns box + rotation of all shapes
2019-01-02 04:59:27 +08:00
__getNodeRect() {
var node = this . getNode ( ) ;
if ( ! node ) {
return {
x : - MAX_SAFE_INTEGER ,
y : - MAX_SAFE_INTEGER ,
width : 0 ,
height : 0 ,
rotation : 0
} ;
}
2019-04-09 01:17:26 +08:00
if ( node . parent && this . parent && node . parent !== this . parent ) {
Util . warn (
'Transformer and attached node have different parents. Konva does not support such case right now. Please move Transformer to the parent of attaching node.'
) ;
}
2020-04-02 23:01:31 +08:00
const shapes = this . nodes ( ) . map ( node = > {
return this . __getNodeShape ( node ) ;
2019-01-02 04:59:27 +08:00
} ) ;
2020-04-02 23:01:31 +08:00
const box = getShapesRect ( shapes ) ;
return rotateAroundPoint ( box , Konva . getAngle ( this . rotation ( ) ) , {
x : 0 ,
y : 0
} ) ;
2019-01-02 04:59:27 +08:00
2020-04-02 23:01:31 +08:00
// return {
// x: node.x() + dx * Math.cos(rotation) + dy * Math.sin(-rotation),
// y: node.y() + dy * Math.cos(rotation) + dx * Math.sin(rotation),
// width: rect.width * node.scaleX(),
// height: rect.height * node.scaleY(),
// rotation: node.rotation()
// };
2019-01-02 04:59:27 +08:00
}
getX() {
return this . _getNodeRect ( ) . x ;
}
getY() {
return this . _getNodeRect ( ) . y ;
}
getWidth() {
return this . _getNodeRect ( ) . width ;
}
getHeight() {
return this . _getNodeRect ( ) . height ;
}
_createElements() {
this . _createBack ( ) ;
ANCHORS_NAMES . forEach (
function ( name ) {
this . _createAnchor ( name ) ;
} . bind ( this )
) ;
this . _createAnchor ( 'rotater' ) ;
}
_createAnchor ( name ) {
var anchor = new Rect ( {
stroke : 'rgb(0, 161, 255)' ,
fill : 'white' ,
strokeWidth : 1 ,
name : name + ' _anchor' ,
dragDistance : 0 ,
2020-04-07 10:51:03 +08:00
draggable : false ,
2019-12-23 23:52:49 +08:00
hitStrokeWidth : TOUCH_DEVICE ? 10 : 'auto'
2019-01-02 04:59:27 +08:00
} ) ;
var self = this ;
anchor . on ( 'mousedown touchstart' , function ( e ) {
self . _handleMouseDown ( e ) ;
} ) ;
2020-04-07 10:51:03 +08:00
// anchor.on('dragstart', function(e) {
// e.cancelBubble = true;
// });
// anchor.on('dragmove', function(e) {
// e.cancelBubble = true;
// });
// anchor.on('dragend', function(e) {
// e.cancelBubble = true;
// });
2019-01-02 04:59:27 +08:00
// add hover styling
2019-05-11 20:56:55 +08:00
anchor . on ( 'mouseenter' , ( ) = > {
2020-04-02 23:01:31 +08:00
var rad = Konva . getAngle ( this . rotation ( ) ) ;
2019-01-02 04:59:27 +08:00
2020-04-02 23:01:31 +08:00
// var scale = this.getNode().getAbsoluteScale();
2019-01-02 04:59:27 +08:00
// If scale.y < 0 xor scale.x < 0 we need to flip (not rotate).
2020-04-02 23:01:31 +08:00
// var isMirrored = false;
var cursor = getCursor ( name , rad , false ) ;
2019-01-02 04:59:27 +08:00
anchor . getStage ( ) . content . style . cursor = cursor ;
2019-05-11 20:56:55 +08:00
this . _cursorChange = true ;
2019-01-02 04:59:27 +08:00
} ) ;
2019-05-11 20:56:55 +08:00
anchor . on ( 'mouseout' , ( ) = > {
if ( ! anchor . getStage ( ) || ! anchor . getParent ( ) ) {
2019-01-02 04:59:27 +08:00
return ;
}
anchor . getStage ( ) . content . style . cursor = '' ;
2019-05-11 20:56:55 +08:00
this . _cursorChange = false ;
2019-01-02 04:59:27 +08:00
} ) ;
this . add ( anchor ) ;
}
_createBack() {
var back = new Shape ( {
name : 'back' ,
width : 0 ,
height : 0 ,
sceneFunc ( ctx ) {
var tr = this . getParent ( ) ;
var padding = tr . padding ( ) ;
ctx . beginPath ( ) ;
ctx . rect (
- padding ,
- padding ,
this . width ( ) + padding * 2 ,
this . height ( ) + padding * 2
) ;
ctx . moveTo ( this . width ( ) / 2 , - padding ) ;
if ( tr . rotateEnabled ( ) ) {
ctx . lineTo (
this . width ( ) / 2 ,
2019-09-18 01:50:28 +08:00
- tr . rotateAnchorOffset ( ) * Util . _sign ( this . height ( ) ) - padding
2019-01-02 04:59:27 +08:00
) ;
}
ctx . fillStrokeShape ( this ) ;
2020-04-02 23:01:31 +08:00
} ,
listening : false
// hitFunc(ctx) {
// var tr = this.getParent();
// var padding = tr.padding();
// ctx.beginPath();
// ctx.rect(
// -padding,
// -padding,
// this.width() + padding * 2,
// this.height() + padding * 2
// );
// ctx.fillStrokeShape(this);
// }
2019-01-02 04:59:27 +08:00
} ) ;
this . add ( back ) ;
}
_handleMouseDown ( e ) {
2019-09-07 03:15:07 +08:00
this . _movingAnchorName = e . target . name ( ) . split ( ' ' ) [ 0 ] ;
2019-01-02 04:59:27 +08:00
// var node = this.getNode();
var attrs = this . _getNodeRect ( ) ;
var width = attrs . width ;
var height = attrs . height ;
2019-09-18 01:50:28 +08:00
2019-01-02 04:59:27 +08:00
var hypotenuse = Math . sqrt ( Math . pow ( width , 2 ) + Math . pow ( height , 2 ) ) ;
2019-05-28 05:16:41 +08:00
this . sin = Math . abs ( height / hypotenuse ) ;
this . cos = Math . abs ( width / hypotenuse ) ;
2019-01-02 04:59:27 +08:00
window . addEventListener ( 'mousemove' , this . _handleMouseMove ) ;
window . addEventListener ( 'touchmove' , this . _handleMouseMove ) ;
window . addEventListener ( 'mouseup' , this . _handleMouseUp , true ) ;
window . addEventListener ( 'touchend' , this . _handleMouseUp , true ) ;
this . _transforming = true ;
2020-04-02 23:01:31 +08:00
var ap = e . target . getAbsolutePosition ( ) ;
var pos = e . target . getStage ( ) . getPointerPosition ( ) ;
this . _anchorDragOffset = {
x : pos.x - ap . x ,
y : pos.y - ap . y
} ;
2020-02-25 09:39:32 +08:00
this . _fire ( 'transformstart' , { evt : e , target : this.getNode ( ) } ) ;
this . getNode ( ) . _fire ( 'transformstart' , { evt : e , target : this.getNode ( ) } ) ;
2019-01-02 04:59:27 +08:00
}
_handleMouseMove ( e ) {
var x , y , newHypotenuse ;
2019-09-07 03:15:07 +08:00
var anchorNode = this . findOne ( '.' + this . _movingAnchorName ) ;
var stage = anchorNode . getStage ( ) ;
2019-01-02 04:59:27 +08:00
2020-02-06 23:14:49 +08:00
stage . setPointersPositions ( e ) ;
2019-01-02 04:59:27 +08:00
2020-04-02 23:01:31 +08:00
const pp = stage . getPointerPosition ( ) ;
var newNodePos = {
x : pp.x - this . _anchorDragOffset . x ,
y : pp.y - this . _anchorDragOffset . y
} ;
2020-04-07 10:51:03 +08:00
const oldAbs = anchorNode . getAbsolutePosition ( ) ;
2020-04-02 23:01:31 +08:00
anchorNode . setAbsolutePosition ( newNodePos ) ;
2020-04-07 10:51:03 +08:00
const newAbs = anchorNode . getAbsolutePosition ( ) ;
if ( oldAbs . x === newAbs . x && oldAbs . y === newAbs . y ) {
return ;
}
2020-04-13 23:12:39 +08:00
// rotater is working very differently, so do it first
if ( this . _movingAnchorName === 'rotater' ) {
var attrs = this . _getNodeRect ( ) ;
x = anchorNode . x ( ) - attrs . width / 2 ;
y = - anchorNode . y ( ) + attrs . height / 2 ;
// hor angle is changed?
let delta = Math . atan2 ( - y , x ) + Math . PI / 2 ;
if ( attrs . height < 0 ) {
delta -= Math . PI ;
}
var oldRotation = Konva . getAngle ( this . rotation ( ) ) ;
const newRotation = oldRotation + delta ;
const tol = Konva . getAngle ( this . rotationSnapTolerance ( ) ) ;
const snappedRot = getSnap ( this . rotationSnaps ( ) , newRotation , tol ) ;
const diff = snappedRot - attrs . rotation ;
const shape = rotateAroundCenter ( attrs , diff ) ;
this . _fitNodesInto ( shape , e ) ;
return ;
}
var padding = 0 ;
// var centeredScaling = this.centeredScaling() || e.altKey;
2020-04-07 10:51:03 +08:00
// if (centeredScaling && this._movingAnchorName.indexOf('left') >= 0) {
// var topLeft = this.findOne('.top-left');
// var bottomRight = this.findOne('.bottom-right');
// var topOffsetX = topLeft.x() + padding;
// var topOffsetY = topLeft.y() + padding;
// var bottomOffsetX = this.getWidth() - bottomRight.x() + padding;
// var bottomOffsetY = this.getHeight() - bottomRight.y() + padding;
// bottomRight.move({
2020-04-13 23:12:39 +08:00
// x: -topOffsetX,
// y: -topOffsetY
2020-04-07 10:51:03 +08:00
// });
// topLeft.move({
// x: bottomOffsetX,
// y: bottomOffsetY
// });
// }
2019-01-02 04:59:27 +08:00
var keepProportion = this . keepRatio ( ) || e . shiftKey ;
2019-09-07 03:15:07 +08:00
if ( this . _movingAnchorName === 'top-left' ) {
2020-04-07 10:51:03 +08:00
// if (centeredScaling) {
// this.findOne('.bottom-right').move({
// x: -anchorNode.x(),
// y: -anchorNode.y()
// });
// }
2019-01-02 04:59:27 +08:00
if ( keepProportion ) {
newHypotenuse = Math . sqrt (
2019-09-18 01:50:28 +08:00
Math . pow (
this . findOne ( '.bottom-right' ) . x ( ) - anchorNode . x ( ) - padding * 2 ,
2
) +
Math . pow (
this . findOne ( '.bottom-right' ) . y ( ) - anchorNode . y ( ) - padding * 2 ,
2
)
2019-01-02 04:59:27 +08:00
) ;
2019-09-07 03:15:07 +08:00
var reverseX =
2019-05-28 05:16:41 +08:00
this . findOne ( '.top-left' ) . x ( ) > this . findOne ( '.bottom-right' ) . x ( )
? - 1
: 1 ;
2019-09-07 03:15:07 +08:00
var reverseY =
this . findOne ( '.top-left' ) . y ( ) > this . findOne ( '.bottom-right' ) . y ( )
? - 1
: 1 ;
x = newHypotenuse * this . cos * reverseX ;
y = newHypotenuse * this . sin * reverseY ;
2019-01-02 04:59:27 +08:00
2019-09-18 01:50:28 +08:00
this . findOne ( '.top-left' ) . x (
this . findOne ( '.bottom-right' ) . x ( ) - x - padding * 2
) ;
this . findOne ( '.top-left' ) . y (
this . findOne ( '.bottom-right' ) . y ( ) - y - padding * 2
) ;
2019-01-02 04:59:27 +08:00
}
2019-09-07 03:15:07 +08:00
} else if ( this . _movingAnchorName === 'top-center' ) {
2020-04-07 10:51:03 +08:00
// if (centeredScaling) {
// this.findOne('.bottom-right').move({
// x: 0,
// y: -anchorNode.y()
// });
// }
2019-09-07 03:15:07 +08:00
this . findOne ( '.top-left' ) . y ( anchorNode . y ( ) ) ;
} else if ( this . _movingAnchorName === 'top-right' ) {
2020-04-13 23:12:39 +08:00
// if (centeredScaling) {
// // this.findOne('.bottom-left').move({
// // x: -(anchorNode.x() - this.width()),
// // y: -anchorNode.y()
// // });
// // this.findOne('.top-left').move({
// // x: -(anchorNode.x() - this.width()),
// // y: anchorNode.y()
// // });
// // this.findOne('.bottom-right').move({
// // x: -(anchorNode.x() - this.width()),
// // y: anchorNode.y()
// // });
// }
2020-04-07 10:51:03 +08:00
// var center = getCenter({
// x
// })
2019-01-02 04:59:27 +08:00
if ( keepProportion ) {
newHypotenuse = Math . sqrt (
2019-09-18 01:50:28 +08:00
Math . pow (
anchorNode . x ( ) - this . findOne ( '.bottom-left' ) . x ( ) - padding * 2 ,
2
) +
Math . pow (
this . findOne ( '.bottom-left' ) . y ( ) - anchorNode . y ( ) - padding * 2 ,
2
)
2019-01-02 04:59:27 +08:00
) ;
2019-09-07 03:15:07 +08:00
var reverseX =
2019-05-28 05:16:41 +08:00
this . findOne ( '.top-right' ) . x ( ) < this . findOne ( '.top-left' ) . x ( )
? - 1
: 1 ;
2019-09-07 03:15:07 +08:00
var reverseY =
this . findOne ( '.top-right' ) . y ( ) > this . findOne ( '.bottom-left' ) . y ( )
? - 1
: 1 ;
x = newHypotenuse * this . cos * reverseX ;
y = newHypotenuse * this . sin * reverseY ;
2019-01-02 04:59:27 +08:00
2019-09-18 01:50:28 +08:00
this . findOne ( '.top-right' ) . x ( x + padding ) ;
this . findOne ( '.top-right' ) . y (
this . findOne ( '.bottom-left' ) . y ( ) - y - padding * 2
) ;
2019-01-02 04:59:27 +08:00
}
2019-09-07 03:15:07 +08:00
var pos = anchorNode . position ( ) ;
2019-01-02 04:59:27 +08:00
this . findOne ( '.top-left' ) . y ( pos . y ) ;
this . findOne ( '.bottom-right' ) . x ( pos . x ) ;
2019-09-07 03:15:07 +08:00
} else if ( this . _movingAnchorName === 'middle-left' ) {
2020-04-07 10:51:03 +08:00
// if (centeredScaling) {
// this.findOne('.bottom-right').move({
// x: -anchorNode.x(),
// y: 0
// });
// }
2019-09-07 03:15:07 +08:00
this . findOne ( '.top-left' ) . x ( anchorNode . x ( ) ) ;
} else if ( this . _movingAnchorName === 'middle-right' ) {
2020-04-07 10:51:03 +08:00
// if (centeredScaling) {
// this.findOne('.top-left').move({
// x: -(anchorNode.x() - this.width()),
// y: 0
// });
// }
2019-09-07 03:15:07 +08:00
this . findOne ( '.bottom-right' ) . x ( anchorNode . x ( ) ) ;
} else if ( this . _movingAnchorName === 'bottom-left' ) {
2020-04-07 10:51:03 +08:00
// if (centeredScaling) {
// this.findOne('.bottom-right').move({
// x: -anchorNode.x(),
// y: -(anchorNode.y() - this.height())
// });
// }
2019-01-02 04:59:27 +08:00
if ( keepProportion ) {
newHypotenuse = Math . sqrt (
2019-09-18 01:50:28 +08:00
Math . pow (
this . findOne ( '.top-right' ) . x ( ) - anchorNode . x ( ) - padding * 2 ,
2
) +
Math . pow (
anchorNode . y ( ) - this . findOne ( '.top-right' ) . y ( ) - padding * 2 ,
2
)
2019-01-02 04:59:27 +08:00
) ;
2019-09-07 03:15:07 +08:00
var reverseX =
2019-05-28 05:16:41 +08:00
this . findOne ( '.top-right' ) . x ( ) < this . findOne ( '.bottom-left' ) . x ( )
? - 1
: 1 ;
2019-09-07 03:15:07 +08:00
var reverseY =
2020-04-11 05:00:25 +08:00
this . findOne ( '.bottom-left' ) . y ( ) < this . findOne ( '.top-right' ) . y ( )
2019-09-07 03:15:07 +08:00
? - 1
: 1 ;
x = newHypotenuse * this . cos * reverseX ;
y = newHypotenuse * this . sin * reverseY ;
2019-01-02 04:59:27 +08:00
2019-09-18 01:50:28 +08:00
this . findOne ( '.bottom-left' ) . x (
this . findOne ( '.top-right' ) . x ( ) - x - padding * 2
) ;
this . findOne ( '.bottom-left' ) . y ( y + padding ) ;
2019-01-02 04:59:27 +08:00
}
2019-09-07 03:15:07 +08:00
pos = anchorNode . position ( ) ;
2019-01-02 04:59:27 +08:00
this . findOne ( '.top-left' ) . x ( pos . x ) ;
this . findOne ( '.bottom-right' ) . y ( pos . y ) ;
2019-09-07 03:15:07 +08:00
} else if ( this . _movingAnchorName === 'bottom-center' ) {
2020-04-07 10:51:03 +08:00
// if (centeredScaling) {
// this.findOne('.top-left').move({
// x: 0,
// y: -(anchorNode.y() - this.height())
// });
// }
2019-09-07 03:15:07 +08:00
this . findOne ( '.bottom-right' ) . y ( anchorNode . y ( ) ) ;
} else if ( this . _movingAnchorName === 'bottom-right' ) {
2020-04-07 10:51:03 +08:00
// if (centeredScaling) {
// this.findOne('.top-left').move({
// x: -(anchorNode.x() - this.width()),
// y: -(anchorNode.y() - this.height())
// });
// }
2019-01-02 04:59:27 +08:00
if ( keepProportion ) {
newHypotenuse = Math . sqrt (
2019-09-18 01:50:28 +08:00
Math . pow ( this . findOne ( '.bottom-right' ) . x ( ) - padding , 2 ) +
Math . pow ( this . findOne ( '.bottom-right' ) . y ( ) - padding , 2 )
2019-01-02 04:59:27 +08:00
) ;
2019-09-07 03:15:07 +08:00
var reverseX =
2019-05-28 05:16:41 +08:00
this . findOne ( '.top-left' ) . x ( ) > this . findOne ( '.bottom-right' ) . x ( )
? - 1
: 1 ;
2019-09-07 03:15:07 +08:00
var reverseY =
this . findOne ( '.top-left' ) . y ( ) > this . findOne ( '.bottom-right' ) . y ( )
? - 1
: 1 ;
x = newHypotenuse * this . cos * reverseX ;
y = newHypotenuse * this . sin * reverseY ;
2019-01-02 04:59:27 +08:00
2019-09-18 01:50:28 +08:00
this . findOne ( '.bottom-right' ) . x ( x + padding ) ;
this . findOne ( '.bottom-right' ) . y ( y + padding ) ;
2019-01-02 04:59:27 +08:00
}
} else {
console . error (
new Error (
2019-09-07 03:15:07 +08:00
'Wrong position argument of selection resizer: ' +
this . _movingAnchorName
2019-01-02 04:59:27 +08:00
)
) ;
}
2019-09-07 03:15:07 +08:00
if ( this . _movingAnchorName === 'rotater' ) {
2019-01-02 04:59:27 +08:00
return ;
}
var centeredScaling = this . centeredScaling ( ) || e . altKey ;
if ( centeredScaling ) {
var topLeft = this . findOne ( '.top-left' ) ;
var bottomRight = this . findOne ( '.bottom-right' ) ;
2019-09-18 01:50:28 +08:00
var topOffsetX = topLeft . x ( ) + padding ;
var topOffsetY = topLeft . y ( ) + padding ;
2019-01-02 04:59:27 +08:00
2019-09-18 01:50:28 +08:00
var bottomOffsetX = this . getWidth ( ) - bottomRight . x ( ) + padding ;
var bottomOffsetY = this . getHeight ( ) - bottomRight . y ( ) + padding ;
2019-01-02 04:59:27 +08:00
2020-04-13 23:12:39 +08:00
if ( Math . abs ( topOffsetY ) > 10 ) {
debugger ;
}
2019-01-02 04:59:27 +08:00
bottomRight . move ( {
x : - topOffsetX ,
y : - topOffsetY
} ) ;
topLeft . move ( {
x : bottomOffsetX ,
y : bottomOffsetY
} ) ;
}
2020-04-07 10:51:03 +08:00
var absPos = this . findOne ( '.top-left' ) . getAbsolutePosition ( ) ;
2019-09-18 01:50:28 +08:00
2019-01-02 04:59:27 +08:00
x = absPos . x ;
y = absPos . y ;
var width =
this . findOne ( '.bottom-right' ) . x ( ) - this . findOne ( '.top-left' ) . x ( ) ;
var height =
this . findOne ( '.bottom-right' ) . y ( ) - this . findOne ( '.top-left' ) . y ( ) ;
2020-04-02 23:01:31 +08:00
this . _fitNodesInto (
2019-01-02 04:59:27 +08:00
{
2020-04-07 10:51:03 +08:00
x : x ,
y : y ,
2019-01-02 04:59:27 +08:00
width : width ,
2020-04-02 23:01:31 +08:00
height : height ,
rotation : Konva.getAngle ( this . rotation ( ) )
2019-01-02 04:59:27 +08:00
} ,
e
) ;
}
_handleMouseUp ( e ) {
this . _removeEvents ( e ) ;
}
2020-04-02 23:01:31 +08:00
getAbsoluteTransform() {
return this . getTransform ( ) ;
}
2019-01-02 04:59:27 +08:00
_removeEvents ( e ? ) {
if ( this . _transforming ) {
this . _transforming = false ;
window . removeEventListener ( 'mousemove' , this . _handleMouseMove ) ;
window . removeEventListener ( 'touchmove' , this . _handleMouseMove ) ;
window . removeEventListener ( 'mouseup' , this . _handleMouseUp , true ) ;
window . removeEventListener ( 'touchend' , this . _handleMouseUp , true ) ;
var node = this . getNode ( ) ;
2020-02-25 09:45:50 +08:00
this . _fire ( 'transformend' , { evt : e , target : node } ) ;
2020-03-03 22:09:21 +08:00
2019-01-02 04:59:27 +08:00
if ( node ) {
2020-03-03 22:09:21 +08:00
node . fire ( 'transformend' , { evt : e , target : node } ) ;
2019-01-02 04:59:27 +08:00
}
2020-03-03 22:09:21 +08:00
this . _movingAnchorName = null ;
2019-01-02 04:59:27 +08:00
}
}
2020-04-02 23:01:31 +08:00
_fitNodesInto ( newAttrs , evt ) {
var oldAttrs = this . _getNodeRect ( ) ;
2019-01-02 04:59:27 +08:00
var boundBoxFunc = this . boundBoxFunc ( ) ;
if ( boundBoxFunc ) {
newAttrs = boundBoxFunc . call ( this , oldAttrs , newAttrs ) ;
}
2020-04-07 10:51:03 +08:00
const minSize = 1 ;
if ( Util . _inRange ( newAttrs . width , - this . padding ( ) * 2 - minSize , minSize ) ) {
2020-04-02 23:01:31 +08:00
this . update ( ) ;
return ;
}
2020-04-07 10:51:03 +08:00
if (
Util . _inRange ( newAttrs . height , - this . padding ( ) * 2 - minSize , minSize )
) {
2020-04-02 23:01:31 +08:00
this . update ( ) ;
return ;
2019-01-02 04:59:27 +08:00
}
2020-04-07 10:51:03 +08:00
// if (newAttrs.width < 0) {
// debugger;
// }
2020-04-02 23:01:31 +08:00
const an = this . _movingAnchorName ;
2020-04-07 10:51:03 +08:00
const allowNegativeScale = true ;
var t = new Transform ( ) ;
t . rotate ( Konva . getAngle ( this . rotation ( ) ) ) ;
2020-04-02 23:01:31 +08:00
if ( an && newAttrs . width < 0 && an . indexOf ( 'left' ) >= 0 ) {
2020-04-07 10:51:03 +08:00
const offset = t . point ( {
x : - this . padding ( ) * 2 ,
y : 0
} ) ;
newAttrs . x += offset . x ;
newAttrs . y += offset . y ;
newAttrs . width += this . padding ( ) * 2 ;
2020-04-02 23:01:31 +08:00
this . _movingAnchorName = an . replace ( 'left' , 'right' ) ;
2020-04-07 10:51:03 +08:00
this . _anchorDragOffset . x -= offset . x ;
this . _anchorDragOffset . y -= offset . y ;
if ( ! allowNegativeScale ) {
this . update ( ) ;
return ;
}
2020-04-02 23:01:31 +08:00
} else if ( an && newAttrs . width < 0 && an . indexOf ( 'right' ) >= 0 ) {
2020-04-07 10:51:03 +08:00
const offset = t . point ( {
x : this.padding ( ) * 2 ,
y : 0
} ) ;
2020-04-02 23:01:31 +08:00
this . _movingAnchorName = an . replace ( 'right' , 'left' ) ;
2020-04-07 10:51:03 +08:00
this . _anchorDragOffset . x -= offset . x ;
this . _anchorDragOffset . y -= offset . y ;
newAttrs . width += this . padding ( ) * 2 ;
if ( ! allowNegativeScale ) {
this . update ( ) ;
return ;
}
}
if ( an && newAttrs . height < 0 && an . indexOf ( 'top' ) >= 0 ) {
const offset = t . point ( {
x : 0 ,
y : - this . padding ( ) * 2
} ) ;
newAttrs . x += offset . x ;
newAttrs . y += offset . y ;
2020-04-02 23:01:31 +08:00
this . _movingAnchorName = an . replace ( 'top' , 'bottom' ) ;
2020-04-07 10:51:03 +08:00
this . _anchorDragOffset . x -= offset . x ;
this . _anchorDragOffset . y -= offset . y ;
newAttrs . height += this . padding ( ) * 2 ;
if ( ! allowNegativeScale ) {
this . update ( ) ;
return ;
}
2020-04-02 23:01:31 +08:00
} else if ( an && newAttrs . height < 0 && an . indexOf ( 'bottom' ) >= 0 ) {
2020-04-07 10:51:03 +08:00
const offset = t . point ( {
x : 0 ,
y : this.padding ( ) * 2
} ) ;
2020-04-02 23:01:31 +08:00
this . _movingAnchorName = an . replace ( 'bottom' , 'top' ) ;
2020-04-07 10:51:03 +08:00
this . _anchorDragOffset . x -= offset . x ;
this . _anchorDragOffset . y -= offset . y ;
newAttrs . height += this . padding ( ) * 2 ;
if ( ! allowNegativeScale ) {
this . update ( ) ;
return ;
}
2020-04-02 23:01:31 +08:00
}
this . _nodes . forEach ( node = > {
var oldRect = this . __getNodeShape ( node , 0 ) ;
var newRect = transformAndRotateShape ( oldRect , oldAttrs , newAttrs ) ;
this . _fitNodeInto ( node , newRect , evt ) ;
} ) ;
this . rotation ( Util . _getRotation ( newAttrs . rotation ) ) ;
this . _resetTransformCache ( ) ;
this . update ( ) ;
this . getLayer ( ) . batchDraw ( ) ;
}
_fitNodeInto ( node : Node , newAttrs , evt ) {
const parentRot = Konva . getAngle ( node . getParent ( ) . getAbsoluteRotation ( ) ) ;
node . rotation ( Util . _getRotation ( newAttrs . rotation - parentRot ) ) ;
2019-01-02 04:59:27 +08:00
var pure = node . getClientRect ( {
skipTransform : true ,
skipShadow : true ,
skipStroke : this.ignoreStroke ( )
} ) ;
2020-04-02 23:01:31 +08:00
var padding = 0 ;
const parentTransform = node
. getParent ( )
. getAbsoluteTransform ( )
. copy ( ) ;
parentTransform . invert ( ) ;
const invertedPoint = parentTransform . point ( {
x : newAttrs.x ,
y : newAttrs.y
} ) ;
newAttrs . x = invertedPoint . x ;
newAttrs . y = invertedPoint . y ;
var absScale = node . getParent ( ) . getAbsoluteScale ( ) ;
pure . width *= absScale . x ;
pure . height *= absScale . y ;
// pure.x -= absPos.x;
// pure.y -= absPos.y;
// newAttrs.x = (newAttrs.x - absPos.x) / absScale.x;
// newAttrs.y = (newAttrs.y - absPos.y) / absScale.y;
2019-10-21 23:12:10 +08:00
var scaleX = pure . width ? ( newAttrs . width - padding * 2 ) / pure.width : 1 ;
var scaleY = pure . height
? ( newAttrs . height - padding * 2 ) / pure . height
: 1 ;
2019-01-02 04:59:27 +08:00
2019-03-07 11:19:32 +08:00
var rotation = Konva . getAngle ( node . rotation ( ) ) ;
2019-01-02 04:59:27 +08:00
var dx = pure . x * scaleX - padding - node . offsetX ( ) * scaleX ;
var dy = pure . y * scaleY - padding - node . offsetY ( ) * scaleY ;
2020-04-02 23:01:31 +08:00
node . setAttrs ( {
2019-01-02 04:59:27 +08:00
scaleX : scaleX ,
scaleY : scaleY ,
x : newAttrs.x - ( dx * Math . cos ( rotation ) + dy * Math . sin ( - rotation ) ) ,
y : newAttrs.y - ( dy * Math . cos ( rotation ) + dx * Math . sin ( rotation ) )
} ) ;
2020-04-02 23:01:31 +08:00
this . _fire ( 'transform' , { evt : evt , target : node } ) ;
node . _fire ( 'transform' , { evt : evt , target : node } ) ;
2019-01-02 04:59:27 +08:00
}
/ * *
* force update of Konva . Transformer .
* Use it when you updated attached Konva . Group and now you need to reset transformer size
* @method
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # forceUpdate
2019-01-02 04:59:27 +08:00
* /
forceUpdate() {
this . _resetTransformCache ( ) ;
this . update ( ) ;
}
update() {
var attrs = this . _getNodeRect ( ) ;
2020-04-02 23:01:31 +08:00
this . rotation ( Util . _getRotation ( attrs . rotation ) ) ;
2019-01-02 04:59:27 +08:00
var width = attrs . width ;
var height = attrs . height ;
var enabledAnchors = this . enabledAnchors ( ) ;
var resizeEnabled = this . resizeEnabled ( ) ;
var padding = this . padding ( ) ;
var anchorSize = this . anchorSize ( ) ;
this . find ( '._anchor' ) . each ( node = >
node . setAttrs ( {
width : anchorSize ,
height : anchorSize ,
offsetX : anchorSize / 2 ,
offsetY : anchorSize / 2 ,
stroke : this.anchorStroke ( ) ,
strokeWidth : this.anchorStrokeWidth ( ) ,
fill : this.anchorFill ( ) ,
cornerRadius : this.anchorCornerRadius ( )
} )
) ;
this . findOne ( '.top-left' ) . setAttrs ( {
2020-04-02 23:01:31 +08:00
x : 0 ,
y : 0 ,
offsetX : anchorSize / 2 + padding ,
offsetY : anchorSize / 2 + padding ,
2019-01-02 04:59:27 +08:00
visible : resizeEnabled && enabledAnchors . indexOf ( 'top-left' ) >= 0
} ) ;
this . findOne ( '.top-center' ) . setAttrs ( {
x : width / 2 ,
2020-04-02 23:01:31 +08:00
y : 0 ,
offsetY : anchorSize / 2 + padding ,
2019-01-02 04:59:27 +08:00
visible : resizeEnabled && enabledAnchors . indexOf ( 'top-center' ) >= 0
} ) ;
this . findOne ( '.top-right' ) . setAttrs ( {
2020-04-02 23:01:31 +08:00
x : width ,
y : 0 ,
offsetX : anchorSize / 2 - padding ,
offsetY : anchorSize / 2 + padding ,
2019-01-02 04:59:27 +08:00
visible : resizeEnabled && enabledAnchors . indexOf ( 'top-right' ) >= 0
} ) ;
this . findOne ( '.middle-left' ) . setAttrs ( {
2020-04-02 23:01:31 +08:00
x : 0 ,
2019-01-02 04:59:27 +08:00
y : height / 2 ,
2020-04-02 23:01:31 +08:00
offsetX : anchorSize / 2 + padding ,
2019-01-02 04:59:27 +08:00
visible : resizeEnabled && enabledAnchors . indexOf ( 'middle-left' ) >= 0
} ) ;
this . findOne ( '.middle-right' ) . setAttrs ( {
2020-04-02 23:01:31 +08:00
x : width ,
2019-01-02 04:59:27 +08:00
y : height / 2 ,
2020-04-02 23:01:31 +08:00
offsetX : anchorSize / 2 - padding ,
2019-01-02 04:59:27 +08:00
visible : resizeEnabled && enabledAnchors . indexOf ( 'middle-right' ) >= 0
} ) ;
this . findOne ( '.bottom-left' ) . setAttrs ( {
2020-04-02 23:01:31 +08:00
x : 0 ,
y : height ,
offsetX : anchorSize / 2 + padding ,
offsetY : anchorSize / 2 - padding ,
2019-01-02 04:59:27 +08:00
visible : resizeEnabled && enabledAnchors . indexOf ( 'bottom-left' ) >= 0
} ) ;
this . findOne ( '.bottom-center' ) . setAttrs ( {
x : width / 2 ,
2020-04-02 23:01:31 +08:00
y : height ,
offsetY : anchorSize / 2 - padding ,
2019-01-02 04:59:27 +08:00
visible : resizeEnabled && enabledAnchors . indexOf ( 'bottom-center' ) >= 0
} ) ;
this . findOne ( '.bottom-right' ) . setAttrs ( {
2020-04-02 23:01:31 +08:00
x : width ,
y : height ,
offsetX : anchorSize / 2 - padding ,
offsetY : anchorSize / 2 - padding ,
2019-01-02 04:59:27 +08:00
visible : resizeEnabled && enabledAnchors . indexOf ( 'bottom-right' ) >= 0
} ) ;
this . findOne ( '.rotater' ) . setAttrs ( {
x : width / 2 ,
2020-04-11 05:00:25 +08:00
y : - this . rotateAnchorOffset ( ) * Util . _sign ( height ) - padding ,
2019-01-02 04:59:27 +08:00
visible : this.rotateEnabled ( )
} ) ;
this . findOne ( '.back' ) . setAttrs ( {
2020-04-11 05:00:25 +08:00
width : width ,
height : height ,
2019-01-02 04:59:27 +08:00
visible : this.borderEnabled ( ) ,
stroke : this.borderStroke ( ) ,
strokeWidth : this.borderStrokeWidth ( ) ,
dash : this.borderDash ( )
} ) ;
}
/ * *
* determine if transformer is in active transform
* @method
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # isTransforming
2019-01-02 04:59:27 +08:00
* @returns { Boolean }
* /
isTransforming() {
return this . _transforming ;
}
/ * *
* Stop active transform action
* @method
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # stopTransform
2019-01-02 04:59:27 +08:00
* @returns { Boolean }
* /
stopTransform() {
if ( this . _transforming ) {
this . _removeEvents ( ) ;
2019-09-07 03:15:07 +08:00
var anchorNode = this . findOne ( '.' + this . _movingAnchorName ) ;
if ( anchorNode ) {
anchorNode . stopDrag ( ) ;
2019-01-02 04:59:27 +08:00
}
}
}
destroy() {
if ( this . getStage ( ) && this . _cursorChange ) {
this . getStage ( ) . content . style . cursor = '' ;
}
Group . prototype . destroy . call ( this ) ;
this . detach ( ) ;
this . _removeEvents ( ) ;
return this ;
}
// do not work as a container
// we will recreate inner nodes manually
toObject() {
return Node . prototype . toObject . call ( this ) ;
}
2020-04-02 23:01:31 +08:00
nodes : GetSet < Node [ ] , this > ;
2019-01-02 04:59:27 +08:00
enabledAnchors : GetSet < string [ ] , this > ;
rotationSnaps : GetSet < number [ ] , this > ;
anchorSize : GetSet < number , this > ;
resizeEnabled : GetSet < boolean , this > ;
rotateEnabled : GetSet < boolean , this > ;
rotateAnchorOffset : GetSet < number , this > ;
2020-02-29 05:38:18 +08:00
rotationSnapTolerance : GetSet < number , this > ;
2019-01-02 04:59:27 +08:00
padding : GetSet < number , this > ;
borderEnabled : GetSet < boolean , this > ;
borderStroke : GetSet < string , this > ;
borderStrokeWidth : GetSet < number , this > ;
borderDash : GetSet < number [ ] , this > ;
anchorFill : GetSet < string , this > ;
anchorStroke : GetSet < string , this > ;
anchorCornerRadius : GetSet < number , this > ;
anchorStrokeWidth : GetSet < number , this > ;
keepRatio : GetSet < boolean , this > ;
centeredScaling : GetSet < boolean , this > ;
ignoreStroke : GetSet < boolean , this > ;
2019-01-28 04:43:50 +08:00
boundBoxFunc : GetSet < ( oldBox : IRect , newBox : IRect ) = > IRect , this > ;
2019-01-02 04:59:27 +08:00
}
2019-01-06 16:01:20 +08:00
function validateAnchors ( val ) {
2019-01-02 04:59:27 +08:00
if ( ! ( val instanceof Array ) ) {
Util . warn ( 'enabledAnchors value should be an array' ) ;
}
if ( val instanceof Array ) {
val . forEach ( function ( name ) {
if ( ANCHORS_NAMES . indexOf ( name ) === - 1 ) {
Util . warn (
'Unknown anchor name: ' +
name +
'. Available names are: ' +
ANCHORS_NAMES . join ( ', ' )
) ;
}
} ) ;
}
return val || [ ] ;
}
2019-01-06 16:01:20 +08:00
Transformer . prototype . className = 'Transformer' ;
2019-02-27 21:06:04 +08:00
_registerNode ( Transformer ) ;
2019-01-06 16:01:20 +08:00
2019-01-02 04:59:27 +08:00
/ * *
* get / set enabled handlers
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # enabledAnchors
2019-01-02 04:59:27 +08:00
* @method
* @param { Array } array
* @returns { Array }
* @example
* // get list of handlers
* var enabledAnchors = transformer . enabledAnchors ( ) ;
*
* // set handlers
* transformer . enabledAnchors ( [ 'top-left' , 'top-center' , 'top-right' , 'middle-right' , 'middle-left' , 'bottom-left' , 'bottom-center' , 'bottom-right' ] ) ;
* /
Factory . addGetterSetter (
Transformer ,
'enabledAnchors' ,
ANCHORS_NAMES ,
2019-01-06 16:01:20 +08:00
validateAnchors
2019-01-02 04:59:27 +08:00
) ;
/ * *
* get / set resize ability . If false it will automatically hide resizing handlers
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # resizeEnabled
2019-01-02 04:59:27 +08:00
* @method
* @param { Array } array
* @returns { Array }
* @example
* // get
* var resizeEnabled = transformer . resizeEnabled ( ) ;
*
* // set
* transformer . resizeEnabled ( false ) ;
* /
Factory . addGetterSetter ( Transformer , 'resizeEnabled' , true ) ;
/ * *
* get / set anchor size . Default is 10
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # validateAnchors
2019-01-02 04:59:27 +08:00
* @method
* @param { Number } 10
* @returns { Number }
* @example
* // get
* var anchorSize = transformer . anchorSize ( ) ;
*
* // set
* transformer . anchorSize ( 20 )
* /
2019-02-25 01:06:04 +08:00
Factory . addGetterSetter ( Transformer , 'anchorSize' , 10 , getNumberValidator ( ) ) ;
2019-01-02 04:59:27 +08:00
/ * *
* get / set ability to rotate .
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # rotateEnabled
2019-01-02 04:59:27 +08:00
* @method
* @param { Boolean } enabled
* @returns { Boolean }
* @example
* // get
* var rotateEnabled = transformer . rotateEnabled ( ) ;
*
* // set
* transformer . rotateEnabled ( false ) ;
* /
Factory . addGetterSetter ( Transformer , 'rotateEnabled' , true ) ;
/ * *
* get / set rotation snaps angles .
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # rotationSnaps
2019-01-02 04:59:27 +08:00
* @method
* @param { Array } array
* @returns { Array }
* @example
* // get
* var rotationSnaps = transformer . rotationSnaps ( ) ;
*
* // set
* transformer . rotationSnaps ( [ 0 , 90 , 180 , 270 ] ) ;
* /
Factory . addGetterSetter ( Transformer , 'rotationSnaps' , [ ] ) ;
/ * *
* get / set distance for rotation handler
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # rotateAnchorOffset
2019-01-02 04:59:27 +08:00
* @method
* @param { Number } offset
* @returns { Number }
* @example
* // get
* var rotateAnchorOffset = transformer . rotateAnchorOffset ( ) ;
*
* // set
* transformer . rotateAnchorOffset ( 100 ) ;
* /
Factory . addGetterSetter (
Transformer ,
'rotateAnchorOffset' ,
50 ,
2019-02-25 01:06:04 +08:00
getNumberValidator ( )
2019-01-02 04:59:27 +08:00
) ;
2020-02-29 05:38:18 +08:00
/ * *
* get / set distance for rotation tolerance
* @name Konva . Transformer # rotationSnapTolerance
* @method
* @param { Number } tolerance
* @returns { Number }
* @example
* // get
* var rotationSnapTolerance = transformer . rotationSnapTolerance ( ) ;
*
* // set
* transformer . rotationSnapTolerance ( 100 ) ;
* /
Factory . addGetterSetter (
Transformer ,
'rotationSnapTolerance' ,
5 ,
getNumberValidator ( )
) ;
2019-01-02 04:59:27 +08:00
/ * *
* get / set visibility of border
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # borderEnabled
2019-01-02 04:59:27 +08:00
* @method
* @param { Boolean } enabled
* @returns { Boolean }
* @example
* // get
* var borderEnabled = transformer . borderEnabled ( ) ;
*
* // set
* transformer . borderEnabled ( false ) ;
* /
Factory . addGetterSetter ( Transformer , 'borderEnabled' , true ) ;
/ * *
* get / set anchor stroke color
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # anchorStroke
2019-01-02 04:59:27 +08:00
* @method
* @param { Boolean } enabled
* @returns { Boolean }
* @example
* // get
* var anchorStroke = transformer . anchorStroke ( ) ;
*
* // set
* transformer . anchorStroke ( 'red' ) ;
* /
Factory . addGetterSetter ( Transformer , 'anchorStroke' , 'rgb(0, 161, 255)' ) ;
/ * *
* get / set anchor stroke width
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # anchorStrokeWidth
2019-01-02 04:59:27 +08:00
* @method
* @param { Boolean } enabled
* @returns { Boolean }
* @example
* // get
* var anchorStrokeWidth = transformer . anchorStrokeWidth ( ) ;
*
* // set
* transformer . anchorStrokeWidth ( 3 ) ;
* /
Factory . addGetterSetter (
Transformer ,
'anchorStrokeWidth' ,
1 ,
2019-02-25 01:06:04 +08:00
getNumberValidator ( )
2019-01-02 04:59:27 +08:00
) ;
/ * *
* get / set anchor fill color
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # anchorFill
2019-01-02 04:59:27 +08:00
* @method
* @param { Boolean } enabled
* @returns { Boolean }
* @example
* // get
* var anchorFill = transformer . anchorFill ( ) ;
*
* // set
* transformer . anchorFill ( 'red' ) ;
* /
Factory . addGetterSetter ( Transformer , 'anchorFill' , 'white' ) ;
/ * *
* get / set anchor corner radius
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # anchorCornerRadius
2019-01-02 04:59:27 +08:00
* @method
* @param { Number } enabled
* @returns { Number }
* @example
* // get
* var anchorCornerRadius = transformer . anchorCornerRadius ( ) ;
*
* // set
* transformer . anchorCornerRadius ( 3 ) ;
* /
Factory . addGetterSetter (
Transformer ,
'anchorCornerRadius' ,
0 ,
2019-02-25 01:06:04 +08:00
getNumberValidator ( )
2019-01-02 04:59:27 +08:00
) ;
/ * *
* get / set border stroke color
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # borderStroke
2019-01-02 04:59:27 +08:00
* @method
* @param { Boolean } enabled
* @returns { Boolean }
* @example
* // get
* var borderStroke = transformer . borderStroke ( ) ;
*
* // set
* transformer . borderStroke ( 'red' ) ;
* /
Factory . addGetterSetter ( Transformer , 'borderStroke' , 'rgb(0, 161, 255)' ) ;
/ * *
* get / set border stroke width
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # borderStrokeWidth
2019-01-02 04:59:27 +08:00
* @method
* @param { Boolean } enabled
* @returns { Boolean }
* @example
* // get
* var borderStrokeWidth = transformer . borderStrokeWidth ( ) ;
*
* // set
* transformer . borderStrokeWidth ( 3 ) ;
* /
Factory . addGetterSetter (
Transformer ,
'borderStrokeWidth' ,
1 ,
2019-02-25 01:06:04 +08:00
getNumberValidator ( )
2019-01-02 04:59:27 +08:00
) ;
/ * *
* get / set border dash array
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # borderDash
2019-01-02 04:59:27 +08:00
* @method
* @param { Boolean } enabled
* @returns { Boolean }
* @example
* // get
* var borderDash = transformer . borderDash ( ) ;
*
* // set
* transformer . borderDash ( [ 2 , 2 ] ) ;
* /
Factory . addGetterSetter ( Transformer , 'borderDash' ) ;
/ * *
* get / set should we keep ratio while resize anchors at corners
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # keepRatio
2019-01-02 04:59:27 +08:00
* @method
* @param { Boolean } keepRatio
* @returns { Boolean }
* @example
* // get
* var keepRatio = transformer . keepRatio ( ) ;
*
* // set
* transformer . keepRatio ( false ) ;
* /
Factory . addGetterSetter ( Transformer , 'keepRatio' , true ) ;
/ * *
* get / set should we resize relative to node ' s center ?
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # centeredScaling
2019-01-02 04:59:27 +08:00
* @method
* @param { Boolean } centeredScaling
* @returns { Boolean }
* @example
* // get
* var centeredScaling = transformer . centeredScaling ( ) ;
*
* // set
* transformer . centeredScaling ( true ) ;
* /
Factory . addGetterSetter ( Transformer , 'centeredScaling' , false ) ;
/ * *
* get / set should we think about stroke while resize ? Good to use when a shape has strokeScaleEnabled = false
* default is false
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # ignoreStroke
2019-01-02 04:59:27 +08:00
* @method
* @param { Boolean } ignoreStroke
* @returns { Boolean }
* @example
* // get
* var ignoreStroke = transformer . ignoreStroke ( ) ;
*
* // set
* transformer . ignoreStroke ( true ) ;
* /
Factory . addGetterSetter ( Transformer , 'ignoreStroke' , false ) ;
/ * *
* get / set padding
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # padding
2019-01-02 04:59:27 +08:00
* @method
* @param { Number } padding
* @returns { Number }
* @example
* // get
* var padding = transformer . padding ( ) ;
*
* // set
* transformer . padding ( 10 ) ;
* /
2019-02-25 01:06:04 +08:00
Factory . addGetterSetter ( Transformer , 'padding' , 0 , getNumberValidator ( ) ) ;
2019-01-02 04:59:27 +08:00
2019-01-06 16:01:20 +08:00
/ * *
* get / set attached node of the Transformer . Transformer will adapt to its size and listen to its events
* @method
* @name Konva . Transformer # Konva . Transformer # node
* @returns { Konva . Node }
* @example
* // get
* const node = transformer . node ( ) ;
*
* // set
* transformer . node ( shape ) ;
* /
Factory . addGetterSetter ( Transformer , 'node' ) ;
2019-01-02 04:59:27 +08:00
2020-04-02 23:01:31 +08:00
/ * *
* get / set attached nodes of the Transformer . Transformer will adapt to their size and listen to their events
* @method
* @name Konva . Transformer # Konva . Transformer # node
* @returns { Konva . Node }
* @example
* // get
* const nodes = transformer . nodes ( ) ;
*
* // set
* transformer . nodes ( [ rect , circle ] ) ;
* /
Factory . addGetterSetter ( Transformer , 'nodes' ) ;
2019-01-02 04:59:27 +08:00
/ * *
* get / set bounding box function
2019-01-06 16:01:20 +08:00
* @name Konva . Transformer # boundBoxFunc
2019-01-02 04:59:27 +08:00
* @method
* @param { Function } func
* @returns { Function }
* @example
* // get
* var boundBoxFunc = transformer . boundBoxFunc ( ) ;
*
* // set
* transformer . boundBoxFunc ( function ( oldBox , newBox ) {
* if ( newBox . width > 200 ) {
* return oldBox ;
* }
* return newBox ;
* } ) ;
* /
Factory . addGetterSetter ( Transformer , 'boundBoxFunc' ) ;
Factory . backCompat ( Transformer , {
lineEnabled : 'borderEnabled' ,
rotateHandlerOffset : 'rotateAnchorOffset' ,
enabledHandlers : 'enabledAnchors'
} ) ;
Collection . mapMethods ( Transformer ) ;