swiper实现滑动放大缩小效果

本文详细介绍了在小程序中实现轮播图的全过程,包括wxml布局、wxss样式及js交互逻辑,通过swiper组件和图片缩放动画效果,提升用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

wxml

<swiper class="swiper-block" bindchange="swiperChange" previous-margin="90rpx" next-margin="90rpx" current="0">

<block wx:for="{{imgUrls}}" wx:index="{{index}}">

<swiper-item class="swiper-item">

<image mode="aspectFill" src="{{item}}" class="slide-image {{currentIndex == index ? 'active' : ''}}" />

</swiper-item>

</block>

</swiper>

 

wxss

.swiper-block {

height: 480rpx;

width: 100%;

}

 

.swiper-item{

display: flex;

flex-direction: column;

justify-content: center;

align-items: flex-start;

overflow: unset;

}

 

.slide-image{

height: 320rpx;

width: 520rpx;

border-radius: 9rpx;

box-shadow: 0px 0px 30rpx rgba(0, 0, 0, 0.2);

margin: 0rpx 30rpx;

z-index: 1;

}

 

.active{

transform: scale(1.14);

transition: all .2s ease-in 0s;

z-index: 20;

}

 

js

Page({

data: {

currentIndex: 0,

imgUrls: [

'/image/b2.jpg',

'/image/b1.jpg',

'/image/b3.jpg'

]

},

swiperChange: function (e) {

this.setData({

currentIndex: e.detail.current

})

}

})

 

 

<think>我们正在讨论如何在鸿蒙的Swiper组件中使用onGestureSwipe事件来实现滑动放大元素的效果。根据用户之前的问题和我们的回答,我们知道onGestureSwipe事件会在用户滑动时连续触发,提供当前页面的索引和标准化的偏移量(offset,范围[-1,1])。我们可以利用这个偏移量来动态调整元素的缩放比例。思路:1.在Swiper组件上设置onGestureSwipe回调。2.在回调函数中,根据offset的值计算缩放比例(scale)。通常,当offset为0时(即位于页面中心),我们希望元素是最大的(例如1.0),当offset向两边移动时,元素逐渐缩小(例如缩小到0.8)。3.将计算出的缩放比例赋值给需要放大的元素(比如Swiper中的每个子组件)。注意:为了看到明显的效果,我们通常会让当前正在滑动的页面(即当前页)的子元素根据偏移量进行缩放,而相邻的页面也可以根据位置进行适当的缩放。但是,用户要求的是“滑动放大元素”,这里我们可以理解为:在滑动过程中,当前页面(index对应的页面)随着滑动偏移,其上的元素逐渐放大(或者也可以理解为,当手指滑动时,当前页面离开中心,同时我们想让该页面的元素在离开过程中放大?这不太符合常规,常规是离开中心时缩小,进入中心时放大)。更常见的交互是:当前位于中心的页面是最大的,而两边的页面较小。在滑动过程中,随着页面的移动,每个页面的缩放比例根据其距离中心的位置动态变化。因此,我们可能需要计算每个页面相对于中心的位置,然后设置缩放。然而,在onGestureSwipe事件中,我们只能得到当前活动页面的索引和偏移量(这个偏移量是相对于当前活动页面的)。但是,我们可以利用这个偏移量来同时调整相邻页面的缩放。但是,请注意:onGestureSwipe事件只提供当前活动页面的偏移量,并不直接提供其他页面的偏移量。所以,我们通常只对当前活动页面和相邻页面做处理。另一种做法:我们可以将缩放比例与offset关联,但注意offset是标准化的,范围是[-1,1]。我们可以这样设计:-当offset=0时,当前页面处于中心,缩放为1(最大)。-当offset向-1或1移动时,当前页面逐渐离开中心,我们希望它逐渐缩小(例如缩小到0.8)。-同时,相邻的页面会进入中心,那么相邻页面的缩放应该逐渐增大(从0.8增大到1)。但是,在Swiper组件中,我们通常只能通过当前活动页面的offset来推断相邻页面的状态。实际上,在滑动过程中,当前页面和相邻页面是同时移动的,我们可以通过当前页面的offset来计算出相邻页面的偏移情况(因为相邻页面会随着滑动而进入)。然而,由于Swiper组件本身并没有直接提供相邻页面的偏移量,我们可能需要一些技巧。一个简单的实现方式是:我们只处理当前活动页面(index)的缩放,而固定其他页面的缩放。但这样效果可能不够好。更高级的做法:我们可以为每个页面存储一个缩放比例,然后在onGestureSwipe事件中,根据当前活动页面的偏移量,同时更新当前页面和相邻页面的缩放比例。但是,考虑到性能,我们可能只更新当前页面和相邻的两个页面(前一个和后一个)。由于用户的问题中要求的是“滑动放大元素”,我们可以先实现当前活动页面的缩放,然后再考虑相邻页面。下面是一个简单的示例:在滑动过程中,当前活动页面(即手指拖动的页面)的缩放比例根据偏移量的绝对值而变化。当偏移量为0时,缩放为1;当偏移量达到±1时,缩放为0.8。公式:scale=1-0.2*|offset|然后,我们可以将这个缩放比例应用到当前活动页面的元素上。但是,注意:在Swiper中,每个页面都是Swiper的子组件,我们需要在构建每个子组件时,根据当前页面的索引和活动页面的索引以及偏移量来计算该页面的缩放比例。因此,我们需要在组件的状态中存储当前活动页面的索引(currentIndex)和偏移量(offsetValue)。然后在构建每个子组件时,判断该子组件是否是当前活动页面,如果是,则使用上面公式计算缩放;如果不是,则我们可以设定一个默认的缩放(比如0.8),或者也可以根据该页面与当前活动页面的距离来设定。然而,在滑动过程中,相邻页面也会随着滑动而移动,我们也可以为它们计算缩放。例如,当前活动页面是index,那么:-页面index-1:它正在从左边进入中心,它的偏移量可以认为是(offset+1)?或者我们可以根据Swiper滑动规则来推断。-实际上,在Swiper滑动过程中,相邻页面会紧跟着当前页面移动,我们可以认为相邻页面的偏移量是(offset+1)或(offset-1)吗?这是不准确的。由于Swiper组件的onGestureSwipe事件只提供当前活动页面的偏移量,我们很难精确计算其他页面的偏移量。因此,我们可能只对当前活动页面进行缩放变化。另一种思路:使用Swiper的displayMode设置为STRETCH,这样每个页面都会拉伸填充,然后我们通过每个页面的位置变化来设置缩放。但这种方法比较复杂。为了简化,我们先实现当前活动页面的缩放变化。步骤:1.在组件中定义状态:currentIndex(当前活动页面的索引)和offset(当前偏移量)。2.在Swiper上设置onGestureSwipe回调,更新offset;同时设置onChange回调,当页面切换时更新currentIndex并重置offset(因为onChange触发时,页面已经切换完成,此时偏移量应该为0)。3.在构建Swiper子组件时,判断每个子组件的索引(index):-如果该子组件是当前活动页面(index===this.currentIndex),则使用公式:scale=1-0.2*Math.abs(this.offset)-否则,我们可以设置一个较小的缩放比例(比如0.8),或者保持不变。但是,注意:在滑动过程中,当前活动页面会变化吗?不会,直到滑动结束并切换到新页面,currentIndex才会改变。所以,在滑动过程中,currentIndex始终是滑动开始时的页面索引。因此,在滑动过程中,我们只改变当前活动页面(currentIndex对应的页面)的缩放比例。但是,当滑动到相邻页面时,我们希望相邻页面逐渐放大,而当前页面逐渐缩小。那么,我们就需要同时更新两个页面的缩放比例。为了实现这个效果,我们需要知道相邻页面的索引,并且能够为它们设置缩放比例。我们可以这样做:在onGestureSwipe回调中,我们除了更新当前活动页面的偏移量,还可以计算出相邻页面的“虚拟偏移量”。例如:-当前活动页面索引为currentIndex,偏移量为offset(范围[-1,1])。-那么,下一个页面(currentIndex+1)的“虚拟偏移量”可以计算为:offset-1(因为当当前页面完全滑出时,下一个页面正好进入中心,此时当前页面的offset为1,下一个页面的偏移量就是0)。-同理,上一个页面(currentIndex-1)的“虚拟偏移量”为:offset+1。然后,我们为每个页面设置一个缩放比例,该比例根据其虚拟偏移量的绝对值来计算:scale=1-0.2*Math.abs(virtualOffset)但是,虚拟偏移量的范围可能超出[-1,1],我们只需要关心当前活动页面及其相邻的两个页面(因为其他页面距离太远,缩放比例可以固定为最小值)。因此,我们可以:-为每个页面的缩放比例创建一个数组(scales),初始值都是0.8。-在onGestureSwipe事件中,我们更新这个数组:scales[currentIndex]=1-0.2*Math.abs(offset);if(currentIndex>0){scales[currentIndex-1]=1-0.2*Math.abs(offset+1);}if(currentIndex<totalCount-1){scales[currentIndex+1]=1-0.2*Math.abs(offset-1);}-然后,在构建每个子组件时,使用该页面对应的scales[index]作为缩放比例。注意:在onChange事件中,当页面切换后,我们需要重置scales数组(将当前活动页面的缩放设为1,其他设为0.8?),或者我们可以不再重置,因为onGestureSwipe事件在滑动结束后不会再触发,而下次滑动开始时又会重新计算。但是,为了避免复杂,我们可以先尝试只改变当前活动页面的缩放,如果用户需要相邻页面也变化,再扩展。根据用户要求,我们先给出一个简单示例:只改变当前活动页面的缩放。示例代码:注意:需要设置displayMode为STRETCH,否则onGestureSwipe事件可能不会触发。步骤:1.定义状态:currentIndex和offsetValue。2.在Swiper的onGestureSwipe事件中更新offsetValue。3.在Swiper的onChange事件中更新currentIndex,并重置offsetValue为0(因为切换后当前页面应该位于中心)。4.在构建Swiper子组件时,根据每个子组件的索引和currentIndex判断,如果是当前活动页面,则使用1-0.2*Math.abs(offsetValue)作为缩放比例;否则使用0.8。但是,注意:在滑动过程中,相邻页面会进入,但我们没有改变它们的缩放,所以它们会保持0.8。这样效果是:只有当前页面在滑动时会缩放,其他页面固定。下面我们写代码:注意:在构建子组件时,我们使用ForEach,并为每个子组件设置scale属性,该属性根据当前页面的索引和状态中的currentIndex以及offsetValue来计算。</think>以下是如何在鸿蒙 Swiper 组件中通过 `onGestureSwipe` 事件实现滑动放大元素的完整示例代码: ```typescript @Entry @Component struct SwiperWithScaleEffect { @State currentScale: number[] = [1.0, 0.9, 0.9] // 初始缩放比例 @State currentIndex: number = 0 // 当前激活页索引 private totalItems: number = 3 // 总页数 // 更新缩放比例 updateScales(index: number, offset: number) { // 计算主页面缩放(1.0 ~ 1.2范围) const mainScale = 1.0 + Math.abs(offset) * 0.2 // 计算相邻页面缩放(0.8 ~ 0.9范围) const neighborScale = 0.9 - Math.abs(offset) * 0.1 // 创建新的缩放数组 let newScales = new Array(this.totalItems).fill(0.8) // 设置当前页缩放 newScales[index] = mainScale // 设置相邻页缩放 if (index > 0) { newScales[index - 1] = neighborScale } if (index < this.totalItems - 1) { newScales[index + 1] = neighborScale } this.currentScale = newScales } build() { Column() { // 带缩放效果Swiper Swiper() { ForEach([0, 1, 2], (index) => { Column() { Text(`页面 ${index + 1}`) .fontSize(24) .fontWeight(FontWeight.Bold) .fontColor(Color.White) Image($r('app.media.bg' + index)) .width('100%') .height(180) .borderRadius(16) } .scale({ x: this.currentScale[index], y: this.currentScale[index] }) .opacity(this.currentScale[index] > 0.85 ? 1 : 0.8) // 根据缩放调整透明度 .backgroundColor(Color.Blue) .padding(20) .width('90%') .justifyContent(FlexAlign.Center) }) } .onGestureSwipe((index, offset) => { // 实时更新缩放比例 this.updateScales(index, offset) }) .onChange((index: number) => { // 页面切换后重置缩放比例 this.currentIndex = index this.currentScale = this.currentScale.map((_, i) => i === index ? 1.0 : 0.9) }) .displayMode(SwiperDisplayMode.STRETCH) // 必须设置此项 .indicator(true) // 显示指示器 .loop(false) // 禁用循环 .height(300) } .width('100%') .height('100%') .padding(20) .backgroundColor('#F0F0F0') } } ``` ### 关键实现原理: 1. **缩放效果计算**: ```typescript const mainScale = 1.0 + Math.abs(offset) * 0.2 // 当前页放大 const neighborScale = 0.9 - Math.abs(offset) * 0.1 // 相邻页缩小 ``` 2. **动态更新策略**: - 当前页根据偏移量 `offset` 从 1.0 放大到 1.2 - 相邻页从 0.9 缩小到 0.8 - 其他页面保持默认 0.8 的缩放 3. **视觉增强处理**: ```typescript .opacity(this.currentScale[index] > 0.85 ? 1 : 0.8) ``` 根据缩放比例动态调整透明度,增强立体感 ### 效果说明: - **滑动过程中**:当前页面会随滑动手势动态放大,相邻页面缩小 - **页面切换后**:新页面放大到 1.0,旧页面缩小到 0.9 - **视觉层次**:通过缩放+透明度变化创建3D卡片效果[^1] ### 优化建议: 1. **性能优化**: ```typescript // 使用translateZ提升GPU渲染性能 .transform({ translateZ: 10 }) ``` 2. **复杂动画组合**: ```typescript .onGestureSwipe((index, offset) => { this.updateScales(index, offset) // 同时旋转效果(可选) const rotation = offset * 5 this.currentRotation[index] = rotation }) ``` 3. **边界处理**: ```typescript // 在页面边缘时禁用过度缩放 if ((index === 0 && offset < 0) || (index === this.totalItems - 1 && offset > 0)) { return } ``` 这种实现方式利用了 `onGestureSwipe` 提供的实时偏移量数据,结合缩放变换创造出流畅的交互效果,适用于卡片式UI、图片浏览器等场景[^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值