XML - прогулка по дереву
Покопавшись в узлах XML в предыдущем уроке и отложив в голове смутные представления о XML, как о то ли веревке с узлами, то ли дереве с веревками, попробуем поползать по этому дереву и понять что это такое.
Когда мне пришлось искать хоть какой-нибудь примерчик по обходу дерева, я перепробовал многие исходники для этих целей. Но к сожалению 100% работающего не нашел.
Есть два основных пути обхода дерева: рекурсией и в трехкадровом цикле.
От рекурсии пришлось практически сразу отказаться, поскольку при большом количестве записей Flash начинает ругаться на медленную работу скрипта.
Оставался обход в трехкадровом цикле. Но исходники, использовавшие этот способ имели существенный недостаток: мое дерево из 350 записей обходилось за 30-35 секунд. Это было неприемлемо. В конце концов был найден способ, условно назовем его "ограничитель итераций", который и будет использован при обходе дерева.
Что из себя представляет обход дерева? Это последовательный поиск следующего по порядку узла XML и применение его атрибутов к объектам Flash..
Логика реализации примерно такова: в переменную помешаем ссылку на первый узел, применяем атрибуты к объекту, смотрим, есть ли дочерние узлы, если есть то в переменную помешаем ссылку на первого ребенка, с ним производим те-же действия, а если нет дочерних узлов, то переходим на брата, если нет брата то переходим на родительский узел и затем на его следующего брата...... даааа... семейка получается не малая... но в скрипте это смотрится проще. В скрипте вообще всё проще чем в жизни. Поэтому предлагаю в него погрузиться.
Этот пример использует функцию applyProp и XML из прошлого урока, поэтому здесь я это не комментирую.
xmlTmp = new XML("<tree><mc1 _x='10' _y='20' _rotation='-10' status='busy'></mc1><mc2 _x='20' _y='40' _rotation='40'></mc2><mc3 _x='30' _y='50' _rotation='60'></mc3><mc4 _x='80' _y='80' _rotation='-115' status='busy'></mc4><mc5 _x='150' _y='120' _rotation='20' status='busy'></mc5></tree>");
XMLNode.prototype.applyProp = applyProp;
function applyProp (to) {
// © Ivan Dembicki, dembicki@narod.ru
var a = this.attributes;
var b = eval(to);
var vars;
var val;
for (vars in a) {
val = a[vars];
if (isNaN(Number(val))) {
b[vars] = val;
} else {
b[vars] = Number(val);
}
}
}
XMLNode.prototype.fncNextNode = function() {// помещаем в прототип узла функцию вычислениЯ следующего узла
// © Ivan Dembicki, dembicki@narod.ru
if (this.firstChild != null) { // если у текущего узла есть ребенок
return this.firstChild; // возвращаем ребенка
}
var n = this; // во временную переменную помещаем текущий узел
while (n.nextSibling == null) { // пока нет брата
if (n.parentNode) { // если есть родитель
n = n.parentNode; // временной переменной задаем значение родителЯ
} else { // иначе, то есть если нет ни брата ни родителЯ, значит
return null; // мы добрались до конца XML и возвращаем null
}
}
return n.nextSibling; // поскольку ранее нашли узел у которого есть брат, возвращаем брата найденного узла
};
xmlTmp.ignoreWhite = true; // просим игнорировать пробелы в нашем XML
nod = xmlTmp.firstChild; // задаем первый узел
это был скрипт первого кадра, второй кадр оставим пустым, а в третьем пишем:
for (z=0; z<50; z++) { // это и есть ограничитель итераций ® *
nod = nod.fncNextNode(); // задаем переменной значение следующего узла
if (nod) { // если следующий узел не пустой
_root.attachMovie("mc", nod.nodeName, i++); // аттачим мувик
nod.applyProp(nod.nodeName); // и присваиваем ему свойства из узла
} else { // а если мы добрались до конца дерева
delete i; // удалЯем ненужные переменные
delete nod;
delete z;
gotoAndStop("park"); // переходим на кадр с меткой park
break; // прерываем цикл
}
}
prevFrame();// если обработались очередные 50 интераций и не перешли в кадр park, то переходим в предыдущий кадр
play(); // и заходим в этот кадр заново
* - мы в одном кадре обрабатываем не более 50 узлов. 50 - цифра весьма условная. ее стоит подбирать в зависимости от нагруженности операций с каждым узлом, добиваясь максимальной производительности обработки.
1. Вкупе с выходом из кадра и повторным заходом в кадр мы добиваемся того, что во время генерации не висим в одном кадре, обнуляя встроенный счетчик максимального количества итераций, как если бы мы обрабатывали все узлы в одном кадре, и не налетаем на сообщение о медленной работе скрипта. При этом можно настроить ограничитель итераций так, чтобы нагруженность скрипта не тормозила анимацию, что тоже очень важно.
2. Обработка происходит не как в обычном трехкадровом цикле: один кадр - одна операция, а пакетами по 50 (в нашем случае), что дает почти 20-30 кратное увеличение производительности !!! в некоторых случаях эти показатели могут быть и большими.
Глубина. Этот пример по своим результатам не будет отличаться от предыдущего. Но попробуйте протестировать на другом XML документе, имеющем большие глубины: более глубокие (вложенные) дочерние узлы будут пропущены, в то время как текущий пример правильно обработает всё дерево.
Глубина вложений узлов очень важна, например при генерации вложенных меню. Но об этом в следующий раз.