HarmonyOS List动画效果

如果我想实现List列表组件 新增 移除 条目时,增加动画效果,请问需要怎么实现呢?有没有什么demo可以参考呢?

HarmonyOS
2天前
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
put_get

index.ets

import { LazyDataSource } from '../model/LazyDataSource'
@Observed
class Item {
  constructor(data: string
  ) {
    this.data = data
  }
  public data: string
  public inDeleteArea: boolean = false
  public offsetY: number = 0
}
@Component
struct CardItem {
  @ObjectLink item: Item
  build() {
    Text('item' + this.item.data)
      .fontSize(16)
      .width("100%")
      .height(74)
      .fontSize(16)
      .textAlign(TextAlign.Center)
      .borderRadius(10)
      .backgroundColor(0xFFFFFF)
      .translate({ y: this.item.offsetY })
  }
}
@Component
struct MySwipeActionItem2 {
  @ObjectLink item: Item
  @State angle: number = 0
  @State angle2: number = 0
  @State scaleDelItem: number = 1
  private selfSize: SizeResult = { width: 0, height: 0 }
  private ITEM_SIZE: number = 40
  private ITEM_SPACE: number = 8
  private SELF_HEIGHT: number = 74
  private SELF_PADDING: number = 12
  private onDelete: () => void = () => {
  }

  @Styles
  buttonStyle() {
    .borderRadius(this.ITEM_SIZE / 2)
    .height(this.ITEM_SIZE)
    .width(this.ITEM_SIZE)
  }
  @Builder
  builder() {
    Stack(){
      Image($r('sys.media.ohos_ic_public_share'))
        .width('70%')
        .height('70%')
        .fillColor($r('sys.color.ohos_id_color_primary_contrary'))
    }
    .buttonStyle()
    .backgroundColor($r('sys.color.ohos_id_color_palette11'))
    .flexGrow(this.item.inDeleteArea ? 0 : 1)
    .visibility(this.item.inDeleteArea ? Visibility.Hidden : Visibility.Visible)
    Stack() {
      Image($r('app.media.app_icon'))
        .width('70%')
        .height('70%')
        .rotate({
          x: 0,
          y: 0,
          z: 1,
          centerX: 20,
          centerY: 4,
          angle: this.angle
        })
        .animation({ curve: Curve.Friction, duration: 200 })
      Image($r('app.media.app_icon'))
        .width('70%')
        .height('70%')
        .rotate({
          x: 0,
          y: 0,
          z: 1,
          centerX: 30,
          centerY: 13,
          angle: -this.angle2
        })
        .animation({ curve: Curve.Friction, duration: 200 })
    }
    .buttonStyle()
    .backgroundColor($r('sys.color.ohos_id_color_warning'))
    .scale({ x: this.scaleDelItem, y: this.scaleDelItem })
    .animation({ curve: Curve.Friction, duration: 200 })
    .onClick(() => {
      if (this.onDelete) {
        this.onDelete()
      }
    })
  }
  build() {
    this.builder()
  }
  onMeasureSize(selfLayoutInfo: GeometryInfo, children: Array<Measurable>, constraint: ConstraintSizeOptions): SizeResult {
    children.forEach((value) => {
      value.measure({ maxWidth: this.ITEM_SIZE, maxHeight: this.ITEM_SIZE, minWidth: 0, minHeight: 0 })
    })
    let width: number = (this.ITEM_SIZE + this.ITEM_SPACE) * children.length + this.SELF_PADDING * 2
    width = Math.max(width, constraint.minWidth as number)
    width = Math.min(width, constraint.maxWidth as number)
    this.selfSize = { height: this.SELF_HEIGHT, width: width }
    return this.selfSize
  }
  onPlaceChildren(selfLayoutInfo: GeometryInfo, children: Array<Layoutable>, constraint: ConstraintSizeOptions): void {
    let a = 0
    let y = (this.selfSize.height - this.ITEM_SIZE) / 2
    let w = this.selfSize.width - this.SELF_PADDING * 2 - (this.ITEM_SIZE + this.ITEM_SPACE) * children.length
    if (!this.item.inDeleteArea) {
      let dw = w * 5 / 6 / children.length;
      let x0 = this.SELF_PADDING + this.ITEM_SPACE / 2 + dw
      let k = this.ITEM_SIZE + this.ITEM_SPACE + dw
      children.forEach((value, index) => {
        value.layout({ x: x0 + index * k, y: y })
        a = x0 + index * k
      })
      this.angle = (this.selfSize.width - 120) / 56 * 12
      this.angle2 = 0;
      this.scaleDelItem = 1 + ((this.selfSize.width - 120) / 56) / 20
    } else if (children.length > 1) {
      let k = ((this.selfSize.width - this.ITEM_SIZE - this.ITEM_SPACE) / 2 - this.SELF_PADDING) / (children.length - 1);
      let x0 = this.SELF_PADDING + (k - this.ITEM_SIZE) / 2
      children.forEach((value, index) => {
        value.layout({ x: x0 + index * k, y: y })
      })
      this.angle = 17
      this.angle2 = 10
      this.scaleDelItem = 1.10
    } else {
      children.forEach((value, index) => {
        value.layout({ x: (this.selfSize.width - this.ITEM_SIZE) / 2, y: y })
      })
      this.angle = 17
      this.angle2 = 10
      this.scaleDelItem = 1.10
    }
  }
}
@Entry
@Component
struct ListItemExample2 {
  @State dataSource: LazyDataSource<Item> = new LazyDataSource()
  @State inDeleteArea: Array<Boolean> = []
  @State scaleItem: number = 0.75
  @State offsetY: number = 0
  @State EndOffset: number = 0
  @State CurrentIndex: string = 'undefined'
  private scroller: Scroller = new Scroller()
  onDelete(item: Item) {
    this.EndOffset = 0
    let index = this.dataSource.indexOf(item)
    animateTo({
      duration: 0,
    }, () => {
      this.scaleItem = 0.75
      this.dataSource.deleteItem(item)
    })
    for (let i = 0; i < this.dataSource.totalCount(); ++i) {
      const element = this.dataSource.getData(i)
      if (i < index) {
        element.offsetY = this.EndOffset
      } else {
        element.offsetY = 60 + this.EndOffset
      }
    }
    for (let i = 0; i < this.dataSource.totalCount(); ++i) {
      const element = this.dataSource.getData(i)
      animateTo({
        curve: Curve.Friction,
        duration: 350,
        delay: Math.abs(index - i) * 16.67,
      }, () => {
        element.offsetY = 0
      })
    }
  }
  onAdd(item: Item){
    animateTo({
      duration: 0,
    }, () => {
      this.scaleItem = 0.75
      this.dataSource.addItem(item)
    })
    for (let i = 0; i < this.dataSource.totalCount(); ++i) {
      const element = this.dataSource.getData(i)
      if (i <= 0) {
        element.offsetY = this.EndOffset
      } else {
        element.offsetY = -60
      }
    }
    for (let i = 0; i < this.dataSource.totalCount(); ++i) {
      const element = this.dataSource.getData(i)
      animateTo({
        curve: Curve.Friction,
        duration: 350,
        delay: Math.abs(0 - i) * 16.67,
      }, () => {
        element.offsetY = 0
      })
    }
  }
  @Builder
  itemEnd(item: Item) {
    MySwipeActionItem2({ item: item as Item, onDelete: () => {
      this.onDelete(item)
    } })
  }
  addItem(item:Item){

    this.onAdd(item)
  }
  build() {
    Column() {
      Button('添加').onClick(()=>{
        this.addItem(new Item('新增的'))
      })
      List({ space: 10, scroller: this.scroller }) {
        LazyForEach(this.dataSource, (item: Item) => {
          ListItem() {
            CardItem({ item: item })
          }
          // .clip(true)
          .zIndex(Number(item.data))
          .transition(TransitionEffect.asymmetric(
            TransitionEffect.IDENTITY,
            TransitionEffect.scale({ x: this.scaleItem, y: this.scaleItem })
              .animation({ duration: 400, curve: Curve.Friction })
              .combine(TransitionEffect.OPACITY.animation({ duration: 100, curve: Curve.Sharp, delay: 100 }))
          ))
          .swipeAction({
            end: {
              builder: this.itemEnd(item),
              onAction: () => {
                this.onDelete(item)
                console.log("offset:" + this.scroller.currentOffset().yOffset)
              },
              actionAreaDistance: 56,
              onEnterActionArea: () => {
                animateTo({ duration: 200, curve: Curve.Friction }, () => {
                  item.inDeleteArea = true;
                })
              },
              onExitActionArea: () => {
                animateTo({ duration: 200, curve: Curve.Friction }, () => {
                  item.inDeleteArea = false;
                })
              }
            }
          })
        }, (item: Item) => item.data)
      }
      .scrollBar(BarState.Off)
      .onScroll((scrollOffset: number, scrollState: ScrollState) => {
        this.EndOffset = scrollOffset
      })
    }
    // .padding(10)
    .backgroundColor('#0D182431')
    .width('100%')
    .height('100%')
  }
  aboutToAppear() {
    for (let i = 0; i < 50; i++) {
      this.dataSource.pushItem(new Item(i.toString()))
    }
  }
}

LazyDataSource.ets

export class LazyDataSource<T> implements IDataSource {
  private elements: T[]
  private listeners: Set<DataChangeListener>
  constructor(elements: T[] = []) {
    this.elements = elements
    this.listeners = new Set()
  }
  public totalCount(): number {
    return this.elements.length
  }
  public getData(index: number): T {
    return this.elements[index]
  }
  public indexOf(item: T): number {
    return this.elements.indexOf(item)
  }
  public pinItem(item: T, index: number): void {
    this.elements.splice(index, 1)
    this.elements.unshift(item)
    this.listeners.forEach(listener => listener.onDataReloaded())
  }
  public addItem(item: T) {
    this.elements.unshift(item)
    this.listeners.forEach(listener => listener.onDataAdd(0))
  }
  public pushItem(item: T) {
    this.elements.push(item)
    this.listeners.forEach(listener => listener.onDataAdd(this.elements.length - 1))
  }
  public insertItem(item: T, index: number) {
    this.elements.splice(index, 0, item)
    this.listeners.forEach(listener => listener.onDataAdd(index))
  }
  public deleteItem(item: T): void {
    const index = this.elements.indexOf(item)
    if (index < 0) {
      return
    }
    this.elements.splice(index, 1)
    this.listeners.forEach(listener => listener.onDataDelete(index))
  }
  public deleteItemByIndex(index: number): void {
    this.elements.splice(index, 1)
    this.listeners.forEach(listener => listener.onDataDelete(index))
  }
  public registerDataChangeListener(listener: DataChangeListener): void {
    this.listeners.add(listener)
  }
  public unregisterDataChangeListener(listener: DataChangeListener): void {
    this.listeners.delete(listener)
  }
}
分享
微博
QQ
微信
回复
1天前
相关问题
如何实现list的折叠动画效果
536浏览 • 1回复 待解决
HarmonyOS 动画效果+手势
67浏览 • 1回复 待解决
HarmonyOS 列表动画效果
10浏览 • 1回复 待解决
HarmonyOS list编辑移动效果
259浏览 • 1回复 待解决
HarmonyOS 旋转动画效果
38浏览 • 1回复 待解决
HarmonyOS 动画效果实现
38浏览 • 1回复 待解决
HarmonyOS .scale没有动画效果
80浏览 • 1回复 待解决
属性动画如何实现宽高动画效果
2070浏览 • 1回复 待解决
如何实现动画转场效果
876浏览 • 1回复 待解决
HarmonyOS grid拖拽和增删动画效果
32浏览 • 1回复 待解决
HarmonyOS 拍摄录制的动画效果
64浏览 • 1回复 待解决
HarmonyOS 实现按钮长按动画效果
86浏览 • 1回复 待解决
HarmonyOS list中item的交互效果处理
429浏览 • 1回复 待解决
如何将List的回弹效果改为阴影效果
477浏览 • 1回复 待解决
panGesture结合动画实现fling效果
923浏览 • 1回复 待解决
Tabs 出现/消失转场动画效果
409浏览 • 1回复 待解决
文字动画效果如何实现
1930浏览 • 0回复 待解决
HarmonyOS grid拖拽效果如何添加动画
31浏览 • 1回复 待解决
HarmonyOS list的item支持托动效果吗?
281浏览 • 1回复 待解决