MyTetra Share
Делитесь знаниями!
Особенности масштабирования графических элементов в QML
Время создания: 19.01.2018 11:45
Автор: xintrea
Текстовые метки: qt, qml, масштаб, scaleб transformation, трансформация, rotation, вращение
Раздел: Компьютер - Программирование - Язык C++ (Си++) - Библиотека Qt - QML
Запись: xintrea/mytetra_syncro/master/base/15163515386tszzxmg2p/text.html на raw.github.com

С размещением графических элементов в QML довольно легко управляться. Но ровно до того момента, пока не возникает необходимости изменять их масштаб (свойство scale). Элемент, у которого изменен масштаб, начинает вести себя неадекватно при любых изменениях его привязки и положения. И с этим надо разобраться.



Масштабирование


Вначале надо понять, как происходит масштабирование элемента. Чтобы это понять, необходимо вначале отобразить несмасштабированные элемент. Вот код, с которым будем экспериментировать:


Window {

visible: true

width: 500

height: 250


Rectangle {

id: plate


x: 100

y: 100

width: 250

height: 125


color: "lightblue"


scale: 1.0

}


Rectangle { // Маленький прямоугольник для ориентира

x: 100; y: 100

width: 3; height: 3

color: "black"

}

}


Этот код выведет следующую картинку:





Теперь надо установить свойство scale со значения 1.0 на 0.5, и вот что получится:





Ого! Наблюдается очень необычное поведение: прямоугольник смасштабировался, но его размер в системе не изменился! Он все так же имеет размер 250x125, хоть на экране он в два раза меньше. То есть, изменилось только его отображение, а занимаемое им место осталось прежним! Другими словами, голубой прямоугольник не сдвинулся к маленькому прямоугольнику своим левым-верхним углом, а совпадает своим центром с центром несмаштабированного прямоугольника.


Может быть, такое поведение масштабированного объекта возникает только при задании координат? А что, если задать привязки? Может быть тогда полный начальный размер учитываться не будет? Надо проверить.


Для проверки надо заменить прямое задание координат (100, 100) на привязки к окну по левому верхнему краю:


// x: 100

// y: 100

anchors.top: parent.top

anchors.left: parent.left


И вот результат:





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


Подытоживая все вышесказанное, можно сказать, что заданное значение scale не влияет на значения width и height. И это очень важно знать, чтобы в дальнейшем правильно понимать происходящее.



Вращение смасштабированного объекта


Для дальнейших действий нужно вернуть код к начальному состоянию, и добавить в него вращение прямоугольника. Вращение должно происходить вокруг верхней средней точки.


Rectangle {

id: plate


x: 100

y: 100

width: 250

height: 125


color: "lightblue"


scale: 1.0


transform: Rotation {

origin.x: plate.width/2

origin.y: 0


angle: 0


NumberAnimation on angle {

running: true

loops: Animation.Infinite

from: 0; to: 360

duration: 3000

}

}

}


Вот что получится:





А что получится, если прямоугольнику задать масштаб 0.5? Вокруг какой точки он будет крутиться? Если установить:


scale: 0.5


То получится следующее:





То есть, масштаб изменился, а точка вращения осталась прежней. И прямоугольник крутится не вокруг своей верхней средней точки, а вокруг верхней средней точки, которая получается когда прямоугольник не смасштабирован.


А как теперь задать верхнюю среднюю точку с учетом масштаба? Придется воспользоваться формулами. При написании формул надо помнить, что значения точки вращения origin.x и origin.y задаются в координатах несмасштабированного прямоугольника.


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


А для координаты Y можно воспользоваться формулой:


origin.y: (plate.height-plate.height*0.5)/2


Где 0.5 - это и есть коэффициент масштабирования. Теперь отмасштабированный прямоугольник будет вращаться вокруг своего видимого центра верхней грани.





Как обойтись без вычислений и упростить себе жизнь


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


Во-первых, в трансформации Scale можно задавать раздельный масштаб по оси X и Y с помощью свойств xScale и yScale. Во-вторых, можно задавать центр масштабирования (свойства origin.x и origin.y), относительно которого будет меняться масштаб. Центр масштабирования - не совсем понятное понятие :), поэтому вот вольный перевод из официальной документации:


Это свойство удерживает точку, из которой объект масштабируется (то есть обозначает точку, которая остается фиксированной относительно родителя по мере роста/уменьшения остальной части).


По-дефолту, значения origin.x и origin.y для трансформации Scale равны 0. То есть, дефолтное масштабирование в трансформации Scale идет относительно левой верхней точки. И это отличается от дефолтного масштабирования с помощью свойства scale, которое масштабирует относительно геометрического центра объекта! Поэтому, если задать масштаб 0.5 не через свойство объекта scale, а через тренсформацию Scale, то результат будет таким:


Код:


Rectangle {

id: plate


x: 100

y: 100

width: 250

height: 125


color: "lightblue"


transform: Scale {

xScale: 0.5

yScale: 0.5

}

}


Снимок экрана:





Видно, что результат отличается от того, что давало изменение свойства scale. А все потому, что свойство scale меняет масштаб относительно центра обекта, а трансформация Scale - относительно origin.x и origin.y, которые равны 0.


На всякий случай надо проверить, а изменились ли ширина и высота объекта при применении транформации? Для проверки надо прижать его к правому нижнему краю:


// x: 100

// y: 100

anchors.bottom: parent.bottom

anchors.right: parent.right





А размер объекта все так же не изменился. То есть, трансформация Scale тоже не влияет на свойства width и height трансформируемого объекта.


Теперь можно начать вращать объект. И делать это можно с помощью цепочки трансформаций. Причем, последовательность действий должна быть следующей:


  1. Вначале надо повернуть объект относительно верхней средней точки
  2. Потом смасштабировать


Вот как это выглядит в коде:


Rectangle {

id: plate


x: 100

y: 100


width: 250

height: 125


color: "lightblue"


transform: [

Rotation {

origin.x: plate.width/2

origin.y: 0


angle: 0


NumberAnimation on angle {

running: true

loops: Animation.Infinite

from: 0; to: 360

duration: 3000

}

},


Scale {

xScale: 0.5

yScale: 0.5

}

]

}


Цепочка трансформаций указывается в виде массива (а точнее, списка) трансформаций. Список трансформаций оформляется с помощью прямоугольных кавычек [ ], где элементы перечисляются через запятую (запятую, кстати, можно не ставить). Трансформации применяются в последовательности их перечисления.


Вот что получится в результате:





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



Так же в этом разделе:
 
MyTetra Share v.0.59
Яндекс индекс цитирования