目錄
(放個目錄方便預覽。知乎不支持目錄,這個目錄是從博客複製過來的,點擊會跳轉到博客)
這是《Qml特效》系列文章的第14篇,濤哥將會教大家一些Qml特效和動畫相關的知識。
前12篇是進場動畫效果 參考了WPS版ppt的動畫,12種基本效果已經全部實現,可以到github TaoQuick項目中預覽:
進場動畫預覽
因為效果都比較簡單,就沒有寫太多說明。
沒有13
從14篇開始,做一些特殊的效果和動畫,同時會講解相關的知識。
文章主要發布在濤哥的博客 和 濤哥的知乎專欄-Qt進階之路
跳動的位元組,以及倒影
其中用到的知識點有: Qml動態創建組件、Qml隨機數生成、Qml給Item拍照(layer屬性)、Qml翻轉。
當然還有一些預備知識:
做特效和動畫,要用到QtQuick的動畫系統,以及ShaderEffect特效。
Qt動畫系統,在幫助文檔有詳細的介紹,搜索關鍵詞」Animation」,濤哥在這裡說一些重點。
濤哥用思維導圖列出了Qml中所有的動畫組件:
直接聲明動畫,指定target和property,之後可以在槽函數/js腳本中通過id控制動畫的運行。
也可以通過設定loops 和 running屬性來控制動畫
Rectangle { id: flashingblob width: 75; height: 75 color: "blue" opacity: 1.0
MouseArea { anchors.fill: parent onClicked: { animateColor.start() animateOpacity.start() } }
PropertyAnimation {id: animateColor; target: flashingblob; properties: "color"; to: "green"; duration: 100}
NumberAnimation { id: animateOpacity target: flashingblob properties: "opacity" from: 0.99 to: 1.0 loops: Animation.Infinite easing {type: Easing.OutBack; overshoot: 500} } }
on語法可以使用動畫組件,也可以用Behavior,直接on某個特定的屬性即可。效果一樣。
on動畫中,如果直接指定了running屬性,默認就會執行這個動畫。
也可以不指定running屬性,其它地方修改這個屬性時,會自動按照動畫來執行。
示例代碼 on動畫
Rectangle { width: 100; height: 100; color: "green" RotationAnimation on rotation { loops: Animation.Infinite from: 0 to: 360 running: true } }
示例代碼 Behavior 動畫
import QtQuick 2.0
Rectangle { id: rect width: 100; height: 100 color: "red"
Behavior on width { NumberAnimation { duration: 1000 } }
MouseArea { anchors.fill: parent onClicked: rect.width = 50 } }
過渡動畫和狀態機動畫,本質還是直接使用動畫組件。只不過是把動畫聲明並存儲起來,以在狀態切換時使用。
這裡先不細說了,後面會有系列文章,會專門講解。
動畫只能控制組件的屬性整體的變化,做特效需要精確到像素。
Qml中提供了ShaderEffect這個組件,就能實現像素級別的操作。
Qml中有一個模塊QtGraphicalEffects,提供了部分特效,就是使用ShaderEffect實現的。
使用ShaderEffect實現特效,需要有一些OpenGL/DirectX知識,了解GPU渲染管線,同時也需要一些數學知識。
大名鼎鼎的ShaderToy網站,就是使用Shader實現各種像素級別的酷炫特效。
ShaderToy
作者iq大神
ShaderToy上面的特效都是可以移植到Qml中的。
封裝了一個組件TSoundByte
//TSoundByte.qml import QtQuick 2.12
Item { id: r implicitWidth: (soundWidth + columnSpacing) * columnCount implicitHeight: (soundHeight + rowSpacing) * rowCount Component { id: soundComp Image { source: "qrc:/EffectImage/Img/soundRect.png" width: soundWidth height: soundHeight } } property bool running: true property int interval: 320 property int soundWidth: 12 property int soundHeight: 6
property int rowCount: 15 property int columnCount: 30
property int rowSpacing: 4 property int columnSpacing: 4
property var objPool: [] property var map:[] property int __arrayRatio: 100 Component.onCompleted: { let startX = 0 let startY = r.height - 12 for (let i = 0; i < columnCount; ++i) { map.push(getRandomInt(0, rowCount))
let px = startX + i * (soundWidth + columnSpacing) for (let j = 0; j < rowCount; ++j) { let py = startY - j * (soundHeight + rowSpacing)
var obj = soundComp.createObject(r, {"x": px, "y": py, "visible": false}) objPool[i * __arrayRatio + j] = obj } } } Timer { interval: r.interval running: r.running repeat: true onTriggered: { map.push(getRandomInt(0, rowCount)) map.shift() for (let i = 0; i < columnCount; ++i) { for (let j = 0; j < rowCount; ++j) { objPool[i * __arrayRatio + j]["visible"] = j < map[i] ? true : false } } } } function getRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min)) + min; //不含最大值,含最小值 } }
這裡的soundComp就是一個Component組件,在槽函數/js函數中,可以用id去動態創建。
soundComp.createObject(r, {「x」: px, 「y」: py, 「visible」: false})
濤哥一次性創建了n * m 個Component,都設置好相對的坐標,存儲到objPool數組中。
visible屬性根據預定義的數組map來控制。
之後在定時器中,用一個隨機高度去刷新map。然後再更新objPool中的visible即可。
import QtQuick 2.12 import QtQuick.Controls 2.12 import "../Effects" Rectangle { anchors.fill: parent color: "black" TSoundByte { id: src anchors.centerIn: parent anchors.verticalCenterOffset: -100 interval: 240 }
ShaderEffectSource { id: mirror width: src.width height: src.height x: src.x y: src.y + src.height + 10 opacity: 0.3 sourceItem: src transform: Rotation { origin.x: mirror.width/2 origin.y: mirror.height/2 axis.x: 1; axis.y: 0; axis.z: 0 angle: 180 } //no effect //textureMirroring: ShaderEffectSource.MirrorHorizontally
} }
先是實例化了一個TSoundByte組件,id為src,之後又在實例src的坐標下面,放了一個ShaderEffectSource,指定其sourceItem為src。
這樣就有了src的一個複製品mirror。然後把mirror翻轉180度,透明度調一下,就變成倒影效果了。