Просмотр полной версии : Таймер не останавливается
Appleman
14.04.2018, 00:42
Друзья! Замутил простенькую анимацию заполнения шкалы. Метод update на входе получает новое значение отображаемого параметра, после чего выполняется:
public function update (value: Number) : void
// Обновляет шкалу, по значению параметра
{
_newFilledHeigth = int(value / (_scaleMax-_scaleMin) * _heigth);
_barTimer = new Timer(BAR_TIMER_INTERVAL);
_barTimer.addEventListener(TimerEvent.TIMER, animatedFill);
_barTimer.start();
}
private function animatedFill(event:TimerEvent) : void
{
_currentFilledHeigth += _changeDirection * (BAR_ONE_TIME_STEP + BAR_STEP_INCREASE * event.target.currentCount); // Заполняем с ускорением
if (Math.abs(_currentFilledHeigth-_newFilledHeigth) <= (BAR_ONE_TIME_STEP + BAR_STEP_INCREASE * event.target.currentCount)) // Если на следующей итерации достигли целевого значения
{
_currentFilledHeigth = _newFilledHeigth;
redrawFilled(); // метод закраски кусочка до уровня _currentFilledHeigth
_barTimer.stop();
_barTimer.removeEventListener(TimerEvent.TIMER, animatedFill);
trace("закончили анимацию");
return;
}
else
{
redrawFilled();
trace("продолжили анимацию");
return;
}
}
Фигня в том, что таймер по факту не останавливается и слушатель события не снимается. Я специально два trace-a добавил, чтобы удостовериться, что соответствующая ветка выполняется. После запуска у меня летит в консоль как из пулемёта "закончили анимацию". При этом сама анимация работает корректно - шкала заполняется до нужного уровня. Я ничего не понимаю.
P.S. про твиннеры я в курсе, интересно самому разобраться.
RedHead90
14.04.2018, 01:16
Возможно ты устанавливаешь новый экземпляр таймера переменной _barTimer до того, как удаляется слушатель, либо снова его запускаешь. Вообще такое лучше делать через ENTER_FRAME, а не таймеры.
Узким местом может быть создание каждый раз НОВОГО таймера при вызове апдейта. Соотв. приватная переменная перестает ссылаться на уже созданный и запущенный таймер, и отписки от него и остановки — не происходит. С таймерами всегда требуется повышенная осторожность)
caseyryan
14.04.2018, 12:06
Зачем тебе вообще этот таймер? Используй лучше TweenMax или TweenLite. Просто при обновлении задаешь новое значение через TweenMax.to(...) и все грамотно анимируется. Только сразу в вызове update перед выполнением любого кода вызывай TweenMax.killTweensOf(твой объект);, чтобы не получилось так, что несколько твинов одновременно делают одно и тоже
Appleman
14.04.2018, 16:19
Wolsh, твоя правда!
Перенёс объявление таймера в конструктор класса, а в методе update() записал _barTimer.reset()
Всё стало работать правильно. Спасибо.
RedHead90
14.04.2018, 19:18
У тебя и в самом слушателе также косяк. Ты в рамках одного метода обращаешься к таймеру и через event.target и через поле _barTimer. И какой смысл в инструкциях return в конце каждого блока if else?
Appleman
14.04.2018, 22:34
RedHead90, ну не то чтобы косяк. Наверное да, не сама красивая запись, но как говорится, на скорость не влияет.
А с return вроде всё понятно. Ставишь после выполнения блока, чтобы дальше и не идти, врем не тратить :)
во-первых, вы используете динамический доступ - так теряется автокомплит и прочие компайл проверки, и да он не быстрый, хоть это и не критично
во-вторых, такой код явно указывает, что автор или не понимает свой код или не следит за его чистотой(и так сойдет)
в-третьих, if(expr), который завершается return - не имеет смысла в else - он и так не выполнится зайдя в блок if, опять пеегруженность кода
RedHead90
14.04.2018, 23:42
да-да. Ты же не ставишь для экономии времени в конце каждого метода return, а тут по факту делаешь тоже самое. А вообще действия, которые выполняются в любом случае, лучше вынести за пределы блока if else, чем дублировать их в каждом блоке, т.е. у тебя должен быть только if на проверку отписки от таймера, а метод redrawFilled() должен стоять перед этой проверкой, ведь он выполняется при любом раскладе. Плюс ты считаешь высоту сразу для двух итераций за раз, и одну из них прямо в условии проверки, что усложняет читаемость кода. В твоем случае достаточно просто проверить, достигло ли значение нужно величины. Код можно упростить
private function animatedFill(event:TimerEvent) : void {
_currentFilledHeigth += _changeDirection * (BAR_ONE_TIME_STEP + BAR_STEP_INCREASE * _barTimer.currentCount); // Заполняем с ускорением
_currentFilledHeigth = _currentFilledHeigth > _newFilledHeigth ? _newFilledHeigth : _currentFilledHeigth;
redrawFilled();
if (_currentFilledHeigth == _newFilledHeigth) {
_barTimer.stop();
_barTimer.removeEventListener(TimerEvent.TIMER, animatedFill);
}
}
или так
private function animatedFill(event:TimerEvent) : void {
_currentFilledHeigth += _changeDirection * (BAR_ONE_TIME_STEP + BAR_STEP_INCREASE * _barTimer.currentCount); // Заполняем с ускорением
if (_currentFilledHeigth >= _newFilledHeigth) {
_currentFilledHeigth = _newFilledHeigth;
_barTimer.stop();
_barTimer.removeEventListener(TimerEvent.TIMER, animatedFill);
}
redrawFilled();
}
}
Второй вариант по идее должен быть быстрее, но первый кажется более читаемым. А вообще, повторюсь, что лучше все, что касается перерисовки экрана, делать в слушателе кадра. Ты заботишься об экономии времени, а таймеры об этом совсем не заботятся. Обработчик таймера при определенных условиях может быть вызван несколько раз перед фактическим обновлением экрана флешки, а увидишь ты результат лишь последнего вызова. Т.е. остальные вызовы будут перерисовывать твой компонент в никуда, словно художники, которые сжигают свои картины, никому не показывая))
Appleman
16.04.2018, 10:16
RedHead90, СлаваRa, ребята, большое спасибо за подробные разъяснения, всё намотал на ус. Да, действительно, когда изначально писал, вообще в событиях "плавал" по полной программе, только сейчас более-менее понимать начал механику.
А вообще, повторюсь, что лучше все, что касается перерисовки экрана, делать в слушателе кадра. Ты заботишься об экономии времени, а таймеры об этом совсем не заботятся. Обработчик таймера при определенных условиях может быть вызван несколько раз перед фактическим обновлением экрана флешки, а увидишь ты результат лишь последнего вызова. Т.е. остальные вызовы будут перерисовывать твой компонент в никуда, словно художники, которые сжигают свои картины, никому не показывая))
Что значит, в слушателе кадра? Какой кадр имеется в виду? У меня проект на FlashDevelop на AIR.
Что значит, в слушателе кадра? Какой кадр имеется в виду? Событие Event.ENTER_FRAME.
Appleman
16.04.2018, 15:39
Прочитал про ENTER_FRAME. Правильно я понял, что событие типа ENTER_FRAME выполняется при каждой смене кадра приложения?
А слушаем мы тогда что, на что addEventListener вешать? На сам анимируемый объект? С таймером-то мы слушали сам таймер.
RedHead90
16.04.2018, 16:03
Данное событие отправляют все экранные объекты, не зависимо от того, добавлены они в список отображения или нет. Слушать можешь любой из них, но оптимальнее всего будет повесить единственный обработчик на объект Stage и в нем уже определять, какие компоненты нуждаются в обновлении и т.д. и вызывать соответствующие методы. Т.е. у тебя получиться такой метод, который управляет жизненным циклом твоей флехи. Плюс, т.к. твоя анимация зависит от времени, тебе надо будет считать время между кадрами. Тут тебе поможет метод flash.utils.getTimer()
Правильно я понял, что событие типа ENTER_FRAME выполняется при каждой смене кадра приложения?Это глобальное событие, инициатором которого является сам плеер. Оно отправляется сразу после того, как "предыдущий кадр" был визуализирован рендером и, соответственно, прямо перед тем как плеер начинает собирать данные для визуализации нового кадра — то есть исполнять код обработчиков событий, выстроившихся в очередь за время последнего рендера. Выполнив весь этот код (рассчитав новые свойства объектов), плеер отправляет еще одно событие — Event.EXIT_FRAME. Если на это событие есть подписки обработчиков, они будут выполнены тут же, и только потом за дело возьмется рендер изображения.
(для расширения кругозора)
Если в коде кадра был вызван метод Stage#invalidate(), то прямо перед рендером будет отправлено событие Event.RENDER, по которому так же можно сделать окончательные перерасчеты свойств визуальных объектов. Этот метод называется "отложенная валидация" и используется, если какие-то свойства объекта могут несколько раз измениться во время исполнении кода одного кадра, а их изменение сопровождается сложными и долгими рассчетами (или изменением свойств детей, которых может быть несколько десятков) — которые оказываются бессмысленными и "холостыми", если через две наносекунды потребуется задать новое значение свойства. В частности, за время одного кадра может быть накоплено несколько событий MOUSE_MOVE или RESIZE, если какой-то шумахер нервно дергает мышью, и на каждое событие надо пересчитать размеры и положения всех визуальных компонентов в окне — а ОТОБРАЖЕНО рендером все-равно будет только последнее изменение. В таком случае объект "накапливает" задачи, не исполняя их, пока не получит событие RENDER. То есть тупо записывает себе _width = newWidth, и только в обработчике события RENDER делает непосредственно this.width = _width и требует ресайза от своих детишек.
undefined
17.04.2018, 08:45
Еще стоит предостеречь от частой ошибки новичков - вешать слушатель enterFrame на каждый displayObject на сцене и потом удивляться почему все тормозит.
Appleman
17.04.2018, 10:03
undefined, своевременно :) Я как раз собирался туда его и повесить. Всё-таки, куда сам слушатель зафигачивать? Про stage вроде решили, что давать ссылки на него каждому малышу не дело. Что остаётся? Какие-то более крупные контейнеры на сцене?
undefined
17.04.2018, 10:17
вешать на общий контейнер приложения.Контейнер потом вызывает методы update у детей.Конечно, если надо проиграть какую-то анимацию, о которой родителям знать необязательно,то можно и внутри дитя слушать enterFrame.Главное контролировать число этих слушателей и не делать ничего тяжелого в них.
Про stage вроде решили, что давать ссылки на него каждому малышу не дело.
Ээ она уже у него есть (https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/DisplayObject.html#stage).Как и ссылка на родителя.Это не очень сочетается с мантрой "дети не должны ничего знать о родителях", но местами это очень удобно.
Это не очень сочетается с мантройВесь Display List полностью противоречит принципам ООП, на которых базируется язык AS3.
Похоже, их создавали в разных Вселенных "программистов" и "дизайнеров".
Я уже не раз приводил в пример, что через отношения DL stage/root/parent/child можно свободно получить ссылку на любой (дисплейный) объект, как бы вы ни прятали его в приватные свойства.
Добавил в Дисплей Лист == выставил на панель.
Работает на vBulletin ® версия 3.7.3. Copyright ©2000-2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Copyright © 1999-2008 Flasher.ru. All rights reserved.