鸿蒙开发画廊效果

鸿蒙开发画廊效果:

画廊这种效果确实不错,看起来高端大气。在Android那边已经有不少案例了,但是鸿蒙这边还是很少。今天来分享一个。

先看下效果图:

在这里插入图片描述没法弄gif,就是左右可以看到前一张,下一张。
也可以看我b站完整的视频:https://www.bilibili.com/video/BV13eHre7EK9/

实现思路:

Swiper的onGestureSwipe里面算偏移量

部分参考代码:
// 联系:893151960
@Component
export struct CardSwiper {
  // 卡片数据源
  data: BaseDataSource<UserDataBean> = new BaseDataSource()
  // 卡片偏移度列表
  @State private cardsOffset: number[] = [];
  // 屏幕宽度
  private displayWidth: number = 0;
  // Swiper 两侧的偏移量
  private swiperMargin: number = CommonConstants.SWIPER_MARGIN;
  // Swiper 当前索引值
  @State private currentSwiperIndex: number = 0;
  private readonly DEVICESIZE: number = 600; // 依据Navigation的mode属性说明,如使用Auto,窗口宽度>=600vp时,采用Split模式显示;窗口宽度<600vp时,采用Stack模式显示。

  onChange?:(index:number)=>void
  controller: SwiperController = new SwiperController();

  aboutToAppear(): void {
    // 获取屏幕大小,用于后续计算卡片的偏移量
    const displayData: display.Display = display.getDefaultDisplaySync();
    this.displayWidth = px2vp(displayData.width);
    if ((display.isFoldable() && display.getFoldStatus() === display.FoldStatus.FOLD_STATUS_EXPANDED) || this.displayWidth >= this.DEVICESIZE) {
      this.displayWidth = px2vp(displayData.width) / 2;
    }
    // 计算当前卡片及关联卡片的偏移量
    this.calculateOffset(0);

  }



  build() {
    Column() {
      Swiper(this.controller) {
        LazyForEach(this.data, (item: UserDataBean, index: number) => {
          CardComponent({
            userInfo: item,
            cardOffset: this.cardsOffset[index],
            cardIndex: index,
            showingCard: this.currentSwiperIndex,
            cardWith:this.displayWidth - 160,
            cardHeight:this.displayWidth - 100,
          })
        },(item: UserDataBean,index:number) => `${index}-${JSON.stringify(item)}`)
      }
      .index($$this.currentSwiperIndex)
      .loop(false)
      .prevMargin(this.swiperMargin)
      .nextMargin(this.swiperMargin)
      .duration(CommonConstants.DURATION)
      .curve(Curve.Friction)
      .onChange((index) => {
        this.calculateOffset(index);
        this.onChange?.(index)
      })
      .indicator(false)
      .onGestureSwipe((index, event) => {
        const currentOffset = event.currentOffset;
        // 获取当前卡片(居中)的原始偏移量
        const maxOffset = this.getMaxOffset(index) / 2;
        // 实时维护卡片的偏移量列表,做到跟手效果
        if (currentOffset < 0) {
          // 向左偏移
          /*
           * 此处计算原理为:按照比例设置卡片的偏移量。
           * 当前卡片居中,向左滑动后将在左边,此时卡片偏移量即为 maxOffset * 2(因为向右对齐)。
           * 所以手指能够滑动的最大距离(this.displayWidth)所带来的偏移量即为 maxOffset。
           * 易得公式:卡片实时偏移量 = (手指滑动长度 / 屏幕宽度) * 卡片最大可偏移量 + 当前偏移量。
           * 之后的计算原理相同,将不再赘述。
           */
          this.cardsOffset[index] = (-currentOffset / this.displayWidth) * maxOffset + maxOffset;
          if (this.isIndexValid(index + 1)) {
            // 下一个卡片的偏移量
            const maxOffset = this.getMaxOffset(index + 1) / 2;
            this.cardsOffset[index + 1] = (-currentOffset / this.displayWidth) * maxOffset;
          }
          if (this.isIndexValid(index - 1)) {
            // 上一个卡片的偏移量
            const maxOffset = this.getMaxOffset(index - 1) / 2;
            this.cardsOffset[index - 1] = (currentOffset / this.displayWidth) * maxOffset + 2 * maxOffset;
          }
        } else if (currentOffset > 0) {
          // 向右滑动
          this.cardsOffset[index] = maxOffset - (currentOffset / this.displayWidth) * maxOffset;
          if (this.isIndexValid(index + 1)) {
            const maxOffset = this.getMaxOffset(index + 1) / 2;
            this.cardsOffset[index + 1] = (currentOffset / this.displayWidth) * maxOffset;
          }
          if (this.isIndexValid(index - 1)) {
            const maxOffset = this.getMaxOffset(index - 1) / 2;
            this.cardsOffset[index - 1] = 2 * maxOffset - (currentOffset / this.displayWidth) * maxOffset;
          }
        }
      })
      .onAnimationStart((index, targetIndex) => {
        this.calculateOffset(targetIndex);
      })
      .height('100%')
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  /**
   * 计算卡片偏移量,并维护偏移量列表。
   * @param targetIndex { number } swiper target card's index.
   */
  calculateOffset(target: number) {
    let left = target - 1;
    let right = target + 1;

    // 计算上一张卡片的偏移值
    if (this.isIndexValid(left)) {
      this.cardsOffset[left] = this.getMaxOffset(left);
    }
    // 计算当前卡片的偏移值
    if (this.isIndexValid(target)) {
      this.cardsOffset[target] = this.getMaxOffset(target) / 2;
    }
    // 下一张片的偏移值
    if (this.isIndexValid(right)) {
      this.cardsOffset[right] = 0;
    }
  }

  /**
   * 检查卡片索引值的合法性。
   * @param index {number} input card's index.
   * @returns true or false.
   */
  isIndexValid(index: number): boolean {
    return index >= 0 && index < this.data.totalCount();
  }

  /**
   * 计算指定卡片的最大偏移量。
   * @param index {number} target card's index.
   * @returns offset value.
   */
  getMaxOffset(index: number): number {
    /*
     * 这里的偏移量指相对容器左侧的值。
     * 计算公式为:屏幕宽度 - Swiper两侧突出的偏移量 - 卡片自身的宽度。
     * 此值即为卡片可偏移的最大值,也就是卡片右对齐的状态值。
     * 如果居中,则将最大偏移量 / 2。
     */
    //let areaWidth = this.data.getAllData()?.[index]?.areaWidth
    return this.displayWidth - (index===this.currentSwiperIndex?this.displayWidth - 110:this.displayWidth - 160) - 2 * this.swiperMargin;
  }
}
现有的demo项目结构图:

在这里插入图片描述有需要源码或者有问题的可私信我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值