Qt QMLアニメーションを自在に操る!transformOriginと代替手段
Qt Quick (QML) における Item.transformOrigin
プロパティは、Item
が変形(回転、拡大縮小など)を行う際の「原点」を設定するために使用されます。この原点は、Item
のローカル座標系内で定義されます。
具体的に言うと、Item.rotation
や Item.scale
のようなプロパティを使って Item
を操作する際、デフォルトでは Item
の中心(x + width/2
, y + height/2
の位置)が変形の中心となります。しかし、transformOrigin
を設定することで、この変形の中心を任意の位置に変更することができます。
transformOrigin
の値
transformOrigin
は enum
型のプロパティで、以下の定義済み定数を使用できます。これらは、Item
の境界ボックスに対する相対的な位置を示します。
Item.BottomRight
: 右下隅Item.Bottom
: 下辺の中央Item.BottomLeft
: 左下隅Item.Right
: 右辺の中央Item.Center
: 中央(デフォルト値)Item.Left
: 左辺の中央Item.TopRight
: 右上隅Item.Top
: 上辺の中央Item.TopLeft
: 左上隅
使用例
例えば、Rectangle
をその左上隅を原点として回転させたい場合、以下のように記述します。
Rectangle {
width: 100
height: 100
color: "blue"
rotation: 45 // 45度回転
// 変形の原点を左上隅に設定
transformOrigin: Item.TopLeft
}
この例では、Rectangle
は自身の左上隅を軸として45度回転します。もし transformOrigin
が設定されていなければ、Rectangle
は自身の中心を軸として回転します。
なぜ transformOrigin
が重要なのか
- コードの簡潔化
transformOrigin
を使用しない場合、手動でItem
の位置を調整したり、複雑なTransform
要素(Rotation
やScale
のoriginX
,originY
プロパティ)を使ったりする必要があり、コードが複雑になる可能性があります。transformOrigin
はこれらのケースをより簡潔に記述できる方法を提供します。 - 配置の柔軟性
特定の要素を別の要素の角や辺を基準に操作したい場合に役立ちます。 - より自然なアニメーション
UI要素が特定のポイントを中心に回転したり拡大縮小したりする際に、transformOrigin
を設定することで、より意図した通りで自然なアニメーションを実現できます。例えば、ドアが開閉するアニメーションでは、蝶番の位置を変形の原点とすることで、現実のドアの動きを再現できます。
注意点
transformOrigin
は、Item
のtransform
プロパティに追加される任意のTransform
要素(例:Rotation
,Scale
,Translation
)のoriginX
およびoriginY
プロパティとは異なる概念です。transformOrigin
はItem
自体の「便利」なプロパティであり、QMLエンジンによって内部的に対応するTransform
要素の原点に変換されます。より複雑な変形が必要な場合は、Item.transform
リストに複数のTransform
要素を追加して、それぞれのoriginX
およびoriginY
を設定することもできます。transformOrigin
は、Item
のx
,y
,width
,height
に基づいて計算されます。これらのプロパティが変更されると、transformOrigin
によって示される絶対的な位置も変更されます。
Item.transformOrigin
は非常に便利なプロパティですが、意図しない動作を引き起こすこともあります。ここでは、よくあるエラーとその解決策をいくつかご紹介します。
エラー: 期待通りの変形原点にならない
原因
- Item.transform との混同
Item.transformOrigin
はItem
全体に対する変形原点ですが、Item.transform
リスト内の個々のTransform
要素(Rotation
、Scale
など)もそれぞれoriginX
/originY
プロパティを持っています。これらが同時に設定されている場合、どちらが優先されるか混乱することがあります。 - デフォルト値の誤解
Item.Center
がデフォルトですが、これを常に意識していないと、意図しない回転や拡大縮小の軸になります。 - 親要素からの影響
レイアウトプロパティ(anchors
など)や親の変形によってItem
の絶対位置が変わる場合、視覚的に原点がズレているように見えることがあります。 - width/height の変更
transformOrigin
はItem
のwidth
とheight
に基づいて計算されます。これらのプロパティが動的に変更される場合、transformOrigin
が指す絶対的な位置も変わってしまいます。
トラブルシューティング
- デフォルトの理解
transformOrigin
を設定しない場合、中心が原点であることを常に念頭に置いてください。 - Item.transform との併用
複数の変形を適用する場合、Item.transformOrigin
よりもItem.transform
リスト内の個々のTransform
要素のoriginX
とoriginY
を使用する方が、より細かく制御できる場合があります。Item.transformOrigin
はItem
全体の「おおまかな」変形原点と考え、複雑な場合はTransform
要素を使うと良いでしょう。 - 視覚的な確認
Rectangle
などでtransformOrigin
の位置に小さな目印(例えば、Rectangle
の子として小さなRectangle
を配置し、x: parent.width * <origin_factor_x>
,y: parent.height * <origin_factor_y>
のように配置)を置いて、実際に変形原点がどこにあるのか視覚的に確認してみてください。 - width/height の固定または追跡
Item
のサイズが動的に変わる場合は、変形を行っている間にwidth
とheight
を固定するか、width
とheight
の変化に合わせてrotation
やscale
を調整することを検討してください。
エラー: パフォーマンスの問題
原因
- 複雑なシーンでの多用
多数のItem
が同時に変形を行い、それぞれのtransformOrigin
が異なる場合、QMLエンジンの計算コストが増加し、特に低スペックなデバイスではパフォーマンスが低下する可能性があります。
トラブルシューティング
- C++での最適化
非常に高いパフォーマンスが要求される場合、QMLだけで複雑な変形を行うのではなく、C++側でOpenGLなどのグラフィックAPIを直接操作して変形を処理することを検討します。 - 必要な場合のみ使用
変形原点を変更する必要があるItem
にのみtransformOrigin
を設定し、それ以外はデフォルトのItem.Center
を使用します。 - プロファイリング
Qt Creator の QML Profiler を使用して、どこでパフォーマンスのボトルネックが発生しているかを確認します。
エラー: アニメーションが不自然に見える
原因
- 変形の種類と原点の不一致
例えば、水平方向にスライドさせるアニメーションでtransformOrigin
をItem.Bottom
に設定しても、変形自体には影響がありません。 - アニメーション中の transformOrigin の変更
アニメーションの途中でtransformOrigin
を変更しようとすると、ジャンプしたり、不自然な動きになったりすることがあります。transformOrigin
は通常、アニメーションの開始前に設定され、アニメーション中は一定に保たれるべきです。
トラブルシューティング
- 変形の種類と原点の確認
行いたい変形(回転、拡大縮小、移動)に対して、設定しているtransformOrigin
が論理的に合っているか確認してください。 - アニメーション前の設定
transformOrigin
は、アニメーションが始まる前に確定させるようにしてください。
エラー: Item の位置が変形後にずれる
原因
- 変形と位置の混同
rotation
やscale
などの変形はItem
の描画を変化させますが、Item
のx
やy
プロパティの論理的な値は変更しません。変形後もItem
のx
/y
は元の位置のままです。これにより、UI上の見た目の位置と、プログラム上で認識されている位置にずれが生じ、他の要素の配置やイベント処理で問題が発生することがあります。
トラブルシューティング
- Transform 要素の translation
あるいは、Item.transform
プロパティにTranslation
要素を追加して、変形後の位置を調整することもできます。 - 必要に応じた x/y の調整
変形後に他の要素の配置やイベント処理で、変形後の見た目の位置を基準にしたい場合は、Item
のx
やy
を変形量に応じて手動で調整する必要があるかもしれません。これは特に複雑なケースで必要になることがあります。 - transform プロパティの理解
Item.transformOrigin
とそれに伴う変形は、あくまで描画の変換であることを理解してください。論理的な位置 (x
,y
) は変わりません。
エラー: エディタやデザインビューでの表示と実行時の表示が異なる
原因
- 異なるQtバージョン/QMLエンジン
開発環境と実行環境でQtのバージョンが異なる場合、QMLエンジンの挙動に微妙な違いが生じることがあります。 - QMLホットリロードやキャッシュの問題
環境によっては、エディタのデザインビューが最新のQMLファイルを正しく反映していないことがあります。
- バージョンの一貫性
開発環境とターゲット環境でQtのバージョンを一致させるようにしてください。 - Qt Creator の再起動
Qt Creator IDE自体を再起動すると、キャッシュの問題が解決することがあります。 - クリーンビルドと再実行
プロジェクトをクリーンビルドし、再度実行してみてください。
Item.transformOrigin
プロパティは、QMLでアイテムの変形(回転や拡大縮小など)の中心点を指定するために使用されます。以下に、いくつかの一般的な使用例とコードを示します。
基本的な回転と原点の指定
この例では、Rectangle
を回転させ、transformOrigin
を変更することで回転の中心がどのように変化するかを示します。
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 640
height: 480
visible: true
title: "TransformOrigin Examples"
// 左上の長方形: デフォルト(中心)を原点として回転
Rectangle {
id: defaultRotationRect
x: 50
y: 50
width: 100
height: 100
color: "red"
rotation: 0 // 初期角度
Text {
anchors.centerIn: parent
text: "Center (Default)"
color: "white"
font.pixelSize: 12
}
// 常に回転
Timer {
interval: 20
repeat: true
running: true
onTriggered: defaultRotationRect.rotation += 1
}
}
// 中央の長方形: 左上隅を原点として回転
Rectangle {
id: topLeftRotationRect
x: 200
y: 50
width: 100
height: 100
color: "green"
rotation: 0 // 初期角度
// 変形の原点を左上隅に設定
transformOrigin: Item.TopLeft
Text {
anchors.centerIn: parent
text: "TopLeft"
color: "white"
font.pixelSize: 12
}
Timer {
interval: 20
repeat: true
running: true
onTriggered: topLeftRotationRect.rotation += 1
}
}
// 右上の長方形: 右下隅を原点として回転
Rectangle {
id: bottomRightRotationRect
x: 350
y: 50
width: 100
height: 100
color: "blue"
rotation: 0 // 初期角度
// 変形の原点を右下隅に設定
transformOrigin: Item.BottomRight
Text {
anchors.centerIn: parent
text: "BottomRight"
color: "white"
font.pixelSize: 12
}
Timer {
interval: 20
repeat: true
running: true
onTriggered: bottomRightRotationRect.rotation += 1
}
}
// 左下の長方形: 拡大縮小の例
Rectangle {
id: scaleRect
x: 50
y: 200
width: 100
height: 100
color: "purple"
scale: 1.0 // 初期スケール
// 変形の原点を左下隅に設定
transformOrigin: Item.BottomLeft
Text {
anchors.centerIn: parent
text: "Scale from BottomLeft"
color: "white"
font.pixelSize: 10
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
// 拡大縮小アニメーション
PropertyAnimation {
target: scaleRect
property: "scale"
from: 1.0
to: 1.5
duration: 1000
loops: Animation.Infinite
running: true
easing.type: Easing.InOutQuad
direction: PropertyAnimation.Alternating // 行き来するアニメーション
}
}
// ドアのアニメーション例 (回転原点をヒンジに設定)
Rectangle {
id: doorFrame
x: 250
y: 200
width: 150
height: 200
color: "brown"
border.color: "darkgray"
border.width: 5
Text {
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: 5
text: "Door Example"
color: "white"
font.pixelSize: 12
}
Rectangle {
id: door
x: 0 // ドアの左端がドア枠の左端に合うように
y: 0
width: parent.width * 0.9 // ドア枠より少し狭く
height: parent.height // ドア枠と同じ高さ
color: "lightgray"
border.color: "black"
border.width: 1
// ドアのヒンジ(蝶番)の位置を回転の原点に設定
// ここでは左端の中央をヒンジとする
transformOrigin: Item.Left
rotation: 0 // 初期角度 (閉じている状態)
// ドアの開閉アニメーション
MouseArea {
anchors.fill: parent
onClicked: {
if (door.rotation === 0) {
doorAnimation.to = -90 // ドアを開く (外側に開く場合)
} else {
doorAnimation.to = 0 // ドアを閉じる
}
doorAnimation.start()
}
}
PropertyAnimation {
id: doorAnimation
target: door
property: "rotation"
duration: 500
easing.type: Easing.OutQuad
}
}
}
}
コードの説明
-
基本的な回転と原点の指定:
defaultRotationRect
:transformOrigin
を設定しない場合、Item.Center
がデフォルトで適用され、長方形の中心を軸に回転します。topLeftRotationRect
:transformOrigin: Item.TopLeft
を設定することで、長方形の左上隅を軸に回転します。bottomRightRotationRect
:transformOrigin: Item.BottomRight
を設定することで、長方形の右下隅を軸に回転します。- これらは
Timer
を使って継続的に回転させており、視覚的に原点の違いがよくわかります。
-
拡大縮小の例:
scaleRect
:transformOrigin: Item.BottomLeft
を設定し、scale
プロパティをアニメーションさせています。これにより、長方形は左下隅を固定点として拡大縮小します。
-
ドアのアニメーション例:
- これは
transformOrigin
の実用的な応用例です。 doorFrame
は単なる見た目の枠です。door
アイテムが実際に回転するドアを表します。transformOrigin: Item.Left
を設定することで、ドアの回転軸を左端(ヒンジの位置)に設定しています。MouseArea
をクリックすると、PropertyAnimation
がdoor.rotation
を0
から-90
(またはその逆) にアニメーションさせます。これにより、ドアがヒンジを中心に開閉するリアルな動きが再現されます。もしtransformOrigin
を設定しないと、ドアは自身の中心を軸に回転してしまい、不自然な動きになります。
- これは
コードの実行方法
このQMLコードを main.qml
などのファイルに保存し、以下のようなシンプルなC++の main.cpp
と .pro
ファイルでプロジェクトを設定して実行できます。
main.cpp
:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml")); // main.qml のパス
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
&app, []() { QCoreApplication::exit(-1); },
Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
yourprojectname.pro
:
QT += quick
CONFIG += c++17
SOURCES += main.cpp
RESOURCES += qml.qrc
# The following makes sure that your QML files are compiled into the application
# so that they are available without having to deploy the QML files separately.
# For more information, see http://doc.qt.io/qt-6/qtquick-deployment.html
qt_add_qml_module(app_transformorigin_example
URI transformorigin.example
VERSION 1.0
QML_FILES main.qml
)
qml.qrc
: (このファイルに main.qml
を追加します)
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
Qt Creator を使っている場合、"Qt Quick Application" テンプレートからプロジェクトを作成し、main.qml
の内容を上記の例に置き換えるのが最も簡単です。
Item.transformOrigin
は便利ですが、特定のシナリオやより複雑な変形の場合には、いくつかの代替手段や、より低レベルな制御を提供する方法が存在します。
Item.transform プロパティと個別の Transform 要素を使用する
これは、Item.transformOrigin
の最も直接的な代替であり、より柔軟性を提供します。Item.transform
プロパティは、Rotation
、Scale
、Translation
などの複数の Transform
要素のリストを受け取ります。それぞれの Transform
要素は、自身の originX
および originY
プロパティを持つことができます。
特徴
- QMLでのみ使用可能
これらの要素はQMLでのみ定義されます。 - 順序の重要性
transform
リスト内の要素の適用順序が結果に影響します。 - より詳細な制御
複数の異なる変形を組み合わせる際に、それぞれの変形に対して異なる原点を設定できます。
使用例
あるアイテムを、まず左上隅を軸に回転させ、次にその中心を軸に拡大縮小させたい場合など。
Rectangle {
width: 100
height: 100
color: "orange"
x: 100
y: 100
transform: [
// まず、アイテムの左上隅 (0, 0) を原点として回転
Rotation {
angle: 45 // 45度回転
originX: 0
originY: 0
},
// 次に、アイテムの中心 (width/2, height/2) を原点として拡大縮小
Scale {
xScale: 1.2
yScale: 1.2
originX: parent.width / 2 // もしくは Item.Center に相当する計算
originY: parent.height / 2
},
// 必要に応じて移動(平行移動)も追加
Translation {
x: 10
y: 5
}
]
Text {
anchors.centerIn: parent
text: "Complex Transform"
color: "white"
font.pixelSize: 10
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
// アニメーションを追加することも可能
// PropertyAnimation {
// target: parent.transform[0] // Rotation 要素をターゲット
// property: "angle"
// from: 0
// to: 360
// duration: 2000
// loops: Animation.Infinite
// }
}
この方法では、originX
と originY
を明示的に数値で指定する必要があります。Item.TopLeft
のような便利な列挙値は使えません。
x と y プロパティを手動で調整する
これは最も原始的な方法で、回転や拡大縮小の前にアイテムの位置を「ずらし」、変形後に元の位置に戻すことで、あたかも特定の点を原点としているかのように見せかける手法です。
特徴
- アニメーションとの組み合わせが困難
特に連続的なアニメーションの場合、x
とy
を同時に正確に調整し続けるのは非常に難しいです。 - 最も柔軟性があるが、最も複雑
任意の点を原点として設定できますが、計算が複雑になります。
使用例
アイテムの右上隅を中心に回転させたいが、transformOrigin: Item.TopRight
を使いたくない(あるいは使えない)場合。
Rectangle {
id: manualRotateRect
width: 100
height: 100
color: "teal"
x: 200
y: 200
property real currentAngle: 0 // 現在の回転角度
// 右上隅を変形原点として扱う
// 1. アイテムを原点に移動 (右上隅が (0,0) になるように)
// 2. 回転
// 3. 元の位置に戻す
transform: [
Translation { x: -manualRotateRect.width; y: 0 }, // 右上隅を原点に移動
Rotation { angle: manualRotateRect.currentAngle }, // 回転
Translation { x: manualRotateRect.width; y: 0 } // 元の位置に戻す
]
Text {
anchors.centerIn: parent
text: "Manual Transform"
color: "white"
font.pixelSize: 10
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
// アニメーション
Timer {
interval: 20
repeat: true
running: true
onTriggered: {
manualRotateRect.currentAngle += 1
if (manualRotateRect.currentAngle >= 360) manualRotateRect.currentAngle = 0
}
}
}
この方法は、数学的な変換行列を理解している場合に、非常に特定のニーズを満たすために利用されます。通常は Item.transformOrigin
や Transform
要素で十分です。
QMLでのカスタムC++要素やシェーダーの利用
非常に高度な変形や、QMLの標準機能では実現できない特殊な視覚効果が必要な場合、カスタムのC++要素をQMLに公開したり、ShaderEffect
や ShaderEffectSource
を使ってカスタムシェーダー(GLSL)を記述したりする方法があります。
特徴
- QMLの範囲を超える
純粋なQMLの範囲を逸脱します。 - ** steepest learning curve:** OpenGL/WebGL、GLSL、C++とQMLの統合についての深い知識が必要です。
- 最高の柔軟性とパフォーマンス
GPUを直接利用できるため、非常に複雑で高性能な視覚効果を実現できます。
- 大量の要素をGPU上で効率的に描画・変形する場合。
- カスタムの波紋効果や歪み効果など、QMLの標準的な回転や拡大縮小だけでは表現できない変形。
// 例 (概念的、完全なシェーダーコードは含まない)
ShaderEffect {
width: 200
height: 200
x: 350
y: 200
property variant source: ShaderEffectSource { sourceItem: myImage; hideSource: true }
property real rotationAngle: 0.0
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_TexCoord0;
varying highp vec2 qt_UV;
void main() {
// ここで独自の変換行列を構築し、qt_Vertex に適用する
// 例: 特定のピボットポイントを中心に回転させる行列を適用
// vec4 translated_vertex = qt_Vertex - pivot_point;
// vec4 rotated_vertex = rotation_matrix * translated_vertex;
// gl_Position = qt_Matrix * (rotated_vertex + pivot_point);
gl_Position = qt_Matrix * qt_Vertex; // 通常の変換
qt_UV = qt_TexCoord0;
}
"
fragmentShader: "..."
// アニメーション
PropertyAnimation {
target: parent
property: "rotationAngle"
from: 0
to: 360
duration: 3000
loops: Animation.Infinite
}
}
Image {
id: myImage
source: "qrc:/images/example.png"
width: 200
height: 200
visible: false // ShaderEffectSourceで参照されるので非表示
}
方法 | 利点 | 欠点 | 最適なケース |
---|---|---|---|
Item.transformOrigin | 最も簡単で直感的。 | 複数の異なる変形に異なる原点は設定できない。 | 単一の変形(回転、拡大縮小)の原点設定。 |
Item.transform | 柔軟性が高い。 複数の変形を組み合わせる。 | originX /originY の数値指定が必要。順序が重要。 | 複雑な変形、複数の変形軸が必要な場合。 |
手動での x /y 調整 | 究極の制御。 | 非常に複雑でエラーを起こしやすい。 | 非常に特殊な、または教育的な目的。 |
カスタムC++/シェーダー | 最高のパフォーマンスと視覚効果の柔軟性。 | 学習曲線が急。 複雑な実装が必要。 | 高度なグラフィック効果、大量の要素の描画。 |