0
votes

Code breaks when anchors overlap. I have anchors for resizing draggable groups of shapes as given in below tutorial.

I also have the functionality to delete the selected group. The delete functionality breaks the code when anchors (of two different groups) overlap and overlapping anchor is selected (mousedown).

if (layer !== null && e.keyCode == 46) {  //Delete Key = 46
    SelectedShape.remove();
    layer.batchDraw();
    }
});

//Selecting on mousedown
layer.on('mousedown', function (evt) {
    SelectedShape = evt.targetNode.getParent();
});

Error message: 0x800a138f - JavaScript runtime error: Unable to get property 'getLayer' of undefined or null reference

http://www.html5canvastutorials.com/labs/html5-canvas-drag-and-drop-resize-and-invert-images/

Sample code

var SelectedShape = null;

function update(activeAnchor) {
    var group = activeAnchor.getParent();
    var topLeft = group.get('.topLeft')[0];
    var topRight = group.get('.topRight')[0];
    var bottomRight = group.get('.bottomRight')[0];
    var bottomLeft = group.get('.bottomLeft')[0];
    var rect = group.get('.rect')[0];

    var anchorX = activeAnchor.getX();
    var anchorY = activeAnchor.getY();

    // update anchor positions
    switch (activeAnchor.getName()) {
        case 'topLeft':
            topRight.setY(anchorY);
            bottomLeft.setX(anchorX);
            break;
        case 'topRight':
            topLeft.setY(anchorY);
            bottomRight.setX(anchorX);
            break;
        case 'bottomRight':
            bottomLeft.setY(anchorY);
            topRight.setX(anchorX);
            break;
        case 'bottomLeft':
            bottomRight.setY(anchorY);
            topLeft.setX(anchorX);
            break;
    }

    rect.setPosition(topLeft.getPosition());

    var width = topRight.getX() - topLeft.getX();
    var height = bottomLeft.getY() - topLeft.getY();
    if (width && height) {
        rect.setSize(width, height);
    }
}
function addAnchor(group, x, y, name) {
    var stage = group.getStage();
    var layer = group.getLayer();

    var anchor = new Kinetic.Circle({
        x: x,
        y: y,
        stroke: '#666',
        fill: '#ddd',
        strokeWidth: 2,
        radius: 8,
        name: name,
        draggable: true,
        dragOnTop: false
    });

    anchor.on('dragmove', function () {
        update(this);
        layer.draw();
    });
    anchor.on('mousedown touchstart', function () {
        group.setDraggable(false);
        this.moveToTop();
    });
    anchor.on('dragend', function () {
        group.setDraggable(true);
        layer.draw();
    });
    group.add(anchor);
}

    var stage = new Kinetic.Stage({
        container: 'container',
        width: 578,
        height: 400
    });
    var darthVaderGroup = new Kinetic.Group({
        x: 270,
        y: 100,
        draggable: true
    });
    var yodaGroup = new Kinetic.Group({
        x: 100,
        y: 110,
        draggable: true
    });

    var Group3 = new Kinetic.Group({
        x: 100,
        y: 300,
        draggable: true
    });
    var layer = new Kinetic.Layer();

    /* add the groups to the layer and the layer to the stage*/
    layer.add(darthVaderGroup);
    layer.add(yodaGroup);
    layer.add(Group3);
    stage.add(layer);

    // darth vader
    var darthVaderImg = new Kinetic.Rect({
        x: 0,
        y: 0,
        width: 200,
        height: 138,
        fill: 'green',
        stroke: 'black',
        strokeWidth: 4,
        name: 'rect'
    });

    darthVaderGroup.add(darthVaderImg);
    addAnchor(darthVaderGroup, 0, 0, 'topLeft');
    addAnchor(darthVaderGroup, 200, 0, 'topRight');
    addAnchor(darthVaderGroup, 200, 138, 'bottomRight');
    addAnchor(darthVaderGroup, 0, 138, 'bottomLeft');

    darthVaderGroup.on('dragstart', function () {
        this.moveToTop();
    });
    // yoda
    var yodaImg = new Kinetic.Rect({
        x: 0,
        y: 0,
        width: 93,
        height: 104,
        fill: 'red',
        stroke: 'black',
        strokeWidth: 4,
        name: 'rect'
    });

    yodaGroup.add(yodaImg);
    addAnchor(yodaGroup, 0, 0, 'topLeft');
    addAnchor(yodaGroup, 93, 0, 'topRight');
    addAnchor(yodaGroup, 93, 104, 'bottomRight');
    addAnchor(yodaGroup, 0, 104, 'bottomLeft');

    yodaGroup.on('dragstart', function () {
        this.moveToTop();
    });

    var rect3 = new Kinetic.Rect({
        x: 0,
        y: 0,
        width: 93,
        height: 104,
        fill: 'blue',
        stroke: 'black',
        strokeWidth: 4,
        name: 'rect'
    });

    Group3.add(rect3);
    addAnchor(Group3, 0, 0, 'topLeft');
    addAnchor(Group3, 93, 0, 'topRight');
    addAnchor(Group3, 93, 104, 'bottomRight');
    addAnchor(Group3, 0, 104, 'bottomLeft');

    Group3.on('dragstart', function () {
        this.moveToTop();
    });

    stage.draw();

    //Deleting selected shape 
    window.addEventListener('keydown', function (e) {
        if (layer !== null && e.keyCode == 46) {  //Delete Key = 46
            SelectedShape.remove();
            layer.batchDraw();
        }
    });

    //Selecting on mousedown
    layer.on('mousedown', function (evt) {
        SelectedShape = evt.targetNode.getParent();
    });
1
What did you want to achieve? Your change reports Uncaught TypeError: Cannot call method 'remove' of null even if you move the mouse to some white space and press delete. In that case SelectedShape is still nullAnto Jurković

1 Answers

0
votes

The first issue: you have to check against null because SelectedShape can be null when you press delete. And I'd change SelectedShape to selectedShape. Because it is not constructor.

The second issue are event handlers which are not detached from anchors before removal of one group shape.

Solution: anchors are now processed in reverse order and events are detached before removal. Code changes:

    // Deleting selected shape 
    window.addEventListener('keydown', function (e) {
        if (layer !== null && e.keyCode == 46) {  //Delete Key = 46
            if (selectedShape != null) {
                var shapeChildren = selectedShape.getChildren();
                for (var i = shapeChildren.length - 1; i >= 0 ; i--)
                    detachChild(shapeChildren[i]);

                selectedShape.remove();

                // layer.draw() what is better
                layer.batchDraw();
            }
        }   
    });

And event detach and parts removal:

function detachChild(childShape) {
    if (childShape instanceof Kinetic.Circle) {
        detachAnchor(childShape);
    } else {
        // image
        childShape.remove();
    }
}

function detachAnchor(anchor) {
    anchor.off("dragmove");
    anchor.off("mousedown touchstart");
    anchor.off("dragend");
    anchor.off("mouseover");
    anchor.off("mouseout");

    anchor.remove();
}

I also tried to used removeChildren() and destroyChildren() but did not manage to get it w/o errors.

Demo at JS Bin