пн-чт 09:00-18:00, пт 09:00-17:00
+7 (812) 648-03-47
+7 (812) 648-03-47 +7 (495) 108-12-75

sales@rusavtomatika.com

Режим работы:

пн-чт 09:00-18:00, пт 09:00-17:00

Адрес:

199178, Санкт-Петербург,
Малый пр. В. О. 57 корп. 3

Карта проезда

Подробнее

Каталог

Расширение графического функционала панелей Weintek средствами встроенного JavaScript

15.09.2023

СОДЕРЖАНИЕ

Введение

1. Замена графического объекта "Динамический рисунок"

1.1 Реализация с помощью объекта "Динамический рисунок"

1.2 Реализация с помощью объекта "JS"

2. Круговой регулятор

2.1 Цветовой круг

2.2 Регулятор яркости

 

 

EasyBuilder Pro позволяет создавать довольно симпатичный пользовательский интерфейс, графическим объектам (кнопки, поля ввода/вывода, спидометр и т.п.) можно придавать различный внешний вид, но требовательный пользователь хочет "как в айфоне" и мы попробуем воссоздать нечто подобное средствами встроенного JavaScript.

 

Введение

Про JavaScript в EasyBuilder Pro нам известно следующее:

 

  1. Требования к оборудованию и ПО:
    • EasyBuilder Pro V6.05.01 или старше:
    • Применяемая версия JavaScript: ECMAScript 2017 (не включая SharedArrayBuffer и Atomics);
    • панели серии cMT X;
    • объект "JS" не поддерживается на 32-битных Android устройствах.
  2. Ограничения:
    • Максимальный размер кода в JS объекте: 100 КБ;
    • Максимальный размер JS файла-ресурса: 10 МБ
    • Максимальный размер памяти для контекста JS*: 20 МБ.
  3. Как работает объект JS (так сказать "под капотом"):
    • создаются JS контекст;
    • создается объект JS;
    • инициализируется конфигурация объекта JS (this.config);
    • инициализируется виджет объекта JS (this.widget);
    • ожидается ответ от всех подписок (subscriptions);
    • оборачивается исходный код объекта JS в асинхронную функцию и она вызывается.

 

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

 

В Руководстве пользователя EasyBuilder Pro описанию объекта JS посвящена глава 43.

Дополнительно информация по SDK расположена на веб-ресурсе https://dl.weintek.com/public/Document/JS_Object_SDK/Current/index.html.

Демо проекты можно взять на вкладке Demo project страницы https://www.weintek.com/globalw/Download/Download.aspx .

На нашем Youtube канале есть видео, демонстрирующее возможности объекта JS.

 

1. Замена графического объекта "Динамический рисунок"

Один из простых объектов, которые бы хотелось заменить на что-нибудь получше – это объект "Динамический рисунок". Для примера возьмем вариант "коробка движется по транспортерной ленте".

 

1.1 Реализация с помощью объекта "Динамический рисунок"

Реализуем его сначала через этот объект, а потом через JS и сравним результат (что было проще и удобнее создавать и что лучше выглядит в Runtime).

Концепция "коробка движется по транспортерной ленте"

 

Транспортер "нарисуем" из примитивов "Линия", "Окружность" и т.п. .

Для удобства манипулирования свойствами объекта создадим несколько адресных меток.

Адресные метки для управления объектом "Динамический рисунок"

 

Пояснения к адресным меткам (тегам):

box_type

 форма, которая будет нарисована в объекте "Динамический рисунок" (линия, прямоугольник, круг и т.д.)

box_x1

 координата X начала объекта "Динамический рисунок"

box_y1

 координата Y начала объекта "Динамический рисунок"

box_x2

 координата X конца объекта "Динамический рисунок"

box_y2

 координата Y конца объекта "Динамический рисунок"

box_height

 высота коробки (задается пользователем)

box_width

 ширина коробки (задается пользователем)

box_position

 положение коробки на транспортёрной ленте (задается со стороны ПЛК)

box_color_frame

 цвет рамки коробки

box_color_fill

 цвет заполнения коробки

box_style

 стиль формы/стрелки

box_line_style

 стиль линии заливки

box_clear

 флаг стирания нарисованного нами объекта "Динамический рисунок"

box_position_old

 предыдущая позиция коробки на транспортёрной ленте

JS_box_height

 высота коробки (для JS отдельно, потому что мы корректируем ее в макросе объекта "Динамический рисунок", значение копируется из поля ввода для box_height)

JS_box_width

 ширина коробки (для JS отдельно)

counter_on_off

 флаг управления выполнением макроса counter (движение по транспортёрной ленте)

 

Параметры нашего объекта следующие:

Параметры объекта "Динамический рисунок"

 

Цветовая палитра нашего объекта показана на рисунке ниже.

Цветовая палитра объекта "Динамический рисунок"

 

Первоначальные значения некоторым параметрам зададим через кнопки "Числовая кнопка" с атрибутом "Установить когда окно открыто".

Задаем начальные значения объекту "Динамический рисунок" через объект "Установить слово"

 

 

В результате наших манипуляций получился вот такой видеокадр.

Мнемосхема с динамическим рисунком

 

Для управления динамическим рисунком напишем небольшой макрос.

macro_command main()

unsigned short box_type = 2
unsigned short container_width = 760 // ширина объекта "Динамический рисунок"
unsigned short container_height = 80 // высота объекта "Динамический рисунок"
unsigned short const_1 = 1 // сплошная заливка
unsigned short color_fill = 6 // цвет заливки - красный

unsigned int box_position, box_position_old
unsigned short box_x1, box_y1, box_x2, box_y2
unsigned short box_width, box_height
bool box_clear = 1
unsigned short box_path // реальная длина пути перемещения бокса

float var_a


// корректируем некорректные размеры бокса
 GetData(box_width, "Local HMI", "box_width", 1)
 GetData(box_height, "Local HMI", "box_height", 1)
 
 if box_height == 0 then
     box_height = 30
 end if
 
 if box_width == 0 then
     box_width = 60
 end if

 if box_height > container_height then
     box_height = container_height
 end if
 
 if box_width > container_width then
     box_width = container_width
 end if

// вычисляем геометрию
box_y1 = container_height - box_height
box_y2 = container_height

box_path = container_width - box_width

GetData(box_position, "Local HMI", "box_position", 1)
GetData(box_position_old, "Local HMI", "box_position_old", 1)

    if box_position <> box_position_old then
    
        SetData(box_clear, "Local HMI", "box_clear", 1) // очищаем рисунок
        
        SetData(box_width, "Local HMI", "box_width", 1)
        SetData(box_height, "Local HMI", "box_height", 1)
        
        var_a = (box_position*box_path)
        box_x1 = (box_position*box_path)/100 // вычисляем новое положение груза
        box_x2 = box_x1 + box_width
        
        box_position_old = box_position // обновляем значение положения
        
        
        // записываем параметры бокса
        SetData(box_x1, "Local HMI", "box_x1", 1)
        SetData(box_y1, "Local HMI", "box_y1", 1)
        
        SetData(box_x2, "Local HMI", "box_x2", 1)
        SetData(box_y2, "Local HMI", "box_y2", 1)        
        
        SetData(const_1, "Local HMI", "box_style", 1) // форма бокса - заполненный прямоугольник
        SetData(color_fill, "Local HMI", "box_color_frame", 1) // цвет заливки бокса - красный
        
        SetData(box_position_old, "Local HMI", "box_position_old", 1) // обновляем значение предыдущей позиции
        SetData(box_type, "Local HMI", "box_type", 1) // обновляем рисунок
        
    end if
end macro_command

 

Зададим макросу периодическое выполнение.

Периодическое выполнение макроса

 

 

Автоматическое изменение положения задаим через другой макрос.

macro_command main()

unsigned int box_position
bool count_up // направление инкремента

   GetData(box_position, "Local HMI", "box_position", 1)
   GetData(count_up, "Local HMI", LB, 555, 1)

   if box_position == 100 then
       count_up = false
   end if

   if box_position == 0 then
       count_up = true
   end if


   if count_up == true then
       box_position = box_position + 1
   else
       box_position = box_position - 1
   end if

   SetData(box_position, "Local HMI", "box_position", 1)
   SetData(count_up, "Local HMI", LB, 555, 1)

end macro_command

 

Зададим макросу периодическое выполнение.

Периодическое выполнение макроса

 

Для удобства наблюдения за поведением/перемещением нашего бокса на транспортёрной ленте добавим управление выполнением макросом.

Добавление управлеия выполненим макроса

 

 

Теперь запустим наш проект в симуляторе.

 

 

 

Теперь попробуем реализовать подобный объект средствами JS.

 

1.2 Реализация с помощью объекта "JS"

Добавляем объект JS со следующей конфигурацией:

Конфигурация объекта JS

 

Для реалистичности добавим и картинку транспортёрной ленты.

Мнемосхема с обоими вариантами отображения движущейся коробки

 

 

 

Исходный код на JS будет весьма простой.

Для получения введённых пользователем размеров коробки мы будем использовать подписку на изменение значения, потому что метод getData() является асинхронным и не позволит получить нам значения регистров в момент его вызова.

var self = this;

var box = new Canvas(); // тот самый "ящик", который будет ездить по транспортёрной ленте
self.widget.add(box);
var container_height = box.height; // высота объекта "JS"
var container_width = box.width; // ширина объекта "JS"

var box_position = 0; // положение коробки на транспортёрной ленте
var box_x = 0, box_y = 0; // координаты верхнего левого угла коробки
var box_width = 0, box_height = 0; // размеры коробки
var box_path = 0; // реальная длина пути перемещения бокса
var color_fill = 'red'; // цвет заливки


// заливаем цветом весь box
box.fillStyle = color_fill;
box.fillRect(box.x, box.y, box.width, box.height);


//===== блок пользовательских функций ===

//--- корректировка некорректных размеров коробки
function box_size_correction() 
{
  if (box_height === 0) {box_height = 30; } // значение по умолчанию
  if (box_width === 0) {box_width = 60; } // значение по умолчанию
  if (box_height > container_height) { box_height = container_height; }
  if (box_width > container_width) { box_width = container_width; }

  box.height = box_height;
  box.width = box_width;
  box.y = container_height - box.height;

  // вычисляем длину пути, по которому может ездить коробка
  box_path = container_width - box.width;


//--- функция перерисовки коробки
function redraw()
{
  box_size_correction(); // корректируем введённые размеры коробки
  box.x = (box_position*box_path)/100; // вычисляем новое положение коробки
}

//////===== блок функций onResponse =====

//--- мониторим изменение тега с положением коробки
self.config.position.onResponse (
     (err, data) => 
     {
       if (err) { console.log('position.onResponse error = ', err.message);}
       else
              box_position = data.values[0];
              redraw();
            }
    })


//--- мониторим изменение тега с высотой коробки
self.config.height.onResponse (
     (err, data) => 
     {
       if (err) { console.log('height.onResponse error = ', err.message);}
       else
              box_height = data.values[0];
              redraw();
            }
    })

//--- мониторим изменение тега с шириной коробки
self.config.width.onResponse (
     (err, data) => 
     {
       if (err) { console.log('width.onResponse error = ', err.message);}
       else
              box_width = data.values[0];
              redraw();
            }
    }) 

 

Теперь запустим наш проект в симуляторе и сравним с вариантом на объекте "Динамический рисунок".

 

 

К плюсам JS можно отнести:

  • весь код находится внутри объекта
  • плавное перемещение объекта;
  • возможность рисовать любые фигуры;
  • возможно определить размеры объекта "JS" внутри скрипта;

 

К минусам JS можно отнести:

  • неудобство отладки (только вывод текста в консоль, нет возможности пройти по шагам и т.п.);
  • бедная подсветка синтаксиса;
  • ошибки выводятся только в консоль (нет системного регистра для мониторинга состояния скрипта как в макросах)

 

2. Круговой регулятор

Некоторые клиенты хотят создавать не только информативный и удобный интерфейс оператора, но и красивый. И в этом они равняются на законодателей мод – мобильные устройства.

Интерфейс оператора, который нарисовал дизайнер

 

Как видно на рисунке, использованы объекты типа "круговой регулятор", один для выбора цвета (подсветки), второй для выбора яркости (освещения).

Попробуем создать максимально похожий интерфейс средствами Easy Builder Pro.

Отметим некоторые нюансы создания интерфейса оператора в нашем примере:

        • подготовка фона будет происходить в стороннем графическом редакторе (GIMP, Photoshop);
        • простые графические элементы (Прямоугольник, Окружность, Число, Текст, Кнопка) будут использоваться из палитры объектов EayBuilder;
        • объекты JS будут реализовывать только дополнительный графический функционал (круговые регуляторы).

 

Приступим к созданию проекта.

Первым делом добавим картинку фона в библиотеку изображений проекта.

Добавление картинки в Библиотеку изображений проекта

 

Примечание:  Обратите внимание, что картинку мы подготовили такого же разрешения как экран панели (1024х600). В противном случае пришлось бы картинку смасштабировать и в Runtime панель бы "подгоняла" размер картинки, на что тратила бы свои ресурсы (EasyBuilder предупреждает об этом во время компиляции проекта).

В окно наш фон добавляем посредством объекта "Изображение", статичный текст посредством объекта "Текст", отображать числа будем с помощью объекта "Число" и т.д.

Мы не будем подробно останавливаться на использовании графических объектов из палитры EasyBuilder (пример создания проекта описан в статье "Пример проекта для панели Weintek, демо проект", описание объектов EasyBuilder находится в главе 13 Руководства пользователя EasyBuilder Pro).

В итоге у нас получился вот такой вот экран:

Интерфейс оператора в режиме разработки

 

Поскольку предполагается передавать выбранный цвет в какое-то устройство, то выведем выбранный цвет покомпонентно в регистры. Выбранную яркость будем отображать в теле регулятора как надпись.

 

Осталось нарисовать круговые регуляторы средствами JavaScript.

Концептуально мы будем рисовать как в двух слоях в графическом редакторе (например, Photoshop, GIMP и т.п.):

        • первый слой – виджет со статичной картинкой (цветовой круг, желтый круг в центре и надпись);
        • второй слой – виджет динамично перерисовывающий указатель касания.

Первый слой кругового регулятора "Цветовой круг"

 

Первый слой кругового регулятора "Яркость"

 

Второй слой круговых регуляторов

 

Рисовать мы будем на объекте Canvas, который ведет себя как HTML5 Canvas https://www.w3schools.com/html/html5_canvas.asp .

 

2.1 Цветовой круг

Поскольку мы будем записывать из JS кода значение выбранного цвета в адресные метки (в теги), то нам необходимо на вкладке "Конфигурирование" добавить эти адресные метки. Делается это нажатием кнопки "Новое значение", в открывшемся окне даем ему имя, выбираем тип "Address".

Примечание:  Внутри JavaScript число представлено в виде 64-битного формата IEEE-754.

 

Добавление параметров в конфигурацию объекта JS

 

Для написания JS кода необходимо перейти на вкладку "Исходный код" и открыть окно для кода как отдельное – так нам будет комфортнее писать код.

Открытие отдельного окна для кода

 

Проверку синтаксиса нашего кода можно произвести нажатием кнопки "Компиляция".

"Компиляция" JS кода

 

Кнопка полезная, но полностью на нее полагаться не стоит.

Лишнюю скобку в строке 3, например, эта проверка заметит, а необъявленный объект grd222 в строке 25 нет.

Компиляция указывает на ошибку

 

В коде первым делом мы отвяжем контекст JS объекта- контейнера в окне EasyBuilder от контекста остальных объектов внутри JS кода, например функций. Иначе при вызове внутри функции какого-либо driver.setData(this.config.tag_1, var_1) вызовет ошибку во время выполнения кода.

 

var self = this; // отвяжем контекст JS объекта от других контекстов this, например от функций

 

Сначала создаем объект "первый слой" типа Canvas. Потом рисуем на нём цветовой круг посредством линейного градиента.

 

// создаем первый "слой" нашего кругового регулятора

var gx1 = new Canvas(); // создаём объект типа "Canvas"

self.widget.add(gx1); // добавляем объект типа "Canvas" в виджет

 

// создаем переменные для работы с координатами/габаритами нашего "контейнера" - объекта JS

// определяем координаты центра "контейнера"

var CX = gx1.width/2; // координата Х центра

var CY = gx1.height/2; // координата Y центра

 

 

//// полезные функции разместим в начале скрипта

 // функция перевода угловых градусов в радианы

function Deg2Rad(num) {

//переводим градусы в радианы

return num * Math.PI/180;

}

 

// рисуем цветовой круг с помощью линейного градиента

for (var i = 0; i < 360; i+=0.05)

{

var rad = i * (2*Math.PI) / 360;

var grd = gx1.createLinearGradient (CX, CY, CX + CX * Math.cos(rad), CY + CY * Math.sin(rad));

 

grd.addColorStop(0, "white");

grd.addColorStop(0.05, "white");

grd.addColorStop(0.15, "hsla(" + i + ", 100%, 50%, 1.0)");

grd.addColorStop(1, "hsla(" + i + ", 100%, 50%, 1.0)");

gx1.strokeStyle = grd;

gx1.beginPath();

gx1.moveTo(CX, CY);

gx1.lineTo(CX + CX * Math.cos(rad), CY + CY * Math.sin(rad));

gx1.stroke();

}

 

Сохраняем наш проект по Ctrl+S и запускаем симуляцию.

Если всё правильно написали, увидим цветовой круг.

Результат выполнения корректного кода JS

 

Если где-то ошиблись, то запускаем Диагностику и смотрим вкладку JS.

Вызов окна Диагностики из контекстного меню симулятора

 

 

Диагностика ошибок на вкладке JS

 

Вывод функции console.log() тоже сюда попадает

Возвращаемся в редактор кода и "дорисовываем" остальные части "первого" слоя.

 

//--- рисуем желтый круг в центре

var ctrRingRadius = CX - (gx1.width/8)

gx1.beginPath();

gx1.lineWidth = 1;

gx1.strokeStyle = "lightgray";

gx1.fillStyle = "#FEE066";

gx1.arc(CX, CY, ctrRingRadius, 0, 360, false);

gx1.stroke();

gx1.fill();

 

//--- рисуем текст в цетре круга

var text1 = 'Освещение';

var text2 = 'SPA зоны';

var text1_width = gx1.measureText(text1).width; // вычисляем ширину первой строки

var text2_width = gx1.measureText(text2).width; // вычисляем ширину второй строки

var font_size = gx1.height/10; // задаем кегль (размер шрифта)

 

gx1.font = 'italic '+font_size+'px Arial';

gx1.fillStyle = "black"; // цвет текста - "чёрный"

gx1.textAlign = "center"; // выравнивание - по центру

gx1.fillText(text1, CX-(text1_width/2)+35, CY-10);

gx1.fillText(text2, CX-(text2_width/2)+30, CY+25);

 

Сохраняем наш проект по Ctrl+S и запускаем симуляцию.

 

"Первый" слой готов

 

 

Дорисуем второй" слой, который отображает индикатор касания.

 

///---- добавляем "второй" слой

var gx2 = new Canvas(); // создаём объект типа "Canvas"

self.widget.add(gx2); // добавляем объект типа "Canvas" в виджет

 

// ставим указатель в начальное положение

var dotRingRadius = (CX - ctrRingRadius)/2;

 

gx2.beginPath();

gx2.lineWidth = 1;

gx2.strokeStyle = "black";

gx2.fillStyle = "white";

gx2.arc(CX, dotRingRadius, dotRingRadius, 0, 2*Math.PI, false);

gx2.stroke();

gx2.fill();

 

Сохраняем наш проект по Ctrl+S и запускаем симуляцию.

 

"Второй" слой с индикатором в начальном положении

 

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

Добавляем обработку касаний пользователем экрана. Сделаем это с помощью MouseArea.

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

 

///---- добавляем объект для фиксирования факта касаний

var mouseArea = new MouseArea();

self.widget.add(mouseArea);

 

Информация о положении мыши предоставляется с помощью таких событий, как click, mousedown, mousemove и mouseup.

И тут у нас два варианта на какое событие перерисовывать: click или mousemove.

Формально нет смысла водить пальцем через полкруга от желтого к синему, можно сразу ткнуть пальце в нужный цвет. В таком случае нам достаточно обработать событие click. Это работает просто и четко. Но в качестве эксперимента мы попробуем реализовать вариант с обработкой события mousemove, когда пользователь непрерывно ведет пальцем по цветовому кругу и мы так же непрерывно записываем текущий цвет в регистры панели.

 

//=========== обработка событие касания ===========

var mousePosition = {x: 0, y: 0}; // создаем переменные под полученные от mouseArea координаты касания

 

mouseArea.on('mousemove', (mouseEvent) => {

mousePosition.x = mouseEvent.x;

mousePosition.y = mouseEvent.y;

requestAnimationFrame(redraw); // вызываем requestAnimationFrame() для плавной анимации перерисовки

});

//=========== конец обработки события ===========

 

Осталось написать саму функцию redraw(), которая собственно и будет заниматься перерисовкой указателя касания в новую точку касания, получем цвета в точке касания, записи цвета в регистры панели.

// создадим переменные для работы с цветом в точке касания

var imgData; // объект, представляющий базовые пиксельные данные для указанной части холста

var red;     // компонента красного цвета

var green;   // компонента зеленого цвета

var blue;    // компонента синего цвета

var alpha;   // альфа-канал

 

//=========== Функция REDRAW (перерисовки указателя касания) ===========

function redraw() {

gx2.clearRect(0, 0, gx2.width, gx2.height);

 

var touch_x; // координаты касания относительно центра виджета/круга

var touch_y;

touch_x = mousePosition.x - CX;

touch_y = CY - mousePosition.y;

 

var R_pointer // расстояние до центра иникатора касания

R_pointer = ctrRingRadius + (CX - ctrRingRadius)/2;

 

var a_deg; // угол до нового положения указателя касания

a_deg = Math.atan2(touch_y, touch_x)*180 / Math.PI; // получаем угол наклона прямой от центра до точки касания в градусах

a_deg = (a_deg > 0) ? a_deg : 360 + a_deg; // переносим 0 градусов в привычное для тригонометрии положение справа от оси Y

 

// координаты новой точки центра указателя касания

var x_new;

var y_new;

x_new = (Math.cos(Math.PI*a_deg/180)*R_pointer) + CX;

y_new = gx1.height - ((Math.sin(Math.PI*a_deg/180)*R_pointer) + CY);

 

// рисуем указатель касания в месте касания

gx2.beginPath();

gx2.lineWidth = 1;

gx2.strokeStyle = "black";

gx2.fillStyle= "white";

gx2.arc(x_new, y_new, dotRingRadius, 0, 2*Math.PI, false);

gx2.stroke();

gx2.fill();

 

// получаем цвет пикселя в месте касания на цветовом круге

imgData = gx1.getImageData(x_new, y_new, 1, 1);

  red = imgData.data[0]; // компонента красного цвета

green = imgData.data[1]; // компонента зеленого цвета

 blue = imgData.data[2]; // компонента синего цвета

alpha = imgData.data[3]; // альфа-канал

 

// для проверки корректности определения цвета на этапе отладки

// нарисуем в сторонке квадрат цветом полученного пикселя

gx2.fillStyle = "rgba("+red+", "+green+", "+blue+", "+alpha+")";

gx2.fillRect(gx2.width-30, gx2.height-30, 30, 30);

 

// запишем цвет в регистры

driver.setData(self.config.color_R, red, (err) => {

if (err) {console.log('Ошибка записи в color_R:', err.message);}});

 

driver.setData(self.config.color_G, green, (err) => {

if (err) {console.log('Ошибка записи в color_G:', err.message);}});

 

driver.setData(self.config.color_B, blue, (err) => {

if (err) {console.log('Ошибка записи в color_B:', err.message);}});

 

};

 

 

Сохраняем наш проект по Ctrl+S и запускаем симуляцию.

Выбор цвета

 

 

 

2.2 Регулятор яркости

Отличие в коде будет только в цвете "цветового круга" – здесь он будет от черного к белому и отсутствии записи значения в регистры панели (выбранную яркость напишем прямо на круге).

 

var self = this; // отвяжем контекст JS объекта от других контекстов this, например от функций

// создаем первый "слой" нашего кругового регулятора

var gx1 = new Canvas();

self.widget.add(gx1);

 

// создаем переменные для работы с координатами/габаритами нашего "контейнера" - объекта JS

// определяем координаты центра "контейнера"

var CX = gx1.width/2;

var CY = gx1.height/2;

 

//// полезные функции разместим в начале скрипта

// функция перевода угловых градусов в радианы

function Deg2Rad(num) {

//переводим градусы в радианы

return num * Math.PI/180;

}

 

// рисуем черно-белый круг с помощью линейного градиента

for (var i = -90; i < 270; i+=0.05)

{

var rad = i * (2*Math.PI)/360;

var grd = gx1.createLinearGradient (CX, CY, CX + CX * Math.cos(rad), CY + CY * Math.sin(rad));

grd.addColorStop(0.5, "hsla(190, 0%, "+i/180+", 1.0)");

gx1.strokeStyle = grd;

gx1.beginPath();

gx1.moveTo(CX, CY);

gx1.lineTo(CX + CX * Math.cos(rad), CY + CY * Math.sin(rad));

gx1.stroke();

}

 

//--- рисуем желтый круг в центре

var ctrRingRadius = CX - (gx1.width/8)

gx1.beginPath();

gx1.lineWidth = 1;

gx1.strokeStyle = "lightgray";

gx1.fillStyle = "#FEE066";

gx1.arc(CX, CY, ctrRingRadius, 0, 360, false);

gx1.stroke();

gx1.fill();

 

//--- рисуем текст в цетре круга

var text1 = 'Яркость';

var text2 = 'общая';

var text1_width = gx1.measureText(text1).width;

var text2_width = gx1.measureText(text2).width;

var font_size = gx1.height/10;

gx1.font = 'italic '+font_size+'px Arial';

gx1.fillStyle = "black";

gx1.textAlign = "center";

gx1.fillText(text1, CX-(text1_width/2)+gx1.width/12, CY-10);

gx1.fillText(text2, CX-(text2_width/2)+gx1.height/12, CY+25);

 

///---- добавляем "второй" слой

var gx2 = new Canvas(); // создаём объект типа "Canvas"

self.widget.add(gx2); // добавляем объект типа "Canvas" в виджет

 

// ставим указатель в начальное положение

var dotRingRadius = (CX - ctrRingRadius)/2;

gx2.beginPath();

gx2.lineWidth = 1;

gx2.strokeStyle = "black";

gx2.fillStyle = "white";

gx2.arc(CX, dotRingRadius, dotRingRadius, 0, 2*Math.PI, false);

gx2.stroke();

gx2.fill();

 

///---- добавляем объект для фиксирования факта касаний

var mouseArea = new MouseArea();

self.widget.add(mouseArea);

 

//=========== Функция REDRAW (перерисовки указателя касания) ===========

function redraw() {

gx2.clearRect(0, 0, gx2.width, gx2.height);

 

var x; // координаты относительно центра виджета/круга

var y;

x = mousePosition.x - CX;

y = CY - mousePosition.y;

 

var R

R = ctrRingRadius + (CX - ctrRingRadius)/2; // вычисляем радиус центра нашего индикатора касаний

 

var a_deg; // угол до нового положения касания

a_deg = (Math.atan2(y, x)*180 / Math.PI); // получаем угол наклона в градусах

 

// переносим 0 градусов в привычное для тригонометрии положение справа от оси Y

a_deg = (a_deg > 0) ? a_deg : 360 + a_deg;

 

var brightness // яркость от 0% до 100%

brightness = a_deg - 90;

brightness = (brightness > 0) ? brightness : brightness + 360;

brightness = Math.round((1-(brightness/360).toFixed(2))*100); // округляем до целого значения

 

//--- рисуем уровень яркости текстом в цетре круга

var text_bright = brightness +'%'; // уровень яркости

var text_bright_width = gx1.measureText(text1).width; // ширина текста

var font_size = gx1.height/10; // размер текста (кегль)

gx2.font = font_size+'px Arial';

gx2.fillStyle = "black"; // цвет такста "черный"

gx2.textAlign = "center"; // вравнивание текста - по центру

gx2.fillText(text_bright, CX-(text_bright_width/2)+gx1.width/5, CY+65);

 

// координаты новой точки центра указателя касания

var x_new;

var y_new;

 

x_new = (Math.cos(Math.PI*a_deg/180)*R) + CX;

y_new = gx1.height - ((Math.sin(Math.PI*a_deg/180)*R) + CY);

 

// рисуем индикатор касания в месте касания

gx2.beginPath();

gx2.lineWidth = 1;

gx2.strokeStyle = "black";

gx2.fillStyle = "white";

gx2.arc(x_new, y_new, dotRingRadius, 0, 2*Math.PI, false);

gx2.stroke();

gx2.fill();

}

 

//=========== обработка событие касания ===========

var mousePosition = {x: 0, y: 0}; // создаем переменные под полученные от mouseArea координаты касания

 

mouseArea.on('mousemove', (mouseEvent) => {

mousePosition.x = mouseEvent.x;

mousePosition.y = mouseEvent.y;

 

requestAnimationFrame(redraw);

});

 

 

Сохраняем наш проект по Ctrl+S и запускаем симуляцию.

 

Регулятор яркости

 

 

 

Примечание: Данный проект является учебным и предназначен для ознакомления пользователей с графическими возможностями объекта JS в EasyBuilder Pro.
Код не является оптимальным.
Для встраивания в свои проекты пользователям необходимо самостоятельно дорабатывать проект для соответствия своим требованиям (требованиям заказчика).

 

 

---

Все вопросы и обсуждения - на нашем форуме.

Видео о панелях Weintek на нашем Youtube канале.