# JavaScript

Copyright&nbsp;©&nbsp;2016–2023 ООО&nbsp;«Открытая мобильная платформа».
Этот документ предоставляется в&nbsp;соответствии
с&nbsp;[Публичной лицензией Creative Commons с&nbsp;указанием авторства версии&nbsp;4.0 Международная](../../LICENSE.CC-BY-4.0.ru.md).

## Определение ресурсов JavaScript в QML

Логика программы для приложения QML может быть определена в JavaScript.
Код JavaScript может быть определен как напрямую в документах QML, так и разделен на файлы
JavaScript (известные как Ресурсы JavaScript в QML).
В QML поддерживаются два вида ресурсов JavaScript: файлы реализации с выделенным кодом и общие
(библиотечные) файлы.
Оба вида ресурса JavaScript могут быть импортированы другими ресурсами JavaScript или включены
в модули QML.

## Разделение ресурсов JavaScript

Большинство файлов JavaScript, импортированных в документ QML, являются реализациями с сохранением
состояния для импортирующего их документа QML.
В этих случаях для правильного поведения каждого экземпляра типа объекта QML, определенного
в документе, требуется отдельная копия объектов и состояния JavaScript.

Поведение по умолчанию при импорте файлов JavaScript - предоставление уникальной изолированной
копии для каждого экземпляра компонента QML.
Если этот файл JavaScript не импортирует какие-либо ресурсы или модули с оператором .import, его
код будет работать в той же области, что и экземпляр компонента QML, и, следовательно, может
обращаться к объектам и свойствам, объявленным в этом компоненте QML, и манипулировать ими.
В противном случае он будет иметь свою собственную уникальную область видимости, и объекты
и свойства компонента QML должны быть переданы в функции файла JavaScript в качестве параметров,
если они требуются.

## Пример импорта: QML

Пример собственной кнопки.
Кнопка определяется в отдельном файле, файл импортируется под своим именем для использования.

## Пример импорта: JavaScript

В общем, простая логика должна быть определена в строке в файле QML, но более сложная логика
должна быть разделена на ресурсы реализации кода для удобства сопровождения и читаемости.

## Общие ресурсы JavaScript

Общие ресурсы JavaScript (библиотеки)
По умолчанию файлы JavaScript, импортированные из QML, делятся своим контекстом с компонентом QML.
Это означает, что файлы JavaScript имеют доступ к одним и тем же объектам QML и могут их изменять.
Как следствие, каждый импорт должен иметь уникальную копию этих файлов.

Ранее мы обсуждали импорт файлов JavaScript с сохранением состояния.
Однако некоторые файлы JavaScript не имеют состояния и действуют больше как библиотеки
многократного использования, в том смысле, что они предоставляют набор вспомогательных функций,
которым ничего не требуется из того места, откуда они были импортированы.
Вы можете сэкономить значительные объемы памяти и ускорить создание компонентов QML, если пометите
такие библиотеки специальной pragma, как показано в следующем примере.

## Пример pragma

Объявление pragma должно появляться перед любым кодом JavaScript, кроме комментариев.
Обратите внимание, что несколько документов QML могут импортировать «factorial.js» и вызывать
функции factorial и factorialCallCount, которые он предоставляет.
Состояние импорта JavaScript является общим для всех документов QML, которые его импортируют,
и поэтому возвращаемое значение функции factorialCallCount может быть ненулевым при вызове
в документе QML, который никогда не вызывает функцию факториала.

## Импорт в QML

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

## Импорт ресурсов JavaScript в QML

Ресурсы JavaScript могут быть импортированы документами QML и другими ресурсами JavaScript.
Ресурсы JavaScript могут быть импортированы через относительные или абсолютные URL-адреса.
В случае относительного URL-адреса местоположение разрешается относительно местоположения документа
QML или ресурса JavaScript, который содержит импорт.
Если файл скрипта недоступен, произойдет ошибка.
Если необходимо извлечь JavaScript из сетевого ресурса, состояние компонента устанавливается
на «Loading» до тех пор, пока скрипт не будет загружен.
Ресурсы JavaScript также могут импортировать модули QML и другие ресурсы JavaScript.
Синтаксис оператора импорта в ресурсе JavaScript немного отличается от оператора импорта в документе QML.

## Импорт ресурса JavaScript из документа QML

Документ QML может импортировать ресурс JavaScript.

Импортированные ресурсы JavaScript всегда указываются с использованием ключевого слова "as".
Квалификатор для ресурсов JavaScript должен начинаться с заглавной буквы и должен быть уникальным,
поэтому всегда существует взаимно-однозначное сопоставление между классификаторами
и файлами JavaScript.
Это также означает, что квалификаторы не могут называться так же, как встроенные объекты
JavaScript, такие как Date и Math.
Функции, определенные в импортированном файле JavaScript, доступны для объектов, определенных
в импортирующем документе QML, с помощью синтаксиса  "Qualifier.functionName(params)".
Функции в ресурсах JavaScript могут принимать параметры, тип которых может быть любым
из поддерживаемых базовых типов QML или типов объектов, а также обычными типами JavaScript.
Обычные правила преобразования типов данных будут применяться к параметрам и возвращаемым
значениям при вызове таких функций из QML.

## Импорт внутри ресурсов JavaScript

В QtQuick 2.0 добавлена поддержка, позволяющая ресурсам JavaScript импортировать другие ресурсы
JavaScript, а также пространства имен типа QML с использованием варианта стандартного
синтаксиса импорта QML.
Из-за способности ресурса JavaScript импортировать другой скрипт или модуль QML таким способом
в QtQuick 2.0, определена некоторая дополнительная семантика:

*	скрипт с импортом не будет наследовать импорт из документа QML, который его импортировал
	(поэтому, например, не удастся получить доступ к Component.errorString);
*	скрипт без импорта будет наследовать импорт из документа QML, который его импортировал
	(например, доступ к Component.errorString будет успешным);
*	общий сценарий (т.е. определяемый как библиотека .pragma) не наследует импорт из любого
	документа QML, даже если он не импортирует другие сценарии или модули.

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

## Пример импорта внутри JavaScript

Иногда желательно, чтобы функции были доступны в контексте импорта без необходимости их уточнения.
В этом случае модули ECMAScript и оператор импорта JavaScript должны использоваться
без квалификатора as.
Например, код QML вызывает метод showCalculations() в script.mjs, который, в свою очередь, может
вызывать factorial() в factorial.mjs, поскольку он включает factorial.mjs с использованием import.

## Пример Импорта внутри JavaScript [2]

Продолжение примера.

## Импорт модуля QML из ресурса JavaScript

Ресурс JavaScript может импортировать модуль QML следующим образом: import Qt.test 1.0 as JsQtTest.

В частности, это может быть полезно для доступа к функциям, предоставляемым через тип синглтона.
Синтаксис import не работает для сценариев, используемых в WorkerScript.

## Canvas — двумерная графика

Элемент Сanvas предоставляет зависящий от разрешения растровый холст, который можно использовать
для графики, игр или рисовать другие визуальные изображения с использованием JavaScript.
Элемент Сanvas основан на элементе Сanvas HTML5.

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

Рисование выполняется с использованием объекта Context2D, как правило, в результате сигнала paint.

*	context : Context2D — контекст для рисования.
*	getContext(contextId, … args) : Context2D — получить контекст.
*	markDirty(rect area) , requestPaint() — запросить перерисовку.
*	paint(rect region) — сигнал, что область нуждается в перерисовке.
*	painted() — сигнал, что отрисовка выполнена.
*	bool save(string filename) — сохранить рисунок в файл.

## Простой пример рисования

Пример простого рисования объекта.

Этот код создаёт заполненный прямоугольник с начальной точкой 50,50 и размером 100 и ломаной
линией, используемой в качестве границы.

Только путем вызова stroke или fill графический объект будет нарисован, и они могут использоваться
независимо друг от друга.
Вызов stroke или fill рисует текущий объект.
Невозможно сохранить текущий графический объект для последующего повторного использования,
только состояние чертежа может быть сохранено и восстановлено.

## Пример простой рисовалки

Пример создания простого графического редактора с помощью графики и обработки событий мыши.

Площадь MouseArea соответствует площади родительского элемента.

Событие, когда пользователь касается экрана, но касание не прерывает, отлавливается обработчиком
onPressed.
Проведенная линия сохраняется в массиве lines.

Как только точка касания изменяется, отрабатывает слот onPositionChanged, в котором к последнему
сохраненному пути добавляется новая точка, затем инициируется перерисовка. 

## WorkerScript: пример QML

Тип WorkerScriptQML позволяет JavaScript-коду запускаться параллельно с потоком графического
интерфейса.

Каждый экземпляр WorkerScript может подключаться к одному сценарию .js.
Когда вызывается WorkerScript.sendMessage(), скрипт запускается в отдельном потоке
(и в отдельном контексте QML).
Когда сценарий завершается, он может отправить ответ обратно в поток GUI, который вызовет
обработчик сигнала WorkerScript.onMessage().

Использование WorkerScript аналогично использованию рабочего объекта QObject, который был перемещен
в другой поток.
Данные передаются между потоками через сигналы.

Используйте WorkerScript для запуска операций в новом потоке.
Это полезно для выполнения операций в фоновом режиме, так что основной поток графического
интерфейса не блокируется.

Сообщения могут передаваться между новым потоком и родительским потоком с помощью sendMessage()
и обработчиком onMessage().

## WorkerScript: пример JS

WorkerScript указывает на файл JavaScript, «script.js», который обрабатывает операции, которые
должны выполняться в новом потоке.

Когда пользователь кликает в любом месте прямоугольника, вызывается sendMessage(), запуская
обработчик WorkerScript.onMessage() в script.js.
Это, в свою очередь, отправляет ответное сообщение, которое затем получает обработчик onMessage()
у myWorker.

## WorkerScript: ограничения

Поскольку функция WorkerScript.onMessage() запускается в отдельном потоке, файл JavaScript
оценивается в контексте, отличном от основного движка QML.
Это означает, что в отличие от обычного файла JavaScript, который импортируется в QML, script.js
в приведенном выше примере не может обращаться к свойствам, методам или другим атрибутам элемента
QML и не может обращаться к любым свойствам контекста, установленным в объекте QML,
через QDeclarativeContext.

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

sendMessage() отправляет данное сообщение обработчику WorkerScript в другом потоке.
Другой WorkerScript может получать это сообщение через обработчик onMessage().

Объект сообщения может содержать только значения следующих типов:

*	boolean, number, string;
*	объекты JavaScript и массивы;
*	объекты ListModel (любой другой тип QObject не разрешен).

Все объекты и массивы копируются в сообщение.
За исключением объектов ListModel, любые изменения другого потока для объекта, переданного
в сообщении, не будут отражаться в исходном объекте.
