В интерфейсных сценариях управляемых форм нередко требуется временно заблокировать кнопку (например, подтверждения отправки, повторного запроса СМС или проведения документа) на несколько секунд, отображая оставшееся время прямо в заголовке кнопки. При реализации такого механизма через периодический обработчик ожидания разработчики часто сталкиваются с неприятным визуальным эффектом: вся форма начинает мерцать каждую секунду при обновлении счетчика.
Разберем причины возникновения этой проблемы и рассмотрим правильные варианты реализации обратного отсчета на клиенте без побочных графических артефактов.
Мерцание управляемой формы при динамическом изменении текста элементов обычно вызывается несколькими факторами:
Рассмотрим базовый и наиболее распространенный подход к реализации таймера. Чтобы избежать лишней нагрузки на контекст формы, сохраним оставшееся время в локальной переменной модуля на клиенте, используя ключевое слово Перем.
Посмотрим на пример организации кода в модуле управляемой формы:
&НаКлиенте
Перем ВремяОкончанияТаймера;
&НаКлиенте
Процедура ЗапуститьОбратныйОтсчет()
// Задаем время блокировки кнопки (например, 5 секунд)
ДлительностьБлокировки = 5;
ВремяОкончанияТаймера = ТекущаяДата() + ДлительностьБлокировки;
// Блокируем кнопку от повторных нажатий
Элементы.КнопкаДействия.Доступность = Ложь;
// Обновляем заголовок перед стартом
ОбновитьТекстКнопки();
// Подключаем периодический обработчик с интервалом в 1 секунду
ПодключитьОбработчикОжидания("ОбработкаТаймераКнопки", 1, Ложь);
КонецПроцедуры
&НаКлиенте
Процедура ОбработкаТаймераКнопки()
ОстатокВремени = ВремяОкончанияТаймера - ТекущаяДата();
Если ОстатокВремени > 0 Тогда
Элементы.КнопкаДействия.Заголовок = "Подождите (" + Формат(ОстатокВремени, "ЧЦ=2; ЧГ=0") + " сек.)";
Иначе
// Отключаем таймер при завершении отсчета
ОтключитьОбработчикОжидания("ОбработкаТаймераКнопки");
// Возвращаем исходное состояние кнопки
Элементы.КнопкаДействия.Заголовок = "Выполнить операцию";
Элементы.КнопкаДействия.Доступность = Истина;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьТекстКнопки()
ОстатокВремени = ВремяОкончанияТаймера - ТекущаяДата();
Элементы.КнопкаДействия.Заголовок = "Подождите (" + Формат(ОстатокВремени, "ЧЦ=2; ЧГ=0") + " сек.)";
КонецПроцедуры
Обратим внимание на то, что для вычисления остатка секунд используется системное время ТекущаяДата(), а не простое вычитание единицы на каждом шаге. Это позволяет избежать накопления погрешности, если клиентское приложение было кратковременно занято другой операцией.
Если после внедрения кода форма продолжает дергаться или мерцать, применим следующие практические рекомендации:
Ширина (например, 20 символов) и установим свойство РастягиватьПоГоризонтали в значение Ложь. Это гарантирует, что изменение длины строки внутри кнопки не спровоцирует пересчет геометрии соседних элементов формы.Перем в шапке модуля формы.Ложь можно вызывать метод с параметром Однократно = Истина и повторно подключать его в конце процедуры, если время еще не истекло. Это исключит наслоение вызовов при задержке графической подсистемы.ОтключитьОбработчикОжидания() должен происходить не только по завершении отсчета, но и в обработчике события формы ПриЗакрытии, чтобы исключить попытки обращения к уничтоженным элементам интерфейса.Если требуется полностью изолировать анимацию или отсчет времени от движка управляемых форм, разберем два альтернативных решения:
Элементы.Кнопка.Доступность = Ложь), а обратный отсчет выводится в отдельную статическую надпись (вид ПолеНадписи) или элемент с видом ПолеИндикатора рядом с кнопкой. В этом случае текст самой кнопки вообще не меняется, что сводит вероятность мерцания к нулю.ПолеHTMLДокумента, содержащий кнопку, стилизованную через CSS. Внутри HTML-документа запускается стандартный таймер JavaScript через setInterval(). Такой таймер работает полностью внутри встроенного браузера без каких-либо перерисовок формы 1С, а по истечении времени передает событие в платформу 1С.