|
|
@@ -0,0 +1,243 @@
|
|
|
+
|
|
|
+@Component
|
|
|
+export struct YTSwiper {
|
|
|
+ //----整体swiper的宽高
|
|
|
+ @Prop swiperWidth: Length = '100%'
|
|
|
+ @Prop swiperHeight: Length = 200
|
|
|
+ @Prop swiperBackgroundColor: ResourceColor = Color.Yellow
|
|
|
+ @Prop swiperBorderRadius: Length | BorderRadiuses | LocalizedBorderRadiuses = 0
|
|
|
+ //---渲染的img数组
|
|
|
+ @Prop @Watch('imageListChange') imageList: ResourceStr[] = []
|
|
|
+ //---这个暂时还没扩展 只支持传7个
|
|
|
+ @Prop displayNumber: number = 7
|
|
|
+ //图片宽高圆角
|
|
|
+ @Prop imgWidth: Length = 109
|
|
|
+ @Prop imgHeight: Length = 153
|
|
|
+ @Prop imgBorderRadius: Length = 5
|
|
|
+ //获取当前选择的图片
|
|
|
+ @Require getCurrentImg: (_: ResourceStr) => void
|
|
|
+ //
|
|
|
+ @State private currentIndex: number = 0
|
|
|
+
|
|
|
+ aboutToAppear() {
|
|
|
+ this.getCurrentImg(this.imageList[this.currentIndex])
|
|
|
+ }
|
|
|
+
|
|
|
+ imageListChange() {
|
|
|
+ this.getCurrentImg(this.imageList[this.currentIndex])
|
|
|
+ }
|
|
|
+
|
|
|
+ build() {
|
|
|
+ RelativeContainer() {
|
|
|
+ // 创建一个扩展的列表,包含原始列表和前几项的副本,以支持视觉上的循环效果
|
|
|
+ ForEach(this.getExtendedList(), (item: ResourceStr, index: number) => {
|
|
|
+ Column() {
|
|
|
+ Image(item)
|
|
|
+ .width(109)
|
|
|
+ .height(153)
|
|
|
+ .borderRadius(12)
|
|
|
+ }
|
|
|
+ .scale({ x: this.calcScale(index), y: this.calcScale(index) })
|
|
|
+ .onClick(() => {
|
|
|
+ this.getUIContext().animateTo({ duration: 300 }, () => {
|
|
|
+ // 更新 currentIndex,确保其在原始列表范围内循环
|
|
|
+ this.currentIndex = index % this.imageList.length
|
|
|
+ })
|
|
|
+ this.getCurrentImg(this.imageList[this.currentIndex])
|
|
|
+ })
|
|
|
+ .alignRules(this.calcAlignRules(index))
|
|
|
+ .id(index.toString())
|
|
|
+ .zIndex(this.calcZIndex(index))
|
|
|
+
|
|
|
+ }, (_: ResourceStr, index: number) => index.toString() + _.toString()) // 提供唯一的键
|
|
|
+ }
|
|
|
+ .width(this.swiperWidth)
|
|
|
+ .height(this.swiperHeight)
|
|
|
+ .borderRadius(this.swiperBorderRadius)
|
|
|
+ .backgroundColor(this.swiperBackgroundColor)
|
|
|
+ .gesture(GestureGroup(GestureMode.Exclusive,
|
|
|
+ PanGesture(new PanGestureOptions({ direction: PanDirection.Horizontal, distance: 100 }))
|
|
|
+ .onActionEnd((event: GestureEvent) => {
|
|
|
+ this.getUIContext().animateTo({ duration: 300 }, () => {
|
|
|
+ if (event.offsetX > 100) {
|
|
|
+ // 向右滑动,显示前一张图片
|
|
|
+ this.currentIndex = this.modulo(this.currentIndex - 1, this.imageList.length);
|
|
|
+ } else if (event.offsetX < -100) {
|
|
|
+ // 向左滑动,显示后一张图片
|
|
|
+ this.currentIndex = this.modulo(this.currentIndex + 1, this.imageList.length);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.getCurrentImg(this.imageList[this.currentIndex])
|
|
|
+ })
|
|
|
+ ))
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 正确处理负数的取模运算
|
|
|
+ * @param dividend 被除数
|
|
|
+ * @param divisor 除数
|
|
|
+ * @returns 正确的模运算结果
|
|
|
+ */
|
|
|
+ private modulo(dividend: number, divisor: number): number {
|
|
|
+ return ((dividend % divisor) + divisor) % divisor;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建一个扩展的列表,用于视觉上的循环展示
|
|
|
+ * 在原始列表后追加前几项,这样在视觉上可以实现无缝循环
|
|
|
+ */
|
|
|
+ private getExtendedList(): ResourceStr[] {
|
|
|
+ // 确保显示数量为奇数,且至少为3
|
|
|
+ const displayCount = Math.max(3, this.displayNumber % 2 === 0 ? this.displayNumber + 1 : this.displayNumber);
|
|
|
+
|
|
|
+ // 计算需要扩展的数量(一半)
|
|
|
+ const extendCount = Math.floor(displayCount / 2);
|
|
|
+
|
|
|
+ // 实际应用中可能需要更复杂的逻辑来处理前后缓冲
|
|
|
+ return [...this.imageList, ...this.imageList.slice(0, extendCount)];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算每个项目相对于当前中心项目的对齐规则
|
|
|
+ * @param index 当前项目的索引(在扩展列表中的位置)
|
|
|
+ * @returns 对齐规则对象
|
|
|
+ */
|
|
|
+ private calcAlignRules(index: number): AlignRuleOption {
|
|
|
+ // 将索引映射回原始列表范围,以便计算相对位置
|
|
|
+ const actualIndex = index % this.imageList.length
|
|
|
+ const currentActualIndex = this.currentIndex % this.imageList.length
|
|
|
+
|
|
|
+ // 计算相对位置
|
|
|
+ let relativePosition = actualIndex - currentActualIndex;
|
|
|
+
|
|
|
+ // 处理循环情况
|
|
|
+ if (relativePosition > this.imageList.length / 2) {
|
|
|
+ relativePosition -= this.imageList.length;
|
|
|
+ } else if (relativePosition < -this.imageList.length / 2) {
|
|
|
+ relativePosition += this.imageList.length;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据相对位置设置对齐规则
|
|
|
+ if (relativePosition === 0) {
|
|
|
+ // 中心图片
|
|
|
+ return {
|
|
|
+ center: { anchor: "__container__", align: VerticalAlign.Center },
|
|
|
+ middle: { anchor: "__container__", align: HorizontalAlign.Center },
|
|
|
+ }
|
|
|
+ } else if (relativePosition === -1) {
|
|
|
+ // 左侧第一张
|
|
|
+ return {
|
|
|
+ center: { anchor: "__container__", align: VerticalAlign.Center },
|
|
|
+ middle: { anchor: this.currentIndex.toString(), align: HorizontalAlign.Start },
|
|
|
+ }
|
|
|
+ } else if (relativePosition === 1) {
|
|
|
+ // 右侧第一张
|
|
|
+ return {
|
|
|
+ center: { anchor: "__container__", align: VerticalAlign.Center },
|
|
|
+ middle: { anchor: this.currentIndex.toString(), align: HorizontalAlign.End },
|
|
|
+ }
|
|
|
+ } else if (relativePosition === -2) {
|
|
|
+ // 左侧第二张
|
|
|
+ return {
|
|
|
+ center: { anchor: "__container__", align: VerticalAlign.Center },
|
|
|
+ middle: {
|
|
|
+ anchor: this.modulo(this.currentIndex - 1, this.imageList.length).toString(),
|
|
|
+ align: HorizontalAlign.Start
|
|
|
+ },
|
|
|
+ }
|
|
|
+ } else if (relativePosition === 2) {
|
|
|
+ // 右侧第二张
|
|
|
+ return {
|
|
|
+ center: { anchor: "__container__", align: VerticalAlign.Center },
|
|
|
+ middle: {
|
|
|
+ anchor: this.modulo(this.currentIndex + 1, this.imageList.length).toString(),
|
|
|
+ align: HorizontalAlign.End
|
|
|
+ },
|
|
|
+ }
|
|
|
+ } else if (relativePosition === -3) {
|
|
|
+ return {
|
|
|
+ center: { anchor: "__container__", align: VerticalAlign.Center },
|
|
|
+ middle: {
|
|
|
+ anchor: this.modulo(this.currentIndex - 2, this.imageList.length).toString(),
|
|
|
+ align: HorizontalAlign.Start
|
|
|
+ },
|
|
|
+ }
|
|
|
+ } else if (relativePosition === 3) {
|
|
|
+ return {
|
|
|
+ center: { anchor: "__container__", align: VerticalAlign.Center },
|
|
|
+ middle: {
|
|
|
+ anchor: this.modulo(this.currentIndex + 2, this.imageList.length).toString(),
|
|
|
+ align: HorizontalAlign.End
|
|
|
+ },
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 其他位置的图片,根据实际需求可以进一步扩展
|
|
|
+ return {
|
|
|
+ center: { anchor: "__container__", align: VerticalAlign.Center },
|
|
|
+ middle: { anchor: "__container__", align: HorizontalAlign.Center },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算每个项目的 Z 轴层级
|
|
|
+ * @param index 当前项目的索引(在扩展列表中的位置)
|
|
|
+ * @returns Z 轴层级值
|
|
|
+ */
|
|
|
+ private calcZIndex(index: number): number {
|
|
|
+ const actualIndex = index % this.imageList.length
|
|
|
+ const currentActualIndex = this.currentIndex % this.imageList.length
|
|
|
+
|
|
|
+ // 计算相对位置
|
|
|
+ let relativePosition = Math.abs(actualIndex - currentActualIndex);
|
|
|
+
|
|
|
+ // 处理循环情况
|
|
|
+ if (relativePosition > this.imageList.length / 2) {
|
|
|
+ relativePosition = this.imageList.length - relativePosition;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据相对位置设置 zIndex
|
|
|
+ if (relativePosition === 0) {
|
|
|
+ return 999; // 中心图片
|
|
|
+ } else if (relativePosition === 1) {
|
|
|
+ return 500; // 第一层图片
|
|
|
+ } else if (relativePosition === 2) {
|
|
|
+ return 200; // 第二层图片
|
|
|
+ } else {
|
|
|
+ return 1; // 其他图片
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据索引差值计算缩放比例
|
|
|
+ * @param index 当前项目的索引(在扩展列表中的位置)
|
|
|
+ * @returns 缩放比例值
|
|
|
+ */
|
|
|
+ private calcScale(index: number): number {
|
|
|
+ const actualIndex = index % this.imageList.length
|
|
|
+ const currentActualIndex = this.currentIndex % this.imageList.length
|
|
|
+
|
|
|
+ // 计算索引差值,考虑循环情况
|
|
|
+ let diff = actualIndex - currentActualIndex
|
|
|
+
|
|
|
+ // 处理循环边界情况
|
|
|
+ if (diff > this.imageList.length / 2) {
|
|
|
+ diff -= this.imageList.length
|
|
|
+ } else if (diff < -this.imageList.length / 2) {
|
|
|
+ diff += this.imageList.length
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据差值设置缩放比例
|
|
|
+ const absDiff = Math.abs(diff);
|
|
|
+
|
|
|
+ if (absDiff === 0) {
|
|
|
+ return 1.0; // 中心图片
|
|
|
+ } else if (absDiff === 1) {
|
|
|
+ return 0.83; // 第一层图片
|
|
|
+ } else if (absDiff === 2) {
|
|
|
+ return 0.64; // 第二层图片
|
|
|
+ } else {
|
|
|
+ return 0.49; // 更远的图片使用最小缩放
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|