HarmonyOS APP里面的搜索的转场效果实现

想要实现一个类似系统设置APP里面的搜索的转场效果,点击搜索框转场到搜索页面,搜索页面的搜索结果还可以进行进一步的push到下一个页面。

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

参考示例如下:

// Navigation.ets
import { AnimateCallback, CustomTransition } from './CustomNavigationUtils'
import { PageOne } from './PageOne'
import { PageTwo } from './PageTwo'
import { PageThree } from './PageThree'

@Entry
@Component
struct NavigationExample {
  @Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack()

  aboutToAppear() {
    if (this.pageInfos === undefined) {
      this.pageInfos = new NavPathStack();
    }
    this.pageInfos.pushPath({ name: 'pageOne' }, false)
  }

  @Builder
  pageMapBuilder(name: string) {
    if (name === 'pageOne') {
      PageOne({ pageId: Date.now() })
    } else if (name === 'pageTwo') {
      PageTwo({ pageId: Date.now() })
    } else if (name === 'pageThree') {
      PageThree({ pageId: Date.now() })
    }
  }

  setCustomAnimation(from: NavContentInfo, to: NavContentInfo, operation: NavigationOperation) {
    if (from.mode === NavDestinationMode.DIALOG || to.mode === NavDestinationMode.DIALOG) {
      return undefined;
    }
    console.log(`current info: ${to.name}, index: ${to.index}, mode: ${to.mode}`);
    console.log(`pre info: ${from.name}, index: ${from.index}, mode: ${from.mode}`);
    console.log(`operation: ${operation}`)
    if (from.index === -1 || to.index === -1) {
      return undefined;
    }
    let customAnimation: NavigationAnimatedTransition = {
      onTransitionEnd: (isSuccess: boolean) => {
        console.log(`current transition result is ${isSuccess}`);
      },
      timeout: 700,
      // 转场开始时系统调用该方法,并传入转场上下文代理对象
      transition: (transitionProxy: NavigationTransitionProxy) => {
        console.log('trigger transition callback');
        // 从封装类CustomTransition中根据子页面的序列获取对应的转场动画回调
        let fromParam: AnimateCallback = CustomTransition.getInstance().getAnimateParam(from.index);
        let toParam: AnimateCallback = CustomTransition.getInstance().getAnimateParam(to.index);
        if (fromParam.start != undefined) {
          fromParam.start(operation === NavigationOperation.PUSH, true);
        }
        if (toParam.start != undefined) {
          toParam.start(operation === NavigationOperation.PUSH, false);
        }
        animateTo({
          duration: toParam.duration, onFinish: () => {
            if (fromParam.onFinish != undefined) {
              fromParam.onFinish(operation === NavigationOperation.PUSH, true);
            }
            if (toParam.onFinish != undefined) {
              toParam.onFinish(operation === NavigationOperation.PUSH, true);
            }
            transitionProxy.finishTransition();
          }
        }, () => {
          if (fromParam.finish != undefined) {
            fromParam.finish(operation === NavigationOperation.PUSH, true);
          }
          if (toParam.finish != undefined) {
            toParam.finish(operation === NavigationOperation.PUSH, false);
          }
        })
      }
    };
    return customAnimation;
  }

  build() {
    Navigation(this.pageInfos) {
    }.title('NavIndex').navDestination(this.pageMapBuilder)
    .hideNavBar(true)
    .customNavContentTransition((from: NavContentInfo, to: NavContentInfo, operation: NavigationOperation) => {
      return this.setCustomAnimation(from, to, operation)
    })
  }
}
// PageOne.ets
import { CustomTransition } from './CustomNavigationUtils'
import { router } from '@kit.ArkUI'

@Component
export struct PageOne {
  @Consume('pageInfos') pageInfos: NavPathStack
  @State y: number = 0
  pageId: number = 0;

  aboutToAppear() {
    this.pageId = this.pageInfos.getAllPathName().length - 1;
    CustomTransition.getInstance().registerNavParam(this.pageId, (isPush: boolean, isExit: boolean) => {
    }, (isPush: boolean, isExit: boolean) => {
    }, (_isPush: boolean, _isExit: boolean) => {
    }, 2000, 0)
  }

  build() {
    NavDestination() {
      Column() {
        Text('设置')
        Column() {
          Button('搜索设置项')
            .onClick(() => {
              this.pageInfos.pushPathByName('pageTwo', null) // 将name指定的NavDestination页面信息入栈
            })

          Column()
            .width('90%')
            .height(500)
            .backgroundColor(Color.Brown)
        }
        .translate({ x: 0, y: this.y })
      }.width('100%').height('100%')
    }
    .title('pageOne')
    .mode(NavDestinationMode.STANDARD)
    .hideTitleBar(true)
    .onShown(() => {
      console.log('pageOne onShown this.y', this.y, JSON.stringify(this.pageInfos))
      animateTo({ duration: 300 }, () => {
        this.y = 0
      })
    })
    .onHidden(() => {
      console.log('pageOne onWillHide')
      this.y = 60
    })
    .onBackPressed(() => {
      // const popDestinationInfo = this.pageInfos.pop() // 弹出路由栈栈顶元素
      router.back()
      // console.log('pop' + '返回值' + JSON.stringify(popDestinationInfo))
      return true
    })
    .onDisAppear(() => {
      CustomTransition.getInstance().unRegisterNavParam(this.pageId)
    })
    .backgroundColor(Color.Gray)
  }
}
// PageTwo.ets
import { CustomTransition } from './CustomNavigationUtils'

@Component
export struct PageTwo {
  @Consume('pageInfos') pageInfos: NavPathStack
  @State x: number = 0
  @State y: number | string = '10%'
  @State showText: string = ''
  @State isTempShow: boolean = false
  pageId: number = 0
  private isBack: boolean = false
  private controller: TextInputController = new TextInputController()

  aboutToAppear() {
    this.pageId = this.pageInfos.getAllPathName().length - 1;
    console.log('this.pageInfos.getAllPathName()', JSON.stringify(this.pageInfos.getAllPathName()))
    CustomTransition.getInstance().registerNavParam(this.pageId, (isPush: boolean, isExit: boolean) => {
    }, (isPush: boolean, isExit: boolean) => {
    }, (_isPush: boolean, _isExit: boolean) => {
    }, 2000, 400)
  }

  build() {
    NavDestination() {
      Column({ space: 20 }) {
        TextInput({ text: $$this.showText, controller: this.controller })
          .backgroundColor('#ffffff')
          .width('90%')
          .height(40)
          .id('username')
          .onChange((value: string) => {
            // 模拟设置项
            if (value) {
              this.isTempShow = true
            } else {
              this.isTempShow = false
            }
          })
        Text('模拟设置')
          .width('90%')
          .height(40)
          .border({ width: 1, radius: 20 })
          .visibility(this.isTempShow ? Visibility.Visible : Visibility.None)
          .onClick(() => {
            this.pageInfos.pushPathByName('pageThree', null) //将name指定的NavDestination页面信息入栈,传递的数据为param
          })
      }
      .width('100%')
      .height('100%')
      .translate({ x: 0, y: this.y })
    }
    .title('动画0')
    .translate({ x: this.x, y: 0 })
    .onWillShow(() => {
      animateTo({ duration: 300 }, () => {
        this.y = 0
      })
    })
    .onWillHide(() => {
      if (this.isBack) {
        return
      }
      animateTo({
        duration: 300, onFinish: () => {
          this.isBack = false
        }
      }, () => {
        this.x = -100
      })
    })
    .onShown(() => {
      animateTo({ duration: 300 }, () => {
        this.x = 0
      })
    })
    .onBackPressed(() => {
      this.isBack = true
      let popDestinationInfo = this.pageInfos.pop() // 弹出路由栈栈顶元素
      console.log('pop' + '返回值' + JSON.stringify(popDestinationInfo))
      return true
    })
    .onDisAppear(() => {
      CustomTransition.getInstance().unRegisterNavParam(this.pageId)
    })
    .backgroundColor(Color.Yellow)
  }
}
// PageThree.ets
import { CustomTransition } from './CustomNavigationUtils'

@Component
export struct PageThree {
  @Consume('pageInfos') pageInfos: NavPathStack
  @State x: number | string = 0
  pageId: number = 0

  aboutToAppear() {
    this.pageId = this.pageInfos.getAllPathName().length - 1;
    console.log('this.pageInfos.getAllPathName()', JSON.stringify(this.pageInfos.getAllPathName()))
    CustomTransition.getInstance().registerNavParam(this.pageId, (isPush: boolean, isExit: boolean) => {
      this.x = isExit ? 0 : isPush ? '100%' : '-100%';
    }, (isPush: boolean, isExit: boolean) => {
      this.x = isExit ? isPush ? '-100%' : '100%' : 0;
    }, (_isPush: boolean, _isExit: boolean) => {
      this.x = 0;
    }, 2000, 400)
  }

  build() {
    NavDestination() {
      Column() {
        Text('动画1')
          .width('80%')
          .height(40)
          .margin(20)
          .textAlign(TextAlign.Center)
      }.width('100%').height('100%')
    }
    .title('动画1')
    .onBackPressed(() => {
      let popDestinationInfo = this.pageInfos.pop() // 弹出路由栈栈顶元素
      console.log('pop' + '返回值' + JSON.stringify(popDestinationInfo))
      return true
    })
    .onDisAppear(() => {
      CustomTransition.getInstance().unRegisterNavParam(this.pageId)
    })
    .translate({ x: this.x, y: 0 })
    .backgroundColor(Color.Yellow)
  }
}
// CustomNavigationUtils.ts
// 自定义接口,用来保存某个页面相关的转场动画回调和参数
export interface AnimateCallback {
  finish: ((isPush: boolean, isExit: boolean) => void | undefined) | undefined;
  start: ((isPush: boolean, isExit: boolean) => void | undefined) | undefined;
  onFinish: ((isPush: boolean, isExit: boolean) => void | undefined) | undefined;
  timeout: (number | undefined) | undefined;
  duration: number;
}

let customTransitionMap: Map<number, AnimateCallback> = new Map()

export class CustomTransition {
  static delegate = new CustomTransition();

  private constructor() {

  }

  static getInstance(): CustomTransition {
    return CustomTransition.delegate;
  }

  // 注册某个页面的动画回调
  // startCallback:用来设置动画开始时页面的状态
  // endCallback:用来设置动画结束时页面的状态
  // onFinish:用来执行动画结束后页面的其他操作

  // timeout:转场结束的超时时间
  registerNavParam(name: number, startCallback: (operation: boolean, isExit: boolean) => void,
    endCallback: (operation: boolean, isExit: boolean) => void,
    onFinish: (operation: boolean, isExit: boolean) => void, timeout: number, duration: number): void {

    if (customTransitionMap.has(name)) {
      let param = customTransitionMap.get(name);
      if (param != undefined) {
        param.start = startCallback;
        param.finish = endCallback;
        param.timeout = timeout;
        param.onFinish = onFinish;
        param.duration = duration;
        return;
      }
    }
    let params: AnimateCallback = {
      timeout: timeout,
      start: startCallback,
      finish: endCallback,
      onFinish: onFinish,
      duration: duration
    };
    customTransitionMap.set(name, params);
  }

  unRegisterNavParam(name: number): void {
    customTransitionMap.delete(name);
  }

  getAnimateParam(name: number): AnimateCallback {
    let result: AnimateCallback = {
      start: customTransitionMap.get(name)?.start,
      finish: customTransitionMap.get(name)?.finish,
      timeout: customTransitionMap.get(name)?.timeout,
      onFinish: customTransitionMap.get(name)?.onFinish,
      duration: customTransitionMap.get(name)?.duration,
    };
    return result;
  }
}

export class FlowFood {
  title: string
  content: string
  itemIndex: number

  constructor(title: string, content: string, itemIndex: number) {
    this.title = title
    this.content = content
    this.itemIndex = itemIndex
  }
}
分享
微博
QQ
微信
回复
1天前
相关问题
HarmonyOS 动画效果实现
38浏览 • 1回复 待解决
HarmonyOS 效果实现方案
439浏览 • 1回复 待解决
HarmonyOS 类似翻页效果实现
82浏览 • 1回复 待解决
HarmonyOS 首页轮播效果实现
40浏览 • 1回复 待解决
PopWindow效果实现有哪些?
713浏览 • 1回复 待解决
动态布局下加载loading效果实现
1029浏览 • 1回复 待解决
引导遮罩效果实现最佳方案
1117浏览 • 1回复 待解决
如何实现动画转场效果
873浏览 • 1回复 待解决
HarmonyOS Web如何搜索html界面的内容?
344浏览 • 1回复 待解决