错误导致 Konva.js 舞台上不时显示任何内容。该怎么办?

Bug causes that nothing is shown on Konva.js stage only from time to time. What to do?

我的脚本有一个非常奇怪的行为:仅时不时(很少一次接连加载 3-4 次,但更可能是每 7 到 150 次试验一次)加载脚本,但我只看到白色 canvas 并收到错误消息:

Uncaught TypeError: Cannot read property 'getParent' of undefinedKonva.Util.addMethods.add @ konva.min.js:44draw @ floorplansurvey.php:950(anonymous function) @ floorplansurvey.php:985images.(anonymous function).onload @ floorplansurvey.php:390

重新加载时它通常会再次运行... 我完全不知道这里发生了什么,错误不可能是 forced/reproduced,我非常感谢你,如果你有什么可以帮助我的。即使是更清晰的分析策略也会有所帮助,很抱歉不具体和长代码

编辑:我在下面做了一个 jsfiddle: https://jsfiddle.net/17548hmv/1/

运行 多次,直到出现错误 这些是代码片段:

@ floorplansurvey.php:390:

function loadImages(sources, draw) {
    //window.location.reload(true);
    var images = {};
    var loadedImages = 0;
    var numImages = 0;
    // get num of sources
    for(var src in sources) {
        numImages++;
    }
    for(var src in sources) {
        images[src] = new Image();
        images[src].onload = function() {
            if(++loadedImages >= numImages) {
                draw(images);
            }
        };
        images[src].src = sources[src];
    }
    delete (loadedImages);
    delete (numImages);
};

@ floorplansurvey.php:985images.(anonymous function).onload:

loadImages(sources, function(images) {
    draw(images);
});

@ floorplansurvey.php:950(anonymous function):

bglayer.add(plan);

@ konva.min.js:44draw: I dont understand this one myself

   add:function(t)
    {if(arguments.length>1)
    {for(var e=0;e<arguments.length;e++)
    this.add(arguments[e]);
    return this}if(t.getParent())return t.moveTo(this),this;
    var n=this.children;
    return 

变量源定义如下:

var sources = {

    Cd: './graphics/Cd.png',
    Cu: './graphics/Cu.png',
    Ca: './graphics/Ca.png',
    Cs: './graphics/Cs.png',
    Cc: './graphics/Cc.png',

    Ed: './graphics/Ed.png',
    Eu: './graphics/Eu.png',
    Ea: './graphics/Ea.png',
    Es: './graphics/Es.png',
    Ec: './graphics/Ec.png',

    Hd: './graphics/Hd.png',
    Hu: './graphics/Hu.png',

...等等

这是绘制函数:

function draw(images) {
for (i=0, len=showdatax.length; i<=len-1; i++)
    {
        //window.location.reload(true);
    var gridcell = new Konva.Rect({
        x: parseInt((imagesizeX - showdatax[i])*scalar-(gridcellsize/2)),
        y: parseInt((imagesizeY - showdatay[i])*scalar-(gridcellsize/2)),
        offset: [0, 0],
        width: gridcellsize,
        height: gridcellsize,
        //fill: 'white',
        //stroke: 'grey',
        //strokeWidth: 2,
        draggable: false,
        id: showdatav[i]
    });
          gridlayer.add(gridcell);
    }
//defaultsettiongs
var init = new Array();
init['x'] = new Array();
init['y'] = new Array();

        init['x']['c'] = 0*scalar;
        init['y']['c'] = 170*scalar; 
        init['x']['e'] = 0*scalar;
        init['y']['e'] = 320*scalar; 
        init['x']['h'] = 0*scalar;
        init['y']['h'] = 470*scalar; 
        init['x']['l'] = 0*scalar;
        init['y']['l'] = 620*scalar; 
        init['x']['s'] = 0*scalar;
        init['y']['s'] = 770*scalar; 
        init['x']['w'] = 0*scalar;
        init['y']['w'] = 920*scalar; var step = 0;
var count = new Array (
'c','e','h','l','s','w');

        count['c'] = 0;

        count['e'] = 0;

        count['h'] = 0;

        count['l'] = 0;

        count['s'] = 0;

        count['w'] = 0;
    var starttime = new Date();
var loghistory = '';
//drag event functions
function mouseoverbox (box,active)
{
    writeMessage('Click and hold the left mouse button and pull this activity icon to the floorplan.');
    box.setFillPatternImage(active);
    box.shadowColor('blue');
    box.moveTo(templayer);
    badgelayer.draw();
    templayer.draw();
}
function dragstarttouchstartbox (box,pos,kind)
{
    //count[kind]++;
    writeMessage('dragstart' + count[kind]);
    var boxx = box.x;
    box.x(pos.x-1.5*gridcellsize);
    var boxy = box.y;
    box.y(pos.y-1.5*gridcellsize);
    var boxrshadowoffsetx = box.shadowOffset();
    box.shadowOffset({x:0.25*gridcellsize,y:0.25*gridcellsize});
    box.moveTo(templayer);
    badgelayer.draw();
    templayer.draw();
}
function dragmovebox (box,pos,kind,success,active,caution)
{
    writeMessage('Your outside of the floorplan. Dropping the activity icon will reset it to its initial position.');
    box.moveTo(templayer);
    var boxx = box.x;
    box.x(pos.x-1.5*gridcellsize);
    var boxy = box.y;
    box.y(pos.y-1.5*gridcellsize);
    var shape = gridlayer.getIntersection(pos);
    if (shape)
    {
        if (!badgelayer.getIntersection(pos))
        {
            writeMessage('Release the mouse button to place this activity icon on position (' + shape.x() + '|' + shape.y() + ')');
            box.x(shape.x()-gridcellsize);
            box.y(shape.y()-gridcellsize);
            box.setFillPatternImage(success);
            box.shadowColor('green');
            badgelayer.draw();
        }
        else
        {
            writeMessage('Position (' + shape.x() + '|' + shape.y() + ') is in use!!! Can\'t allocate second activiy icon here.');
            var boxx = box.x;
            box.x(pos.x-1.5*gridcellsize);
            var boxy = box.y;
            box.y(pos.y-1.5*gridcellsize);
            box.setFillPatternImage(caution);
            box.shadowColor('red');
            badgelayer.draw();
        }
    }
    else
    {
        box.setFillPatternImage(active);
        box.shadowColor('blue');
        templayer.draw();
        badgelayer.draw();
    }
}
function dragendtouchendbox (box,pos,kind,caution,defaultimage)
{
    var shape = gridlayer.getIntersection(pos);
    box.moveTo(badgelayer);
    templayer.draw();
    if (shape && !(badgelayer.getIntersection(pos)))
    {
        writeMessage('Activity icon successfully placed at position (' + shape.x() + '|' + shape.y() + ').');
        box.shadowOffset({x:0,y:0});
        box.shadowColor('green');
    }
    else
    {
        box.shadowColor('red');
        box.setFillPatternImage(caution);
        badgelayer.draw();
        var fromouttween = new Konva.Tween
        ({
            node: box,
            x: init['x'][kind]+imagesizeX*scalar+ gridcellsize,
            y: init['y'][kind],
            easing: Konva.Easings['EaseOut'],
            duration: 0.3
        });
        if (!shape)
        {
            writeMessage('Please drop the activity icons inside the floorplan.');
        }
        else if (badgelayer.getIntersection(pos))
        {
            writeMessage('Please don\'t drop the activity icons onto a used place.');
        }
        fromouttween.play();
        box.shadowColor('black');
        box.setFillPatternImage(defaultimage);
        box.shadowOffset({x:0,y:0});
        badgelayer.draw();
        pos.x=-100;
        pos.y=-100;
    }
    count['kind']++;
    step++;
    writeResultToForm(pos,count,kind,step,loghistory,starttime);
    badgelayer.draw();
    templayer.draw();
    //writeMessage('dragend');
}
function mouseoutbox (box,defaultimage)
{
    box.setFillPatternImage(defaultimage);
    box.moveTo(badgelayer);
    box.shadowColor('black');
    if (!validateForm(false))
    {
        writeMessage('There are still activity icons left to place.');
    }
    else
    {
        writeMessage('All activity icons are placed successfully. You can click "continue" now or change your placements.');
    }
    badgelayer.draw();
    templayer.draw();
}
function alertbox (box,caution)
{
    box.setFillPatternImage(caution);
    box.shadowcolor('red');
    badgelayer.draw;
}

//cbox and text
    var textc = new Konva.Text({
        x: init['x']['c']+imagesizeX*scalar+gridcellsize*5,
        y: init['y']['c']+gridcellsize,
        text: 'Cooking',
        align: 'left',
        width: badgetextwidth
    });
    var boxc = new Konva.Rect({
        x: init['x']['c']+imagesizeX*scalar+gridcellsize,
        y: init['y']['c'],
        offset: [0, 0],
        width: 3*gridcellsize,
        height: 3*gridcellsize,
        fillPatternImage: images.Cd,
        fillPatternScaleX: scalar,
        fillPatternScaleY: scalar,
        stroke: 'black',
        strokeWidth: 4,
        draggable: true,
        cornerRadius: gridcellsize/2,
        shadowColor: 'black',
        shadowBlur: 10,
        shadowOffset: {x : 0, y : 0},
        shadowOpacity: 0.5
    });
    var boxcd = new Konva.Rect({
        x: init['x']['c']+imagesizeX*scalar+gridcellsize,
        y: init['y']['c'],
        offset: [0, 0],
        width: 3*gridcellsize,
        height: 3*gridcellsize,
        fillPatternImage: images.Cu,
        fillPatternScaleX: scalar,
        fillPatternScaleY: scalar,
        stroke: 'grey',
        strokeWidth: 2,
        draggable: false,
        cornerRadius: gridcellsize/2,
    });
    boxc.on('mouseover ', function() {
        mouseoverbox (this,images.Ca);
        });
    boxc.on('dragstart', function() {
        var pos = stage.getPointerPosition();
        dragstarttouchstartbox (this,pos,'c');
    });
    boxc.on('dragmove', function () {
        var pos = stage.getPointerPosition();
        dragmovebox(this,pos,'c',images.Cs,images.Ca,images.Cc);
    });
    boxc.on('dragend', function() {
        var pos = stage.getPointerPosition();
        dragendtouchendbox (this,pos,'c',images.Cc,images.Cd);
    });
    boxc.on('mouseout ', function() {
        mouseoutbox (this,images.Cd)
    });
    boxc.on('foo', function() {
        alertbox (this,images.Cd)
    });
//ebox and text
    var texte = new Konva.Text({
        x: init['x']['e']+imagesizeX*scalar+gridcellsize*5,


...and so on for all the boxes addes here (5 times as long):


        defaultlayer.add(boxcd);
        badgelayer.add(boxc);
        defaultlayer.add(textc);

        defaultlayer.add(boxed);
        badgelayer.add(boxe);
        defaultlayer.add(texte);

        defaultlayer.add(boxhd);
        badgelayer.add(boxh);
        defaultlayer.add(texth);

        defaultlayer.add(boxld);
        badgelayer.add(boxl);
        defaultlayer.add(textl);

        defaultlayer.add(boxsd);
        badgelayer.add(boxs);
        defaultlayer.add(texts);

        defaultlayer.add(boxwd);
        badgelayer.add(boxw);
        defaultlayer.add(textw);
        stage.add(bglayer);
    stage.add(gridlayer);
    stage.add(defaultlayer);
    stage.add(badgelayer);
    stage.add(templayer);
}

好的,抱歉让我等了这么久:

解决方案并不那么容易找到。我发现脚本是异步加载的,并且通过使用 chrome 时间线分析,png 尚不可用。所以我转到我的代码,必须区分哪些东西真的应该在 draw 函数中,哪些不应该。我在执行 draw() 之前将计划添加到加载的图像(源)数组(src = ...),然后添加

sources.onload= loadImages(sources, function(images) {
    draw(images);
});

在我文件的末尾...不,它没有任何问题。 konva.js 与它无关,到目前为止(我几乎准备好了)就像为我的项目设计的一样

感谢@lavrton 的提示,它让我走上了正确的道路

你可以这样写:

var imageObj = new Image();

imageObj.src = 'http://localhost:8000/plugins/kamiyar/logo.png';

imageObj.onload = function() {

    var img = new Konva.Image({
        image: imageObj,
        x: stage.getWidth() / 2 - 200 / 2,
        y: stage.getHeight() / 2 - 137 / 2,
        width: 200,
        height: 137,
        draggable: true,
        stroke: 'blue',
        strokeWidth: 1,
        dash: [1,5],
        strokeEnabled: false
    });

    layer.add(img);

    layer.draw();
};

抱歉我的英语不好 ;)