完整代碼 : https://github.com/dazeGitHub/TestHarmonyBanner
定義組件 SwiperComponentHalfFold :
import { SwiperData } from '../model/SwiperData';
import Logger from '../utils/Logger';
/**
* 層疊輪播圖組件
* 是 SwiperComponent 的修改版, 只顯示右側(cè)的折疊部分
*/
@Component
export struct SwiperComponentHalfFold {
@State currentIndex: number = 0;
@State swiperData: SwiperData[] = [
new SwiperData($r("app.media.mp_chart"), 'MpChart圖表實(shí)現(xiàn)案例'),
new SwiperData($r("app.media.lottie"), 'Lottie動(dòng)畫'),
new SwiperData($r("app.media.component_tack"), '組件堆疊'),
new SwiperData($r("app.media.ic_swiper1"), '輪播1'),
new SwiperData($r("app.media.ic_swiper2"), '輪播2'),
new SwiperData($r("app.media.ic_swiper3"), '輪播3'),
];
private halfCount: number = Math.floor(6 / 2); //半數(shù) = 總數(shù) / 2
private manualSlidingDuration: number = 800; //手動(dòng)滑動(dòng)時(shí)長(zhǎng)
private automaticSlidingDuration: number = 300;
private automaticSwitchTime: number = 5000;
private offsetXValue = 15;
@State swiperInterval: number = 0
aboutToAppear(): void {
this.currentIndex = this.halfCount;
this.swiperInterval = setInterval(() => {
this.startAnimation(true, this.manualSlidingDuration);
}, this.automaticSwitchTime);
}
/**
* 獲取圖片系數(shù)
* @param index:索引值
* @returns
*/
getImgCoefficients(index: number): number {
const coefficient: number = this.currentIndex - index; // 計(jì)算圖片左右位置
const tempCoefficient: number = Math.abs(coefficient);
if (tempCoefficient <= this.halfCount) {
return coefficient;
}
const dataLength: number = this.swiperData.length;
let tempOffset: number = dataLength - tempCoefficient; // 判斷圖片位于左右層級(jí)位置
if (tempOffset <= this.halfCount) { //如果在左側(cè)
if (coefficient > 0) {
return -tempOffset;
}
return tempOffset;
}
return 0;
}
/**
* 計(jì)算偏移量
* @param index:索引值
* @returns
*/
getOffSetX(index: number): number {
const offsetIndex: number = this.getImgCoefficients(index);
const tempOffset: number = Math.abs(offsetIndex);
let offsetX: number = 0;
if (tempOffset === 1) {
// 根據(jù)圖片層級(jí)系數(shù)來決定左右偏移量
offsetX = -15 * offsetIndex;
}
if (tempOffset === 2) {
// 根據(jù)圖片層級(jí)系數(shù)來決定左右偏移量
offsetX = -this.offsetXValue * offsetIndex;
}
Logger.info("TAG", "index = " + index + " offsetX = " + offsetX)
return offsetX;
}
// 性能:顯式動(dòng)畫(https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/ts-explicit-animation-0000001478341181-V2)
startAnimation(isLeft: boolean, duration: number): void {
animateTo({
duration: duration,
}, () => {
const dataLength: number = this.swiperData.length;
const tempIndex: number = isLeft ? this.currentIndex + 1 : this.currentIndex - 1 + dataLength;
this.currentIndex = tempIndex % dataLength;
})
}
build() {
Column() {
Stack() {
// LazyForEach必須在容器組件內(nèi)使用,僅有List粉楚、Grid恒水、Swiper以及WaterFlow組件支持?jǐn)?shù)據(jù)懶加載,其他組件仍然是一次性加載所有的數(shù)據(jù)梧乘。
ForEach(this.swiperData, (item: SwiperData, index: number) => {
Stack({ alignContent: Alignment.BottomStart }) {
Image(item.imageSrc)
.objectFit(ImageFit.Cover)
.width('100%')
.height('100%')
.borderRadius($r('app.string.main_page_top_borderRadius'))
// 輪播圖底部蒙層 必定在上方
Stack() {
Column() {
}
.width('100%')
.height('100%')
.backgroundColor(Color.Black)
.opacity(0.3)
.borderRadius({
topLeft: 0,
topRight: 0,
bottomLeft: $r('app.string.main_page_top_borderRadius'),
bottomRight: $r('app.string.main_page_top_borderRadius')
})
Text(item.name)
.width('100%')
.height('100%')
.fontSize(16)
.fontColor(Color.White)
.textAlign(TextAlign.Start)
.padding($r('app.string.main_page_padding5'))
}
.height($r('app.string.bottom_title_height'))
}
.backgroundColor(Color.White)
.borderRadius(8)
.offset({
x: this.getOffSetX(index),
y: 0
})
.blur(index !== this.currentIndex ? 12 : 0)
// TODO: 知識(shí)點(diǎn):通過animateTo實(shí)現(xiàn)動(dòng)畫并且同時(shí)改變currentIndex數(shù)據(jù)中間值來判斷組件zIndex實(shí)現(xiàn)切換動(dòng)畫
.zIndex(index !== this.currentIndex && this.getImgCoefficients(index) === 0 ?
0 : 2 - Math.abs(this.getImgCoefficients(index)))
.width($r('app.string.swiper_stack_width'))
// .height(index !== this.currentIndex ? $r('app.string.swiper_stack_height1') : $r('app.string.swiper_stack_height2'))
.height(
index === this.currentIndex ?
$r("app.string.swiper_stack_height1")
: ((index === this.currentIndex - 1 || index === this.currentIndex + 1) ?
$r("app.string.swiper_stack_height2") :
$r("app.string.swiper_stack_height3"))
)
.onClick(() => {
// 點(diǎn)擊輪播圖Item時(shí)绊序,根據(jù)點(diǎn)擊的模塊信息,將頁面放入路由棧
// DynamicsRouter.push(item.routerInfo, item.param);
})
})
}
.onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, currentRatio: number) => {
clearInterval(this.swiperInterval);
if (isVisible && currentRatio >= 1.0) {
this.swiperInterval = setInterval(() => {
this.startAnimation(true, this.manualSlidingDuration);
}, this.automaticSwitchTime)
}
if (!isVisible && currentRatio <= 0.0) {
clearInterval(this.swiperInterval);
}
})
//高度必須和 app.string.swiper_stack_height1 相同
.height($r('app.string.swiper_stack_height1'))
.width('100%')
.gesture(
PanGesture({ direction: PanDirection.Horizontal })
.onActionStart((event: GestureEvent) => {
clearInterval(this.swiperInterval);
this.startAnimation(event.offsetX < 0, this.automaticSlidingDuration);
})
.onActionEnd(() => {
this.swiperInterval = setInterval(() => {
this.startAnimation(true, this.manualSlidingDuration);
}, this.automaticSwitchTime);
})
)
.alignContent(Alignment.Start)
.backgroundColor($r("app.color.green"))
.clip(true) //裁剪超出 banner 左側(cè)的層疊部分
.margin({left: "100vp"})
// .margin({left: -this.offsetXValue * 2}) //整體左移動(dòng)兩個(gè)位移
//底部指示器
Row({ space: 10 }) {
ForEach(this.swiperData, (item: SwiperData, index: number) => {
Ellipse(index !== this.currentIndex ? { width: 8, height: 8 } : { width: 10, height: 8 })
.fill(index !== this.currentIndex ? Color.Black : Color.Red)
.fillOpacity(0.6)
})
}
.margin({ top: 12 })
}
.width('100%')
.height('100%')
.backgroundColor($r("app.color.blue"))
.justifyContent(FlexAlign.Start)
}
}
使用組件 SwiperComponentHalfFold :
import { SwiperComponentHalfFold } from '../components/SwiperComponentHalfFold'
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
// SwiperComponent()
// SwiperComponentThreeEle()
SwiperComponentHalfFold()
}
.width('100%')
}
.height('100%')
}
}
運(yùn)行結(jié)果如下 :
Snipaste_2024-04-13_15-34-29.png