目錄

(放個目錄方便預覽。知乎不支持目錄,這個目錄是從博客複製過來的,點擊會跳轉到博客)

  • 簡介
  • 關於文章
  • 跟上節奏 效果預覽
  • QtQuick動畫系統
    • 動畫組件
    • 動畫的使用
      • 用例一 直接聲明動畫
      • 用例二 on語法
      • 用例三 Transitions或狀態機
  • ShaderEffect
  • 跟上節奏 效果源碼
    • 組件的封裝
    • 大致原理
    • 組件的使用
    • 倒影效果原理

簡介

這是《Qml特效》系列文章的第14篇,濤哥將會教大家一些Qml特效和動畫相關的知識。

前12篇是進場動畫效果 參考了WPS版ppt的動畫,12種基本效果已經全部實現,可以到github TaoQuick項目中預覽:

進場動畫預覽

因為效果都比較簡單,就沒有寫太多說明。

沒有13

沒有13

沒有13

從14篇開始,做一些特殊的效果和動畫,同時會講解相關的知識。

關於文章

文章主要發布在濤哥的博客 和 濤哥的知乎專欄-Qt進階之路

跟上節奏 效果預覽

跳動的位元組,以及倒影

其中用到的知識點有: Qml動態創建組件、Qml隨機數生成、Qml給Item拍照(layer屬性)、Qml翻轉。

當然還有一些預備知識:

做特效和動畫,要用到QtQuick的動畫系統,以及ShaderEffect特效。

QtQuick動畫系統

動畫組件

Qt動畫系統,在幫助文檔有詳細的介紹,搜索關鍵詞」Animation」,濤哥在這裡說一些重點。

濤哥用思維導圖列出了Qml中所有的動畫組件:

  • 右邊帶虛線框的部分比較常用,是做動畫必須要掌握的,尤其是屬性動畫PropertyAnimation和數值動畫NumberAinmation。 常見的各種坐標動畫、寬高動畫、透明度動畫、顏色動畫等等,都可以用這些組件來實現。
  • 底下的States、Behavior 和 Traisitions,也是比較常用的和動畫相關的組件。可在幫助文檔搜索 關鍵詞」Qt Quick States」、」Behavior」、」Animation and Transitions」。後續的文章,濤哥會專門講解。
  • 左邊的Animator系列,屬於Scene Graph渲染層面的優化,其屬性Change信號只在最終值時發出,不發出中間值,使用的時候需要注意。
  • 頂上的AnimationController,屬於高端玩家,用來控制整個動畫的進度。

動畫的使用

用例一 直接聲明動畫

直接聲明動畫,指定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語法

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
}
}

用例三 Transitions或狀態機

過渡動畫和狀態機動畫,本質還是直接使用動畫組件。只不過是把動畫聲明並存儲起來,以在狀態切換時使用。

這裡先不細說了,後面會有系列文章,會專門講解。

ShaderEffect

動畫只能控制組件的屬性整體的變化,做特效需要精確到像素。

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度,透明度調一下,就變成倒影效果了。


推薦閱讀:
相关文章