====== Qt Quick: Изображения, Ввод, Анимация ======
===== Элементы графики =====
==== Растровые изображения ====
Можно использовать форматы: **JPG**, **PNG** и **SVG**.\\
=== Элемент Image ===
Этот элемент отображает файл изображения, указанный в свойстве **source**, файл может находится как локально так и в сети.\\
Есть интерактивный редактор для изменения свойств.\\
Свойствами можно подвергать изображение трансформациям, типа увеличение/уменьшение (**scale**), поворот (**rotation**), сглаживание (**smooth**).\\
:!: Пример: Изображение с уменьшением, поворотом и сглаживанием
import QtQuick 2.12; import QtQuick.Controls 2.2; import QtQuick.Layouts 1.12
ApplicationWindow
{
width: img.width; height: img.height; visible: true;
Rectangle
{
anchors.fill: parent
Image
{
id: img
source: "qrc:/../../../../Downloads/11.jpg"
x: 0; y: 0
smooth: true
scale: 0.75
rotation: -30
}
}
}
Для более тонкой настройки трансформации, ее можно задавать списками (в квадратных скобках).\\
:!: Пример: Аналогично, только трансформацию задаем двумя списками свойству **transform**
import QtQuick 2.12; import QtQuick.Controls 2.2; import QtQuick.Layouts 1.12
ApplicationWindow
{
width: img.width; height: img.height; visible: true;
Rectangle
{
anchors.fill: parent
Image
{
id: img
source: "qrc:/../../../../Downloads/11.jpg"
x: 0; y: 0
smooth: true
transform:
[
Scale
{
origin.x: width / 2
origin.y: width / 2
xScale: 0.75
yScale: 0.75
},
Rotation
{
origin.x: width / 2
origin.y: width / 2
angle: -30.0
}
]
}
}
}
Добавим элемент загрузки **BusyIndicator**
:!: Пример: Загрузка и отображение изображения из web
import QtQuick 2.12; import QtQuick.Controls 2.2; import QtQuick.Layouts 1.12
ApplicationWindow
{
width: 640; height: 480; visible: true;
Rectangle
{
anchors.fill: parent
Image
{
id: img
source: "http://qt-book.com/pic.jpg"
anchors.fill: parent
smooth: true
Column
{
anchors.centerIn: parent
visible: img.status == Image.Loading ? true:false
Text
{
text: "Загрузка..."
}
BusyIndicator
{}
}
}
}
}
=== BorderImage ===
Элемент для нормального масштабирования графики, особенно полезно при закругленных углах (кнопок и т.д.). Элемент разбивает изображение на девять частей.\\
:!: Пример: Масштабируемое изображение кнопки, с закругленными углами
import QtQuick 2.12
BorderImage
{
source: ""
width: 100; height: 45
border {left: 30; top: 15; right: 30; bottom: 15}
}
==== Градиент ====
----
В QML есть только один градиент - линейный, если нужен другой, можно воспользоваться трансформацией.\\
Для создания, нужно свойству **gradient** присвоить элемент **Gradient**, он содержит точки останова (**GradientStop**) с позициями от 0 до 1.\\
Создание градиентов может потребовать много ресурсов, поэтому эффективнее использовать изображение градиента.\\
:!: Пример: Градиент с тремя точками останова, трансформирован в диагональ
import QtQuick 2.12; import QtQuick.Controls 2.2; import QtQuick.Layouts 1.12
ApplicationWindow
{
width: 640; height: 480; visible: true;
Rectangle
{
anchors.fill: parent
gradient: Gradient
{
GradientStop{position: 0.0; color: "blue"}
GradientStop{position: 0.7; color: "gold"}
GradientStop{position: 1.0; color: "silver"}
}
rotation: 30
scale: 1.5
}
}
==== Рисование Canvas ====
----
**Canvas** представляет собой элемент холста, на котором можно выполнять растровые операции, по сути аналогичен **QPaintDevice**.\\
Сам QML описательный язык, поэтому алгоритмы рисования реализуются с использованием **JavaScript**, в свойстве обработки **onPaint**.\\
:!: Пример: Прикольный узор, нарисованный на холсте
import QtQuick 2.12; import QtQuick.Controls 2.2; import QtQuick.Layouts 1.12
ApplicationWindow
{
width: 480; height: 480; visible: true;
Rectangle
{
anchors.fill: parent
Canvas
{
anchors.fill: parent;
onPaint:
{
function drawFantasy()
{
ctx.beginPath();
ctx.translate(parent.width / 2, parent.height / 2);
var fAngle = 91 * 3.14156 / 180
for(var i = 0; i < 300; ++i)
{
var n = i * 2;
ctx.moveTo(0, 0);
ctx.lineTo(n, 0);
ctx.translate(n, 0);
ctx.rotate(fAngle)
}
ctx.closePath();
}
var ctx = getContext("2d");
ctx.clearRect(0, 0, parent.width, parent.height);
ctx.save();
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
drawFantasy();
ctx.stroke();
ctx.restore();
}
}
}
}
:!: Пример: Градиент на холсте
import QtQuick 2.12; import QtQuick.Controls 2.2; import QtQuick.Layouts 1.12
ApplicationWindow
{
width: 480; height: 480; visible: true;
Rectangle
{
anchors.fill: parent
Canvas
{
id: canv
width: 320; height: 320;
onPaint:
{
var ctx = getContext("2d");
ctx.strokeStyle= "blue";
ctx.lineWidth = 15;
var gradient = ctx.createLinearGradient(canv.width, canv.height, 0, 0);
gradient.addColorStop(0, "Indigo");
gradient.addColorStop(0.5, "Bisque");
gradient.addColorStop(1, "ForestGreen");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canv.width, canv.height);
ctx.strokeRect(0, 0, canv.width, canv.height);
}
}
}
}
===== Пользовательский ввод =====
==== Область мыши ====
----
Для получения событий мыши служат специальные элементы - **MouseArea**, по сути просто прямоугольные области, в которых осуществляется ввод информации от мыши.\\
:!: Пример: Нажатие левой и правой кнопок мыши
import QtQuick 2.12; import QtQuick.Controls 2.2; import QtQuick.Layouts 1.12
ApplicationWindow
{
width: 300; height: 300; visible: true;
Rectangle
{
anchors.fill: parent
color: "lightgreen"
Text
{
id: txt
anchors.centerIn: parent
text: "This is text
Click here
"
horizontalAlignment: Text.AlignHCenter
}
MouseArea
{
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed:
{
if(mouse.button == Qt.LeftButton)
parent.color= "red"
else
parent.color= "blue"
}
onReleased: parent.color= "lightgreen"
}
}
}
Так же, можно использовать свойство **containsMouse**, для обнаружения находится ли курсор над областью.\\
Аналогично можно использовать свойства **onEntered** и **onExit**.\\
:!: Пример: Обнаружение находится ли курсор над областью
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 300; height: 300; visible: true;
Rectangle
{
anchors.fill: parent
color: mousearea.containsMouse ? "red" : "lightgreen"
Text
{
anchors.centerIn: parent
text: "Hover me
"
}
MouseArea
{
id: mousearea
anchors.fill: parent
hoverEnabled: true
}
}
}
==== Сигналы ====
----
Сигналы в QML это события, которые уже прикреплены к свойствам, с кодом исполнения в них, эти свойства называются с префиксом **on** и являются по сути слотами.\\
Можно определять собственные сигналы:\\
**signal [( , ...)]**\\
К каждому такому сигналу автоматически создастся соответствующий обработчик (слот), с префиксом **on**.\\
Насколько я понимаю, вызвать наш сигнал может только какое то действие т.е. стандартный сигнал (точнее его обработчик **on..**), поэтому любой собственный сигнал будет так или иначе связан с уже существующим, стандартным сигналом.., плюс есть как минимум в том, что наши собственные сигналы будут доступны на уровнях выше т.е. извне, в случае например объявления объекта в отдельном файле.\\
:!: Пример: Сигнал движения курсора мыши
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 300; height: 300; visible: true;
Rectangle
{
anchors.fill: parent
color: mousearea.containsMouse ? "red" : "lightgreen"
signal myMouseSignal(int x, int y)
onMyMouseSignal:
{
txt.text= "X:"+x +"; Y:"+y +"
"
}
Text
{
id: txt
anchors.centerIn: parent
}
MouseArea
{
anchors.fill: parent
hoverEnabled: true
onMouseXChanged: parent.myMouseSignal(mouseX, mouseY)
onMouseYChanged: parent.myMouseSignal(mouseX, mouseY)
}
}
}
:!: Пример: Собственный элемент+ передача сигнала наружу
// Файл button.qml
import QtQuick 2.0
BorderImage
{
property alias text: txt.text
signal clicked;
source: "qrc:/button.png"
width: txt.width +15
height: txt.height +15
border {left: 15; top: 12; right: 15; bottom: 15}
Text
{
id: txt
color: "white"
anchors.centerIn: parent
}
MouseArea
{
anchors.fill: parent
onClicked: parent.clicked();
onPressed: parent.source= "button-press.png"
onReleased: parent.source= "button.png"
}
}
// Основной файл
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 150; height: 100; visible: true;
Rectangle
{
anchors.fill: parent
Button
{
anchors.centerIn: parent
text: "Click this"
onClicked: text= "Clicked"
}
}
}
==== Ввод с клавиатуры ====
----
В основном ввод с клавиатуры обрабатывается двумя элементами: **TextInput** (однострочный) и **TextEdit** (многострочный).\\
Размер элемента будет соответствовать введенному тексту, что бы при пустом вводе элемент не исчезал, нужно задавать мин.ширину
:!: Пример: Простое поле ввода текста
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: txt.width + 20; height: 100; visible: true;
Rectangle
{
anchors.fill: parent
TextInput
{
id: txt
anchors.centerIn: parent
color: "red"
text: "Text in this"
font.pixelSize: 32
focus: true
}
}
}
==== Фокус ====
----
Если на форме один элемент, он получает фокус автоматом, далее, фокус можно изменить ручным выбором мышкой либо табом. Програмно можно воспользоваться свойством **focus**.\\
Нетекстовые элементы так же могут иметь фокус.\\
Для управления порядком фокуса табом, используется т.н. прикрепляемое свойство "**KeyNavigation.tab: **". В этом свойстве так же доступны клавиши типа **KeyNavigation.left**, **KeyNavigation.right**, **KeyNavigation.up**, **KeyNavigation.down** и т.д.\\
:!: Пример: Два поля ввода, выделение их фокусировки
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 200; height: 80; visible: true;
Rectangle
{
anchors.fill: parent
TextEdit
{
anchors
{
left: parent.left
right: parent.horizontalCenter
top: parent.top
bottom: parent.bottom
}
text: "Text1\nText1\nText1"
font.pixelSize: 20
color: focus ? "red":"black"
focus: true
}
TextEdit
{
anchors
{
left: parent.horizontalCenter
right: parent.right
top: parent.top
bottom: parent.bottom
}
text: "Text2\nText2\nText2"
font.pixelSize: 20
color: focus ? "red":"black"
}
}
}
==== Сырой ввод ====
----
С помощью прикрепляемого свойства **Keys** можно получить доступ к событиям клавиатуры, с полной информацией о событии.\\
:!: Пример: Перемещение элемента с помощью клавиш-стрелок
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 200; height: 80; visible: true;
Rectangle
{
anchors.fill: parent
Text
{
x: 20; y: 20;
text: "Move this text"
horizontalAlignment: Text.AlignHCenter
Keys.onLeftPressed: x -= 3
Keys.onRightPressed: x += 3
Keys.onUpPressed: y -= 3
Keys.onDownPressed: y += 3
focus: true
}
}
}
При помощи **Keys.forwardTo** можно пересылать события другим элементам, для дальнейшей обработки.\\
:!: Пример: Так же, можно сделать это в одном обработчике
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 200; height: 80; visible: true;
Rectangle
{
anchors.fill: parent
Text
{
x: 20; y: 20;
text: "Move this text"
horizontalAlignment: Text.AlignHCenter
focus: true
Keys.onPressed:
{
if(event.key === Qt.Key_Left) { x -= 3; }
else if(event.key === Qt.Key_Right) { x += 3; }
else if(event.key === Qt.Key_Down) { y += 3; }
else if(event.key === Qt.Key_Up) {y -= 3; }
else if(event.key === Qt.Key_Plus) {font.pixelSize++; }
else if(event.key === Qt.Key_Minus) {font.pixelSize--; }
}
}
}
}
==== Мультитач ====
----
Область региона- **MultiPointTouchArea**. Он содержит в себе элементы обработки события касания- **TouchPoint**, их должно быть столько, сколько одновременных касаний мы собираемся принимать, его можно сравнить с **MouseArea**.\\
:!: Пример: Обработка пяти одновременных касаний
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 400; height: 400; visible: true
Rectangle
{
anchors.fill: parent
color: "black"
MultiPointTouchArea
{
anchors.fill: parent
minimumTouchPoints: 1
maximumTouchPoints: 5
touchPoints:
[
TouchPoint {},
TouchPoint {},
TouchPoint {},
TouchPoint {},
TouchPoint {}
]
Repeater
{
model: parent.touchPoints
Rectangle
{
color: "white"
x: modelData.x; y: modelData.y
width: 30; height: 30
visible: modelData.pressed
}
}
}
}
}
Так же, у **TouchPoint** есть следующие св-ва:
| pressed | При касании == true |
| pressure | Сила нажатия (не все устр-ва поддерживают) |
| previous{X,Y} | Пред координаты касания |
| start{X,Y} | Начальные координаты касания |
| x,y | Текущие координаты касания |
===== Анимация =====
==== Анимация при изменении свойств ====
----
Для анимации свойств существует элемент **PropertyAnimation**, с ним можно менять сразу **несколько свойств одновременно**.\\
:!: Пример: Одновременное изменение **x** и **y**
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 400; height: 400; visible: true
Rectangle
{
anchors.fill: parent
color: "lightgreen"
Image
{
id: img
x: 0; y: 0;
source: "qrc:/button.png"
}
PropertyAnimation
{
target: img
properties: "x,y"
// Начальное и конечное значение для св-в
from: 0; to: 400 - img.height
// Длительность мс
duration: 1500
running: true
loops: Animation.Infinite
// Режим. скорость и т.д. есть интерактивный редактор
easing.type: Easing.OutExpo
}
}
}
=== Изменение числовых значений ===
----
Более эффективная реализация для **real** и **int**.\\
:!: Пример: Изменение размера вложенного прямоугольника x2
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 300; height: 100; visible: true
Rectangle
{
id: par
anchors.fill: parent
color: "lightgreen"
Rectangle
{
x: 0; y: 0;
height: 100
color: "red"
NumberAnimation on width
{
id: anim1
from: 300; to: 0
duration: 2000
easing.type: Easing.InOutCubic
onStopped:
{
anim2.start()
}
}
NumberAnimation on width
{
id: anim2
from: 0; to: 300
duration: 2000
easing.type: Easing.InOutCubic
running: false
onStopped:
{
anim1.start()
}
}
}
}
}
=== Изменение цвета ===
----
**ColorAnimation** управляет изменением цвета, так же используется **from** и **to**.\\
:!: Пример: Изменение цвета
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 300; height: 100; visible: true
Rectangle
{
anchors.fill: parent
ColorAnimation on color
{
from: "lightgreen"
to: "red"
duration: 1500
running: true
loops: Animation.Infinite
}
}
}
=== Поворот ===
----
**RotationAnimation** описывает поворот элемента.\\
Его свойство **direction** задает направление поворота:
| RotationAnimation.Clockwise | по часовой (по умолчанию) |
| RotationAnimation.Counterclockwise | против часовой |
| RotationAnimationShortest | угол поворота в наименьшую сторону исходя из "from-to" |
:!: Пример: Поворот по часовой стрелке
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 150; height: 150; visible: true
Rectangle
{
anchors.fill: parent
Image
{
source: "qrc:/button.png"
anchors.centerIn: parent
smooth: true
RotationAnimation on rotation
{
from: 0
to: 360
duration: 2000
loops: Animation.Infinite
easing.type: Easing.InOutBack
}
}
}
}
==== Анимация поведения ====
----
**Behavior** реагирует на изменение свойств элементов, соответственно в эти моменты может вызывать другую анимацию и т.д.\\
В следующем примере, **Behavior** реагирует на изменение **x** и **y** у изображения, внутри другой элемент анимации, изменение же свойств изображения вызывается из событиями мыши в **MouseArea**
:!: Пример: Анимация вызывается поведением
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 360; height: 360; visible: true
Rectangle
{
id: rect
anchors.fill: parent
Image
{
id: img
x: 10; y: 10;
source: "qrc:/button.png"
smooth: true
Text
{
anchors.verticalCenter: img.verticalCenter
anchors.top: img.bottom
text: "Move cursor"
}
Behavior on x
{
NumberAnimation
{
duration: 500
easing.type: Easing.OutBack
}
}
Behavior on y
{
NumberAnimation
{
duration: 500
easing.type: Easing.OutBack
}
}
}
MouseArea
{
anchors.fill: rect
hoverEnabled: true
onMouseXChanged: img.x= mouseX
onMouseYChanged: img.y= mouseY
}
}
}
==== Параллельные и последовательные анимации ====
----
Анимации могут быть объединены в группы, эти группы выполняются **последовательно** (SequentialAnimation) либо **параллельно** (ParallelAnimation). Группы могут быть вложенными друг в друга. Если элемент группы не указан, анимации выполняются параллельно.\\
:!: Пример: Параллельная анимация. Увеличение размера и прозрачность
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 360; height: 360; visible: true
Rectangle
{
anchors.fill: parent
Image
{
id: img
source: "qrc:/button.png"
smooth: true
anchors.centerIn: parent
}
ParallelAnimation
{
NumberAnimation
{
target: img
properties: "scale"
from: 0.1
to: 3.0
duration: 2000
easing.type: Easing.InOutCubic
}
NumberAnimation
{
target: img
properties: "opacity"
from: 1.0
to: 0
duration: 2000
}
running: true
loops: Animation.Infinite
}
}
}
:!: Пример: Тоже самое, но немного другой подход
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 360; height: 360; visible: true
Rectangle
{
anchors.fill: parent
Image
{
id: img
source: "qrc:/button.png"
smooth: true
anchors.centerIn: parent
NumberAnimation on scale
{
from: 0.1
to: 3.0
duration: 2000
easing.type: Easing.InOutCubic
loops: Animation.Infinite
}
NumberAnimation on opacity
{
from: 1.0
to: 0
duration: 2000
loops: Animation.Infinite
}
}
}
}
Пример последовательной анимации: После нажатия мыши объект упадет вниз, повернется вокруг своей оси, полежит немного, затем поднимется обратно вверх:
:!: Пример:
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 130; height: 450; visible: true
Rectangle
{
anchors.fill: parent
Image
{
id: img
source: "qrc:/button.png"
smooth: true
Text
{
anchors.horizontalCenter: parent.horizontalAlignment
anchors.top: parent.bottom
text: "Click for do it"
}
MouseArea
{
anchors.fill: img
onClicked: anim.running= true
}
SequentialAnimation
{
id: anim
NumberAnimation
{
target: img
from: 20; to: 300;
properties: "y"
duration: 1000
easing.type: Easing.OutBounce
}
RotationAnimation
{
target: img
from: 0; to: 360;
properties: "rotation"
duration: 1000
}
PauseAnimation {duration: 500}
NumberAnimation
{
target: img
from: 300; to: 20;
properties: "y"
duration: 1000
easing.type: Easing.OutBounce
}
}
}
}
}
==== Состояния и переходы ====
=== Состояния ===
----
Управлять состоянием можно при помощи элемента **State**, поддерживает наборы списков, описания элементов с набором их состояний.\\
Все объекты **обязательно** должны быть **именованными**.\\
Список состояний присваивается свойству **states** а необходимый набор присваивается свойству **state**, так и происходит смена состояний.\\
Изменение свойств внутри состояния перечисляется в в элементе **PropertyChanges**
:!: Пример: Два определенных состояния прямоугольника и смена между ними
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 360; height: 360; visible: true
Rectangle
{
anchors.fill: parent
Rectangle
{
id: rect
state: "State2"
Text
{
id: txt
anchors.centerIn: parent
}
}
states:
[
State
{
name: "State1"
PropertyChanges
{
target: rect
color: "lightgreen"
width: 150; height: 60;
}
PropertyChanges
{
target: txt
text: "State2: Click"
}
},
State
{
name: "State2"
PropertyChanges
{
target: rect
color: "yellow"
width: 200; height: 120;
}
PropertyChanges
{
target: txt
text: "State1: Click"
}
}
]
MouseArea
{
anchors.fill: parent
onClicked: parent.state= (parent.state== "State1") ? "State2":"State1"
}
}
}
==== Переходы ====
----
Свойство **transitions** задает список переходов, в списке задаем элементы **Transition**, каждый элемент содержит два момента:
* **from / to** - начальное/конечное состояние перехода (**States**, рассмотренные выше)
* **PropertyAnimation** - управление анимацией перехода
:!: Пример: Переход между состояниями
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 360; height: 360; visible: true
Rectangle
{
anchors.fill: parent
Rectangle
{
id: rect
width: 100; height: 100
color: "magenta"
state: "State1"
Text
{
anchors.centerIn: parent
text: "Click this"
}
MouseArea
{
anchors.fill: parent
onClicked: rect.state = (rect.state == "State1") ? "State2":"State1"
}
states:
[
State
{
name: "State1"
PropertyChanges {target: rect; x: 0; y: 0}
},
State
{
name: "State2"
PropertyChanges {target: rect; x: 200; y: 200}
}
]
transitions:
[
Transition
{
from: "State1"; to: "State2";
PropertyAnimation
{
target: rect;
properties: "x,y";
duration: 1000
easing.type: Easing.OutBack
}
},
Transition
{
from: "State2"; to: "State1";
PropertyAnimation
{
target: rect;
properties: "x,y";
duration: 1000
easing.type: Easing.InBounce
}
}
]
}
}
}
Если свойства перехода будут одинаковыми, тогда можно использовать **шаблонный переход**:
:!: Пример: Тоже самое только шаблонный переход
import QtQuick 2.12; import QtQuick.Controls 2.2
ApplicationWindow
{
width: 300; height: 300; visible: true
Rectangle
{
anchors.fill: parent
Rectangle
{
id: rect
width: 100; height: 100
color: "magenta"
state: "State1"
Text
{
anchors.centerIn: parent
text: "Click this"
}
MouseArea
{
anchors.fill: parent
onClicked: rect.state = (rect.state == "State1") ? "State2":"State1"
}
states:
[
State
{
name: "State1"
PropertyChanges {target: rect; x: 0; y: 0}
},
State
{
name: "State2"
PropertyChanges {target: rect; x: 200; y: 200}
}
]
transitions:
[
Transition
{
from: "*"; to: "*";
PropertyAnimation
{
target: rect;
properties: "x,y";
duration: 1000
easing.type: Easing.OutBack
}
}
]
}
}
}
==== Модуль частиц ====
----
Движок для работы с частицами, в роли частиц могут выступать картинки и т.д.
:!: Пример: Кнопкопад
import QtQuick 2.12; import QtQuick.Controls 2.2; import QtQuick.Particles 2.0
ApplicationWindow
{
width: 360; height: 360; visible: true
Rectangle
{
anchors.fill: parent
color: "MidnightBlue"
ParticleSystem
{
anchors.fill: parent
ImageParticle
{
source: "qrc:/button.png"
}
Emitter
{
width: parent.width
height: parent.height
anchors.bottom: parent.bottom
lifeSpan: 10000
sizeVariation: 16
emitRate: 20
velocity: AngleDirection
{
angle: 90
angleVariation: 10
magnitude: 100
}
}
}
}
}