自定义控件动画过渡排序qml
演示效果基于qt6.7.0完美运行 6.2.4下也能直接跑 但是拖拽的坐标位置判断存在小问题,应该是因为qt6.7作了鼠标优化,在6.2版本下修复问题不大 在6.2版本下问题已修复
main.qml
import QtQuick
import QtQuick.Controls
import QtQml.Models
import QtQuick.Layouts
import TestModel
import './qml'
Window {
id: root
width: 1280
height: 800
onWidthChanged: {
}
visible: true
title: qsTr("自动测试管理系统")
Material.theme: Material.Dark
Material.accent: '#00a2ff' //3FDAC5
property bool waitForDevReadyChecked: $Config.getTestTypeChecked(0)
property int testIndex: 0
property bool begingTest: false
property bool testing: false
property bool thisTestEnd: false
property int begingTestDuration: 700
property int toTestingDuration: 700
property var toTestingCurve: listEmphasized
property var listEmphasized: [
0.05, 0,
0.13, 0.06,
0.16, 0.4,
0.21, 0.82,
0.25, 1,
1,1
]
property var listEmphasizedRing: [
0.05, 0,
0.13, 0.06,
0.16, 0.4,
0.21, 0.82,
0.25, 1,
1,1
]
property var listEmphasizedDecelerate: [
0.05, 0.7,
0.10, 1.0,
1,1
]
// Behavior on color {
// ColorAnimation {
// duration: 300
// easing.type: Easing.Bezier
// easing.bezierCurve: listEmphasizedDecelerate
// }
// }
Image {
id: backImage
source: "qrc:/imgSrc/new4/background.png"
}
// FontLoader {
// id: douyuFont
// source: "qrc:/douyu.ttf"
// }
// Rectangle {
// id: sourceRectangle
// width: 300
// height: 300
// visible: true
// anchors.centerIn: parent
// gradient: Gradient {
// GradientStop { position: 0; color: "steelblue" }
// GradientStop { position: 1; color: "black" }
// }
// }
function starTests() {
$Test.initTestModelList(testModelList)
$Test.starTestByIndex(testIndex, true)
thisTestEnd = false
// 截图方法
// root.contentItem.grabToImage(function(result) {
// result.saveToFile("testImage.png")
// })
}
Connections {
target: $Test
function onOneTestOver(ind,continueOrNot) {
console.log("onOneTestOver " + ind + " " + continueOrNot)
if(!continueOrNot)
{
testing = false
delayTestIndexTimer.start()
testIndex = 0
return
}
if(testIndex<5)
{
++testIndex
$Test.starTestByIndex(testIndex, false)
}
else
{
testing = false
testIndex = 0
delayTestIndexTimer.start()
// testing = false
}
}
function onAllTestOver() {
console.log("onAllTestOver")
}
// function onStopTest() {
// console.log("onStopTest")
// }
}
Timer {
id: delayTestIndexTimer
interval: 150
onTriggered: {
thisTestEnd = true
begingTest = false
}
}
Timer {
id: delayTestIndexTimer2
interval: 150
onTriggered: {
begingTest = false
}
}
Image {
id: logImage
width: 55
height: 56
anchors.top: parent.top
anchors.topMargin: 16
anchors.left: parent.left
anchors.leftMargin: 40
fillMode: Image.PreserveAspectFit
source: "qrc:/imgSrc/new4/test.png"
}
Text {
id: title
text: "自动测试管理系统"
font.family: douyuFont.name//name="DOUYUFont"
color: 'white'
font.pixelSize: 41
font.letterSpacing: 3
anchors.left: logImage.right
anchors.leftMargin: 15
anchors.bottom: logImage.bottom
anchors.bottomMargin: -5
verticalAlignment: Text.AlignBottom
}
Transition {
id: addTransition
ParallelAnimation {
SequentialAnimation {
OpacityAnimator {
duration: 100
from: 0
to: 0
}
OpacityAnimator {
duration: toTestingDuration-200
from: 0
to: 1
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
PropertyAnimation {
duration: toTestingDuration-100
property: "x"
from: 100
to: 0
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
}
TestModelList {
id: testModelList
}
Rectangle {
width: 40 + ( testing ? listView.infoRecWidth : 350 )
x: listView.x-20
y: listView.y-20
height: 40 + ( listView.contentHeight < listView.height
? listView.contentHeight : listView.height )
color: '#15ffffff'
opacity: begingTest ? 1 : 0
radius: 10
Behavior on opacity {
OpacityAnimator {
duration: begingTestDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Behavior on width {
PropertyAnimation {
duration: begingTestDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
}
ListView {
id: listView
property int textSize: 24
property int infoRecWidth: 600
property int maxItemZ: 100
model: testModelList
cacheBuffer: 10000
boundsBehavior: "StopAtBounds"
width: testing ? listView.infoRecWidth : 350
height: testing ? 655 : contentHeight
interactive: thisTestEnd
spacing: 20
clip: testing
contentY: (!thisTestEnd && listView.contentHeight > listView.height) ? (contentHeight-height) : contentY
x: testing ? 40 : 90
y: testing ? 105 : ((parent.height - 538) / 2)
z: 999
Behavior on x {
PropertyAnimation{
duration: toTestingDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Behavior on y {
PropertyAnimation {
duration: toTestingDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
function getColor(value) {
switch (value) {
case 0:
return '9e59b1'
case 1:
return 'b15959'
case 2:
return 'b19059'
case 3:
return '85b159'
case 4:
return '59b183'
case 5:
return '59a6b1'
case 6:
return '597ab1'
default:
return '333333'
}
}
populate: Transition {
NumberAnimation {
properties: "x,y"
duration: 500
easing.type: Easing.Bezier
easing.bezierCurve: listEmphasized
}
}
moveDisplaced: Transition {
NumberAnimation {
properties: "x,y"
duration: 350
easing.type: Easing.Bezier
easing.bezierCurve: listEmphasized
}
}
move: Transition {
NumberAnimation {
properties: "x,y"
duration: 350
easing.type: Easing.Bezier
easing.bezierCurve: listEmphasized
}
}
delegate: Item {
id: listViewDelegateItem
width: testing ? 250 : 350
height: listDelegate.height+infoDelegate.height
Behavior on x {
enabled: !listViewDelegateItemMousArea.pressedOrNot
PropertyAnimation{
duration: 350
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Behavior on y {
enabled: !listViewDelegateItemMousArea.pressedOrNot
PropertyAnimation {
duration: 350
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Rectangle {
id: listDelegate
property string colorText: listView.getColor(model.type)
width: !testing ? parent.width : (testIndex<=model.index?(testIndex === model.index ? 312:listView.infoRecWidth):parent.width)
height: begingTest ? (testIndex === model.index? 50:40) : 74
color: "#" + colorText
// border.color: "blue"
radius: 5//testing?(testIndex>=model.index?9:3):9
Behavior on width {
NumberAnimation {
duration: toTestingDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Behavior on height {
NumberAnimation {
duration: toTestingDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Behavior on radius {
NumberAnimation {
duration: toTestingDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Text {
anchors.verticalCenter: parent.verticalCenter
x: (listDelegate.width - implicitWidth)/2
text: model.name
color: 'white'
font.bold: true
font.pixelSize: listView.textSize
scale: begingTest ? (testIndex === model.index? 1:0.8) : 1
Behavior on scale {
NumberAnimation {
duration: begingTestDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
}
MouseArea {
id: listViewDelegateItemMousArea
property bool pressedOrNot: false
property int oldX: 0
property int oldY: 0
property int lastX: 0
property int lastY: 0
property var oldGlobalPoint
anchors.fill: parent
enabled: !begingTest
acceptedButtons: "LeftButton" || "RightButton"
onPositionChanged: {
if(model.index !== 0 && model.index !== 5)
{
listViewDelegateItem.x += mouseX - oldX
listViewDelegateItem.y += mouseY - oldY
}
}
onContainsMouseChanged: {
if(containsMouse)
{
if(pressed)
return
lastX = listViewDelegateItem.x
lastY = listViewDelegateItem.y
}
}
onDoubleClicked: {
checkBox.checked = !checkBox.checked
}
onPressed: {
listViewDelegateItem.z = listView.maxItemZ++
pressedOrNot = true
oldX = mouseX
oldY = mouseY
oldGlobalPoint = mapToGlobal(mouseX, mouseY)
// parent.color = "#90" + parent.colorText
}
onReleased: {
pressedOrNot = false
// parent.color = "#b0" + parent.colorText
var newPoint = mapToGlobal(mouseX, mouseY)
var newIndex = model.index
- Math.round((oldGlobalPoint.y-newPoint.y-((height+listView.spacing)/2)
*((Math.abs(oldGlobalPoint.y-newPoint.y)>height)
?((oldGlobalPoint.y>newPoint.y)
?1:-1):0))
/ (height+listView.spacing))
if(model.index !== 0 && model.index !== 5)
{
if(newIndex < 1)
newIndex = 1
else if(newIndex > 4)
newIndex = 4
// console.log("old index:" + index + ", new index:" + newIndex)
if(newIndex === model.index)
{
listViewDelegateItem.x = lastX
listViewDelegateItem.y = lastY
return
}
testModelList.moveModel(model.index, newIndex)
listViewDelegateItem.x = lastX
}
}
}
MouseArea {
anchors.fill: parent
enabled: testing
onClicked: {
infoDelegate.show = !infoDelegate.show
}
}
CheckBox {
id: checkBox
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 10
anchors.margins: 20
// checked: model.item.checked
opacity: begingTest ? 0 : 1
onCheckedChanged: {
if(model.item.type === 0)
{
waitForDevReadyChecked = checked
// console.log("waitForDevReadyChecked:"+waitForDevReadyChecked)
}
// if(model.checked === checked)
// return
// testModelList.getModel(model.index).checked = checkBox.checked
model.item.checked = checkBox.checked
$Config.setTestTypeChecked(model.type,checked)
// console.log("model: " + model.name + " checked: " + checked + " " + model.checked)
}
Component.onCompleted: {
checked = model.checked
}
Behavior on opacity {
OpacityAnimator {
duration: begingTestDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
}
ProgressBar {
Material.accent: "#"+listDelegate.colorText
indeterminate: true
height: 1
anchors.bottom: parent.bottom
opacity: (model.item.running && !infoDelegate.show) ? 1 : 0
x: listDelegate.radius
width: listDelegate.width-listDelegate.radius*2
Behavior on opacity {
OpacityAnimator {
duration: begingTestDuration-200
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
}
}
Rectangle {
id: skipDelegate
height: listDelegate.height
opacity: testing && !model.checked ? 1 : 0
width: testing && !model.checked ? listView.infoRecWidth : 350
radius: listDelegate.radius
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop {
position: 0.30;
color: '#00000000'
}
GradientStop {
position: 0.85;
color: "#30"+listDelegate.colorText
}
}
Behavior on width {
PropertyAnimation {
duration: begingTestDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Behavior on opacity {
OpacityAnimator {
duration: begingTestDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Text {
text: "已跳过"
color: '#c0'+listDelegate.colorText
font.pixelSize: logListView.textSize
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 10
font.bold: true
}
}
Rectangle {
id: infoDelegate
property bool show: true
anchors.top: listDelegate.bottom
height: testing && model.checked && show ? (testIndex>=model.index?logColumn.implicitHeight+40:0):0
opacity: height>0 && show ? 1 : 0
width: testing ? listView.infoRecWidth : 350
color: "#30"+listDelegate.colorText
radius: listDelegate.radius
clip: true
MouseArea {
anchors.fill: parent
enabled: testing
onDoubleClicked: {
infoDelegate.show = !infoDelegate.show
}
}
Behavior on width {
PropertyAnimation {
duration: begingTestDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Behavior on height {
NumberAnimation {
duration: begingTestDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Behavior on radius {
NumberAnimation {
duration: begingTestDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Behavior on opacity {
OpacityAnimator {
duration: begingTestDuration
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
Column {
id: logColumn
width: parent.width - 40
x: 20
y: 20
spacing: 10
move: Transition {
NumberAnimation {
properties: "x,y"
duration: toTestingDuration - 100
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
add: addTransition
ListView {
id: logListView
property int textSize: listView.textSize
model: item.logList
cacheBuffer: 10000
boundsBehavior: "StopAtBounds"
interactive: false
width: parent.width
height: contentHeight//count * (logListView.textSize+15)
spacing: 10
// onContentHeightChanged: {
// if(contentHeight)
// height = contentHeight
// // console.log(contentHeight + " " + contentY)
// }
delegate: Text {
text: {
switch(item.logState) {
case -1:
return "未知结果: " + item.log
case 0:
return "错误: " + item.log
case 1:
return "成功: " + item.log
case 2:
return item.log
case 3:
return "警告: " + item.log
}
}
color: {
switch(item.logState) {
case -1:
return '#9A9EF9'
case 0:
return '#cc0000'
case 1:
return '#00cc00'
case 2:
return "#eeeeee"//'#'+listDelegate.colorText
case 3:
return '#cccc00'
}
}
font.pixelSize: logListView.textSize
// height: text === "" ? 0 : implicitHeight
}
add: addTransition
remove: Transition {
ParallelAnimation {
OpacityAnimator {
duration: toTestingDuration-200
from: 1
to: 0
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
PropertyAnimation {
duration: toTestingDuration
property: "x"
from: 0
to: - 20
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
}
}
// Text {
// lineHeight: 1.5
// height: text===""? 0 : implicitHeight
// text: model.item.logList
// color: '#'+listDelegate.colorText
// // font.bold: true
// }
Text {
id: log2Text
text: model.item.log2
font.bold: true
color: 'white'
font.pixelSize: logListView.textSize
onTextChanged: {
opacity = 0
log2TextOpacityAnimator.start()
}
}
OpacityAnimator {
id: log2TextOpacityAnimator
target: log2Text
duration: 900
from: 0
to: 1
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
ProgressBar {
Material.accent: "#"+listDelegate.colorText
indeterminate: true
height: 1
anchors.bottom: parent.bottom
opacity: (model.item.running && infoDelegate.show) ? 1 : 0
x: parent.radius
width: parent.width-parent.radius*2
Behavior on opacity {
OpacityAnimator {
duration: begingTestDuration - 200
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
}
}
}
}
ArrowShape {
x: 500
y: 160
property int selfOpacity: 0
opacity: begingTest ? 0 : selfOpacity
Component.onCompleted: {
selfOpacity = 1
}
Behavior on opacity {
OpacityAnimator {
duration: 1200
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
MouseArea {
anchors.fill: parent
onPressAndHold: {
for(var i=1;i<testModelList.rowCount();++i) {
var target = testModelList.getModelIndexByType(i)
if(target !== i)
testModelList.moveModel(testModelList.getModelIndexByType(i),i)
}
}
}
}
HistoryModelList {
id: historyModelList
}
TestDetailModelList {
id: testDetailModelList
}
Rectangle {
anchors.fill: historyListView
anchors.topMargin: -20
anchors.bottomMargin: -20
color: '#201eadff'
radius: 7
}
ListView {
id: historyListView
x: parent.width/2 + 40
y: 105
model: historyModelList
width: 580
height: parent.height-y-160
spacing: 8
clip: true
Component.onCompleted: {
historyModelList.loadAllHistory()
}
populate: Transition {
NumberAnimation {
properties: "x,y"
duration: 500
easing.type: Easing.Bezier
easing.bezierCurve: listEmphasized
}
}
moveDisplaced: Transition {
NumberAnimation {
properties: "x,y"
duration: 350
easing.type: Easing.Bezier
easing.bezierCurve: listEmphasized
}
}
move: Transition {
NumberAnimation {
properties: "x,y"
duration: 350
easing.type: Easing.Bezier
easing.bezierCurve: listEmphasized
}
}
delegate: Item {
width: historyListView.width
height: 30
MouseArea {
anchors.fill: parent
onClicked: {
testDetailModelList.loadTestDetail(model.testHistoryID)
detailItemPopupText.text = model.devNumber
detailItemPopup.open()
}
}
Row {
anchors.fill: parent
spacing: 11
leftPadding: 20
Text {
width: 95
text: devNumber
color: 'white'
}
Text {
text: devModel
color: 'white'
}
Text {
text: devBatch
color: 'white'
}
Text {
text: startTime
color: 'white'
}
Text {
text: endTime
color: 'white'
}
Text {
text: remark
color: 'white'
}
}
}
}
Popup {
id: detailItemPopup
property string devNum
Material.theme: Material.Light
anchors.centerIn: Overlay.overlay
width: 600
height: 400
modal: true
focus: true
clip: true
background: Rectangle {
color: '#f0111111'
radius: 7
}
// Overlay.modal: Rectangle{
// color: '#40000000'
// }
// onOpened: {
// testDetailInfoTextRec.opacity = 0
// }
Text {
id: detailItemPopupText
x: 20
y: 20
font.pixelSize: 24
color: 'white'
font.bold: true
}
ListView {
y: 80
width: parent.width
height: parent.height - 20
clip: true
spacing: 30
interactive: false
model: testDetailModelList
delegate: Item {
width: historyListView.width
height: 20
MouseArea {
id: testDetailDelegateMouseArea
anchors.fill: parent
hoverEnabled: true
}
Row {
anchors.fill: parent
spacing: 20
leftPadding: 20
height: 20
Text {
width: 150
text: {
switch(testType)
{
case 0:
return "等待设备就绪"
case 1:
return "蓝牙测试"
case 2:
return "串口测试"
case 3:
return "按键、标定开关测试"
case 4:
return "LED、指示灯显示测试"
case 5:
return "长按关机测试"
default:
return "未知"
}
}
color: 'white'
}
Text {
text: {
switch(result)
{
case -1:
return "失败"
case 0:
return "警告"
case 1:
return "成功"
case 2:
return "跳过"
default:
return "未知"
}
}
color: {
switch(result) {
case -1:
return '#cc0000'
case 0:
return '#cccc00'
case 1:
return '#00cc00'
case 2:
return "#eeeeee"//'#'+listDelegate.colorText
default:
return '#eeeeee'
}
}
}
Text {
text: startTime
color: 'white'
}
Text {
text: endTime
color: 'white'
}
}
ToolTip {
visible: testDetailDelegateMouseArea.containsMouse
text: info
font.pixelSize: 20
delay: 300
}
}
// Rectangle {
// id: testDetailInfoTextRec
// opacity: 0
// color: '#f0222222'
// radius: 7
// width: parent.width - 40
// height: 200
// x: 20
// y: parent.height-height
// Text {
// id: testDetailInfoText
// x: 20
// y: 20
// color: 'white'
// }
// Behavior on opacity {
// OpacityAnimator {
// duration: 1200
// easing.type: Easing.Bezier
// easing.bezierCurve: toTestingCurve
// }
// }
// }
}
}
BigButton {
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: 60
anchors.bottomMargin: 40
btntext: testing ? "结束测试" : "开始测试"
color1: '#597ab1'
opacity: waitForDevReadyChecked ? 0 : 1
onClick: {
if(!testing)
{
begingTest = true
beginTestTimer.start()
}
else
{
if(!thisTestEnd)
{
$Test.stopTest()
console.log("$Test.stopTest()")
}
else
{
testing = false
delayTestIndexTimer2.start()
}
}
}
Behavior on opacity {
OpacityAnimator {
duration: 500
easing.type: Easing.Bezier
easing.bezierCurve: toTestingCurve
}
}
}
Timer {
id: beginTestTimer
interval: 150
onTriggered: {
testing = true
// begingTest = false
starTests()
}
}
}