Skip to content

Commit ef1f706

Browse files
authored
Merge pull request zhuzichu520#595 from Polaris-Night/main
feat: FluCarousel支持纵向轮播
2 parents 9ac58a8 + 8377fb5 commit ef1f706

File tree

6 files changed

+352
-95
lines changed

6 files changed

+352
-95
lines changed

example/example_en_US.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,11 @@ Updated content:
11231123
<source>Carousel map, support infinite carousel, infinite swipe, and components implemented with ListView</source>
11241124
<translation type="unfinished"></translation>
11251125
</message>
1126+
<message>
1127+
<location filename="qml/page/T_Carousel.qml" line="203"/>
1128+
<source>Auto play</source>
1129+
<translation type="unfinished"></translation>
1130+
</message>
11261131
</context>
11271132
<context>
11281133
<name>T_CheckBox</name>

example/example_zh_CN.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,12 +1142,17 @@ Updated content:
11421142
<message>
11431143
<location filename="qml/page/T_Carousel.qml" line="10"/>
11441144
<source>Carousel</source>
1145-
<translation type="unfinished">轮播图</translation>
1145+
<translation>轮播图</translation>
11461146
</message>
11471147
<message>
11481148
<location filename="qml/page/T_Carousel.qml" line="36"/>
11491149
<source>Carousel map, support infinite carousel, infinite swipe, and components implemented with ListView</source>
1150-
<translation type="unfinished">轮播图,支持无限轮播,无限滑动,用ListView实现的组件</translation>
1150+
<translation>轮播图,支持无限轮播,无限滑动,用ListView实现的组件</translation>
1151+
</message>
1152+
<message>
1153+
<location filename="qml/page/T_Carousel.qml" line="203"/>
1154+
<source>Auto play</source>
1155+
<translation>自动轮播</translation>
11511156
</message>
11521157
</context>
11531158
<context>

example/qml/page/T_Carousel.qml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,97 @@ FluScrollablePage{
121121

122122
}
123123
}
124+
CodeExpander{
125+
Layout.fillWidth: true
126+
Layout.topMargin: -6
127+
code:'FluCarousel{
128+
id:carousel
129+
width: 400
130+
height: 300
131+
delegate: Component{
132+
Image {
133+
anchors.fill: parent
134+
source: model.url
135+
asynchronous: true
136+
fillMode:Image.PreserveAspectCrop
137+
}
138+
}
139+
Component.onCompleted: {
140+
carousel.model = [{url:"qrc:/example/res/image/banner_1.jpg"},{url:"qrc:/example/res/image/banner_2.jpg"},{url:"qrc:/example/res/image/banner_3.jpg"}]
141+
}
142+
}'
143+
}
124144

145+
FluFrame{
146+
Layout.fillWidth: true
147+
Layout.preferredHeight: 300 + topPadding + bottomPadding
148+
padding: 10
149+
Layout.topMargin: 10
150+
RowLayout{
151+
anchors.fill: parent
152+
Item{
153+
Layout.preferredWidth: 400
154+
Layout.fillHeight: true
155+
FluShadow{
156+
radius: 8
157+
}
158+
FluCarousel{
159+
anchors.fill: parent
160+
orientation: Qt.Vertical
161+
autoPlay: auto_play_switch.checked
162+
loopTime:1500
163+
indicatorGravity: Qt.AlignVCenter | Qt.AlignRight
164+
indicatorMarginTop:15
165+
delegate: Component{
166+
Item{
167+
anchors.fill: parent
168+
Image {
169+
anchors.fill: parent
170+
source: model.url
171+
asynchronous: true
172+
fillMode:Image.PreserveAspectCrop
173+
}
174+
Rectangle{
175+
height: 40
176+
width: parent.width
177+
anchors.bottom: parent.bottom
178+
color: "#33000000"
179+
FluText{
180+
anchors.fill: parent
181+
verticalAlignment: Qt.AlignVCenter
182+
horizontalAlignment: Qt.AlignHCenter
183+
text:model.title
184+
color: FluColors.Grey10
185+
}
186+
}
187+
}
188+
}
189+
Layout.topMargin: 20
190+
Layout.leftMargin: 5
191+
Component.onCompleted: {
192+
var arr = []
193+
arr.push({url:"qrc:/example/res/image/banner_1.jpg",title:"共同应对全球性问题"})
194+
arr.push({url:"qrc:/example/res/image/banner_2.jpg",title:"三小只全程没互动"})
195+
arr.push({url:"qrc:/example/res/image/banner_3.jpg",title:"有效投资扩大 激发增长动能"})
196+
model = arr
197+
}
198+
}
199+
}
200+
FluToggleSwitch{
201+
id: auto_play_switch
202+
Layout.alignment: Qt.AlignRight
203+
text: qsTr("Auto play")
204+
}
205+
}
206+
}
125207
CodeExpander{
126208
Layout.fillWidth: true
127209
Layout.topMargin: -6
128210
code:'FluCarousel{
129211
id:carousel
130212
width: 400
131213
height: 300
214+
orientation: Qt.Vertical
132215
delegate: Component{
133216
Image {
134217
anchors.fill: parent

src/Qt5/imports/FluentUI/Controls/FluCarousel.qml

Lines changed: 120 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import FluentUI 1.0
44

55
Item {
66
property bool autoPlay: true
7+
property int orientation: Qt.Horizontal
78
property int loopTime: 2000
89
property var model
910
property Component delegate
@@ -14,7 +15,7 @@ Item {
1415
property int indicatorMarginTop: 0
1516
property int indicatorMarginBottom: 20
1617
property int indicatorSpacing: 10
17-
property alias indicatorAnchors: layout_indicator.anchors
18+
property alias indicatorAnchors: indicator_loader.anchors
1819
property Component indicatorDelegate : com_indicator
1920
id:control
2021
width: 400
@@ -24,13 +25,24 @@ Item {
2425
}
2526
QtObject{
2627
id:d
27-
property bool flagXChanged: false
28+
property bool isManualMoving: false
2829
property bool isAnimEnable: control.autoPlay && list_view.count>3
30+
onIsAnimEnableChanged: {
31+
if(isAnimEnable){
32+
timer_run.restart()
33+
}else{
34+
timer_run.stop()
35+
}
36+
}
2937
function setData(data){
30-
if(!data){
38+
if(!data || !Array.isArray(data)){
3139
return
3240
}
3341
content_model.clear()
42+
list_view.resetPos()
43+
if(data.length === 0){
44+
return
45+
}
3446
content_model.append(data[data.length-1])
3547
content_model.append(data)
3648
content_model.append(data[0])
@@ -49,7 +61,7 @@ Item {
4961
clip: true
5062
boundsBehavior: ListView.StopAtBounds
5163
model:content_model
52-
maximumFlickVelocity: 4 * (list_view.orientation === Qt.Horizontal ? width : height)
64+
maximumFlickVelocity: 4 * (control.orientation === Qt.Vertical ? height : width)
5365
preferredHighlightBegin: 0
5466
preferredHighlightEnd: 0
5567
highlightMoveDuration: 0
@@ -63,7 +75,7 @@ Item {
6375
d.setData(control.model)
6476
}
6577
}
66-
orientation : ListView.Horizontal
78+
orientation : control.orientation
6779
delegate: Item{
6880
id:item_control
6981
width: ListView.view.width
@@ -88,34 +100,63 @@ Item {
88100
}
89101
}
90102
onMovementEnded:{
91-
d.flagXChanged = false
103+
d.isManualMoving = false
92104
list_view.highlightMoveDuration = 0
93-
currentIndex = list_view.contentX/list_view.width
94-
if(currentIndex === 0){
95-
currentIndex = list_view.count-2
96-
}else if(currentIndex === list_view.count-1){
97-
currentIndex = 1
105+
if(control.orientation === Qt.Vertical){
106+
currentIndex = (list_view.contentY - list_view.originY) / list_view.height
107+
if(currentIndex === 0){
108+
currentIndex = list_view.count - 2
109+
}else if(currentIndex === list_view.count - 1) {
110+
currentIndex = 1
111+
}
112+
} else {
113+
currentIndex = (list_view.contentX - list_view.originX) / list_view.width
114+
if(currentIndex === 0){
115+
currentIndex = list_view.count - 2
116+
}else if(currentIndex === list_view.count - 1){
117+
currentIndex = 1
118+
}
98119
}
99120
if(d.isAnimEnable){
100121
timer_run.restart()
101122
}
102123
}
103124
onMovementStarted: {
104-
d.flagXChanged = true
125+
d.isManualMoving = true
105126
timer_run.stop()
106127
}
107128
onContentXChanged: {
108-
if(d.flagXChanged){
109-
var maxX = Math.min(list_view.width*(currentIndex+1),list_view.count*list_view.width)
110-
var minX = Math.max(0,(list_view.width*(currentIndex-1)))
111-
if(contentX>=maxX){
112-
contentX = maxX
129+
if(d.isManualMoving && control.orientation === Qt.Horizontal){
130+
const range = getPosRange(list_view.width, currentIndex)
131+
if(contentX >= range.max){
132+
contentX = range.max
113133
}
114-
if(contentX<=minX){
115-
contentX = minX
134+
if(contentX <= range.min){
135+
contentX = range.min
116136
}
117137
}
118138
}
139+
onContentYChanged: {
140+
if(d.isManualMoving && control.orientation === Qt.Vertical){
141+
const range = getPosRange(list_view.height, currentIndex)
142+
if(contentY >= range.max){
143+
contentY = range.max
144+
}
145+
if(contentY <= range.min){
146+
contentY = range.min
147+
}
148+
}
149+
}
150+
function resetPos() {
151+
contentX = 0
152+
contentY = 0
153+
}
154+
function getPosRange(size, index) {
155+
return {
156+
"min": Math.max(0, size * (index - 1)),
157+
"max": Math.min(size * (index + 1), list_view.count * size)
158+
}
159+
}
119160
}
120161
Component{
121162
id:com_indicator
@@ -140,9 +181,9 @@ Item {
140181
}
141182
}
142183
}
143-
Row{
144-
id:layout_indicator
145-
spacing: control.indicatorSpacing
184+
185+
Loader{
186+
id: indicator_loader
146187
anchors{
147188
horizontalCenter:(indicatorGravity & Qt.AlignHCenter) ? parent.horizontalCenter : undefined
148189
verticalCenter: (indicatorGravity & Qt.AlignVCenter) ? parent.verticalCenter : undefined
@@ -155,28 +196,66 @@ Item {
155196
rightMargin: control.indicatorMarginBottom
156197
topMargin: control.indicatorMarginBottom
157198
}
158-
visible: showIndicator
159-
Repeater{
160-
id:repeater_indicator
161-
model: list_view.count
162-
FluLoader{
163-
property int displayIndex: {
164-
if(index === 0)
165-
return list_view.count-3
166-
if(index === list_view.count-1)
167-
return 0
168-
return index-1
199+
active: showIndicator
200+
sourceComponent: control.orientation === Qt.Vertical ? column_indicator : row_indicator
201+
}
202+
203+
Component{
204+
id: row_indicator
205+
Row{
206+
id:layout_indicator
207+
spacing: control.indicatorSpacing
208+
Repeater{
209+
id:repeater_indicator
210+
model: list_view.count
211+
FluLoader{
212+
property int displayIndex: {
213+
if(index === 0)
214+
return list_view.count-3
215+
if(index === list_view.count-1)
216+
return 0
217+
return index-1
218+
}
219+
property int realIndex: index
220+
property bool checked: list_view.currentIndex === index
221+
sourceComponent: {
222+
if(index===0 || index===list_view.count-1)
223+
return undefined
224+
return control.indicatorDelegate
225+
}
169226
}
170-
property int realIndex: index
171-
property bool checked: list_view.currentIndex === index
172-
sourceComponent: {
173-
if(index===0 || index===list_view.count-1)
174-
return undefined
175-
return control.indicatorDelegate
227+
}
228+
}
229+
}
230+
231+
Component{
232+
id: column_indicator
233+
Column{
234+
id:layout_indicator
235+
spacing: control.indicatorSpacing
236+
Repeater{
237+
id:repeater_indicator
238+
model: list_view.count
239+
FluLoader{
240+
property int displayIndex: {
241+
if(index === 0)
242+
return list_view.count-3
243+
if(index === list_view.count-1)
244+
return 0
245+
return index-1
246+
}
247+
property int realIndex: index
248+
property bool checked: list_view.currentIndex === index
249+
sourceComponent: {
250+
if(index===0 || index===list_view.count-1)
251+
return undefined
252+
return control.indicatorDelegate
253+
}
176254
}
177255
}
178256
}
179257
}
258+
180259
Timer{
181260
id:timer_anim
182261
interval: 250
@@ -198,10 +277,10 @@ Item {
198277
}
199278
}
200279
function changedIndex(index){
201-
d.flagXChanged = true
280+
d.isManualMoving = true
202281
timer_run.stop()
203282
list_view.currentIndex = index
204-
d.flagXChanged = false
283+
d.isManualMoving = false
205284
if(d.isAnimEnable){
206285
timer_run.restart()
207286
}

0 commit comments

Comments
 (0)