HarmonyOS5 便捷生活类——实现Swiper滑动过程中的缩放效果

加入下方官方班级,得鸿蒙礼盒

一起拿鸿蒙礼盒,戳我戳我!!

本期活动时间:2025年8月1日-12月31日


问题现象

如何实现图片滑动且滑动过程中有缩放动效?

效果预览

背景知识

Swiper组件提供了customContentTransition的API,通过自定义页面切换的动画来实现图片滑动过程中的缩放效果。

解决方案

  1. 创建继承IDataSource的类MyDataSource,监听滑动的图片。
  2. 图片展示时,在onChange中根据当前图片的索引,来确定前后图片的缩放值。
  3. 在customContentTransition中获取SwiperContentTransitionProxy回调的proxy对象,根据对象的属性selectedIndex、index、position、mainAxisLength计算滑动图片的缩放值,实现滑动时的缩放效果。
class MyDataSource implements IDataSource {
private listeners: DataChangeListener[] = [];
private list: string[] = []

constructor(list: string[]) {
this.list = list
}

totalCount(): number {
return this.list.length
}

getData(index: number): string {
return this.list[index]
}

registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}

unregisterDataChangeListener() {
}
}

const MAX_SCALE = 1 // 最大缩放
const MIN_SCALE = 0.75 // 最小缩放
// 可通过以下两个参数变化观察效果,然后优化
const PAGE_DURATION = 200 // 子控件动画时长
const SWIPER_DURATION = 300 // swiper组件切换动画时长
// 拖动时用来计算缩放,影响拖动缩放速度,可根据屏幕尺寸来定
const DRAGGING_MAX_DISTANCE = 1000
const DISPLAY_COUNT: number = 1

@Entry
@Component
struct SwiperPageOne {
private swiperController: SwiperController = new SwiperController()
private data: MyDataSource = new MyDataSource([])
@State currentIndex: number = 0
@State scaleArray: number[] = []; // 缩放值
startSwiperOffset: number = 0 // 开始滑动距离
@State NextIndex: number = 0
@State PreIndex: number = 0
@State imageArr: string[] = [
'app.media.drag_and_exchange_ic_public_app1',
'app.media.drag_and_exchange_ic_public_app2',
'app.media.drag_and_exchange_ic_public_app3',
'app.media.drag_and_exchange_ic_public_app4',
'app.media.drag_and_exchange_ic_public_app5',
'app.media.drag_and_exchange_ic_public_app6',
]

aboutToAppear(): void {
let list: string[] = []
for (let i = 0; i <= 5; i++) {
list.push(this.imageArr[i]);
this.scaleArray.push(i == 0 ? MAX_SCALE : MIN_SCALE)
}
this.data = new MyDataSource(list)
}

build() {
Column({ space: 25 }) {
Swiper(this.swiperController) {
LazyForEach(this.data, (item: string, index: number) => {
Column() {
// swiper内容区域
Image($r(item))
.width(200)
.height(250)
}
.alignSelf(ItemAlign.Center)
.height('100%')
.scale({ x: this.scaleArray[index], y: this.scaleArray[index] })
.animation({
duration: PAGE_DURATION,
curve: Curve.Linear
})
}, (item: string) => item)
}
.displayMode(SwiperDisplayMode.STRETCH)
.displayCount(DISPLAY_COUNT)
.width('100%')
.height('100%')
.index(this.currentIndex)
.cachedCount(2)
.indicator(false)
.autoPlay(false)
.loop(true)
.duration(SWIPER_DURATION)
.itemSpace(0)
.nextMargin(10)
.prevMargin(180)
.curve(Curve.Linear)
.onChange((index) => {
this.currentIndex = index
// 设置当前index缩放值为最大值
this.scaleArray[this.currentIndex] = MAX_SCALE;
if (this.currentIndex == 0) {
// 当前index=0时,设置上一张图片的缩放值
this.scaleArray[this.scaleArray.length - 1] = MIN_SCALE
} else {
// 当前index不为0时,设置上一张图片的缩放值
this.scaleArray[this.currentIndex -1] = MIN_SCALE
}

if (this.currentIndex == this.scaleArray.length - 1) {
// 当index为最后一张图片时,设置下一张图片的缩放值
this.scaleArray[0] = MIN_SCALE
} else {
// 当index不为最后一张时,设置下一张图片的缩放值
this.scaleArray[this.currentIndex + 1] = MIN_SCALE
}
})
.customContentTransition({
// 页面移除视窗时超时1000ms下渲染树
timeout: 1000,
// 对视窗内所有页面逐帧回调transition,在回调中修改opacity、scale、translate、zIndex等属性值,实现自定义动画
transition: (proxy: SwiperContentTransitionProxy) => {
if (this.startSwiperOffset == 0) {
this.startSwiperOffset = proxy.position * proxy.mainAxisLength;
}
let offset: number = proxy.position * proxy.mainAxisLength // 移动距离
let currentScale: number = this.scaleArray[proxy.index] // 当前index缩放值

let nextIndex = (proxy.index == this.scaleArray.length - 2 ? 0 : proxy.index + 1) // 计算下一个index
let preIndex = (proxy.index == 0 ? this.scaleArray.length - 2 : proxy.index - 1) // 计算上一个index

let nextScale: number = this.scaleArray[nextIndex] // 下一个index缩放值
let preScale: number = this.scaleArray[preIndex] // 上一个index缩放值
// 滑动距离
let distance = Math.abs(offset)
currentScale = MAX_SCALE - Math.min(distance / DRAGGING_MAX_DISTANCE, MAX_SCALE - MIN_SCALE) // 当前缩放值
if (this.startSwiperOffset > offset) {
nextScale = MIN_SCALE + Math.min(distance / DRAGGING_MAX_DISTANCE, MAX_SCALE - MIN_SCALE)
preScale = MIN_SCALE
} else {
preScale = MIN_SCALE + Math.min(distance / DRAGGING_MAX_DISTANCE, MAX_SCALE - MIN_SCALE)
nextScale = MIN_SCALE
}
this.scaleArray[this.currentIndex] = currentScale // 当前index缩放值
this.scaleArray[nextIndex] = nextScale // 下一个index缩放值
this.scaleArray[preIndex] = preScale // 上一个index缩放值
}
})
}
.width('100%')
.height(250)
}
}

常见FAQ

Q:如何在Swiper滑动过程中实时获取其子组件的坐标?

A:使用Swiper的onGestureSwipe方法,可以实现监听滑动的距离以及过程。

.onGestureSwipe((index: number, extraInfo: SwiperAnimationEvent) => {
console.info('index: ' + index);
console.info('current offset: ' + extraInfo.currentOffset);
})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值