konva/src/shapes/Transformer.js

1137 lines
32 KiB
JavaScript
Raw Normal View History

2017-12-19 15:31:36 +08:00
(function(Konva) {
'use strict';
2018-02-05 13:10:05 +08:00
var ATTR_CHANGE_LIST = [
'resizeEnabledChange',
2018-08-10 11:22:08 +08:00
'rotateAnchorOffsetChange',
'enabledAnchorsChange',
'anchorSizeChange',
'borderEnabledChange',
'borderStrokeChange',
'borderStrokeWidthChange',
'anchorStrokeChange',
2018-08-10 14:19:56 +08:00
'anchorStrokeWidthChange',
'anchorFillChange'
2018-02-05 13:10:05 +08:00
].join(' ');
2018-03-15 13:11:58 +08:00
var NODE_RECT = 'nodeRect';
2018-02-05 13:10:05 +08:00
var TRANSFORM_CHANGE_STR = [
'xChange.resizer',
'yChange.resizer',
2018-02-09 15:28:19 +08:00
'widthChange.resizer',
'heightChange.resizer',
2018-02-05 13:10:05 +08:00
'scaleXChange.resizer',
'scaleYChange.resizer',
'skewXChange.resizer',
'skewYChange.resizer',
'rotationChange.resizer',
'offsetXChange.resizer',
'offsetYChange.resizer',
'transformsEnabledChange.resizer'
].join(' ');
var REDRAW_CHANGE_STR = [
'widthChange.resizer',
'heightChange.resizer',
'scaleXChange.resizer',
'scaleYChange.resizer',
'skewXChange.resizer',
'skewYChange.resizer',
'rotationChange.resizer',
'offsetXChange.resizer',
'offsetYChange.resizer'
].join(' ');
2018-04-24 12:08:02 +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
};
function getCursor(anchorName, rad, isMirrored) {
2018-03-07 12:40:38 +08:00
if (anchorName === 'rotater') {
return 'crosshair';
}
2018-04-24 12:08:02 +08:00
rad += Konva.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;
}
2018-03-21 16:21:55 +08:00
var angle = (Konva.Util._radToDeg(rad) % 360 + 360) % 360;
2018-03-07 12:40:38 +08:00
if (
Konva.Util._inRange(angle, 315 + 22.5, 360) ||
Konva.Util._inRange(angle, 0, 22.5)
) {
// TOP
return 'ns-resize';
} else if (Konva.Util._inRange(angle, 45 - 22.5, 45 + 22.5)) {
// TOP - RIGHT
return 'nesw-resize';
} else if (Konva.Util._inRange(angle, 90 - 22.5, 90 + 22.5)) {
// RIGHT
return 'ew-resize';
} else if (Konva.Util._inRange(angle, 135 - 22.5, 135 + 22.5)) {
// BOTTOM - RIGHT
return 'nwse-resize';
} else if (Konva.Util._inRange(angle, 180 - 22.5, 180 + 22.5)) {
// BOTTOM
return 'ns-resize';
} else if (Konva.Util._inRange(angle, 225 - 22.5, 225 + 22.5)) {
// BOTTOM - LEFT
return 'nesw-resize';
} else if (Konva.Util._inRange(angle, 270 - 22.5, 270 + 22.5)) {
// RIGHT
return 'ew-resize';
} else if (Konva.Util._inRange(angle, 315 - 22.5, 315 + 22.5)) {
// BOTTOM - RIGHT
return 'nwse-resize';
} else {
// how can we can there?
// TODO: throw error
2018-03-21 16:21:55 +08:00
Konva.Util.error(
'Transformer has unknown angle for cursor detection: ' + angle
);
2018-03-07 12:40:38 +08:00
return 'pointer';
}
}
2018-03-15 13:11:58 +08:00
/**
* Transformer constructor. Transformer is a special type of group that allow you transform Konva
2018-08-10 14:19:56 +08:00
* 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.
2018-03-15 13:11:58 +08:00
* @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 []
2018-08-10 11:22:08 +08:00
* @param {Number} [config.rotateAnchorOffset] Default is 50
2018-03-16 09:55:02 +08:00
* @param {Number} [config.padding] Default is 0
2018-09-09 03:39:23 +08:00
* @param {Boolean} [config.borderEnabled] Should we draw border? Default is true
2018-08-10 11:22:08 +08:00
* @param {String} [config.borderStroke] Border stroke color
* @param {Number} [config.borderStrokeWidth] Border stroke size
* @param {Array} [config.borderDash] Array for border dash.
2018-08-10 14:19:56 +08:00
* @param {String} [config.anchorFill] Anchor fill color
* @param {String} [config.anchorStroke] Anchor stroke color
2018-08-10 11:22:08 +08:00
* @param {Number} [config.anchorStrokeWidth] Anchor stroke size
* @param {Number} [config.anchorSize] Default is 10
2018-03-15 13:11:58 +08:00
* @param {Boolean} [config.keepRatio] Should we keep ratio when we are moving edges? Default is true
2018-08-10 11:22:08 +08:00
* @param {Array} [config.enabledAnchors] Array of names of enabled handles
2018-06-15 15:50:36 +08:00
* @param {Function} [config.boundBoxFunc] Bounding box function
2018-03-15 13:11:58 +08:00
* @example
* var transformer = new Konva.Transformer({
* node: rectangle,
2018-08-10 11:22:08 +08:00
* rotateAnchorOffset: 60,
* enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right']
2018-03-15 13:11:58 +08:00
* });
* layer.add(transformer);
*/
Konva.Transformer = function(config) {
2017-12-19 15:31:36 +08:00
this.____init(config);
};
var ANCHORS_NAMES = [
2018-02-01 11:25:42 +08:00
'top-left',
'top-center',
'top-right',
'middle-right',
'middle-left',
'bottom-left',
'bottom-center',
'bottom-right'
];
Konva.Transformer.prototype = {
2017-12-19 15:31:36 +08:00
_centroid: false,
____init: function(config) {
// call super constructor
Konva.Group.call(this, config);
this.className = 'Transformer';
2017-12-19 15:31:36 +08:00
this._createElements();
2018-02-09 15:28:19 +08:00
// bindings
2018-03-21 16:21:55 +08:00
this._handleMouseMove = this._handleMouseMove.bind(this);
this._handleMouseUp = this._handleMouseUp.bind(this);
2018-03-10 09:39:48 +08:00
this.update = this.update.bind(this);
// update transformer data for certain attr changes
2018-03-10 09:39:48 +08:00
this.on(ATTR_CHANGE_LIST, this.update);
2018-02-06 09:55:29 +08:00
2018-03-15 13:11:58 +08:00
if (this.getNode()) {
this.update();
}
2017-12-19 15:31:36 +08:00
},
2018-06-15 15:50:36 +08:00
/**
* alias to `setNode`
* @method
* @memberof Konva.Transformer.prototype
* @returns {Konva.Transformer}
* @example
* transformer.attachTo(shape);
*/
2017-12-19 15:31:36 +08:00
attachTo: function(node) {
2018-03-15 13:11:58 +08:00
this.setNode(node);
},
2018-05-07 10:57:10 +08:00
/**
2018-06-15 15:50:36 +08:00
* attach transformer to a Konva.Node. Transformer will adapt to its size and listen its events
2018-05-07 10:57:10 +08:00
* @method
* @memberof Konva.Transformer.prototype
* @returns {Konva.Transformer}
* @example
2018-06-15 15:50:36 +08:00
* transformer.setNode(shape);
2018-05-07 10:57:10 +08:00
*/
2018-03-15 13:18:46 +08:00
setNode: function(node) {
2018-03-15 13:11:58 +08:00
if (this._node) {
2018-01-12 11:09:30 +08:00
this.detach();
}
2018-03-15 13:11:58 +08:00
this._node = node;
this._resetTransformCache();
2018-02-09 15:28:19 +08:00
2018-05-16 11:32:57 +08:00
node.on(TRANSFORM_CHANGE_STR, this._resetTransformCache.bind(this));
node.on(
REDRAW_CHANGE_STR,
function() {
if (!this._transforming) {
this.update();
}
}.bind(this)
);
2018-03-15 13:11:58 +08:00
2018-05-07 10:57:10 +08:00
// TODO: why do we need this?
2018-03-15 13:11:58 +08:00
var elementsCreated = !!this.findOne('.top-left');
if (elementsCreated) {
this.update();
}
2018-05-07 10:57:10 +08:00
return this;
2018-03-15 13:11:58 +08:00
},
2018-03-15 13:18:46 +08:00
getNode: function() {
2018-03-15 13:11:58 +08:00
return this._node;
2018-01-12 11:09:30 +08:00
},
2018-05-07 10:57:10 +08:00
/**
* detach transformer from a attached node
* @method
* @memberof Konva.Transformer.prototype
* @returns {Konva.Transformer}
* @example
* transformer.detach();
*/
2018-01-12 11:09:30 +08:00
detach: function() {
2018-03-15 15:11:01 +08:00
if (this.getNode()) {
this.getNode().off('.resizer');
2018-03-21 10:56:00 +08:00
this._node = undefined;
2018-03-15 15:11:01 +08:00
}
2018-05-16 11:32:57 +08:00
this._resetTransformCache();
},
_resetTransformCache: function() {
2018-03-21 10:56:00 +08:00
this._clearCache(NODE_RECT);
this._clearCache('transform');
this._clearSelfAndDescendantCache('absoluteTransform');
2018-02-09 15:28:19 +08:00
},
_getNodeRect: function() {
2018-03-15 13:11:58 +08:00
return this._getCache(NODE_RECT, this.__getNodeRect);
},
__getNodeRect: function() {
2018-02-09 15:28:19 +08:00
var node = this.getNode();
2018-03-15 13:11:58 +08:00
if (!node) {
return {
x: -Number.MAX_SAFE_INTEGER,
y: -Number.MAX_SAFE_INTEGER,
width: 0,
height: 0,
rotation: 0
};
}
2018-02-09 15:28:19 +08:00
var rect = node.getClientRect({ skipTransform: true });
var rotation = Konva.getAngle(node.rotation());
var dx = rect.x * node.scaleX() - node.offsetX() * node.scaleX();
var dy = rect.y * node.scaleY() - node.offsetY() * node.scaleY();
2018-02-09 15:28:19 +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()
};
},
getX: function() {
return this._getNodeRect().x;
},
getY: function() {
return this._getNodeRect().y;
},
getRotation: function() {
return this._getNodeRect().rotation;
},
getWidth: function() {
return this._getNodeRect().width;
},
getHeight: function() {
return this._getNodeRect().height;
2017-12-19 15:31:36 +08:00
},
_createElements: function() {
2017-12-28 09:53:57 +08:00
this._createBack();
ANCHORS_NAMES.forEach(
2018-02-01 11:25:42 +08:00
function(name) {
this._createAnchor(name);
}.bind(this)
);
2017-12-19 15:31:36 +08:00
this._createAnchor('rotater');
},
_createAnchor: function(name) {
var anchor = new Konva.Rect({
stroke: 'rgb(0, 161, 255)',
fill: 'white',
strokeWidth: 1,
name: name + ' _anchor',
2018-08-09 13:24:50 +08:00
dragDistance: 0,
draggable: true
2017-12-19 15:31:36 +08:00
});
var self = this;
anchor.on('mousedown touchstart', function(e) {
2018-03-21 16:21:55 +08:00
self._handleMouseDown(e);
2017-12-19 15:31:36 +08:00
});
2018-08-09 13:24:50 +08:00
anchor.on('dragstart', function(e) {
e.cancelBubble = true;
});
2018-08-10 14:19:56 +08:00
anchor.on('dragmove', function(e) {
e.cancelBubble = true;
});
anchor.on('dragend', function(e) {
e.cancelBubble = true;
});
2017-12-19 15:31:36 +08:00
// add hover styling
2018-02-10 10:28:06 +08:00
anchor.on('mouseenter', function() {
2017-12-19 15:31:36 +08:00
var layer = this.getLayer();
2018-03-21 16:21:55 +08:00
var tr = this.getParent();
var rad = Konva.getAngle(tr.rotation());
2018-07-03 05:48:35 +08:00
var scale = tr.getNode().getAbsoluteScale();
// If scale.y < 0 xor scale.x < 0 we need to flip (not rotate).
2018-07-03 13:15:38 +08:00
var isMirrored = scale.y * scale.x < 0;
var cursor = getCursor(name, rad, isMirrored);
2018-03-21 16:21:55 +08:00
anchor.getStage().content.style.cursor = cursor;
2018-03-07 12:40:38 +08:00
layer.batchDraw();
2017-12-19 15:31:36 +08:00
});
anchor.on('mouseout', function() {
var layer = this.getLayer();
2017-12-21 12:07:04 +08:00
if (!layer) {
return;
}
2018-03-21 16:21:55 +08:00
anchor.getStage().content.style.cursor = '';
2018-03-07 12:40:38 +08:00
layer.batchDraw();
2017-12-19 15:31:36 +08:00
});
this.add(anchor);
},
2017-12-28 09:53:57 +08:00
_createBack: function() {
var back = new Konva.Shape({
name: 'back',
width: 0,
height: 0,
listening: false,
sceneFunc: function(ctx) {
2018-03-16 09:55:02 +08:00
var tr = this.getParent();
var padding = tr.getPadding();
2017-12-28 09:53:57 +08:00
ctx.beginPath();
2018-03-16 09:55:02 +08:00
ctx.rect(
-padding,
-padding,
this.width() + padding * 2,
this.height() + padding * 2
);
ctx.moveTo(this.width() / 2, -padding);
if (tr.rotateEnabled()) {
2018-03-21 16:21:55 +08:00
ctx.lineTo(
this.width() / 2,
2018-08-10 11:22:08 +08:00
-tr.rotateAnchorOffset() * Konva.Util._sign(this.height())
2018-03-21 16:21:55 +08:00
);
2018-02-01 11:25:42 +08:00
}
2017-12-28 09:53:57 +08:00
ctx.fillStrokeShape(this);
}
});
this.add(back);
},
2018-03-21 16:21:55 +08:00
_handleMouseDown: function(e) {
this.movingResizer = e.target.name().split(' ')[0];
2017-12-19 15:31:36 +08:00
2018-02-09 15:28:19 +08:00
// var node = this.getNode();
var attrs = this._getNodeRect();
var width = attrs.width;
var height = attrs.height;
2017-12-19 15:31:36 +08:00
var hypotenuse = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
this.sin = height / hypotenuse;
this.cos = width / hypotenuse;
2018-03-21 16:21:55 +08:00
window.addEventListener('mousemove', this._handleMouseMove);
window.addEventListener('touchmove', this._handleMouseMove);
2018-04-17 11:24:06 +08:00
window.addEventListener('mouseup', this._handleMouseUp, true);
window.addEventListener('touchend', this._handleMouseUp, true);
2018-02-05 13:32:48 +08:00
this._transforming = true;
2018-03-10 09:39:48 +08:00
this.fire('transformstart');
this.getNode().fire('transformstart');
2017-12-19 15:31:36 +08:00
},
2018-03-21 16:21:55 +08:00
_handleMouseMove: function(e) {
2017-12-21 10:57:16 +08:00
var x, y, newHypotenuse;
2017-12-19 15:31:36 +08:00
var resizerNode = this.findOne('.' + this.movingResizer);
var stage = resizerNode.getStage();
var box = stage.getContent().getBoundingClientRect();
var zeroPoint = {
x: box.left,
y: box.top
};
var pointerPos = {
left: e.clientX !== undefined ? e.clientX : e.touches[0].clientX,
top: e.clientX !== undefined ? e.clientY : e.touches[0].clientY
};
var newAbsPos = {
x: pointerPos.left - zeroPoint.x,
y: pointerPos.top - zeroPoint.y
};
resizerNode.setAbsolutePosition(newAbsPos);
2018-03-14 11:22:07 +08:00
var keepProportion = this.keepRatio() || e.shiftKey;
2017-12-19 15:31:36 +08:00
if (this.movingResizer === 'top-left') {
if (keepProportion) {
newHypotenuse = Math.sqrt(
Math.pow(this.findOne('.bottom-right').x() - resizerNode.x(), 2) +
Math.pow(this.findOne('.bottom-right').y() - resizerNode.y(), 2)
);
2017-12-19 15:31:36 +08:00
x = newHypotenuse * this.cos;
y = newHypotenuse * this.sin;
2017-12-19 15:31:36 +08:00
this.findOne('.top-left').x(this.findOne('.bottom-right').x() - x);
this.findOne('.top-left').y(this.findOne('.bottom-right').y() - y);
}
2017-12-19 15:31:36 +08:00
} else if (this.movingResizer === 'top-center') {
this.findOne('.top-left').y(resizerNode.y());
} else if (this.movingResizer === 'top-right') {
if (keepProportion) {
newHypotenuse = Math.sqrt(
Math.pow(this.findOne('.bottom-left').x() - resizerNode.x(), 2) +
Math.pow(this.findOne('.bottom-left').y() - resizerNode.y(), 2)
);
2017-12-19 15:31:36 +08:00
x = newHypotenuse * this.cos;
y = newHypotenuse * this.sin;
2017-12-19 15:31:36 +08:00
this.findOne('.top-right').x(x);
this.findOne('.top-right').y(this.findOne('.bottom-left').y() - y);
}
2017-12-19 15:31:36 +08:00
var pos = resizerNode.position();
this.findOne('.top-left').y(pos.y);
this.findOne('.bottom-right').x(pos.x);
} else if (this.movingResizer === 'middle-left') {
this.findOne('.top-left').x(resizerNode.x());
} else if (this.movingResizer === 'middle-right') {
this.findOne('.bottom-right').x(resizerNode.x());
} else if (this.movingResizer === 'bottom-left') {
if (keepProportion) {
newHypotenuse = Math.sqrt(
Math.pow(this.findOne('.top-right').x() - resizerNode.x(), 2) +
Math.pow(this.findOne('.top-right').y() - resizerNode.y(), 2)
);
2017-12-19 15:31:36 +08:00
x = newHypotenuse * this.cos;
y = newHypotenuse * this.sin;
2017-12-19 15:31:36 +08:00
this.findOne('.bottom-left').x(this.findOne('.top-right').x() - x);
this.findOne('.bottom-left').y(y);
}
2017-12-19 15:31:36 +08:00
2017-12-21 10:57:16 +08:00
pos = resizerNode.position();
2017-12-19 15:31:36 +08:00
this.findOne('.top-left').x(pos.x);
this.findOne('.bottom-right').y(pos.y);
} else if (this.movingResizer === 'bottom-center') {
this.findOne('.bottom-right').y(resizerNode.y());
} else if (this.movingResizer === 'bottom-right') {
if (keepProportion) {
newHypotenuse = Math.sqrt(
Math.pow(this.findOne('.bottom-right').x(), 2) +
Math.pow(this.findOne('.bottom-right').y(), 2)
);
2017-12-19 15:31:36 +08:00
x = newHypotenuse * this.cos;
y = newHypotenuse * this.sin;
2017-12-19 15:31:36 +08:00
this.findOne('.bottom-right').x(x);
this.findOne('.bottom-right').y(y);
}
2017-12-19 15:31:36 +08:00
} else if (this.movingResizer === 'rotater') {
2018-03-16 09:55:02 +08:00
var padding = this.getPadding();
2018-02-09 15:28:19 +08:00
var attrs = this._getNodeRect();
x = resizerNode.x() - attrs.width / 2;
y = -resizerNode.y() + attrs.height / 2;
2017-12-19 15:31:36 +08:00
var dAlpha = Math.atan2(-y, x) + Math.PI / 2;
2018-03-21 16:21:55 +08:00
if (attrs.height < 0) {
dAlpha -= Math.PI;
}
2017-12-19 15:31:36 +08:00
2017-12-21 11:34:29 +08:00
var rot = Konva.getAngle(this.rotation());
2017-12-19 15:31:36 +08:00
2017-12-21 11:34:29 +08:00
var newRotation =
Konva.Util._radToDeg(rot) + Konva.Util._radToDeg(dAlpha);
2018-02-09 15:28:19 +08:00
var alpha = Konva.getAngle(this.getNode().rotation());
2017-12-19 15:31:36 +08:00
var newAlpha = Konva.Util._degToRad(newRotation);
2018-01-02 15:46:32 +08:00
var snaps = this.rotationSnaps();
var offset = 0.1;
for (var i = 0; i < snaps.length; i++) {
var angle = Konva.getAngle(snaps[i]);
var dif =
Math.abs(angle - Konva.Util._degToRad(newRotation)) % (Math.PI * 2);
if (dif < offset) {
newRotation = Konva.Util._radToDeg(angle);
newAlpha = Konva.Util._degToRad(newRotation);
}
}
2018-03-16 09:55:02 +08:00
var dx = padding;
var dy = padding;
2018-03-21 16:21:55 +08:00
this._fitNodeInto({
rotation: Konva.angleDeg
? newRotation
: Konva.Util._degToRad(newRotation),
x:
attrs.x +
(attrs.width / 2 + padding) *
(Math.cos(alpha) - Math.cos(newAlpha)) +
(attrs.height / 2 + padding) *
(Math.sin(-alpha) - Math.sin(-newAlpha)) -
(dx * Math.cos(rot) + dy * Math.sin(-rot)),
y:
attrs.y +
(attrs.height / 2 + padding) *
(Math.cos(alpha) - Math.cos(newAlpha)) +
(attrs.width / 2 + padding) *
(Math.sin(alpha) - Math.sin(newAlpha)) -
(dy * Math.cos(rot) + dx * Math.sin(rot)),
width: attrs.width + padding * 2,
height: attrs.height + padding * 2
});
2017-12-19 15:31:36 +08:00
} else {
console.error(
new Error(
'Wrong position argument of selection resizer: ',
this.movingResizer
)
);
}
if (this.movingResizer === 'rotater') {
return;
}
2018-03-07 12:24:48 +08:00
var absPos = this.findOne('.top-left').getAbsolutePosition(
this.getParent()
);
2017-12-19 15:31:36 +08:00
if (e.altKey) {
var topLeft = this.findOne('.top-left');
var bottomRight = this.findOne('.bottom-right');
var topOffsetX = topLeft.x();
var topOffsetY = topLeft.y();
var bottomOffsetX = this.getWidth() - bottomRight.x();
var bottomOffsetY = this.getHeight() - bottomRight.y();
bottomRight.move({
x: -topOffsetX,
y: -topOffsetY
});
topLeft.move({
x: bottomOffsetX,
y: bottomOffsetY
});
absPos = topLeft.getAbsolutePosition(this.getParent());
}
2017-12-21 10:57:16 +08:00
x = absPos.x;
y = absPos.y;
var width =
this.findOne('.bottom-right').x() - this.findOne('.top-left').x();
2017-12-19 15:31:36 +08:00
2017-12-21 10:57:16 +08:00
var height =
this.findOne('.bottom-right').y() - this.findOne('.top-left').y();
2017-12-19 15:31:36 +08:00
2018-02-09 15:28:19 +08:00
this._fitNodeInto({
x: x + this.offsetX(),
y: y + this.offsetY(),
2017-12-19 15:31:36 +08:00
width: width,
height: height
2017-12-21 10:57:16 +08:00
});
2017-12-19 15:31:36 +08:00
},
2018-03-21 16:21:55 +08:00
_handleMouseUp: function() {
2018-02-05 13:32:48 +08:00
this._removeEvents();
},
_removeEvents: function() {
if (this._transforming) {
this._transforming = false;
2018-03-21 16:21:55 +08:00
window.removeEventListener('mousemove', this._handleMouseMove);
window.removeEventListener('touchmove', this._handleMouseMove);
2018-04-17 11:24:06 +08:00
window.removeEventListener('mouseup', this._handleMouseUp, true);
window.removeEventListener('touchend', this._handleMouseUp, true);
this.fire('transformend');
2018-07-12 12:04:20 +08:00
var node = this.getNode();
if (node) {
node.fire('transformend');
}
2018-02-05 13:32:48 +08:00
}
2017-12-19 15:31:36 +08:00
},
2018-04-11 09:36:08 +08:00
_fitNodeInto: function(newAttrs) {
2018-03-16 09:55:02 +08:00
// waring! in this attrs padding may be included
2018-04-11 09:36:08 +08:00
var boundBoxFunc = this.getBoundBoxFunc();
if (boundBoxFunc) {
var oldAttrs = this._getNodeRect();
newAttrs = boundBoxFunc.call(this, oldAttrs, newAttrs);
}
2018-02-09 15:28:19 +08:00
this._settings = true;
var node = this.getNode();
2018-04-11 09:36:08 +08:00
if (newAttrs.rotation !== undefined) {
this.getNode().rotation(newAttrs.rotation);
2017-12-19 15:31:36 +08:00
}
2018-02-09 15:28:19 +08:00
var pure = node.getClientRect({ skipTransform: true });
2018-03-16 09:55:02 +08:00
var padding = this.getPadding();
2018-04-11 09:36:08 +08:00
var scaleX = (newAttrs.width - padding * 2) / pure.width;
var scaleY = (newAttrs.height - padding * 2) / pure.height;
2018-02-09 15:28:19 +08:00
var rotation = Konva.getAngle(node.getRotation());
var dx = pure.x * scaleX - padding - node.offsetX() * scaleX;
var dy = pure.y * scaleY - padding - node.offsetY() * scaleY;
2018-02-18 11:45:24 +08:00
2018-02-09 15:28:19 +08:00
this.getNode().setAttrs({
scaleX: scaleX,
scaleY: scaleY,
2018-04-11 09:36:08 +08:00
x: newAttrs.x - (dx * Math.cos(rotation) + dy * Math.sin(-rotation)),
y: newAttrs.y - (dy * Math.cos(rotation) + dx * Math.sin(rotation))
2018-02-09 15:28:19 +08:00
});
this._settings = false;
2018-02-01 11:25:42 +08:00
this.fire('transform');
2018-02-09 15:28:19 +08:00
this.getNode().fire('transform');
2018-03-10 09:39:48 +08:00
this.update();
2017-12-19 15:31:36 +08:00
this.getLayer().batchDraw();
},
2018-03-15 13:18:46 +08:00
/**
2018-05-07 10:57:10 +08:00
* force update of Konva.Transformer.
* Use it when you updated attached Konva.Group and now you need to reset transformer size
2018-03-15 13:18:46 +08:00
* @method
* @memberof Konva.Transformer.prototype
*/
2018-03-15 13:11:58 +08:00
forceUpdate: function() {
2018-05-16 11:32:57 +08:00
this._resetTransformCache();
2018-03-15 13:11:58 +08:00
this.update();
},
2018-03-10 09:39:48 +08:00
update: function() {
2018-02-09 15:28:19 +08:00
var attrs = this._getNodeRect();
2018-07-03 05:48:35 +08:00
var node = this.getNode();
var scale = { x: 1, y: 1 };
if (node && node.getParent()) {
scale = node.getParent().getAbsoluteScale();
}
2018-07-03 05:48:35 +08:00
var invertedScale = {
x: 1 / scale.x,
y: 1 / scale.y
};
2018-02-09 15:28:19 +08:00
var width = attrs.width;
var height = attrs.height;
2017-12-19 15:31:36 +08:00
2018-08-10 11:22:08 +08:00
var enabledAnchors = this.enabledAnchors();
2018-01-02 13:25:36 +08:00
var resizeEnabled = this.resizeEnabled();
2018-03-16 09:55:02 +08:00
var padding = this.getPadding();
2017-12-28 09:32:00 +08:00
var anchorSize = this.getAnchorSize();
this.find('._anchor').setAttrs({
width: anchorSize,
height: anchorSize,
offsetX: anchorSize / 2,
2018-08-10 11:22:08 +08:00
offsetY: anchorSize / 2,
stroke: this.getAnchorStroke(),
strokeWidth: this.getAnchorStrokeWidth(),
fill: this.getAnchorFill()
});
2017-12-28 09:32:00 +08:00
this.findOne('.top-left').setAttrs({
2018-03-16 09:55:02 +08:00
x: -padding,
y: -padding,
2018-07-03 05:48:35 +08:00
scale: invertedScale,
2018-08-10 11:22:08 +08:00
visible: resizeEnabled && enabledAnchors.indexOf('top-left') >= 0
2017-12-19 15:31:36 +08:00
});
2017-12-28 09:32:00 +08:00
this.findOne('.top-center').setAttrs({
2017-12-19 15:31:36 +08:00
x: width / 2,
2018-03-16 09:55:02 +08:00
y: -padding,
2018-07-03 05:48:35 +08:00
scale: invertedScale,
2018-08-10 11:22:08 +08:00
visible: resizeEnabled && enabledAnchors.indexOf('top-center') >= 0
2017-12-19 15:31:36 +08:00
});
2017-12-28 09:32:00 +08:00
this.findOne('.top-right').setAttrs({
2018-03-16 09:55:02 +08:00
x: width + padding,
y: -padding,
2018-07-03 05:48:35 +08:00
scale: invertedScale,
2018-08-10 11:22:08 +08:00
visible: resizeEnabled && enabledAnchors.indexOf('top-right') >= 0
2017-12-19 15:31:36 +08:00
});
2017-12-28 09:32:00 +08:00
this.findOne('.middle-left').setAttrs({
2018-03-16 09:55:02 +08:00
x: -padding,
2017-12-28 09:32:00 +08:00
y: height / 2,
2018-07-03 05:48:35 +08:00
scale: invertedScale,
2018-08-10 11:22:08 +08:00
visible: resizeEnabled && enabledAnchors.indexOf('middle-left') >= 0
2017-12-19 15:31:36 +08:00
});
2017-12-28 09:32:00 +08:00
this.findOne('.middle-right').setAttrs({
2018-03-16 09:55:02 +08:00
x: width + padding,
2017-12-28 09:32:00 +08:00
y: height / 2,
2018-07-03 05:48:35 +08:00
scale: invertedScale,
2018-08-10 11:22:08 +08:00
visible: resizeEnabled && enabledAnchors.indexOf('middle-right') >= 0
2017-12-19 15:31:36 +08:00
});
2017-12-28 09:32:00 +08:00
this.findOne('.bottom-left').setAttrs({
2018-03-16 09:55:02 +08:00
x: -padding,
y: height + padding,
2018-07-03 05:48:35 +08:00
scale: invertedScale,
2018-08-10 11:22:08 +08:00
visible: resizeEnabled && enabledAnchors.indexOf('bottom-left') >= 0
2017-12-19 15:31:36 +08:00
});
2017-12-28 09:32:00 +08:00
this.findOne('.bottom-center').setAttrs({
2017-12-19 15:31:36 +08:00
x: width / 2,
2018-03-16 09:55:02 +08:00
y: height + padding,
2018-07-03 05:48:35 +08:00
scale: invertedScale,
2018-08-10 11:22:08 +08:00
visible: resizeEnabled && enabledAnchors.indexOf('bottom-center') >= 0
2017-12-19 15:31:36 +08:00
});
2017-12-28 09:32:00 +08:00
this.findOne('.bottom-right').setAttrs({
2018-03-16 09:55:02 +08:00
x: width + padding,
y: height + padding,
2018-07-03 05:48:35 +08:00
scale: invertedScale,
2018-08-10 11:22:08 +08:00
visible: resizeEnabled && enabledAnchors.indexOf('bottom-right') >= 0
2017-12-19 15:31:36 +08:00
});
2018-08-10 11:22:08 +08:00
var scaledRotateAnchorOffset =
-this.rotateAnchorOffset() * Math.abs(invertedScale.y);
2017-12-28 09:32:00 +08:00
this.findOne('.rotater').setAttrs({
2017-12-19 15:31:36 +08:00
x: width / 2,
2018-08-10 11:22:08 +08:00
y: scaledRotateAnchorOffset * Konva.Util._sign(height),
2018-07-03 05:48:35 +08:00
scale: invertedScale,
2018-02-01 11:25:42 +08:00
visible: this.rotateEnabled()
2017-12-19 15:31:36 +08:00
});
2017-12-28 09:53:57 +08:00
this.findOne('.back').setAttrs({
2018-07-03 05:48:35 +08:00
width: width * scale.x,
height: height * scale.y,
scale: invertedScale,
2018-08-10 11:22:08 +08:00
visible: this.borderEnabled(),
stroke: this.getBorderStroke(),
strokeWidth: this.getBorderStrokeWidth(),
dash: this.getBorderDash()
2017-12-28 09:53:57 +08:00
});
2017-12-21 12:07:04 +08:00
},
2018-05-07 10:57:10 +08:00
/**
* determine if transformer is in active transform
* @method
* @memberof Konva.Transformer.prototype
* @returns {Boolean}
*/
2018-04-11 09:36:08 +08:00
isTransforming: function() {
return this._transforming;
},
2018-05-07 10:57:10 +08:00
/**
* Stop active transform action
* @method
* @memberof Konva.Transformer.prototype
* @returns {Boolean}
*/
2018-04-11 09:36:08 +08:00
stopTransform: function() {
if (this._transforming) {
this._removeEvents();
2018-08-10 14:19:56 +08:00
var resizerNode = this.findOne('.' + this.movingResizer);
if (resizerNode) {
resizerNode.stopDrag();
}
2018-04-11 09:36:08 +08:00
}
},
2017-12-21 12:07:04 +08:00
destroy: function() {
Konva.Group.prototype.destroy.call(this);
2018-03-15 15:11:01 +08:00
this.detach();
2018-02-05 13:32:48 +08:00
this._removeEvents();
2018-03-15 13:11:58 +08:00
},
// do not work as a container
// we will recreate inner nodes manually
toObject: function() {
return Konva.Node.prototype.toObject.call(this);
2017-12-19 15:31:36 +08:00
}
};
Konva.Util.extend(Konva.Transformer, Konva.Group);
2017-12-19 15:31:36 +08:00
2018-02-05 13:10:05 +08:00
function validateResizers(val) {
if (!(val instanceof Array)) {
2018-08-10 11:22:08 +08:00
Konva.Util.warn('enabledAnchors value should be an array');
2018-02-05 13:10:05 +08:00
}
if (val instanceof Array) {
val.forEach(function(name) {
if (ANCHORS_NAMES.indexOf(name) === -1) {
2018-02-05 13:10:05 +08:00
Konva.Util.warn(
'Unknown anchor name: ' +
2018-02-05 13:10:05 +08:00
name +
'. Available names are: ' +
ANCHORS_NAMES.join(', ')
2018-02-05 13:10:05 +08:00
);
}
});
}
return val || [];
}
2018-03-15 13:11:58 +08:00
/**
* get/set enabled handlers
2018-08-10 11:22:08 +08:00
* @name enabledAnchors
2018-03-15 13:11:58 +08:00
* @method
* @memberof Konva.Transformer.prototype
* @param {Array} array
* @returns {Array}
* @example
* // get list of handlers
2018-08-10 11:22:08 +08:00
* var enabledAnchors = transformer.enabledAnchors();
2018-03-15 13:11:58 +08:00
*
* // set handlers
2018-08-10 11:22:08 +08:00
* transformer.enabledAnchors(['top-left', 'top-center', 'top-right', 'middle-right', 'middle-left', 'bottom-left', 'bottom-center', 'bottom-right']);
2018-03-15 13:11:58 +08:00
*/
2018-02-01 11:25:42 +08:00
Konva.Factory.addGetterSetter(
Konva.Transformer,
2018-08-10 11:22:08 +08:00
'enabledAnchors',
ANCHORS_NAMES,
2018-02-05 13:10:05 +08:00
validateResizers
2018-02-01 11:25:42 +08:00
);
2018-03-15 13:11:58 +08:00
/**
* get/set resize ability. If false it will automatically hide resizing handlers
* @name resizeEnabled
* @method
* @memberof Konva.Transformer.prototype
* @param {Array} array
* @returns {Array}
* @example
* // get
2018-06-15 15:50:36 +08:00
* var resizeEnabled = transformer.resizeEnabled();
2018-03-15 13:11:58 +08:00
*
* // set
2018-06-15 15:50:36 +08:00
* transformer.resizeEnabled(false);
2018-03-15 13:11:58 +08:00
*/
Konva.Factory.addGetterSetter(Konva.Transformer, 'resizeEnabled', true);
/**
* get/set anchor size. Default is 10
* @name validateAnchors
* @method
* @memberof Konva.Transformer.prototype
* @param {Number} 10
* @returns {Number}
* @example
* // get
* var anchorSize = transformer.anchorSize();
*
* // set
* transformer.anchorSize(20)
*/
Konva.Factory.addGetterSetter(
Konva.Transformer,
'anchorSize',
10,
Konva.Validators.getNumberValidator()
);
2018-03-15 13:11:58 +08:00
/**
* get/set ability to rotate.
* @name rotateEnabled
* @method
* @memberof Konva.Transformer.prototype
2018-06-15 15:50:36 +08:00
* @param {Boolean} enabled
* @returns {Boolean}
2018-03-15 13:11:58 +08:00
* @example
* // get
2018-06-15 15:50:36 +08:00
* var rotateEnabled = transformer.rotateEnabled();
2018-03-15 13:11:58 +08:00
*
* // set
2018-06-15 15:50:36 +08:00
* transformer.rotateEnabled(false);
2018-03-15 13:11:58 +08:00
*/
2018-02-01 11:25:42 +08:00
Konva.Factory.addGetterSetter(Konva.Transformer, 'rotateEnabled', true);
2018-03-15 13:11:58 +08:00
/**
* get/set rotation snaps angles.
* @name rotationSnaps
* @method
* @memberof Konva.Transformer.prototype
* @param {Array} array
* @returns {Array}
* @example
* // get
2018-06-15 15:50:36 +08:00
* var rotationSnaps = transformer.rotationSnaps();
2018-03-15 13:11:58 +08:00
*
* // set
2018-06-15 15:50:36 +08:00
* transformer.rotationSnaps([0, 90, 180, 270]);
2018-03-15 13:11:58 +08:00
*/
Konva.Factory.addGetterSetter(Konva.Transformer, 'rotationSnaps', []);
2018-03-15 13:11:58 +08:00
/**
* get/set distance for rotation handler
2018-08-10 11:22:08 +08:00
* @name rotateAnchorOffset
2018-03-15 13:11:58 +08:00
* @method
* @memberof Konva.Transformer.prototype
2018-06-15 15:50:36 +08:00
* @param {Number} offset
* @returns {Number}
2018-03-15 13:11:58 +08:00
* @example
* // get
2018-08-10 11:22:08 +08:00
* var rotateAnchorOffset = transformer.rotateAnchorOffset();
2018-03-15 13:11:58 +08:00
*
* // set
2018-08-10 11:22:08 +08:00
* transformer.rotateAnchorOffset(100);
2018-03-15 13:11:58 +08:00
*/
Konva.Factory.addGetterSetter(
Konva.Transformer,
'rotateAnchorOffset',
50,
Konva.Validators.getNumberValidator()
);
2018-03-15 13:11:58 +08:00
/**
* get/set visibility of border
2018-08-10 11:22:08 +08:00
* @name borderEnabled
* @method
* @memberof Konva.Transformer.prototype
* @param {Boolean} enabled
* @returns {Boolean}
* @example
* // get
* var borderEnabled = transformer.borderEnabled();
*
* // set
* transformer.borderEnabled(false);
*/
Konva.Factory.addGetterSetter(Konva.Transformer, 'borderEnabled', true);
/**
* get/set anchor stroke color
* @name anchorStroke
* @method
* @memberof Konva.Transformer.prototype
* @param {Boolean} enabled
* @returns {Boolean}
* @example
* // get
* var anchorStroke = transformer.anchorStroke();
*
* // set
* transformer.anchorStroke('red');
*/
Konva.Factory.addGetterSetter(
Konva.Transformer,
'anchorStroke',
'rgb(0, 161, 255)'
);
/**
* get/set anchor stroke width
* @name anchorStrokeWidth
* @method
* @memberof Konva.Transformer.prototype
* @param {Boolean} enabled
* @returns {Boolean}
* @example
* // get
* var anchorStrokeWidth = transformer.anchorStrokeWidth();
*
* // set
* transformer.anchorStrokeWidth(3);
*/
Konva.Factory.addGetterSetter(
Konva.Transformer,
'anchorStrokeWidth',
1,
Konva.Validators.getNumberValidator()
);
2018-08-10 11:22:08 +08:00
/**
* get/set anchor fill color
* @name anchorFill
* @method
* @memberof Konva.Transformer.prototype
* @param {Boolean} enabled
* @returns {Boolean}
* @example
* // get
* var anchorFill = transformer.anchorFill();
*
* // set
* transformer.anchorFill('red');
*/
Konva.Factory.addGetterSetter(Konva.Transformer, 'anchorFill', 'white');
/**
* get/set border stroke color
* @name borderStroke
* @method
* @memberof Konva.Transformer.prototype
* @param {Boolean} enabled
* @returns {Boolean}
* @example
* // get
* var borderStroke = transformer.borderStroke();
*
* // set
* transformer.borderStroke('red');
*/
Konva.Factory.addGetterSetter(
Konva.Transformer,
'borderStroke',
'rgb(0, 161, 255)'
);
/**
* get/set border stroke width
* @name borderStrokeWidth
2018-03-15 13:11:58 +08:00
* @method
* @memberof Konva.Transformer.prototype
2018-06-15 15:50:36 +08:00
* @param {Boolean} enabled
* @returns {Boolean}
2018-03-15 13:11:58 +08:00
* @example
* // get
2018-08-10 11:22:08 +08:00
* var borderStrokeWidth = transformer.borderStrokeWidth();
2018-03-15 13:11:58 +08:00
*
* // set
2018-08-10 11:22:08 +08:00
* transformer.borderStrokeWidth(3);
2018-03-15 13:11:58 +08:00
*/
Konva.Factory.addGetterSetter(
Konva.Transformer,
'borderStrokeWidth',
1,
Konva.Validators.getNumberValidator()
);
2018-08-10 11:22:08 +08:00
/**
* get/set border dash array
* @name borderDash
* @method
* @memberof Konva.Transformer.prototype
* @param {Boolean} enabled
* @returns {Boolean}
* @example
* // get
* var borderDash = transformer.borderDash();
*
* // set
* transformer.borderDash([2, 2]);
*/
Konva.Factory.addGetterSetter(Konva.Transformer, 'borderDash');
2018-03-15 13:11:58 +08:00
/**
* get/set should we keep ratio while resize anchors at corners
2018-03-15 13:11:58 +08:00
* @name keepRatio
* @method
* @memberof Konva.Transformer.prototype
2018-06-15 15:50:36 +08:00
* @param {Boolean} keepRatio
* @returns {Boolean}
2018-03-15 13:11:58 +08:00
* @example
* // get
2018-06-15 15:50:36 +08:00
* var keepRatio = transformer.keepRatio();
2018-03-15 13:11:58 +08:00
*
* // set
2018-06-15 15:50:36 +08:00
* transformer.keepRatio(false);
2018-03-15 13:11:58 +08:00
*/
2018-03-14 11:22:07 +08:00
Konva.Factory.addGetterSetter(Konva.Transformer, 'keepRatio', true);
2018-03-16 09:55:02 +08:00
/**
* get/set padding
* @name padding
* @method
* @memberof Konva.Transformer.prototype
2018-06-15 15:50:36 +08:00
* @param {Number} padding
* @returns {Number}
2018-03-16 09:55:02 +08:00
* @example
* // get
2018-06-15 15:50:36 +08:00
* var padding = transformer.padding();
2018-03-16 09:55:02 +08:00
*
* // set
2018-06-15 15:50:36 +08:00
* transformer.padding(10);
2018-03-16 09:55:02 +08:00
*/
Konva.Factory.addGetterSetter(
Konva.Transformer,
'padding',
0,
Konva.Validators.getNumberValidator()
);
2018-03-16 09:55:02 +08:00
2018-03-15 13:11:58 +08:00
Konva.Factory.addOverloadedGetterSetter(Konva.Transformer, 'node');
2017-12-28 09:32:00 +08:00
2018-06-15 15:50:36 +08:00
/**
* get/set bounding box function
* @name boundBoxFunc
* @method
* @memberof Konva.Transformer.prototype
* @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;
* });
*/
2018-04-11 09:36:08 +08:00
Konva.Factory.addGetterSetter(Konva.Transformer, 'boundBoxFunc');
2018-08-10 11:22:08 +08:00
Konva.Factory.backCompat(Konva.Transformer, {
lineEnabled: 'borderEnabled',
rotateHandlerOffset: 'rotateAnchorOffset',
enabledHandlers: 'enabledAnchors'
});
Konva.Collection.mapMethods(Konva.Transformer);
2017-12-19 15:31:36 +08:00
})(Konva);