ArkTS开发系列之导航 (2.7动画)

上篇回顾: ArkTS开发系列之导航 (2.6 图形)

本篇内容:动画的学习使用

一、 知识储备

1. 布局更新动画

  • 包含显式动画(animateTo)和属性动画(animation)
动画类型名称特点
显式动画闭包内的变化都会触发动画执行, 可以做较复杂的动画
属性动画属性变化时触发动画执行, 设置简单

说白了,显示动画就是靠闭包事件触发,属性动画是挂在组件身上的属性变化触发

  • 显式动画 可以通过修改组件的布局方式、宽高、位置触发动画
 animateTo({ duration: 1000, curve: Curve.Ease }, () => {
            // 动画闭包中根据标志位改变控制第一个Button宽高的状态变量,使第一个Button做宽高动画
            if (this.flag) {
              this.myWidth = 100;
              this.myHeight = 50;
            } else {
              this.myWidth = 200;
              this.myHeight = 100;
            }
            this.flag = !this.flag;
          });
  • 属性动画
 Image($r('app.media.ic_hm_logo'))
    .width(160)
    .height(160)
    .alignRules({
      top: { anchor: '__container__', align: VerticalAlign.Top },
      right: { anchor: '__container__', align: this.itemAlign }
    })
    .margin({ top: this.mTop, right: 35 })
    .id('logo')
    .animation({ duration: 1000, curve: Curve.Ease }) //animation只对上面的设置生效
    .onClick(() => {
      this.mTop = 111
    })

2. 组件内转场动画(transition)

  • transition常见用法
Button()   //删除插入同一个动画
  .transition({ type: TransitionType.All, scale: { x: 0, y: 0 } })
Button()//删除插入不同动画
  .transition({ type: TransitionType.Insert, translate: { x: 200, y: -200 }, opacity: 0 })
  .transition({ type: TransitionType.Delete, rotate: { x: 0, y: 0, z: 1, angle: 360 } })

3. 弹簧曲线动画

  • 分为两类,一个springCurve,一个springMotion和responsiveSpringMotion。
  • springCurve
springCurve(velocity: number, mass: number, stiffness: number, damping: number)
//velocity:  初速度
//mass:弹簧系统的质量
//stiffness:弹簧系统的刚度
//damping:弹簧系统的阻尼
  • springMotion和responsiveSpringMotion。此动画适合跟手动画,duration设置无效。跟手过程推荐使用responsiveSpringMotion,松手时使用springMotion
springMotion(response?: number, dampingFraction?: number, overlapDuration?: number)
responsiveSpringMotion(response?: number, dampingFraction?: number, overlapDuration?: number)
//response:可选参数 弹簧自然振动周期
//dampingFraction:可选参数阻尼系数
//overlapDuration:可选参数弹性动画衔接时长
  • 注意: springCurve可以设置初速度,单一属性存在多个动画时,可以正常叠加动画效果。springMotion,由于开发者不可修改速度机制,所以在单一属性存在多个动画时,后一动画会取代前一动画。并继承前一动画的速度
  .onTouch(event => {
    if (event.type == TouchType.Move) {
      animateTo({ curve: curves.responsiveSpringMotion() }, () => {
        this.mRight = event.touches[0].screenX - 80;
      })
    } else if (event.type == TouchType.Up) {
      animateTo({curve: curves.springMotion()}, ()=>{
        this.mRight = 35;
      })
    }
  })

4. 放大缩小动画

  • sharedTransition,
  • Exchange类型的共享元素转场动画,就是前后两个页面都设置有相同id的动画,那么转场时,将会自动触发缩放动画
      .sharedTransition('sharedTransition', { duration: 1000, curve: Curve.Linear })
  • Static类型的共享元素转场动画, 只需要在一个页面设置即可,在转换页面时自动触发透明度从0到该组件原设定的的透明度的动画。位置等其他属性不变
   .sharedTransition('input', { duration: 500, curve: Curve.Linear, type: SharedTransitionEffectType.Static })

5. 页面转场动画

  • 页面转场切换时,效果触发在页面上,在pageTransition函数中设置PageTransitionEntert和PageTransitionExit动画
PageTransitionEnter({type?: RouteType,duration?: number,curve?: Curve | string,delay?: number})
PageTransitionExit({type?: RouteType,duration?: number,curve?: Curve | string,delay?: number})
//type:默认是RouteType.None表示对页面栈的push和pop操作均生效,
//duration: 动画时长
//curve: 动画效果
//delay: 动画延时
转场动画设置
  pageTransition() {
    PageTransitionEnter({ type: RouteType.Push, duration: 1000, })
      .slide(SlideEffect.Right)
    PageTransitionEnter({ type: RouteType.Pop, duration: 1000, })
      .slide(SlideEffect.Left)

    PageTransitionExit({type: RouteType.Push, duration: 1000})
      .slide(SlideEffect.Left)
    PageTransitionExit({type: RouteType.Pop, duration: 1000})
      .slide(SlideEffect.Right)
  }

二、 效果一览

在这里插入图片描述

三、 源码剖析

import promptAction from '@ohos.promptAction';
import picker from '@ohos.file.picker';
import thermal from '@ohos.thermal';
import router from '@ohos.router';
import curves from '@ohos.curves';

@Component
@Entry
struct LoginPage {
  @State account: string = '';
  @State password: string = '';
  dialog: CustomDialogController = new CustomDialogController({
    builder: TipDialog({
      cancel: this.onCancel,
      commit: this.onCommit,
      msg: this.account
    }),
    alignment: DialogAlignment.Center
  });

  onCancel() {
    promptAction.showToast({ message: '取消登录' })
  }

  onCommit() {
    promptAction.showToast({ message: '登录成功' })

    router.pushUrl({ url: 'pages/router/MyRouter' })
  }

  @State color: Color = 0x1f2937;
  @State itemAlign: HorizontalAlign = HorizontalAlign.Center
  allAlign: HorizontalAlign[] = [HorizontalAlign.Center, HorizontalAlign.End]
  alignIndex: number = 0;
  @State mTop: number = 106
  @State flag: boolean = true
  @State translateX: number = 0;
  @State mRight: number = 35;

  build() {
    Column() {
      RelativeContainer() {
        // Photo()

        Text('欢迎登录')
          .fontSize(25)
          .fontColor(this.color)
          .margin({ top: 111, left: 33 })
          .id('tips')
          .alignRules({
            top: { anchor: '__container__', align: VerticalAlign.Top },
            left: { anchor: '__container__', align: HorizontalAlign.Start }
          })
          .width('100%')
          .fontWeight(FontWeight.Bold)

        Image($r('app.media.ic_hm_logo'))
          .width(160)
          .height(160)
          .alignRules({
            top: { anchor: '__container__', align: VerticalAlign.Top },
            right: { anchor: '__container__', align: this.itemAlign }
          })
          .margin({ top: this.mTop, right: this.mRight })
          .id('logo')
          .animation({ duration: 1000, curve: Curve.Ease }) //animation只对上面的设置生效  属性动画

          .onTouch(event => {
            if (event.type == TouchType.Move) {
              animateTo({ curve: curves.responsiveSpringMotion() }, () => {
                this.mRight = event.touches[0].screenX - 80;
              })
            } else if (event.type == TouchType.Up) {
              animateTo({ curve: curves.springMotion() }, () => {
                this.mRight = 35;
              })
            }
          })
          .onClick(() => {
            if (this.mTop == 111) {
              this.mTop = 106
            } else {
              this.mTop = 111;
            }

            animateTo({ curve: Curve.LinearOutSlowIn, duration: 1000 }, () => {
              this.flag = !this.flag;
            })
            this.translateX = -1;
            animateTo({ curve: curves.springCurve(2000, 1, 1, 1.2), duration: 1000 }, () => {
              this.translateX = 0;
            })
          })

        if (this.flag) {
          Image($r('app.media.ic_hm_logo'))
            .width(160)
            .height(160)
            .alignRules({
              top: { anchor: '__container__', align: VerticalAlign.Top },
              right: { anchor: '__container__', align: this.itemAlign }
            })
            .margin({ top: 580, right: 35 })
            .id('bottom_logo')
            .translate({ x: this.translateX })
            .transition({ type: TransitionType.Insert, opacity: 1, scale: { x: 0, y: 0 } })
            .transition({ type: TransitionType.Delete, opacity: 0, scale: { x: 0, y: 0 } })
        }

        Image($r('app.media.ic_hm_logo'))
          .width(160)
          .height(160)
          .alignRules({
            top: { anchor: '__container__', align: VerticalAlign.Top },
            right: { anchor: '__container__', align: this.itemAlign }
          })
          .margin({ top: 600, right: 35 })
          .id('bottom_animateTo')
          .translate({ x: this.translateX })
          .sharedTransition('sharedTransition', { duration: 1000, curve: Curve.Linear })
          .onClick(() => {
            router.pushUrl({ url: 'pages/router/MyRouter' })
          })


        Text('用户名:')
          .fontSize(14)
          .fontColor(this.color)
          .alignRules({
            top: { anchor: 'tips', align: VerticalAlign.Bottom },
            left: { anchor: 'tips', align: HorizontalAlign.Start }
          })
          .margin({ top: 140, left: 33 })
          .height(20)
          .textAlign(TextAlign.Center)
          .id('account')
          .width(60)

        TextInput({ placeholder: '请输入账号' })
          .alignRules({
            top: { anchor: 'account', align: VerticalAlign.Bottom },
            left: { anchor: 'account', align: HorizontalAlign.Start }
          })
          .margin({ top: 13, left: 33, right: 33 })
          .id("etAccount")
          .height(46)
          .fontColor(this.color)
          .onChange(val => {
            this.account = val;
          })

        Text('密码:')
          .fontSize(14)
          .fontColor('#333333')
          .alignRules({
            top: { anchor: 'etAccount', align: VerticalAlign.Bottom },
            left: { anchor: 'etAccount', align: HorizontalAlign.Start }
          })
          .width(60)
          .height(20)
          .margin({ top: 15, left: 33 })
          .textAlign(TextAlign.Center)
          .id('password')
        TextInput({ placeholder: '请输入密码' })
          .alignRules({
            top: { anchor: 'password', align: VerticalAlign.Bottom },
            left: { anchor: 'password', align: HorizontalAlign.Start }
          })
          .type(InputType.Password)
          .margin({ top: 13, left: 33, right: 33 })
          .id("etPassword")
          .height(46)
          .onChange(val => {
            this.password = val;
          })

        Toggle({ type: ToggleType.Checkbox })
          .onChange((isChecked) => {
            if (isChecked) {
              promptAction.showToast({ message: "记住账号密码" })
            } else {
              promptAction.showToast({ message: "不记住密码" })
            }
          })
          .alignRules({
            top: { anchor: 'password', align: VerticalAlign.Bottom },
            left: { anchor: 'password', align: HorizontalAlign.Start }
          })
          .height(15)
          .margin({
            top: 80, left: 33
          })
          .id('isRecord')

        Text('记住密码')
          .fontColor("#999999")
          .fontSize(12)
          .height(15)
          .margin({
            top: 80,
            left: 6
          })
          .alignRules({
            top: { anchor: 'password', align: VerticalAlign.Bottom },
            left: { anchor: 'isRecord', align: HorizontalAlign.End }
          })
          .id('tvRecord')

        Button('登录', { type: ButtonType.Capsule, stateEffect: true })
          .onClick(() => {
            this.dialog.open()
            animateTo({ duration: 10000, curve: Curve.LinearOutSlowIn }, () => { //显式动画
              this.alignIndex = (this.alignIndex + 1) % this.allAlign.length;
              this.itemAlign = this.allAlign[this.alignIndex]
            })
          })
          .alignRules({
            top: {
              anchor: 'tvRecord', align: VerticalAlign.Bottom
            },
            left: { anchor: 'etAccount', align: HorizontalAlign.Start },
            right: { anchor: 'etAccount', align: HorizontalAlign.End }
          })
          .height(47)
          .margin({
            right: 23,
            top: 19,
            left: 23
          })
          .backgroundColor(0x00c250)
          .type(ButtonType.Capsule)
          .id('btCommit')
      }.width('100%')
      .height('100%')
    }
  }

  pageTransition() {
    PageTransitionEnter({ type: RouteType.Push, duration: 1000, })
      .slide(SlideEffect.Right)
    PageTransitionEnter({ type: RouteType.Pop, duration: 1000, })
      .slide(SlideEffect.Left)

    PageTransitionExit({ type: RouteType.Push, duration: 1000 })
      .slide(SlideEffect.Left)
    PageTransitionExit({ type: RouteType.Pop, duration: 1000 })
      .slide(SlideEffect.Right)
  }
}

@CustomDialog
struct TipDialog {
  dialogController: CustomDialogController;
  cancel: () => void
  commit: () => void
  msg: string;

  build() {
    Column() {
      Text('确定登录当前账号“' + this.msg + '”吗')
        .fontColor(Color.Red)
        .fontSize(44)
      Row() {

        Button('确定')
          .type(ButtonType.Capsule)
          .onClick(() => {
            this.commit()
            this.dialogController.close()
          })

        Button('取消')
          .type(ButtonType.Capsule)
          .onClick(() => {
            this.cancel()
            this.dialogController.close()
          })
      }
    }
  }
}

@Component
struct Photo {
  @State imgListData: string[] = [];

  getAllImg() { //获取照片url集

    try {
      let photoSelectOptions = new picker.PhotoSelectOptions(); //媒体库选择器设置
      photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; //图片类型
      photoSelectOptions.maxSelectNumber = 5; //最多 选取五张

      let photoPicker = new picker.PhotoViewPicker; //初始化图片选择器
      photoPicker.select(photoSelectOptions).then(photoSelectResult => { //选择结果回调
        this.imgListData = photoSelectResult.photoUris;
        console.error(`imgListData size is ${this.imgListData.length}`)
      }).catch(err => {
        console.error(`select failed code is ${err.code}, msg : ${err.message}`)
      })
    } catch (err) {
      console.error(`load photo failed ${err.code}, msg: ${err.message}`)
    }

  }

  async aboutToAppear() {
    this.getAllImg(); //获取图片url
  }

  build() {

  }
}

import http from '@ohos.net.http';
import ResponseCode from '@ohos.net.http';
import image from '@ohos.multimedia.image';
import picker from '@ohos.file.picker';
import router from '@ohos.router';

@Entry
@Component
struct MyRouter {
  private tabsController: TabsController = new TabsController();
  @State currentIndex: number = 0;

  @Builder TabBuild(title: string, targetIndex: number, SelectedImg: Resource, normalImg: Resource) {
    Column() { //自定义tab
      Image(this.currentIndex == targetIndex ? SelectedImg : normalImg)
        .size({ width: 25, height: 25 })
      Text(title)
        .fontSize(16).fontColor(this.currentIndex == targetIndex ? 0x00c250 : 0x333333)
    }
    .width('100%')
    .height(48)
    .justifyContent(FlexAlign.Center)
    .onClick(() => {
      console.error(`targetIndex is ${targetIndex}`)
      this.currentIndex = targetIndex;
      this.tabsController.changeIndex(this.currentIndex)
    })
  }

  @State image: PixelMap = undefined; //创建PixelMap状态变量

  private imgUrl: string = 'https://img1.baidu.com/it/u=3241660985,1063915045&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1194';

  loadImg() { //获取网络美女图片
    http.createHttp().request(this.imgUrl, (err, data) => {
      if (err) {
        console.error(`err is ${JSON.stringify(err)}`)
      } else {
        let code = data.responseCode;
        if (ResponseCode.ResponseCode.OK == code) {
          let res: any = data.result;
          let imageSource = image.createImageSource(res)
          let options = {
            alphaTye: 0, //透明度
            editable: false, //是否可编辑
            pixelFormat: 3, //像素格式
            scaleMode: 1, //缩略值
            size: { height: 100, wight: 100 } //创建图片大小
          }
          imageSource.createPixelMap(options).then(pixelMap => {
            this.image = pixelMap;
          })
        }
      }
    })
  }

  aboutToAppear() {

    // this.context = getContext();
    this.loadImg()
  }

  settings: RenderingContextSettings = new RenderingContextSettings(true)
  context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  offContext: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(600, 600, this.settings)
  img: ImageBitmap = new ImageBitmap('../../../resources/base/media/ic_hm_logo.svg')
  scroller: Scroller = new Scroller();
  @State _50: number = vp2px(50)
  @State _100: number = vp2px(100)
  @State _85: number = vp2px(85)
  @State _15: number = vp2px(15)
  @State _: number = vp2px(50)

  build() {
    Tabs({
      barPosition: BarPosition.End,
      controller: this.tabsController
    }) { //end start  首尾位置设置   controller 绑定tabs的控制器
      TabContent() { //内容页面组件
        MyNavigation().backgroundColor(0xf7f7f7)
      }
      .tabBar(this.TabBuild('首页', 0, $r('app.media.ic_hm_home_selected'), $r('app.media.ic_hm_home_normal')))

      TabContent() {
        Image(this.image).height('100%').width('100%')
      }
      .tabBar(this.TabBuild('直播', 1, $r('app.media.ic_hm_living_selected'), $r('app.media.ic_hm_living_normal')))

      TabContent() {
        Scroll(this.scroller) {
          Column() {
            Row() {
              Image(this.imgUrl)
                .width('30%')
                .height('20%')
                .border({ width: 1 })
                .objectFit(ImageFit.Contain) //等比缩放,图片完全显示
                .margin(15)
                .overlay('Contain', { align: Alignment.Bottom, offset: { x: 0, y: 30 } })
                .colorFilter([ //添加滤镜效果
                  1, 1, 0, 0, 0,
                  0, 1, 0, 0, 0,
                  0, 0, 1, 0, 0,
                  0, 0, 0, 1, 0
                ])
                .syncLoad(true) //同步加载图片
                .onComplete(msg => {
                  if (msg) {
                    console.error(`widthVal = ${msg.width}\n height = ${msg.height} \n componentW = ${msg.componentWidth} \n status = ${msg.loadingStatus}`)
                  }
                })
                .onError(() => {
                  console.error('err')
                })

              Image(this.imgUrl)
                .width('30%')
                .height('20%')
                .border({ width: 1 })
                .objectFit(ImageFit.Cover) //等比缩放, 图片显示部分
                .margin(15)
                .overlay('Cover', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })

              Image(this.imgUrl)
                .width('30%')
                .height('20%')
                .border({ width: 1 })
                .objectFit(ImageFit.Auto) //自适应显示
                .margin(15)
            }

            Row() {
              Image(this.imgUrl)
                .width('30%')
                .height('20%')
                .border({ width: 1 })
                .objectFit(ImageFit.Fill) //填充显示,不保持等比缩放
                .renderMode(ImageRenderMode.Template) //图片渲染模式
                .margin(15)
                .overlay('fill', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })

              Image(this.imgUrl)
                .sourceSize({ //设置图片解码尺寸
                  width: 65, height: 40
                })
                .width('30%')
                .height('20%')
                .border({ width: 1 })
                .objectFit(ImageFit.ScaleDown) //保持原宽高比 等比缩放或不变
                .objectRepeat(ImageRepeat.XY) //设置图片在xy轴上重复显示
                .margin(15)
                .overlay('scaleDown', { align: Alignment.Bottom, offset: { x: 0, y: 30 } })

              Image(this.imgUrl)
                .width('30%')
                .height('20%')
                .border({ width: 1 })
                .objectFit(ImageFit.None) //保持图片原尺寸
                .interpolation(ImageInterpolation.High) //图片插值,抗锯齿,使图片看着更清晰
                .margin(15)
                .overlay("none", { align: Alignment.Bottom, offset: { x: 0, y: 30 } })
            }

          }

        }.width('100%')
        .height('100%')
      }
      .tabBar(this.TabBuild('朋友圈', 2, $r('app.media.ic_hm_friend_selected'), $r("app.media.ic_hm_friend_normal")))

      TabContent() {
        Canvas(this.context)
          .width('100%')
          .height('100%')
          .backgroundColor(Color.Yellow)
          .onReady(() => {

            this.offContext.drawImage(this.image, 110, 400, 130, 130)
            let imageData = this.offContext.getImageData(150, 450, 130, 130)
            this.offContext.putImageData(imageData, 130, 130)
            let img = this.offContext.transferToImageBitmap();
            this.context.transferFromImageBitmap(img) //.截图画图

            this.context.beginPath()
            this.context.rect(110, 10, 210, 110)
            this.context.stroke()
            this.context.font = '46px'
            this.context.fillText('矩形', 120, 20) //画矩形

            this.context.beginPath()
            this.context.arc(110, 150, 50, 0, 6.28)
            this.context.stroke()
            this.context.font = '56px'
            this.context.fillText('圆形', 120, 220) //画圆形

            this.context.beginPath()
            this.context.ellipse(110, 250, 50, 100, Math.PI * 0.25, Math.PI * 0, Math.PI * 2)
            this.context.stroke() //画椭圆形

            let line = this.context.createLinearGradient(110, 500, 110, 600)
            line.addColorStop(0, '#f00')
            line.addColorStop(0.5, '#ff0')
            line.addColorStop(1, '#f0f')
            this.context.fillStyle = line
            this.context.fillRect(110, 500, 100, 100) //渐变色
            this.context.fillText('我是中华人民共和国的公民', 110, 500) //写字

            let path = new Path2D();
            path.moveTo(100, 100)
            path.lineTo(100, 150)
            path.lineTo(25, 150)
            path.lineTo(175, 150)
            path.lineTo(100, 150)
            path.lineTo(100, 200)
            path.lineTo(40, 200)
            path.lineTo(160, 200)
            path.lineTo(100, 200)
            path.closePath()
            this.context.strokeStyle = '#f00'
            this.context.lineWidth = 5
            this.context.stroke(path)

            let path2 = new Path2D();
            path2.moveTo(40, 215)
            path2.lineTo(160, 215)
            path2.lineTo(160, 335)
            path2.lineTo(40, 335)
            path2.lineTo(40, 215)
            this.context.stroke(path2) //再次画个“吉”
          })
      }
      .tabBar(this.TabBuild('画布', 3, $r('app.media.ic_hm_logo'), $r('app.media.icon')))

      TabContent() {
        Column() {

          Text('原始尺寸Circle')
          Circle({ width: 100, height: 100 }).fill(0x00c250)
          Row({ space: 10 }) {

            Column() {
              Text('shape内放大的Circle')
              Shape() {
                Rect({ width: 100, height: 100 }).fill(0xf7f7f7)
                Circle({ width: 150, height: 150 }).fill(0x00c250)
              }.viewPort({ x: 0, y: 0, width: 100, height: 100 }) //根据这个视口与宽高比进行放大shape内的组件大小
              .width(150)
              .height(150)


              Path()
                .width(100)
                .height(100)
                .commands(`M${this._50} 0 L${this._50} ${this._50} L0 ${this._50} L${this._100} ${this._50} L${this._50} ${this._50} L${this._50} ${this._100} L${this._15} ${this._100} L${this._85} ${this._100} L${this._50} ${this._100} Z`)
                .fillOpacity(0) //实心不填充颜色
                .fill(Color.Red)
                .stroke(Color.Red)
                .strokeWidth(5)

              Polyline()
                .width(100)
                .height(100)
                .stroke(Color.Red)
                .strokeWidth(5)
                .points([[10, 10], [90, 10], [90, 90], [10, 90], [10, 10]])
                .fillOpacity(0) //实心不填充颜色


            }
          }
        }
      }
      .tabBar(this.TabBuild('我的', 4, $r('app.media.ic_hm_my_selected'), $r('app.media.ic_hm_my_normal')))
    }
    .vertical(false) //tabs垂直与横向设置
    .scrollable(false) //禁止页面滑动
    .barMode((BarMode.Fixed)) //Fixed 固定    Scrollable  可以滑动,当tab多时用
    .onChange((index) => { //页面滑动监听
      console.error(`this is ${index}`)
      this.currentIndex = index;
      // this.tabsController.changeIndex(this.currentIndex)
    })
  }
}

@Component
struct MyNavigation {
  private arr: number[] = [1, 2, 3];

  build() {
    Column() {
      Navigation() {
        TextInput({ placeholder: '请输入...' })
          .width('90%')
          .height(40)
          .backgroundColor('#ffffff')
          .sharedTransition('input', { duration: 500, curve: Curve.Linear, type: SharedTransitionEffectType.Static })

        Image($r('app.media.ic_hm_logo'))
          .width(300)
          .height(300)
          .sharedTransition('sharedTransition', { duration: 500, curve: Curve.Linear })
          .onClick(() => {
            router.back()
          })

        List({ space: 12 }) {
          ForEach(this.arr, item => {
            ListItem() {
              NavRouter() {
                Text("NavRouter" + item)
                  .width('100%')
                  .height(72)
                  .backgroundColor(Color.White)
                  .borderRadius(36)
                  .fontSize(16)
                  .fontWeight(500)
                  .textAlign(TextAlign.Center)
                NavDestination() {
                  Text(`NavDestinationContent${item}`)
                }
                .title(`NavDestinationTitle${item}`)
              }
            }
          })
        }
      }
      .title('主标题')
      .mode(NavigationMode.Stack)
      .titleMode(NavigationTitleMode.Mini)
      .menus([
        { value: "", icon: './../../../resources/base/media/icon.png', action: () => {
        } },
        { value: "", icon: './../../../resources/base/media/icon.png', action: () => {
        } }
      ])
      .toolBar({ items: [
        { value: 'func', icon: './../../../resources/base/media/icon.png', action: () => {
        } },
        { value: 'func', icon: './../../../resources/base/media/icon.png', action: () => {
        } }
      ] })
    }
  }
  pageTransition() {
    PageTransitionEnter({ type: RouteType.Push, duration: 1000, })
      .slide(SlideEffect.Right)
    PageTransitionEnter({ type: RouteType.Pop, duration: 1000, })
      .slide(SlideEffect.Left)

    PageTransitionExit({type: RouteType.Push, duration: 1000})
      .slide(SlideEffect.Left)
    PageTransitionExit({type: RouteType.Pop, duration: 1000})
      .slide(SlideEffect.Right)
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/737633.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CV每日论文--2024.6.24

1、Whiteboard-of-Thought: Thinking Step-by-Step Across Modalities 中文标题:思维白板:跨模式逐步思考 简介:这段话描述了一种利用思维白板提示来增强大型语言模型在视觉推理任务中的性能的方法。 人类在解决需要视觉思考的问题时,通常会…

计算机网络知识点汇总(三)

1.2 计算机网络体系结构与参考模型 1.2.1 计算机网络分层结构 计算机网络的各层及其协议的集合称为网络的体系结构(Architecture)。换言之,计算机网络的体系结构就是这个计算机网络及其所应完成的功能的精确定义。要强调的是,这些功能究竟是用何种硬件…

中小企业应该怎么挑选和使用进销存管理系统?

中小企业应该怎么挑选和使用进销存管理系统? 一、中小企业为啥需要进销存系统? 进销存系统,简单来说,就是一套集进货、销售、库存于一体的管理软件。它通过对企业生产经营中物料流、资金流进行条码全程跟踪管理,能提…

LED恒流调光电路

LED等在工作的时候发热较大,所以通常选用铝基板作为底板;常用白色油墨。 LED必须在恒流源电路下工作,下图为最简单的恒流源:B极电压3.3V不变左下侧蓝色的为稳压二极管,由于BE极可以看做二极管,压降为0.7V&…

国产MCU芯片(1):航顺芯片MCU概览

前言: 截止2023年底,全国有3451家芯片设计公司,已经IPO的就有168家,尚未IPO的3283家中超过一半的年营收在1000万以下,迅猛发展的几年的确有些国产芯片开始站上赛道,这也是国际大背景下的一种必然选择,毕竟突然间出现的大市场需要国产顶上,但资本市场是周期性的,国产替…

自动驾驶---Perception之Lidar点云3D检测

1 背景 Lidar点云技术的出现是基于摄影测量技术的发展、计算机及高新技术的推动以及全球定位系统和惯性导航系统的发展,使得通过激光束获取高精度的三维数据成为可能。随着技术的不断进步和应用领域的拓展,Lidar点云技术将在测绘、遥感、环境监测、机器人…

CMake笔记之CMAKE_INSTALL_PREFIX详解以及ROS中可执行文件为什么会在devel_lib中

CMake笔记之CMAKE_INSTALL_PREFIX详解以及ROS中可执行文件为什么会在devel_lib中 code review! 文章目录 CMake笔记之CMAKE_INSTALL_PREFIX详解以及ROS中可执行文件为什么会在devel_lib中1.CMAKE_INSTALL_PREFIX详解变量作用设置 CMAKE_INSTALL_PREFIX示例影响范围常见用法特别…

maya模型仓鼠制作

小仓鼠建模(6)_哔哩哔哩_bilibili 20240623作品---个人评价:第一次做的,虽然有点丑,但是还能看!希望后面有些进步

第10章 启动过程组 (识别干系人)

第10章 启动过程组 10.2识别干系人,在第三版教材第361~362页; 文字图片音频方式 视频13 第一个知识点:主要工具与技术 1、数据收集 问卷调查 包括一对一调查、焦点小组讨论,或其他大规模信息收集技术 头脑风暴 头脑风暴&#xff…

章十九、JavaVUE —— 框架、指令、声明周期、Vue-cli、组件路由、Element

目录 一、 框架 ● vue.js 框架 ● 特点 ● Vue 安装 二、 第一个vue程序 ● 创建项目 ​编辑 ● 导入 vue.js ● 创建vue对象,设置属性,使用模版渲染到页面 介绍 — Vue.js (vuejs.org) 三、 vue指令 ● v-text ● v-html ● v-…

5个wordpress成品站主题

Sora索啦高端制造业wordpress主题 红色高端制造业wordpress主题,适合外贸企业出海建独立站的wordpress模板。 https://www.jianzhanpress.com/?p5885 Polar钋啦wordpress外贸主题 制造业wordpress网站模板,适合生产制造企业官方网站使用的wordpress外…

产品体验周刊第1期(2024-6-24)

产品体验 微信公众号改版 微信公众号文章底部改版,原“点赞”“再看”相关的读者互动模块固定底部栏。作者内容与读者反馈本就是一起组成内容的必要元素,相比较于抖音、小红书,微信公众号的改版可能来的稍微晚了一点。 微信输入法 平常发微…

Linux根目录挂载点(/dev/mapper/centos-root)扩容

如果我们在安装系统是采用自定义分区的话,就可以提前规划好这个事情。但是如果平常没注意就直接采用默认安装的方式的话。一旦 根目录的容量耗尽,将会影响业务的运行。今天我们来扩容逻辑卷。 默认安装的话会给home目录分比较多的空间,我们可…

浅析缓存技术

缓存技术的原理 缓存技术通过在内存中存储数据副本来加速数据访问。当应用程序需要数据时,首先检查缓存是否存在数据副本,如果有则直接返回,否则再从原始数据源获取。这种机制大大减少了访问时间,提升了系统的响应速度和整体性能。…

家政预约小程序14权限配置

目录 1 创建用户2 创建角色3 启用登录4 实现退出总结 我们现在小程序端的功能基本开发好了,小程序开发好之后需要给运营人员提供管理后台,要分配账号、配置权限,我们本篇就介绍一下权限如何分配。 1 创建用户 在微搭中,用户分为内…

使用Fiddler如何创造大量数据!

1、找到评论提交接口 找到我们的评论 2、构造数据 怎么再次发送呢? 这里发送了4次 我们创造了4条数据,我们再去评论区瞅瞅 3、如何解决图片显示问题? 手机端-设置-Wlan-高级-网址不适用代理,将不需要图片的域名加入 4、不抓包的…

MySQL锁、加锁机制(超详细)—— 锁分类、全局锁、共享锁、排他锁;表锁、元数据锁、意向锁;行锁、间隙锁、临键锁;乐观锁、悲观锁

文章目录 一、概述1.1 MySQL锁的由来1.2 锁定义1.3 锁分类 二、共享锁与排他锁2.1 共享锁(S锁)2.2 排他锁(X锁)2.3 MySQL锁的释放 三、全局锁3.1 介绍3.2 语法3.3 特点 四、表级锁4.1 介绍4.2 表锁4.3 元数据锁(Meta D…

SpringUtils.getBean 空指针异常问题

因为这个项目license问题无法开源,更多技术支持与服务请加入我的知识星球。 今天在新的jeecg-boot里加入下面的代码 /*** 部门经理处理类** author nbacheng* date 2023-08-06*/ AllArgsConstructor Component("DepManagerHandler") DependsOn({"Sp…

基于SSM+Jsp的校园餐厅管理

开发语言:Java框架:ssm技术:JSPJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包…

Ftrans文件安全传输系统 无缝替代FTP实现安全可靠传输

文件安全传输系统是专门设计用来确保文件在传输过程中安全性的软件或服务,现在这类产品很多都被用于替代FTP。 替代FTP的需求主要源于FTP在安全性、效率和功能方面存在一些限制和不足。下面是FTP的优势和劣势的概述: FTP的优势: 普及性和广…