Как программно сгенерировать изображение аналоговых часов со стрелками в 1С?

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

При разработке интерфейсов или печатных форм в 1С:Предприятии иногда возникает нестандартная задача: зафиксировать текущее время и вывести графическое изображение аналоговых часов со стрелками, дополнив его текстовой информацией. Такое решение может потребоваться для оформления пропусков, путевых листов, отчетов или просто для создания интерактивных виджетов на управляемой форме.

В рамках данной статьи мы подробно разберем несколько практических способов реализации этой идеи — от классических тригонометрических расчетов до современного использования векторной графики SVG и интерактивных Canvas-элементов в поле HTML-документа. Мы проанализируем преимущества каждого подхода и выберем оптимальные сценарии для их применения.

Способ 1. Динамическая генерация SVG-картинки через текстовые шаблоны (Рекомендуемый)

Начиная с версии платформы 8.3.15, в 1С реализована полноценная поддержка векторного формата SVG. Это позволяет нам отказаться от сложных пиксельных расчетов на стороне сервера и переложить отрисовку на графический движок платформы. Изображение SVG великолепно масштабируется, не теряет четкости при печати и весит считанные байты.

Суть метода заключается в том, что мы подготавливаем статический XML-шаблон циферблата часов, а положение стрелок определяем с помощью стандартных механизмов трансформации SVG (а именно — поворота rotate вокруг центра координат). Нам не нужно вычислять координаты концов стрелок с помощью синусов и косинусов — достаточно передать угол поворота в градусах.

Давайте разберем математику расчета углов:

  1. Минутная стрелка: Полный круг составляет 360 градусов, что соответствует 60 минутам. Таким образом, одна минута — это ровно 6 градусов. Формула: УголМинут = Минуты * 6.
  2. Часовая стрелка: Циферблат разделен на 12 часов, то есть каждый час равен 30 градусам (360 / 12). Чтобы часовая стрелка двигалась плавно, а не перескакивала резко раз в час, мы должны учесть текущие минуты. Каждая минута сдвигает часовую стрелку на 0.5 градуса (30 градусов / 60 минут). Формула: УголЧасов = (Час % 12) * 30 + Минуты * 0.5.

Ниже представлен готовый XML-шаблон SVG-часов. Обратите внимание на маркеры [УголЧасов] и [УголМинут], которые мы будем динамически заменять в коде 1С:


<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
  <!-- Циферблат -->
  <circle cx="100" cy="100" r="95" stroke="#2c3e50" stroke-width="5" fill="#ecf0f1" />
  
  <!-- Часовые отметки -->
  <line x1="100" y1="10" x2="100" y2="25" stroke="#2c3e50" stroke-width="4" />
  <line x1="100" y1="190" x2="100" y2="175" stroke="#2c3e50" stroke-width="4" />
  <line x1="10" y1="100" x2="25" y2="100" stroke="#2c3e50" stroke-width="4" />
  <line x1="190" y1="100" x2="175" y2="100" stroke="#2c3e50" stroke-width="4" />

  <!-- Часовая стрелка -->
  <g transform="rotate([УголЧасов] 100 100)">
    <line x1="100" y1="100" x2="100" y2="55" stroke="#2c3e50" stroke-width="8" stroke-linecap="round" />
  </g>

  <!-- Минутная стрелка -->
  <g transform="rotate([УголМинут] 100 100)">
    <line x1="100" y1="100" x2="100" y2="25" stroke="#2980b9" stroke-width="5" stroke-linecap="round" />
  </g>

  <!-- Центральная точка -->
  <circle cx="100" cy="100" r="8" fill="#e74c3c" />
</svg>

Теперь напишем функцию на языке 1С, которая принимает время, выполняет замену параметров в шаблоне и возвращает готовый объект типа Картинка:


Функция СгенерироватьКартинкуЧасовSVG(Знач ТекущееВремя) Экспорт

	Час = Час(ТекущееВремя);
	Минуты = Минута(ТекущееВремя);
	
	// Расчет углов поворота стрелок
	УголМинут = Минуты * 6;
	УголЧасов = (Час % 12) * 30 + Минуты * 0.5;
	
	// Получаем текст шаблона SVG (например, из макета типа "Текстовый документ")
	ТекстШаблона = ПолучитьМакет("ШаблонSVGЧасов").ПолучитьТекст();
	
	// Подставляем рассчитанные значения углов
	ТекстSVG = СтрЗаменить(ТекстШаблона, "[УголЧасов]", Формат(УголЧасов, "ЧГ=0"));
	ТекстSVG = СтрЗаменить(ТекстSVG, "[УголМинут]", Формат(УголМинут, "ЧГ=0"));
	
	// Преобразуем строку XML в двоичные данные UTF-8
	ДвоичныеДанныеSVG = ПолучитьДвоичныеДанныеИзСтроки(ТекстSVG, КодировкаТекста.UTF8, Ложь);
	
	// Возвращаем готовую векторную картинку
	Возврат Новый Картинка(ДвоичныеДанныеSVG);

КонецФункции

Этот метод идеален для вывода в табличные документы печатных форм. Полученную картинку можно вставить в область ячеек макета стандартным методом Рисунки.Добавить().

Способ 2. Попиксельная тригонометрическая отрисовка в табличном документе

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

Для этого мы программно создаем область, например, размером 60 на 60 ячеек, задаем им минимально возможную высоту строк и ширину колонок, чтобы ячейки стали идеально квадратными. Закрашивая фон отдельных ячеек, мы можем рисовать линии.

Встроенные тригонометрические функции 1С Синус() и Косинус() принимают параметры только в радианах. Для перевода градусов в радианы применим формулу:

Радианы = Градусы * (3.141592653589793 / 180)

Давайте посмотрим на пример реализации отрисовки стрелок на пиксельной сетке табличного документа:


Процедура ОтрисоватьСтрелкуНаСетке(ТабДок, Знач ЦентрX, Знач ЦентрY, Знач ДлинаСтрелки, Знач УголВГрадусах, Знач ЦветСтрелки)
	
	// Переводим угол в радианы с учетом сдвига (в тригонометрии 0 градусов - это 3 часа, на часах - 12 часов)
	УголВРадианах = (УголВГрадусах - 90) * (3.141592653589793 / 180);
	
	// Отрисовываем стрелку по точкам от центра к краю
	Для ТекущийРадиус = 0 По ДлинаСтрелки Цикл
		
		ТекущийX = Окр(ЦентрX + ТекущийРадиус * Косинус(УголВРадианах));
		ТекущийY = Окр(ЦентрY + ТекущийРадиус * Синус(УголВРадианах));
		
		// Закрашиваем ячейку, выполняющую роль пикселя
		ЯчейкаПиксель = ТабДок.Область(ТекущийY, ТекущийX);
		ЯчейкаПиксель.ЦветФона = ЦветСтрелки;
		
	КонецЦикла;

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

Важное ограничение метода: Из-за отсутствия сглаживания (антиалиасинга) стрелки, идущие под углами, отличными от прямых, будут выглядеть дискретно («зубчатыми» линиями). Тем не менее, этот метод полностью автономен и работает на любых, даже самых старых версиях платформы.

Способ 3. Векторная отрисовка стрелок через графические объекты табличного документа

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

Мы берем заранее подготовленную красивую подложку циферблата в виде картинки и накладываем ее фоном на область ячеек. Поверх этой области мы программно создаем графические объекты Рисунок с типом ТипРисункаТабличногоДокумента.Прямая.

Основная сложность здесь заключается в позиционировании линий под нужным углом. В 1С объект "Прямая" рисуется из верхнего левого угла рамки в нижний правый. Чтобы направить стрелку в произвольную четверть круга, мы можем использовать недокументированный, но крайне эффективный прием: задание отрицательных значений высоты или ширины для графического объекта. Изменение знака ширины зеркально отразит линию по горизонтали, а высоты — по вертикали. Зная координаты центра часов и координаты конца стрелки, вычисляемые через Синус() и Косинус(), мы легко позиционируем "прямые" в пространстве макета.

Способ 4. Интерактивные часы на форме через Canvas и Поле HTML-документа

Если наша цель — вывести динамические, постоянно тикающие часы на управляемую форму, то регулярное обновление картинки на сервере по обработчику ожидания (например, раз в секунду) — это крайне неэффективное решение. Оно будет создавать колоссальную нагрузку на сервер 1С, вызывать мерцание интерфейса и приводить к потере фокуса ввода у активных элементов формы (особенно в терминальном режиме работы через RDP).

Для решения этой проблемы мы можем использовать Поле HTML документа. Отрисовка и анимация часов будут происходить на стороне клиента средствами веб-технологий HTML5 Canvas и JavaScript. Сервер 1С в этом процессе вообще не участвует.

Создадим на форме строковый реквизит, например, АдресЧасовHTML, свяжем его с элементом формы Поле HTML документа и поместим туда следующий код:


<html>
<head>
	<style>
		body { margin: 0; display: flex; justify-content: center; align-items: center; background-color: transparent; overflow: hidden; }
		canvas { background-color: transparent; }
	</style>
</head>
<body>
	<canvas id="canvas_clock" width="150" height="150"></canvas>
	<script>
		const canvas = document.getElementById("canvas_clock");
		const ctx = canvas.getContext("2d");
		const radius = canvas.height / 2;
		ctx.translate(radius, radius);
		setInterval(drawClock, 1000);

		function drawClock() {
			ctx.clearRect(-radius, -radius, canvas.width, canvas.height);
			drawDial(ctx, radius);
			drawTime(ctx, radius);
		}

		function drawDial(ctx, radius) {
			ctx.beginPath();
			ctx.arc(0, 0, radius - 5, 0, 2 * Math.PI);
			ctx.fillStyle = '#white';
			ctx.fill();
			ctx.strokeStyle = '#2c3e50';
			ctx.lineWidth = 4;
			ctx.stroke();
		}

		function drawTime(ctx, radius) {
			const now = new Date();
			let hour = now.getHours();
			let minute = now.getMinutes();
			let second = now.getSeconds();
			
			// Рассчитываем углы
			hour = (hour % 12) * Math.PI / 6 + (minute * Math.PI / (6 * 60)) + (second * Math.PI / (360 * 60));
			minute = (minute * Math.PI / 30) + (second * Math.PI / (30 * 60));
			second = (second * Math.PI / 30);
			
			// Отрисовка стрелок
			drawHand(ctx, hour, radius * 0.5, 6, '#2c3e50');
			drawHand(ctx, minute, radius * 0.75, 4, '#2980b9');
			drawHand(ctx, second, radius * 0.85, 2, '#e74c3c');
		}

		function drawHand(ctx, pos, length, width, color) {
			ctx.beginPath();
			ctx.lineWidth = width;
			ctx.lineCap = "round";
			ctx.strokeStyle = color;
			ctx.moveTo(0, 0);
			ctx.rotate(pos);
			ctx.lineTo(0, -length);
			ctx.stroke();
			ctx.rotate(-pos);
		}
	</script>
</body>
</html>

Этот скрипт запускает локальный таймер внутри браузера пользователя, который каждую секунду плавно и без задержек обновляет положение стрелок, используя системное время клиентской машины. При этом интерфейс 1С остается полностью отзывчивым.

Способ 5. Оптимизация производительности: кэширование предрассчитанных картинок

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

Поскольку на аналоговом циферблате положение часовой и минутной стрелок имеет ограниченное число комбинаций, мы можем применить стратегию кэширования:

  1. Всего существует 720 уникальных минут в 12-часовом цикле (12 часов * 60 минут).
  2. Если нам достаточно точности хода в 5 минут, то количество комбинаций сокращается всего до 144 вариантов картинок (12 часов * 12 пятиминутных интервалов).
  3. Мы можем один раз программно сгенерировать все возможные SVG-картинки и сохранить их в РегистрСведений с измерениями Час и Минута, поместив результат в ресурс с типом ХранилищеЗначения.

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

Подведем итоги

Для большинства современных прикладных задач в 1С лучшим выбором будет Способ 1 (Динамический SVG). Он обеспечивает безупречное качество вывода, идеален для печатных форм и не требует сторонних компонент. Если же требуется динамическое отображение "живых" часов на форме монитора или панели оператора, то наилучшую производительность и плавность анимации гарантирует Способ 4 (HTML5 Canvas в Поле HTML документа).

← На главную