Как сделать кнопку с обратным отсчетом времени без мерцания формы в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В интерфейсных сценариях управляемых форм нередко требуется временно заблокировать кнопку (например, подтверждения отправки, повторного запроса СМС или проведения документа) на несколько секунд, отображая оставшееся время прямо в заголовке кнопки. При реализации такого механизма через периодический обработчик ожидания разработчики часто сталкиваются с неприятным визуальным эффектом: вся форма начинает мерцать каждую секунду при обновлении счетчика.

Разберем причины возникновения этой проблемы и рассмотрим правильные варианты реализации обратного отсчета на клиенте без побочных графических артефактов.

Причины мерцания интерфейса и сбоев ввода

Мерцание управляемой формы при динамическом изменении текста элементов обычно вызывается несколькими факторами:

  1. Пересчет компоновки формы (Layout): когда текст на кнопке меняет свою длину (например, меняется количество символов), платформа выполняет перерасчет ширины самой кнопки и всех соседних элементов в группе. Постоянный динамический пересчет размеров визуальных контейнеров приводит к заметным скачкам и мерцанию интерфейса.
  2. Привязка к реквизитам данных формы: если значение таймера сохраняется в реквизите формы, а не в локальной переменной клиентского модуля, каждое его изменение запускает внутренние механизмы отслеживания модифицированности данных и перерисовки зависимых элементов.
  3. Вызов тяжелых методов обновления: принудительный вызов методов перерисовки или серверных процедур в теле обработчика ожидания перезагружает контекст формы.
  4. Сброс фокуса пользователя: частое срабатывание некорректно настроенного таймера может прерывать активный ввод пользователя в текстовых полях или самопроизвольно закрывать выпадающие списки выбора.

Базовая реализация через обработчик ожидания

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

Посмотрим на пример организации кода в модуле управляемой формы:


&НаКлиенте
Перем ВремяОкончанияТаймера;

&НаКлиенте
Процедура ЗапуститьОбратныйОтсчет()

    // Задаем время блокировки кнопки (например, 5 секунд)
    ДлительностьБлокировки = 5;
    ВремяОкончанияТаймера = ТекущаяДата() + ДлительностьБлокировки;

    // Блокируем кнопку от повторных нажатий
    Элементы.КнопкаДействия.Доступность = Ложь;
    
    // Обновляем заголовок перед стартом
    ОбновитьТекстКнопки();

    // Подключаем периодический обработчик с интервалом в 1 секунду
    ПодключитьОбработчикОжидания("ОбработкаТаймераКнопки", 1, Ложь);

КонецПроцедуры

&НаКлиенте
Процедура ОбработкаТаймераКнопки()

    ОстатокВремени = ВремяОкончанияТаймера - ТекущаяДата();

    Если ОстатокВремени > 0 Тогда
        Элементы.КнопкаДействия.Заголовок = "Подождите (" + Формат(ОстатокВремени, "ЧЦ=2; ЧГ=0") + " сек.)";
    Иначе
        // Отключаем таймер при завершении отсчета
        ОтключитьОбработчикОжидания("ОбработкаТаймераКнопки");
        
        // Возвращаем исходное состояние кнопки
        Элементы.КнопкаДействия.Заголовок = "Выполнить операцию";
        Элементы.КнопкаДействия.Доступность = Истина;
    КонецЕсли;

КонецПроцедуры

&НаКлиенте
Процедура ОбновитьТекстКнопки()
    ОстатокВремени = ВремяОкончанияТаймера - ТекущаяДата();
    Элементы.КнопкаДействия.Заголовок = "Подождите (" + Формат(ОстатокВремени, "ЧЦ=2; ЧГ=0") + " сек.)";
КонецПроцедуры

Обратим внимание на то, что для вычисления остатка секунд используется системное время ТекущаяДата(), а не простое вычитание единицы на каждом шаге. Это позволяет избежать накопления погрешности, если клиентское приложение было кратковременно занято другой операцией.

Способы устранения мерцания и визуальной нестабильности

Если после внедрения кода форма продолжает дергаться или мерцать, применим следующие практические рекомендации:

  1. Фиксация ширины элемента: в свойствах кнопки в конфигураторе зададим жесткое значение свойства Ширина (например, 20 символов) и установим свойство РастягиватьПоГоризонтали в значение Ложь. Это гарантирует, что изменение длины строки внутри кнопки не спровоцирует пересчет геометрии соседних элементов формы.
  2. Отказ от реквизитов формы в пользу переменных: убедимся, что счетчик не хранится в реквизитах объекта или формы. Используем только клиентские переменные Перем в шапке модуля формы.
  3. Использование однократного обработчика ожидания: вместо постоянного тика с параметром Ложь можно вызывать метод с параметром Однократно = Истина и повторно подключать его в конце процедуры, если время еще не истекло. Это исключит наслоение вызовов при задержке графической подсистемы.
  4. Обязательное отключение таймера: вызов метода ОтключитьОбработчикОжидания() должен происходить не только по завершении отсчета, но и в обработчике события формы ПриЗакрытии, чтобы исключить попытки обращения к уничтоженным элементам интерфейса.

Альтернативные способы оформления

Если требуется полностью изолировать анимацию или отсчет времени от движка управляемых форм, разберем два альтернативных решения:

  1. Разделение индикации и кнопки: сама кнопка сразу делается недоступной (Элементы.Кнопка.Доступность = Ложь), а обратный отсчет выводится в отдельную статическую надпись (вид ПолеНадписи) или элемент с видом ПолеИндикатора рядом с кнопкой. В этом случае текст самой кнопки вообще не меняется, что сводит вероятность мерцания к нулю.
  2. Поле HTML-документа: на форму помещается элемент с типом ПолеHTMLДокумента, содержащий кнопку, стилизованную через CSS. Внутри HTML-документа запускается стандартный таймер JavaScript через setInterval(). Такой таймер работает полностью внутри встроенного браузера без каких-либо перерисовок формы 1С, а по истечении времени передает событие в платформу 1С.
← На главную