DPS_Manage/Component/PromotionCard.qml

257 lines
8.3 KiB
QML
Raw Permalink Normal View History

2025-05-29 14:04:05 +08:00
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
Item {
id: root
width: 800
height: 400
property var originalModel: []
property alias model: listView.model
property int currentIndex: listView.currentIndex
property int cardSpacing : -100
property int switchInterval: 999999999
signal clickedFunction(data:var)
// 初始化循环模型
onOriginalModelChanged: {
if(originalModel.length <= 0)return
var loopedModel = [];
loopedModel = loopedModel.concat(originalModel.slice(-1));
loopedModel = loopedModel.concat(originalModel);
loopedModel = loopedModel.concat(originalModel.slice(0, 2));
listView.model = loopedModel;
}
Timer {
id: autoSwitch
interval: switchInterval
repeat: true
running: true
onTriggered: {
listView.currentIndex = listView.currentIndex + 1;
}
}
ListView {
id: listView
anchors.fill: parent
orientation: ListView.Horizontal
snapMode: ListView.SnapOneItem
highlightRangeMode: ListView.StrictlyEnforceRange
preferredHighlightBegin: (width - delegateWidth) / 2
preferredHighlightEnd: preferredHighlightBegin + delegateWidth
spacing: -100
leftMargin: delegateWidth * 0.2
rightMargin: delegateWidth * 0.2
boundsBehavior: Flickable.StopAtBounds
flickDeceleration: 1500
readonly property real delegateWidth: width * 0.85
onCurrentIndexChanged: {
//位移到最后跳回第一
if (currentIndex === (originalModel.length + 1)) {
currentIndex = 1;
}
//位移到最前跳回最后
else if (currentIndex === 0) {
currentIndex = originalModel.length;
}
}
onModelChanged: {
if(model && model.length > 0){
currentIndex = 1
listView.positionViewAtIndex(1, ListView.Center)
}
}
Component.onCompleted: {
}
delegate: Item {
id: delegateItem
width: listView.delegateWidth
height: listView.height
// 被动式偏移计算
property real _rawCenterOffset: 0
property real _stableCenterOffset: 0
property real centerOffset: _stableCenterOffset
property real itemScale: 0.8 + (1 - Math.abs(centerOffset)) * 0.2
property real sideMargin: Math.abs(centerOffset) * 40
Timer {
id: offsetTracker
interval: 16
repeat: true
running: true
onTriggered: {
const itemCenter = x + width / 2
const viewCenter = listView.contentX + listView.width / 2
_rawCenterOffset = (itemCenter - viewCenter) / (listView.width / 2)
if (Math.abs(_rawCenterOffset - _stableCenterOffset) > 0.001) {
_stableCenterOffset = _rawCenterOffset
}
}
}
// // 延迟定位修正
// Timer {
// id: positionCorrector
// interval: 50
// onTriggered: {
// if (listView.currentIndex === index && Math.abs(centerOffset) > 0.1) {
// listView.positionViewAtIndex(index, ListView.Center)
// }
// }
// }
// onCenterOffsetChanged: {
// if (index === listView.currentIndex) {
// positionCorrector.restart()
// }
// }
Rectangle {
id: imageContainer
anchors {
fill: parent
leftMargin: delegateItem.sideMargin * 0.8
rightMargin: delegateItem.sideMargin * 0.8
topMargin: 20 * (1 - Math.abs(delegateItem.centerOffset))
bottomMargin: 20 * (1 - Math.abs(delegateItem.centerOffset))
}
radius: 10
border.color: "#e0e0e0"
border.width: 2
clip: true
Image {
id: contentImage
anchors.fill: parent
source: modelData.img
fillMode: Image.PreserveAspectCrop
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
width: imageContainer.width
height: imageContainer.height
radius: imageContainer.radius
}
}
}
layer.enabled: true
layer.effect: DropShadow {
radius: 8
samples: 16
color: "#40000000"
}
}
MouseArea {
id: mouse_effect
anchors.fill: parent
hoverEnabled: true
onClicked: {
listView.currentIndex = index
clickEffect.start(mouseX, mouseY)
var realIndex = index - 1;
if (realIndex < 0) {
realIndex = originalModel.length - 1;
} else if (realIndex >= originalModel.length) {
realIndex = 0;
}
root.clickedFunction(originalModel[realIndex]);
}
onWheel: {
var buf = 1;
if (wheel.angleDelta.y > 0) buf = -1
var newIndex = listView.currentIndex + buf;
listView.currentIndex = newIndex;
}
Rectangle {
id: clickEffect
width: 0
height: width
radius: width / 2
color: "#40ffffff"
visible: false
x: 0
y: 0
function start(x, y) {
clickEffect.x = 0 - delegateItem.width
clickEffect.y = 0 - delegateItem.height
visible = true
anim.start()
}
SequentialAnimation {
id: anim
ParallelAnimation {
NumberAnimation {
target: clickEffect
property: "width"
from: 0
to: Math.max(delegateItem.width, delegateItem.height) * 4
duration: 800
easing.type: Easing.OutQuad
}
NumberAnimation {
target: clickEffect
property: "opacity"
from: 0.8
to: 0
duration: 800
easing.type: Easing.OutQuad
}
}
ScriptAction {
script: clickEffect.visible = false
}
}
}
}
transform: Scale {
origin.x: delegateItem.width / 2
origin.y: delegateItem.height / 2
xScale: itemScale
yScale: itemScale
}
Behavior on scale {
NumberAnimation { duration: 300; easing.type: Easing.OutQuad }
}
}
layoutDirection: Qt.LeftToRight
highlightMoveDuration: 300
}
PageIndicator {
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
margins: 1
}
count: originalModel.length
currentIndex: {
var index = listView.currentIndex - 1 >= 0? listView.currentIndex - 1 : originalModel.length - 1
if(index >= originalModel.length)index = 0
return index
}
}
}