实现思路:
-
给Swiper组件内的子组件设置旋转属性rotate。
Stack() {
this.swiperItemSlotParam(item);
}
// 设置组件旋转
.rotate({
x: 0,
y: 1,
z: 0,
angle: this.angleList[index],
centerX: this.centerXList[index],
centerY: '50%',
centerZ: 0,
perspective: 0
});
-
给Swiper组件设置页面切换动画属性customContentTransition,在页面切换时逐帧触发回调,在回调中设置子组件的rotate属性值。
// 自定义Swiper页面切换动画
.customContentTransition({
// 页面移除视窗时超时1000ms下渲染树
timeout: 1000,
transition: (proxy: SwiperContentTransitionProxy) => {
// 旋转角度
let angle = 0;
console.info('proxy.position===>' + proxy.position);
console.info('proxy.index===>' + proxy.index);
// position为index页面相对于selectedIndex对应页面的起始位置的移动比例,向左移动减小,向右移动增加
if (proxy.position < 0 && proxy.position > -1) {
// 当前页向左滑出或上一页向右滑入
angle = proxy.position * 90;
// 设置index页面的旋转中心轴为右侧边缘
this.centerXList[proxy.index] = '100%';
} else if (proxy.position > 0 && proxy.position < 1) {
// 当前页向右滑出或下一页向左滑入
angle = proxy.position * 90;
// 设置index页面的旋转中心轴为左侧边缘
this.centerXList[proxy.index] = '0%';
} else {
// position小于-1时表示向左完全滑出区域,大于1时表示向右完全滑出区域,重置角度
angle = 0;
}
// 修改index页的旋转角
this.angleList[proxy.index] = angle;
}
});
完整示例代码:
@Component
@Entry
export struct Swiper3D {
// Swiper数据
private swiperList: MySwiperItem[][] = [
[new MySwiperItem('模块1', '#4B48F7'),
new MySwiperItem('模块2', '#46B1E3'),
new MySwiperItem('模块3', '#61CFBE')]
];
build() {
Column() {
// 轮播网格
ForEach(this.swiperList, (item: MySwiperItem[]) => {
Custom3DComponentPage({
items: item,
swiperItemSlotParam: (item: MySwiperItem) => {
this.mySwiperItem(item);
}
});
});
};
}
// 自定义3D立方体旋转轮播项UI内容
@Builder
mySwiperItem(item: MySwiperItem) {
Column() {
Text(item.title).fontSize(24);
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
.backgroundColor(item.colors);
}
}
export class MySwiperItem {
// 标题
title: string;
// 颜色
colors: Color | string;
constructor(title: string, colors: Color | string) {
this.title = title;
this.colors = colors;
}
}
@Component
export struct Custom3DComponentPage {
// --------------------暴露外部属性----------------------------
// 动画持续时间,默认500ms
duration: number = 500;
// 是否自动播放
autoPlay: boolean = false;
// 是否循环播放
loop: boolean = true;
// 轮播数据
items: ESObject[] = [];
// 轮播页插槽参数
@BuilderParam swiperItemSlotParam: (item: ESObject) => void;
// --------------------私有属性----------------------------
// 当前项下标
@State currentIndex: number = 0;
// 旋转角度列表
@State angleList: number[] = [];
// 旋转中心点列表
@State centerXList: Array<number | string> = [];
// 轮播控制器
private swiperController: SwiperController = new SwiperController();
build() {
Swiper(this.swiperController) {
ForEach(this.items, (item: ESObject, index: number) => {
Stack() {
this.swiperItemSlotParam(item);
}
// 设置组件旋转
.rotate({
x: 0,
y: 1,
z: 0,
angle: this.angleList[index],
centerX: this.centerXList[index],
centerY: '50%',
centerZ: 0,
perspective: 0
});
}, ((item: ESObject, index: number) => `${JSON.stringify(item)}_${index}`));
}
.loop(this.loop)
.autoPlay(this.autoPlay)
.duration(this.duration)
.onChange((index: number) => {
this.currentIndex = index;
})
// 自定义Swiper页面切换动画
.customContentTransition({
// 页面移除视窗时超时1000ms下渲染树
timeout: 1000,
transition: (proxy: SwiperContentTransitionProxy) => {
// 旋转角度
let angle = 0;
console.info('proxy.position===>' + proxy.position);
console.info('proxy.index===>' + proxy.index);
// position为index页面相对于selectedIndex对应页面的起始位置的移动比例,向左移动减小,向右移动增加
if (proxy.position < 0 && proxy.position > -1) {
// 当前页向左滑出或上一页向右滑入
angle = proxy.position * 90;
// 设置index页面的旋转中心轴为右侧边缘
this.centerXList[proxy.index] = '100%';
} else if (proxy.position > 0 && proxy.position < 1) {
// 当前页向右滑出或下一页向左滑入
angle = proxy.position * 90;
// 设置index页面的旋转中心轴为左侧边缘
this.centerXList[proxy.index] = '0%';
} else {
// position小于-1时表示向左完全滑出区域,大于1时表示向右完全滑出区域,重置角度
angle = 0;
}
// 修改index页的旋转角
this.angleList[proxy.index] = angle;
}
});
}
}
实现效果: