konva
Version:
<p align="center"> <img src="https://raw.githubusercontent.com/konvajs/konvajs.github.io/master/apple-touch-icon-180x180.png" alt="Konva logo" height="180" /> </p>
641 lines (640 loc) • 25.4 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var Util_1 = require("./Util");
var Factory_1 = require("./Factory");
var Container_1 = require("./Container");
var Global_1 = require("./Global");
var Canvas_1 = require("./Canvas");
var DragAndDrop_1 = require("./DragAndDrop");
var Global_2 = require("./Global");
var PointerEvents = require("./PointerEvents");
var STAGE = 'Stage', STRING = 'string', PX = 'px', MOUSEOUT = 'mouseout', MOUSELEAVE = 'mouseleave', MOUSEOVER = 'mouseover', MOUSEENTER = 'mouseenter', MOUSEMOVE = 'mousemove', MOUSEDOWN = 'mousedown', MOUSEUP = 'mouseup', POINTERMOVE = 'pointermove', POINTERDOWN = 'pointerdown', POINTERUP = 'pointerup', POINTERCANCEL = 'pointercancel', LOSTPOINTERCAPTURE = 'lostpointercapture', CONTEXTMENU = 'contextmenu', CLICK = 'click', DBL_CLICK = 'dblclick', TOUCHSTART = 'touchstart', TOUCHEND = 'touchend', TAP = 'tap', DBL_TAP = 'dbltap', TOUCHMOVE = 'touchmove', WHEEL = 'wheel', CONTENT_MOUSEOUT = 'contentMouseout', CONTENT_MOUSEOVER = 'contentMouseover', CONTENT_MOUSEMOVE = 'contentMousemove', CONTENT_MOUSEDOWN = 'contentMousedown', CONTENT_MOUSEUP = 'contentMouseup', CONTENT_CONTEXTMENU = 'contentContextmenu', CONTENT_CLICK = 'contentClick', CONTENT_DBL_CLICK = 'contentDblclick', CONTENT_TOUCHSTART = 'contentTouchstart', CONTENT_TOUCHEND = 'contentTouchend', CONTENT_DBL_TAP = 'contentDbltap', CONTENT_TAP = 'contentTap', CONTENT_TOUCHMOVE = 'contentTouchmove', CONTENT_POINTERMOVE = 'contentPointermove', CONTENT_POINTERDOWN = 'contentPointerdown', CONTENT_POINTERUP = 'contentPointerup', CONTENT_WHEEL = 'contentWheel', RELATIVE = 'relative', KONVA_CONTENT = 'konvajs-content', SPACE = ' ', UNDERSCORE = '_', CONTAINER = 'container', MAX_LAYERS_NUMBER = 5, EMPTY_STRING = '', EVENTS = [
MOUSEENTER,
MOUSEDOWN,
MOUSEMOVE,
MOUSEUP,
MOUSEOUT,
TOUCHSTART,
TOUCHMOVE,
TOUCHEND,
MOUSEOVER,
WHEEL,
CONTEXTMENU,
POINTERDOWN,
POINTERMOVE,
POINTERUP,
POINTERCANCEL,
LOSTPOINTERCAPTURE
], eventsLength = EVENTS.length;
function addEvent(ctx, eventName) {
ctx.content.addEventListener(eventName, function (evt) {
ctx[UNDERSCORE + eventName](evt);
}, false);
}
var NO_POINTERS_MESSAGE = "Pointer position is missing and not registered by the stage. Looks like it is outside of the stage container. You can set it manually from event: stage.setPointersPositions(event);";
exports.stages = [];
function checkNoClip(attrs) {
if (attrs === void 0) { attrs = {}; }
if (attrs.clipFunc || attrs.clipWidth || attrs.clipHeight) {
Util_1.Util.warn('Stage does not support clipping. Please use clip for Layers or Groups.');
}
return attrs;
}
var Stage = (function (_super) {
__extends(Stage, _super);
function Stage(config) {
var _this = _super.call(this, checkNoClip(config)) || this;
_this._buildDOM();
_this._bindContentEvents();
exports.stages.push(_this);
_this.on('widthChange.konva heightChange.konva', _this._resizeDOM);
_this.on('visibleChange.konva', _this._checkVisibility);
_this.on('clipWidthChange.konva clipHeightChange.konva clipFuncChange.konva', function () {
checkNoClip(_this.attrs);
});
_this._checkVisibility();
return _this;
}
Stage.prototype._validateAdd = function (child) {
var isLayer = child.getType() === 'Layer';
var isFastLayer = child.getType() === 'FastLayer';
var valid = isLayer || isFastLayer;
if (!valid) {
Util_1.Util.throw('You may only add layers to the stage.');
}
};
Stage.prototype._checkVisibility = function () {
var style = this.visible() ? '' : 'none';
this.content.style.display = style;
};
Stage.prototype.setContainer = function (container) {
if (typeof container === STRING) {
if (container.charAt(0) === '.') {
var className = container.slice(1);
container = document.getElementsByClassName(className)[0];
}
else {
var id;
if (container.charAt(0) !== '#') {
id = container;
}
else {
id = container.slice(1);
}
container = document.getElementById(id);
}
if (!container) {
throw 'Can not find container in document with id ' + id;
}
}
this._setAttr(CONTAINER, container);
if (this.content) {
this.content.parentElement.removeChild(this.content);
container.appendChild(this.content);
}
return this;
};
Stage.prototype.shouldDrawHit = function () {
return true;
};
Stage.prototype.clear = function () {
var layers = this.children, len = layers.length, n;
for (n = 0; n < len; n++) {
layers[n].clear();
}
return this;
};
Stage.prototype.clone = function (obj) {
if (!obj) {
obj = {};
}
obj.container = document.createElement('div');
return Container_1.Container.prototype.clone.call(this, obj);
};
Stage.prototype.destroy = function () {
_super.prototype.destroy.call(this);
var content = this.content;
if (content && Util_1.Util._isInDocument(content)) {
this.container().removeChild(content);
}
var index = exports.stages.indexOf(this);
if (index > -1) {
exports.stages.splice(index, 1);
}
return this;
};
Stage.prototype.getPointerPosition = function () {
if (!this.pointerPos) {
Util_1.Util.warn(NO_POINTERS_MESSAGE);
}
return this.pointerPos;
};
Stage.prototype.getStage = function () {
return this;
};
Stage.prototype.getContent = function () {
return this.content;
};
Stage.prototype._toKonvaCanvas = function (config) {
config = config || {};
var x = config.x || 0, y = config.y || 0, canvas = new Canvas_1.SceneCanvas({
width: config.width || this.width(),
height: config.height || this.height(),
pixelRatio: config.pixelRatio || 1
}), _context = canvas.getContext()._context, layers = this.children;
if (x || y) {
_context.translate(-1 * x, -1 * y);
}
layers.each(function (layer) {
if (!layer.isVisible()) {
return;
}
var layerCanvas = layer._toKonvaCanvas(config);
_context.drawImage(layerCanvas._canvas, x, y, layerCanvas.getWidth() / layerCanvas.getPixelRatio(), layerCanvas.getHeight() / layerCanvas.getPixelRatio());
});
return canvas;
};
Stage.prototype.getIntersection = function (pos, selector) {
var layers = this.children, len = layers.length, end = len - 1, n, shape;
for (n = end; n >= 0; n--) {
shape = layers[n].getIntersection(pos, selector);
if (shape) {
return shape;
}
}
return null;
};
Stage.prototype._resizeDOM = function () {
if (this.content) {
var width = this.width(), height = this.height(), layers = this.getChildren(), len = layers.length, n, layer;
this.content.style.width = width + PX;
this.content.style.height = height + PX;
this.bufferCanvas.setSize(width, height);
this.bufferHitCanvas.setSize(width, height);
for (n = 0; n < len; n++) {
layer = layers[n];
layer.setSize({ width: width, height: height });
layer.draw();
}
}
};
Stage.prototype.add = function (layer) {
if (arguments.length > 1) {
for (var i = 0; i < arguments.length; i++) {
this.add(arguments[i]);
}
return this;
}
_super.prototype.add.call(this, layer);
var length = this.children.length;
if (length > MAX_LAYERS_NUMBER) {
Util_1.Util.warn('The stage has ' +
length +
' layers. Recommended maximin number of layers is 3-5. Adding more layers into the stage may drop the performance. Rethink your tree structure, you can use Konva.Group.');
}
layer._setCanvasSize(this.width(), this.height());
layer.draw();
if (Global_1.Konva.isBrowser) {
this.content.appendChild(layer.canvas._canvas);
}
return this;
};
Stage.prototype.getParent = function () {
return null;
};
Stage.prototype.getLayer = function () {
return null;
};
Stage.prototype.hasPointerCapture = function (pointerId) {
return PointerEvents.hasPointerCapture(pointerId, this);
};
Stage.prototype.setPointerCapture = function (pointerId) {
PointerEvents.setPointerCapture(pointerId, this);
};
Stage.prototype.releaseCapture = function (pointerId) {
PointerEvents.releaseCapture(pointerId, this);
};
Stage.prototype.getLayers = function () {
return this.getChildren();
};
Stage.prototype._bindContentEvents = function () {
if (!Global_1.Konva.isBrowser) {
return;
}
for (var n = 0; n < eventsLength; n++) {
addEvent(this, EVENTS[n]);
}
};
Stage.prototype._mouseenter = function (evt) {
this.setPointersPositions(evt);
this._fire(MOUSEENTER, { evt: evt, target: this, currentTarget: this });
};
Stage.prototype._mouseover = function (evt) {
this.setPointersPositions(evt);
this._fire(CONTENT_MOUSEOVER, { evt: evt });
this._fire(MOUSEOVER, { evt: evt, target: this, currentTarget: this });
};
Stage.prototype._mouseout = function (evt) {
this.setPointersPositions(evt);
var targetShape = this.targetShape;
if (targetShape && !DragAndDrop_1.DD.isDragging) {
targetShape._fireAndBubble(MOUSEOUT, { evt: evt });
targetShape._fireAndBubble(MOUSELEAVE, { evt: evt });
this.targetShape = null;
}
else if (!DragAndDrop_1.DD.isDragging) {
this._fire(MOUSELEAVE, {
evt: evt,
target: this,
currentTarget: this
});
this._fire(MOUSEOUT, {
evt: evt,
target: this,
currentTarget: this
});
}
this.pointerPos = undefined;
this._fire(CONTENT_MOUSEOUT, { evt: evt });
};
Stage.prototype._mousemove = function (evt) {
if (Global_1.Konva.UA.ieMobile) {
return this._touchmove(evt);
}
this.setPointersPositions(evt);
var shape;
if (!DragAndDrop_1.DD.isDragging) {
shape = this.getIntersection(this.getPointerPosition());
if (shape && shape.isListening()) {
var differentTarget = !this.targetShape || this.targetShape !== shape;
if (!DragAndDrop_1.DD.isDragging && differentTarget) {
if (this.targetShape) {
this.targetShape._fireAndBubble(MOUSEOUT, { evt: evt }, shape);
this.targetShape._fireAndBubble(MOUSELEAVE, { evt: evt }, shape);
}
shape._fireAndBubble(MOUSEOVER, { evt: evt }, this.targetShape);
shape._fireAndBubble(MOUSEENTER, { evt: evt }, this.targetShape);
this.targetShape = shape;
}
else {
shape._fireAndBubble(MOUSEMOVE, { evt: evt });
}
}
else {
if (this.targetShape && !DragAndDrop_1.DD.isDragging) {
this.targetShape._fireAndBubble(MOUSEOUT, { evt: evt });
this.targetShape._fireAndBubble(MOUSELEAVE, { evt: evt });
this._fire(MOUSEOVER, {
evt: evt,
target: this,
currentTarget: this
});
this.targetShape = null;
}
this._fire(MOUSEMOVE, {
evt: evt,
target: this,
currentTarget: this
});
}
this._fire(CONTENT_MOUSEMOVE, { evt: evt });
}
if (evt.cancelable) {
evt.preventDefault();
}
};
Stage.prototype._mousedown = function (evt) {
if (Global_1.Konva.UA.ieMobile) {
return this._touchstart(evt);
}
this.setPointersPositions(evt);
var shape = this.getIntersection(this.getPointerPosition());
Global_1.Konva.listenClickTap = true;
if (shape && shape.isListening()) {
this.clickStartShape = shape;
shape._fireAndBubble(MOUSEDOWN, { evt: evt });
}
else {
this._fire(MOUSEDOWN, {
evt: evt,
target: this,
currentTarget: this
});
}
this._fire(CONTENT_MOUSEDOWN, { evt: evt });
};
Stage.prototype._mouseup = function (evt) {
if (Global_1.Konva.UA.ieMobile) {
return this._touchend(evt);
}
this.setPointersPositions(evt);
var shape = this.getIntersection(this.getPointerPosition()), clickStartShape = this.clickStartShape, clickEndShape = this.clickEndShape, fireDblClick = false;
if (Global_1.Konva.inDblClickWindow) {
fireDblClick = true;
clearTimeout(this.dblTimeout);
}
else if (!DragAndDrop_1.DD.justDragged) {
Global_1.Konva.inDblClickWindow = true;
clearTimeout(this.dblTimeout);
}
else if (DragAndDrop_1.DD) {
DragAndDrop_1.DD.justDragged = false;
}
this.dblTimeout = setTimeout(function () {
Global_1.Konva.inDblClickWindow = false;
}, Global_1.Konva.dblClickWindow);
if (shape && shape.isListening()) {
this.clickEndShape = shape;
shape._fireAndBubble(MOUSEUP, { evt: evt });
if (Global_1.Konva.listenClickTap &&
clickStartShape &&
clickStartShape._id === shape._id) {
shape._fireAndBubble(CLICK, { evt: evt });
if (fireDblClick && clickEndShape && clickEndShape._id === shape._id) {
shape._fireAndBubble(DBL_CLICK, { evt: evt });
}
}
}
else {
this._fire(MOUSEUP, { evt: evt, target: this, currentTarget: this });
if (Global_1.Konva.listenClickTap) {
this._fire(CLICK, { evt: evt, target: this, currentTarget: this });
}
if (fireDblClick) {
this._fire(DBL_CLICK, {
evt: evt,
target: this,
currentTarget: this
});
}
}
this._fire(CONTENT_MOUSEUP, { evt: evt });
if (Global_1.Konva.listenClickTap) {
this._fire(CONTENT_CLICK, { evt: evt });
if (fireDblClick) {
this._fire(CONTENT_DBL_CLICK, { evt: evt });
}
}
Global_1.Konva.listenClickTap = false;
if (evt.cancelable) {
evt.preventDefault();
}
};
Stage.prototype._contextmenu = function (evt) {
this.setPointersPositions(evt);
var shape = this.getIntersection(this.getPointerPosition());
if (shape && shape.isListening()) {
shape._fireAndBubble(CONTEXTMENU, { evt: evt });
}
else {
this._fire(CONTEXTMENU, {
evt: evt,
target: this,
currentTarget: this
});
}
this._fire(CONTENT_CONTEXTMENU, { evt: evt });
};
Stage.prototype._touchstart = function (evt) {
this.setPointersPositions(evt);
var shape = this.getIntersection(this.getPointerPosition());
Global_1.Konva.listenClickTap = true;
if (shape && shape.isListening()) {
this.tapStartShape = shape;
shape._fireAndBubble(TOUCHSTART, { evt: evt });
if (shape.isListening() && shape.preventDefault() && evt.cancelable) {
evt.preventDefault();
}
}
else {
this._fire(TOUCHSTART, {
evt: evt,
target: this,
currentTarget: this
});
}
this._fire(CONTENT_TOUCHSTART, { evt: evt });
};
Stage.prototype._touchend = function (evt) {
this.setPointersPositions(evt);
var shape = this.getIntersection(this.getPointerPosition()), fireDblClick = false;
if (Global_1.Konva.inDblClickWindow) {
fireDblClick = true;
clearTimeout(this.dblTimeout);
}
else {
Global_1.Konva.inDblClickWindow = true;
clearTimeout(this.dblTimeout);
}
this.dblTimeout = setTimeout(function () {
Global_1.Konva.inDblClickWindow = false;
}, Global_1.Konva.dblClickWindow);
if (shape && shape.isListening()) {
shape._fireAndBubble(TOUCHEND, { evt: evt });
if (Global_1.Konva.listenClickTap &&
this.tapStartShape &&
shape._id === this.tapStartShape._id) {
shape._fireAndBubble(TAP, { evt: evt });
if (fireDblClick) {
shape._fireAndBubble(DBL_TAP, { evt: evt });
}
}
if (shape.isListening() && shape.preventDefault() && evt.cancelable) {
evt.preventDefault();
}
}
else {
this._fire(TOUCHEND, { evt: evt, target: this, currentTarget: this });
if (Global_1.Konva.listenClickTap) {
this._fire(TAP, { evt: evt, target: this, currentTarget: this });
}
if (fireDblClick) {
this._fire(DBL_TAP, {
evt: evt,
target: this,
currentTarget: this
});
}
}
this._fire(CONTENT_TOUCHEND, { evt: evt });
if (Global_1.Konva.listenClickTap) {
this._fire(CONTENT_TAP, { evt: evt });
if (fireDblClick) {
this._fire(CONTENT_DBL_TAP, { evt: evt });
}
}
Global_1.Konva.listenClickTap = false;
};
Stage.prototype._touchmove = function (evt) {
this.setPointersPositions(evt);
var shape;
if (!DragAndDrop_1.DD.isDragging) {
shape = this.getIntersection(this.getPointerPosition());
if (shape && shape.isListening()) {
shape._fireAndBubble(TOUCHMOVE, { evt: evt });
if (shape.isListening() && shape.preventDefault() && evt.cancelable) {
evt.preventDefault();
}
}
else {
this._fire(TOUCHMOVE, {
evt: evt,
target: this,
currentTarget: this
});
}
this._fire(CONTENT_TOUCHMOVE, { evt: evt });
}
if (DragAndDrop_1.DD.isDragging && DragAndDrop_1.DD.node.preventDefault() && evt.cancelable) {
evt.preventDefault();
}
};
Stage.prototype._wheel = function (evt) {
this.setPointersPositions(evt);
var shape = this.getIntersection(this.getPointerPosition());
if (shape && shape.isListening()) {
shape._fireAndBubble(WHEEL, { evt: evt });
}
else {
this._fire(WHEEL, {
evt: evt,
target: this,
currentTarget: this
});
}
this._fire(CONTENT_WHEEL, { evt: evt });
};
Stage.prototype._pointerdown = function (evt) {
if (!Global_1.Konva._pointerEventsEnabled) {
return;
}
this.setPointersPositions(evt);
var shape = PointerEvents.getCapturedShape(evt.pointerId) ||
this.getIntersection(this.getPointerPosition());
if (shape) {
shape._fireAndBubble(POINTERDOWN, PointerEvents.createEvent(evt));
}
};
Stage.prototype._pointermove = function (evt) {
if (!Global_1.Konva._pointerEventsEnabled) {
return;
}
this.setPointersPositions(evt);
var shape = PointerEvents.getCapturedShape(evt.pointerId) ||
this.getIntersection(this.getPointerPosition());
if (shape) {
shape._fireAndBubble(POINTERMOVE, PointerEvents.createEvent(evt));
}
};
Stage.prototype._pointerup = function (evt) {
if (!Global_1.Konva._pointerEventsEnabled) {
return;
}
this.setPointersPositions(evt);
var shape = PointerEvents.getCapturedShape(evt.pointerId) ||
this.getIntersection(this.getPointerPosition());
if (shape) {
shape._fireAndBubble(POINTERUP, PointerEvents.createEvent(evt));
}
PointerEvents.releaseCapture(evt.pointerId);
};
Stage.prototype._pointercancel = function (evt) {
if (!Global_1.Konva._pointerEventsEnabled) {
return;
}
this.setPointersPositions(evt);
var shape = PointerEvents.getCapturedShape(evt.pointerId) ||
this.getIntersection(this.getPointerPosition());
if (shape) {
shape._fireAndBubble(POINTERUP, PointerEvents.createEvent(evt));
}
PointerEvents.releaseCapture(evt.pointerId);
};
Stage.prototype._lostpointercapture = function (evt) {
PointerEvents.releaseCapture(evt.pointerId);
};
Stage.prototype.setPointersPositions = function (evt) {
var contentPosition = this._getContentPosition(), x = null, y = null;
evt = evt ? evt : window.event;
if (evt.touches !== undefined) {
if (evt.touches.length > 0) {
var touch = evt.touches[0];
x = touch.clientX - contentPosition.left;
y = touch.clientY - contentPosition.top;
}
}
else {
x = evt.clientX - contentPosition.left;
y = evt.clientY - contentPosition.top;
}
if (x !== null && y !== null) {
this.pointerPos = {
x: x,
y: y
};
}
};
Stage.prototype._setPointerPosition = function (evt) {
Util_1.Util.warn('Method _setPointerPosition is deprecated. Use "stage.setPointersPositions(event)" instead.');
this.setPointersPositions(evt);
};
Stage.prototype._getContentPosition = function () {
var rect = this.content.getBoundingClientRect
? this.content.getBoundingClientRect()
: { top: 0, left: 0 };
return {
top: rect.top,
left: rect.left
};
};
Stage.prototype._buildDOM = function () {
this.bufferCanvas = new Canvas_1.SceneCanvas();
this.bufferHitCanvas = new Canvas_1.HitCanvas({ pixelRatio: 1 });
if (!Global_1.Konva.isBrowser) {
return;
}
var container = this.container();
if (!container) {
throw 'Stage has no container. A container is required.';
}
container.innerHTML = EMPTY_STRING;
this.content = document.createElement('div');
this.content.style.position = RELATIVE;
this.content.style.userSelect = 'none';
this.content.className = KONVA_CONTENT;
this.content.setAttribute('role', 'presentation');
container.appendChild(this.content);
this._resizeDOM();
};
Stage.prototype.cache = function () {
Util_1.Util.warn('Cache function is not allowed for stage. You may use cache only for layers, groups and shapes.');
return this;
};
Stage.prototype.clearCache = function () {
return this;
};
Stage.prototype.batchDraw = function () {
this.children.each(function (layer) {
layer.batchDraw();
});
return this;
};
return Stage;
}(Container_1.Container));
exports.Stage = Stage;
Stage.prototype.nodeType = STAGE;
Global_2._registerNode(Stage);
Factory_1.Factory.addGetterSetter(Stage, 'container');