Проблема с загрузкой изображений на несколько холстов - PullRequest
2 голосов
/ 11 марта 2011

У меня есть таблица элементов canvas, которая имитирует мозаичное изображение. Мне нужно заполнить только те плитки, которые в данный момент видны в окне просмотра. Вот код, который я использую

for (var ix = visibleRange.firstX; ix <= visibleRange.lastX; ix++) {
        for (var iy = visibleRange.firstY; iy <= visibleRange.lastY; iy++) {
            var canvasDispJq = $("canvas#" + frameResId + "-" + ix + "-" + iy + "-    disp");
            var canvasDisp = canvasDispJq.get(0);
            var image = new Image();
            var context = canvasDisp.getContext("2d");
            image.onload = function() {
                context.drawImage(image, 0, 0);
            }
            image.src = self.baseURL + '/' + canvasDispJq.attr("id");
        }
    }
};

Но проблема в том, что загружается только последняя плитка из диапазона. То же самое, но реализовано таким образом, работает нормально:

$("table#canvasTable-" + frameResId + " canvas.display").each(function() {
        var canvasDispJq = $(this);
        if ((canvasDispJq.position().top + self.tileSize + imagePosition.y) > 0 &&
                (canvasDispJq.position().left + self.tileSize + imagePosition.x) > 0 &&
                (canvasDispJq.position().top + imagePosition.y) < self.containerHeight   &&
                (canvasDispJq.position().left + imagePosition.x) < self.containerWidth)   {
            var canvasDisp = canvasDispJq.get(0);
            var image = new Image();
            var context = canvasRaw.getContext("2d");
            image.onload = function() {
                context.drawImage(image, 0, 0);
            }
            image.src = self.baseURL + '/' + canvasDispJq.attr("id");
        }
});

Разница в том, как вычисляется видимый диапазон, не должна иметь значения, потому что я проверил в Firebug, и для обоих подходов плитки для рендеринга выбраны правильно, и фактические изображения плиток загружаются с сервера, но для первого подхода отображается только последняя плитка, в то время как для второй все плитки отображаются правильно.

Заранее спасибо за любые предложения.

1 Ответ

2 голосов
/ 11 марта 2011

Причина разницы в первую очередь здесь:

var image = new Image();
var context = canvasDisp.getContext("2d");
image.onload = function() {
    context.drawImage(image, 0, 0);
}

Что делает, это создает замыкание и присваивает его событию load изображения.Закрытие закрывается по переменным context и image (и нескольким другим).Ключевым моментом здесь является то, что он имеет постоянную ссылку на эти переменные, а не копию их значений при создании функции.Поскольку приведенный выше код находится в цикле, все создаваемые функции (замыкания) будут ссылаться на те же context и image - последние, созданные циклом.

Это непроисходит в версии each, потому что в этой версии замыкание закрывается по переменной, которая не изменяется, one context и image, созданным при вызове функции итератора, которую выпереход к each.

Подробнее о замыканиях здесь: Замыкания не сложны

Возможно немного не по теме, возможно, нет: это могло бы быть более ясным, если бы ваши var были в другом месте.var иногда неправильно понимают. В частности, не такое же, как объявления переменных в некоторых других языках (например, C, C ++, Java и C #), частично потому, что JavaScript необласть действия уровня блока (только область уровня функции и глобальная область).

Вот как интерпретатор JavaScript видит вашу первую версию:

var ix, iy, canvasDispJq, canvasDisp, image, context;
for (ix = visibleRange.firstX; ix <= visibleRange.lastX; ix++) {
        for (iy = visibleRange.firstY; iy <= visibleRange.lastY; iy++) {
            canvasDispJq = $("canvas#" + frameResId + "-" + ix + "-" + iy + "-    disp");
            canvasDisp = canvasDispJq.get(0);
            image = new Image();
            context = canvasDisp.getContext("2d");
            image.onload = function() {
                context.drawImage(image, 0, 0);
            };
            image.src = self.baseURL + '/' + canvasDispJq.attr("id");
        }
    }
}

Обратите внимание, что все var операторов переместились наверх, потому что именно так они будут обрабатываться.Независимо от того, где var появляется в функции, оно вступает в силу с самого начала функции и происходит только один раз.Если var имеет инициализатор, это становится назначением, где var.Таким образом, var x = 2; - это на самом деле две совершенно разные вещи, которые обрабатываются интерпретатором в разное время: var x, который происходит при входе в функцию до того, как что-либо еще произойдет, и x = 2;, который происходит там, где он появляется напошаговое выполнение кода функции.

(Кроме того, не по теме: нет необходимости в ; после закрытия } цикла for; но вам нужен один послеприсваивание обработчику onload. Я сделал оба этих мода также aboev.)

Пишем код так, как его увидит интерпретатор, теперь (во всяком случае, для меня) становится понятнее, что каждый иззамыкания, которые вы назначаете для onload, будут ссылаться на те же самые context и image переменные, и поэтому они все будут видеть последние.

Если выЕсли вы хотите использовать не-1062 * версию, вам просто нужно немного ее изменить, чтобы закрытия onload закрывали свои собственные копии context и image.Это выглядит так:

var ix, iy, canvasDispJq, canvasDisp, image, context;
for (ix = visibleRange.firstX; ix <= visibleRange.lastX; ix++) {
        for (iy = visibleRange.firstY; iy <= visibleRange.lastY; iy++) {
            canvasDispJq = $("canvas#" + frameResId + "-" + ix + "-" + iy + "-    disp");
            canvasDisp = canvasDispJq.get(0);
            image = new Image();
            context = canvasDisp.getContext("2d");
            image.onload = createOnloadHandler(context);
            image.src = self.baseURL + '/' + canvasDispJq.attr("id");
        }
    }
}

function createOnloadHandler(ct, img) {

    return function() {
        context.drawImage(img, 0, 0);
    };
}

Теперь замыкание, созданное функцией createOnloadHandler, закрывается по ct и img, а не context и image.Каждое закрытие получает свою собственную копию (переданную в вызов createOnloadHandler, который создал это закрытие).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...