Performance slow with kineticjs

2019-02-15 13:35发布

Sorry if this question was asked already, I've tried to find it by couldn't.

I have a canvas that should eventually show about 400-500 rectangles 20-30 pixels height/width. Each one of them should move one pixel left and up on mouse over and back on mouse out to create sort of selected behavior. Now, my code works great with small amount of shapes, but for 500 of them it starts slowing down dramatically. From some example on the internet I saw that I can create "animation layer" and move the object i need to animate there. But, this still requires me to redraw the main layer to remove the moved item from it's previous position... Here's the code:

var seatMap = {};

seatMap.seatTypes = {
    economy: {
        width: 20,
        height: 20,
        fill: 'red',
        stroke: 'black',
        strokeWidth: 4,
        cornerRadius: 5
    },
    business: {
        width: 22,
        height: 22,
        fill: 'red',
        stroke: 'black',
        strokeWidth: 4,
        cornerRadius: 5
    },
    first: {
        width: 25,
        height: 25,
        fill: 'red',
        stroke: 'black',
        strokeWidth: 4,
        cornerRadius: 5
    }
};

seatMap.Map = function (params) {
    var stage = new Kinetic.Stage({
        container: params.container,
        width: params.width,
        height: params.height
    });

    var mainLayer = new Kinetic.Layer();
    var animationLayer = new Kinetic.Layer();

    stage.add(mainLayer);
    stage.add(animationLayer);

    var addSeat = function (object) {
        object.seat.mainLayerRef = mainLayer;
        object.seat.animationLayerRef = animationLayer;

        mainLayer.add(object.seat);
    };

    var refresh = function () {
        mainLayer.draw();
    }

    return {
        refresh: refresh,
        addSeat: addSeat
    }
}

seatMap.Seat = function (params) {

    var seatType = params.seatType == null ? seatMap.seatTypes.economy : params.seatType;

    var seat = new Kinetic.Rect({
        width: seatType.width,
        height: seatType.height,
        x: params.x,
        y: params.y,
        fill: seatType.fill,
        stroke: seatType.stroke,
        strokewidth: seatType.strokewidth,
        cornerRadius: seatType.cornerRadius,
        listening: true
    });
    seat.staticXPosition = params.x;
    seat.staticYPosition = params.y;

    seat.on('mouseover', function (event) {
        event.shape.moveTo(event.shape.animationLayerRef);
        event.shape.setX(event.shape.staticXPosition - 2);
        event.shape.setY(event.shape.staticYPosition - 2);
        event.shape.animationLayerRef.draw();
        event.shape.mainLayerRef.draw();
    });
    seat.on('mouseout', function (event) {
        event.shape.setX(event.shape.staticXPosition);
        event.shape.setY(event.shape.staticYPosition);
        event.shape.moveTo(event.shape.mainLayerRef);
        event.shape.animationLayerRef.draw();
        event.shape.mainLayerRef.draw();
    });

    return {
        seat: seat,
    }
}

And the code that renders the canvas:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript" src="scripts/jquery-1.8.0.js"></script>
    <script type="text/javascript" src="scripts/kinetic-v3.10.5.min.js"></script>
    <script type="text/javascript" src="scripts/local/seatMapKineticExtender.js"></script>
    <!--<script type="text/javascript" src="scripts/local/app.js"></script>-->
    <script>
        $(function () {
            var map = new seatMap.Map({
                container: 'stage',
                width: 1000,
                height: 420
            });

            for (var i = 0; i < 800; i += 30) {
                for (var j = 0; j < 500; j+=30) {
                    var seat = new seatMap.Seat({
                        seatType: seatMap.seatTypes.business,
                        x: i,
                        y: j
                    });
                    map.addSeat(seat);
                }
            }
            //var seat = new seatMap.Seat({
            //    seatType: seatMap.seatTypes.business,
            //    x: 200,
            //    y: 200
            //});

            //map.addSeat(seat);
            map.refresh();
        });
    </script>
</head>
<body>
    <div id="stage" style="width: 1000px; height: 420px; margin: 0 auto; border: 1px solid black; position: relative">
        <div style="position: absolute; top: 0; z-index: 1000">
            <button id="zoomIn" type="button">Zoom In</button>
            <button id="zoomOut" type="button">Zoom Out</button>
        </div>

    </div>
</body>
</html>

I feel that I do something very wrong here, but, unfortunately I'm not familiar with kineticjs enough. Can anyone point me to the right direction?

Thanks in advance, Danny

3条回答
Lonely孤独者°
2楼-- · 2019-02-15 13:48

Alright, this was a good challenge. I thought that the problem was in the shape events but the improvement there were ever so small.

The events in KineticJS are applied to the specific on screen shapes by searching through the co-ordinates of the models until one matches the current mouse co-ordinates. So by having only one layer, we increase the size of the array to search.

The fix is to use many layers. I added a layer for every row.

Some of the other changes in the code below are:

  1. Don't move shapes between layers. It causes an array splice and an index shift.
  2. Use the relative move(x,y) method instead of setX() setY() methods for small shifts.
  3. The animation layer concept you read in a tutorial is used for doing actual animation on a timed frame interval. We are just doing a normal shape move on an event.
  4. Minor JS improvement; When returning an object to hide private data and methods, don't use new for creating an object.

Page

<script>
    $(function () {
        var map = seatMap.Map({
            container: 'stage',
            width: 1000,
            height: 420
        });

        for (var i = 0; i < 800; i += 30) {
            var layer = map.createLayer();
            for (var j = 0; j < 500; j+=30) {
                var seat = seatMap.Seat({
                    seatType: seatMap.seatTypes.business,
                    x: i,
                    y: j
                });
                map.addSeat(seat, layer);
            }
            layer.draw();
        }
        //var seat = new seatMap.Seat({
        //    seatType: seatMap.seatTypes.business,
        //    x: 200,
        //    y: 200
        //});

        //map.addSeat(seat);
        map.refresh();
    });
</script>

Code

seatMap.Map = function (params) {
var stage = new Kinetic.Stage({
    container: params.container,
    width: params.width,
    height: params.height
});

var addSeat = function (object, layer) {
    object.seat.layer = layer;
    layer.add(object.seat);     
};

var refresh = function () {
    mainLayer.draw();
};

var createLayer = function() {
    var layer = new Kinetic.Layer();
    stage.add(layer);
    return layer;
};
return {
    createLayer : createLayer,
    refresh: refresh,
    addSeat: addSeat
};
}

seatMap.Seat = function (params) {

var seatType = params.seatType == null ? seatMap.seatTypes.economy : params.seatType;

var seat = new Kinetic.Rect({
    width: seatType.width,
    height: seatType.height,
    x: params.x,
    y: params.y,
    fill: seatType.fill,
    stroke: seatType.stroke,
    strokewidth: seatType.strokewidth,
    cornerRadius: seatType.cornerRadius,
    listening: true
});
seat.staticXPosition = params.x;
seat.staticYPosition = params.y;

seat.on('mouseover', function (event) {
    event.shape.move(-3,-3);
    event.shape.layer.draw();
});
seat.on('mouseout', function (event) {

    event.shape.move(3,3);
    event.shape.layer.draw();
});

return {
    seat: seat,
};
}
查看更多
狗以群分
3楼-- · 2019-02-15 13:52

Please check out KineticJS v4.0.0, the event detection engine was rewritten and yields instantaneous hit detection even if there are hundreds of thousands of shapes:

http://kineticjs.com/

查看更多
\"骚年 ilove
4楼-- · 2019-02-15 13:58

Drawing 500 shapes will do that. Of course performance will also depend on computer speed, browser (chrome is currently the fastest - but obviously you'll need acceptable performance on all browsers).

Check out this link about performance and ways to improve it.

查看更多
登录 后发表回答