mirror of
https://github.com/konvajs/konva.git
synced 2025-09-18 09:50:05 +08:00
rewrote dynamic drag and drop layer because the old implementation had too many problems. This resolves all of the dynamic drag and drop issues in one go. draw() method can now be applied to any node, not just the Stage and Layers. drag events now bubble
This commit is contained in:
@@ -11,8 +11,18 @@
|
||||
* @param {Number} width
|
||||
* @param {Number} height
|
||||
*/
|
||||
Kinetic.Canvas = function(width, height, pixelRatio) {
|
||||
this.pixelRatio = pixelRatio || _pixelRatio;
|
||||
Kinetic.Canvas = function(config) {
|
||||
this.init(config);
|
||||
};
|
||||
|
||||
Kinetic.Canvas.prototype = {
|
||||
init: function(config) {
|
||||
var config = config || {},
|
||||
width = config.width || 0,
|
||||
height = config.height || 0,
|
||||
pixelRatio = config.pixelRatio || _pixelRatio;
|
||||
|
||||
this.pixelRatio = pixelRatio;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.element = document.createElement('canvas');
|
||||
@@ -21,10 +31,8 @@
|
||||
this.element.style.border = 0;
|
||||
this.element.style.background = 'transparent';
|
||||
this.context = this.element.getContext('2d');
|
||||
this.setSize(width || 0, height || 0);
|
||||
};
|
||||
|
||||
Kinetic.Canvas.prototype = {
|
||||
this.setSize(width, height);
|
||||
},
|
||||
/**
|
||||
* clear canvas
|
||||
* @name clear
|
||||
|
@@ -196,28 +196,36 @@
|
||||
children[n].index = n;
|
||||
}
|
||||
},
|
||||
/*
|
||||
* draw both scene and hit graphs
|
||||
*/
|
||||
draw: function() {
|
||||
this.drawScene();
|
||||
this.drawHit();
|
||||
},
|
||||
drawScene: function(canvas) {
|
||||
var clip = !!this.getClipFunc() && canvas;
|
||||
var layer = this.getLayer(),
|
||||
clip = !!this.getClipFunc(),
|
||||
stage = this.getStage(),
|
||||
children, len;
|
||||
|
||||
if (!canvas && layer) {
|
||||
canvas = layer.getCanvas();
|
||||
}
|
||||
|
||||
if(layer && layer.getClearBeforeDraw()) {
|
||||
canvas.clear();
|
||||
}
|
||||
|
||||
if(this.isVisible()) {
|
||||
if (clip) {
|
||||
canvas._clip(this);
|
||||
}
|
||||
if(this.isVisible()) {
|
||||
var children = this.children, len = children.length;
|
||||
|
||||
children = this.children,
|
||||
len = children.length;
|
||||
|
||||
for(var n = 0; n < len; n++) {
|
||||
children[n].drawScene(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
if (clip) {
|
||||
canvas.getContext().restore();
|
||||
}
|
||||
}
|
||||
},
|
||||
drawHit: function() {
|
||||
var clip = !!this.getClipFunc() && this.nodeType !== 'Stage',
|
||||
|
@@ -11,25 +11,6 @@
|
||||
Kinetic.getNodeDragging = function() {
|
||||
return Kinetic.DD.node;
|
||||
};
|
||||
|
||||
Kinetic.DD._setupDragLayerAndGetContainer = function(no) {
|
||||
var stage = no.getStage(), nodeType = no.nodeType, lastContainer, group;
|
||||
|
||||
// re-construct node tree
|
||||
no._eachAncestorReverse(function(node) {
|
||||
if(node.nodeType === 'Layer') {
|
||||
stage.dragLayer.setAttrs(node.getAttrs());
|
||||
lastContainer = stage.dragLayer;
|
||||
stage.add(stage.dragLayer);
|
||||
}
|
||||
else if(node.nodeType === 'Group') {
|
||||
group = new Kinetic.Group(node.getAttrs());
|
||||
lastContainer.add(group);
|
||||
lastContainer = group;
|
||||
}
|
||||
});
|
||||
return lastContainer;
|
||||
};
|
||||
Kinetic.DD._initDragLayer = function(stage) {
|
||||
stage.dragLayer = new Kinetic.Layer();
|
||||
stage.dragLayer.getCanvas().getElement().className = 'kinetic-drag-and-drop-layer';
|
||||
@@ -75,12 +56,11 @@
|
||||
}
|
||||
// else if group, shape, or layer
|
||||
else {
|
||||
if((nodeType === 'Group' || nodeType === 'Shape') && node.getDragOnTop() && dd.prevParent) {
|
||||
node.moveTo(dd.prevParent);
|
||||
if((nodeType === 'Group' || nodeType === 'Shape') && node.getDragOnTop()) {
|
||||
node.getStage().dragLayer.remove();
|
||||
dd.prevParent = null;
|
||||
}
|
||||
|
||||
node.moveToTop();
|
||||
node.getLayer().draw();
|
||||
}
|
||||
|
||||
@@ -97,7 +77,11 @@
|
||||
}
|
||||
};
|
||||
Kinetic.Node.prototype._startDrag = function(evt) {
|
||||
var dd = Kinetic.DD, that = this, stage = this.getStage(), pos = stage.getUserPosition();
|
||||
var dd = Kinetic.DD,
|
||||
that = this,
|
||||
stage = this.getStage(),
|
||||
layer = this.getLayer(),
|
||||
pos = stage.getUserPosition();
|
||||
|
||||
if(pos) {
|
||||
var m = this.getTransform().getTranslation(), ap = this.getAbsolutePosition(), nodeType = this.nodeType, container;
|
||||
@@ -105,34 +89,34 @@
|
||||
dd.node = this;
|
||||
dd.offset.x = pos.x - ap.x;
|
||||
dd.offset.y = pos.y - ap.y;
|
||||
dd.anim.node = this;
|
||||
|
||||
// Stage and Layer node types
|
||||
if(nodeType === 'Stage' || nodeType === 'Layer') {
|
||||
dd.anim.node = this;
|
||||
dd.anim.start();
|
||||
}
|
||||
|
||||
// Group or Shape node types
|
||||
else {
|
||||
if(this.getDragOnTop()) {
|
||||
container = dd._setupDragLayerAndGetContainer(this);
|
||||
dd.anim.node = stage.dragLayer;
|
||||
dd.prevParent = this.getParent();
|
||||
|
||||
|
||||
// WARNING: it's important to delay the moveTo operation,
|
||||
// layer redraws, and anim.start() until after the method execution
|
||||
// has completed or else there will be a flicker on mobile devices
|
||||
// due to the time it takes to append the dd canvas to the DOM
|
||||
setTimeout(function() {
|
||||
if(dd.node) {
|
||||
that.moveTo(container);
|
||||
dd.prevParent.getLayer().draw();
|
||||
stage.dragLayer.draw();
|
||||
//setTimeout(function() {
|
||||
//if(dd.node) {
|
||||
// clear shape from layer canvas
|
||||
that.setVisible(false);
|
||||
layer.draw();
|
||||
that.setVisible(true);
|
||||
stage.add(stage.dragLayer);
|
||||
dd.anim.start();
|
||||
}
|
||||
}, 0);
|
||||
//}
|
||||
//}, 0);
|
||||
}
|
||||
else {
|
||||
dd.anim.node = this.getLayer();
|
||||
dd.anim.start();
|
||||
}
|
||||
}
|
||||
|
37
src/Layer.js
37
src/Layer.js
@@ -34,8 +34,6 @@
|
||||
* @methodOf Kinetic.Layer.prototype
|
||||
*/
|
||||
draw: function() {
|
||||
var context = this.getContext();
|
||||
|
||||
// before draw handler
|
||||
if(this.beforeDrawFunc !== undefined) {
|
||||
this.beforeDrawFunc.call(this);
|
||||
@@ -116,7 +114,8 @@
|
||||
* @methodOf Kinetic.Layer.prototype
|
||||
*/
|
||||
getCanvas: function() {
|
||||
return this.canvas;
|
||||
var stage = this.getStage();
|
||||
return (stage && stage._isTempDDLayerActive()) ? stage.dragLayer.canvas : this.canvas;
|
||||
},
|
||||
/**
|
||||
* get layer canvas context
|
||||
@@ -124,7 +123,7 @@
|
||||
* @methodOf Kinetic.Layer.prototype
|
||||
*/
|
||||
getContext: function() {
|
||||
return this.canvas.context;
|
||||
return this.getCanvas().getContext();
|
||||
},
|
||||
/**
|
||||
* clear canvas tied to the layer
|
||||
@@ -138,11 +137,11 @@
|
||||
setVisible: function(visible) {
|
||||
Kinetic.Node.prototype.setVisible.call(this, visible);
|
||||
if(visible) {
|
||||
this.canvas.element.style.display = 'block';
|
||||
this.getCanvas().element.style.display = 'block';
|
||||
this.hitCanvas.element.style.display = 'block';
|
||||
}
|
||||
else {
|
||||
this.canvas.element.style.display = 'none';
|
||||
this.getCanvas().element.style.display = 'none';
|
||||
this.hitCanvas.element.style.display = 'none';
|
||||
}
|
||||
},
|
||||
@@ -150,13 +149,13 @@
|
||||
Kinetic.Node.prototype.setZIndex.call(this, index);
|
||||
var stage = this.getStage();
|
||||
if(stage) {
|
||||
stage.content.removeChild(this.canvas.element);
|
||||
stage.content.removeChild(this.getCanvas().element);
|
||||
|
||||
if(index < stage.getChildren().length - 1) {
|
||||
stage.content.insertBefore(this.canvas.element, stage.getChildren()[index + 1].canvas.element);
|
||||
stage.content.insertBefore(this.getCanvas().element, stage.getChildren()[index + 1].getCanvas().element);
|
||||
}
|
||||
else {
|
||||
stage.content.appendChild(this.canvas.element);
|
||||
stage.content.appendChild(this.getCanvas().element);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -164,21 +163,21 @@
|
||||
Kinetic.Node.prototype.moveToTop.call(this);
|
||||
var stage = this.getStage();
|
||||
if(stage) {
|
||||
stage.content.removeChild(this.canvas.element);
|
||||
stage.content.appendChild(this.canvas.element);
|
||||
stage.content.removeChild(this.getCanvas().element);
|
||||
stage.content.appendChild(this.getCanvas().element);
|
||||
}
|
||||
},
|
||||
moveUp: function() {
|
||||
if(Kinetic.Node.prototype.moveUp.call(this)) {
|
||||
var stage = this.getStage();
|
||||
if(stage) {
|
||||
stage.content.removeChild(this.canvas.element);
|
||||
stage.content.removeChild(this.getCanvas().element);
|
||||
|
||||
if(this.index < stage.getChildren().length - 1) {
|
||||
stage.content.insertBefore(this.canvas.element, stage.getChildren()[this.index + 1].canvas.element);
|
||||
stage.content.insertBefore(this.getCanvas().element, stage.getChildren()[this.index + 1].getCanvas().element);
|
||||
}
|
||||
else {
|
||||
stage.content.appendChild(this.canvas.element);
|
||||
stage.content.appendChild(this.getCanvas().element);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,8 +187,8 @@
|
||||
var stage = this.getStage();
|
||||
if(stage) {
|
||||
var children = stage.getChildren();
|
||||
stage.content.removeChild(this.canvas.element);
|
||||
stage.content.insertBefore(this.canvas.element, children[this.index + 1].canvas.element);
|
||||
stage.content.removeChild(this.getCanvas().element);
|
||||
stage.content.insertBefore(this.getCanvas().element, children[this.index + 1].getCanvas().element);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -198,8 +197,8 @@
|
||||
var stage = this.getStage();
|
||||
if(stage) {
|
||||
var children = stage.getChildren();
|
||||
stage.content.removeChild(this.canvas.element);
|
||||
stage.content.insertBefore(this.canvas.element, children[1].canvas.element);
|
||||
stage.content.removeChild(this.getCanvas().element);
|
||||
stage.content.insertBefore(this.getCanvas().element, children[1].getCanvas().element);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -210,7 +209,7 @@
|
||||
* remove layer from stage
|
||||
*/
|
||||
remove: function() {
|
||||
var stage = this.getStage(), canvas = this.canvas, element = canvas.element;
|
||||
var stage = this.getStage(), canvas = this.getCanvas(), element = canvas.element;
|
||||
Kinetic.Node.prototype.remove.call(this);
|
||||
|
||||
if(stage && canvas && Kinetic.Type._isInDocument(element)) {
|
||||
|
22
src/Node.js
22
src/Node.js
@@ -696,7 +696,11 @@
|
||||
|
||||
//if width and height are defined, create new canvas to draw on, else reuse stage buffer canvas
|
||||
if(config.width && config.height) {
|
||||
canvas = new Kinetic.SceneCanvas(config.width, config.height, 1);
|
||||
canvas = new Kinetic.SceneCanvas({
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
pixelRatio: 1
|
||||
});
|
||||
}
|
||||
else {
|
||||
canvas = this.getStage().bufferCanvas;
|
||||
@@ -910,6 +914,22 @@
|
||||
events[i].handler.apply(this, [evt]);
|
||||
}
|
||||
}
|
||||
},
|
||||
/*
|
||||
* draw both scene and hit graphs.
|
||||
* @name draw
|
||||
* @methodOf Kinetic.Node.prototype
|
||||
* the scene renderer
|
||||
*/
|
||||
draw: function() {
|
||||
var layer = this.getLayer();
|
||||
|
||||
if(layer && layer.getClearBeforeDraw()) {
|
||||
layer.getCanvas().clear();
|
||||
}
|
||||
|
||||
this.drawScene();
|
||||
this.drawHit();
|
||||
}
|
||||
};
|
||||
|
||||
|
10
src/Shape.js
10
src/Shape.js
@@ -168,7 +168,10 @@
|
||||
delete Kinetic.Global.shapes[this.colorKey];
|
||||
},
|
||||
drawScene: function(canvas) {
|
||||
var attrs = this.attrs, drawFunc = attrs.drawFunc, canvas = canvas || this.getLayer().getCanvas(), context = canvas.getContext();
|
||||
var attrs = this.getAttrs(),
|
||||
drawFunc = attrs.drawFunc,
|
||||
canvas = canvas || this.getLayer().getCanvas(),
|
||||
context = canvas.getContext();
|
||||
|
||||
if(drawFunc && this.isVisible()) {
|
||||
context.save();
|
||||
@@ -180,7 +183,10 @@
|
||||
}
|
||||
},
|
||||
drawHit: function() {
|
||||
var attrs = this.attrs, drawFunc = attrs.drawHitFunc || attrs.drawFunc, canvas = this.getLayer().hitCanvas, context = canvas.getContext();
|
||||
var attrs = this.getAttrs(),
|
||||
drawFunc = attrs.drawHitFunc || attrs.drawFunc,
|
||||
canvas = this.getLayer().hitCanvas,
|
||||
context = canvas.getContext();
|
||||
|
||||
if(drawFunc && this.isVisible() && this.isListening()) {
|
||||
context.save();
|
||||
|
15
src/Stage.js
15
src/Stage.js
@@ -163,7 +163,10 @@
|
||||
quality = config.quality || null,
|
||||
x = config.x || 0,
|
||||
y = config.y || 0,
|
||||
canvas = new Kinetic.SceneCanvas(config.width || this.getWidth(), config.height || this.getHeight()),
|
||||
canvas = new Kinetic.SceneCanvas({
|
||||
width: config.width || this.getWidth(),
|
||||
height: config.height || this.getHeight()
|
||||
}),
|
||||
context = canvas.getContext(),
|
||||
layers = this.children;
|
||||
|
||||
@@ -296,6 +299,16 @@
|
||||
getDragLayer: function() {
|
||||
return this.dragLayer;
|
||||
},
|
||||
getParent: function() {
|
||||
return null;
|
||||
},
|
||||
getLayer: function() {
|
||||
return null;
|
||||
},
|
||||
_isTempDDLayerActive: function() {
|
||||
var dragLayer = this.dragLayer;
|
||||
return dragLayer && dragLayer.getStage();
|
||||
},
|
||||
_setUserPosition: function(evt) {
|
||||
if(!evt) {
|
||||
evt = window.event;
|
||||
|
@@ -46,26 +46,40 @@ Test.Modules.DD = {
|
||||
// which can't be simulated. call _endDrag manually
|
||||
Kinetic.DD._endDrag();
|
||||
},
|
||||
'*test dragstart, dragmove, dragend': function(containerId) {
|
||||
'test dragstart, dragmove, dragend': function(containerId) {
|
||||
var stage = new Kinetic.Stage({
|
||||
container: containerId,
|
||||
width: 578,
|
||||
height: 200,
|
||||
throttle: 999
|
||||
height: 200
|
||||
});
|
||||
var layer = new Kinetic.Layer();
|
||||
|
||||
var greenCircle = new Kinetic.Circle({
|
||||
x: 40,
|
||||
y: 40,
|
||||
radius: 20,
|
||||
strokeWidth: 4,
|
||||
fill: 'green',
|
||||
stroke: 'black',
|
||||
opacity: 0.5
|
||||
});
|
||||
|
||||
|
||||
var circle = new Kinetic.Circle({
|
||||
x: 380,
|
||||
y: stage.getHeight() / 2,
|
||||
radius: 70,
|
||||
strokeWidth: 4,
|
||||
fill: 'red',
|
||||
stroke: 'black'
|
||||
stroke: 'black',
|
||||
opacity: 0.5
|
||||
|
||||
});
|
||||
|
||||
circle.setDraggable(true);
|
||||
|
||||
layer.add(circle);
|
||||
layer.add(greenCircle);
|
||||
stage.add(layer);
|
||||
|
||||
var top = stage.content.getBoundingClientRect().top;
|
||||
@@ -87,7 +101,7 @@ Test.Modules.DD = {
|
||||
*/
|
||||
|
||||
layer.on('dragmove', function() {
|
||||
console.log('move');
|
||||
//console.log('move');
|
||||
});
|
||||
|
||||
circle.on('dragend', function() {
|
||||
@@ -135,6 +149,10 @@ Test.Modules.DD = {
|
||||
test(dragEnd, 'dragend event was not triggered');
|
||||
|
||||
warn(layer.toDataURL() === dataUrls['drag circle after'], 'end data url is incorrect');
|
||||
|
||||
console.log(layer);
|
||||
|
||||
console.log(layer.eventListeners['dragmove']);
|
||||
},
|
||||
'cancel drag and drop by setting draggable to false': function(containerId) {
|
||||
var stage = new Kinetic.Stage({
|
||||
|
Reference in New Issue
Block a user