Низкая производительность с kineticjs

Извините, если этот вопрос уже задавался, я пытался найти его, но не смог.

У меня есть холст, который в конечном итоге должен отображать около 400-500 прямоугольников высотой/шириной 20-30 пикселей. Каждый из них должен перемещаться на один пиксель влево и вверх при наведении мыши и назад при отведении мыши, чтобы создать своего рода выбранное поведение. Теперь мой код отлично работает с небольшим количеством фигур, но для 500 из них он начинает резко замедляться. Из какого-то примера в Интернете я увидел, что могу создать «анимационный слой» и переместить туда объект, который мне нужно анимировать. Но это по-прежнему требует, чтобы я перерисовал основной слой, чтобы удалить перемещенный элемент из его предыдущей позиции... Вот код:

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,
    }
}

И код, который отображает холст:

<!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>

Я чувствую, что делаю что-то очень неправильное здесь, но, к сожалению, я недостаточно знаком с kineticjs. Может ли кто-нибудь указать мне правильное направление?

Заранее спасибо, Дэнни


person Dimkin    schedule 22.08.2012    source источник


Ответы (3)


Ладно, это был хороший вызов. Я думал, что проблема была в форме событий, но улучшения там были очень малы.

События в KineticJS применяются к определенным фигурам на экране путем поиска по координатам моделей до тех пор, пока одна из них не будет соответствовать текущим координатам мыши. Таким образом, имея только один слой, мы увеличиваем размер массива для поиска.

Исправление состоит в том, чтобы использовать много слоев. Я добавил слой для каждой строки.

Некоторые другие изменения в приведенном ниже коде:

  1. Не перемещайте фигуры между слоями. Это вызывает объединение массивов и сдвиг индекса.
  2. Используйте относительный метод move(x,y) вместо методов setX() setY() для небольших смен.
  3. Концепция слоя анимации, о которой вы прочитали в учебнике, используется для создания фактической анимации с заданным интервалом кадров. Мы просто делаем обычное движение формы на мероприятии.
  4. Незначительное улучшение JS; При возврате объекта, чтобы скрыть частные данные и методы, не используйте new для создания объекта.

Страница

<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>

Код

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,
};
}
person Gerard Sexton    schedule 23.08.2012
comment
Спасибо, я действительно рассматривал это решение, но подумал, может быть, я что-то упускаю и могу сделать все это в одном слое. Кроме того, я изменил анимацию, и теперь зависшая фигура растет во всех направлениях, поэтому мне не нужно перерисовывать mainLayer, и все работает гладко. Спасибо еще раз - person Dimkin; 27.08.2012

Пожалуйста, ознакомьтесь с KineticJS v4.0.0, механизм обнаружения событий был переписан и обеспечивает мгновенное обнаружение попаданий даже при наличии сотен тысяч фигур:

http://kineticjs.com/

person Eric Rowell    schedule 25.08.2012

Для этого достаточно нарисовать 500 фигур. Конечно, производительность также будет зависеть от скорости компьютера, браузера (в настоящее время chrome является самым быстрым, но, очевидно, вам потребуется приемлемая производительность во всех браузерах).

Перейдите по этой ссылке о производительности и способах ее повышения.

person Jarrod    schedule 22.08.2012