【HarmonyOS】深入了解 ArkUI 的动画交互以提高用户体验

news2024/10/5 14:24:17

        从今天开始,博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”,对于刚接触这项技术的小伙伴在学习鸿蒙开发之前,有必要先了解一下鸿蒙,从你的角度来讲,你认为什么是鸿蒙呢?它出现的意义又是什么?鸿蒙仅仅是一个手机操作系统吗?它的出现能够和Android和IOS三分天下吗?它未来的潜力能否制霸整个手机市场呢?

抱着这样的疑问和对鸿蒙开发的好奇,让我们开始今天对ArkUI动画操作的掌握吧!

目录

ArkUI动画操作

属性动画

显示动画

组件转场动画

弹簧曲线动画

路径动画

共享元素转场动画

页面转场动画


ArkUI动画操作

在学习动画操作之前,我们先了解一下动画实现的原理,动画的实现原理说白了就是无数个静态画面快速播放,达到我们肉眼是无法识别的临界值,呈现我们视觉感官的就是动态画面,像电影拍摄拍出来的胶卷就是一帧一帧的画面,它的播放速度是每秒24帧,只要播放速度足够快,人眼就部分识别它是静态的了。

所以接下来我们开始学习如何在ArkUI中实现动画操作,动画可以说是app当中必备的一个功能了,它可以大大提高用户交互时的一个体验。我们在开发应用时实现动画也是一样的,我们只需要把一个物体运动的开始状态和结束状态以及中间的每一帧画面快速地描述出来,那么动画就形成了,当然这个描述肯定不需要我们自己写代码把每一帧都描述出来,ArkUI底层简化了我们的开发,我们只需要把一个组件运行时它的一个初始和结束状态描述出来,ArkUI就会自动帮助我们去填充中间的每一帧画面,这样就有了动画的效果了。

识别动画的实现方式有很多,而在ArkUI中比较常见的实现方式就有以下几种实现方式:

动画是通过设置组件的animation属性来给组件添加动画,当组件的width、height、Opacity、backgroundColor、scale、rotate、translate等属性变更时,可以实现渐变过渡效果。动画设置简单,属性变化时自动触发动画,以下是animation属性可以传递使用的动画参数:

名称参数类型必填描述
durationnumber设置动画时长,默认值:1000,单位毫秒
temponumber动画播放速度。数值越大,速度越快。默认值:1
curvestring|Curve设置动画曲线。默认值:Curve.EaseInOut,平滑开始和结束。
delaynumber设置动画延迟执行的时长。默认值:0,单位:毫秒
iterationsnumber设置播放次数。默认值:1,取值范围 [-1,+\infty),-1表示无限次播放
playModePlayMode动画播放模式,默认播放完成后重头开始播放,默认值:PlayMode.Normal
onFinish()=>void状态回调,动画播放完成时触发

关于curve属性的详解,以下是其常用的函数名称:

名称描述
Linear表示动画从头到尾的速度都是相同的。
Ease表示动画以低速开始,然后加快,在结束前变慢。
EaseIn表示动画以低速开始
EaseOut表示动画以低速结束
EaseInOut表示动画以低速开始和结束
FastOutSlowIn标准曲线
LinearOutSlowIn减速曲线
FastOutLinearIn加速曲线
ExtremeDeceleration急缓曲线
Sharp锐利曲线
Rhythm节奏曲线
Smooth平滑曲线

属性动画

属性动画无需使用闭包,把animation属性加在要做属性动画的组件的属性后面即可,想要组件随某个属性值变化而产生动画,此属性需要加在animation属性之前,有的属性变化不希望通过animation产生属性动画,可以放在animation之后。属性动画的接口函数如下:

animation(value: AnimateParam)

以下给出属性动画执行的范例: 

@Entry
@Component

struct test {
  @State boxWidth: number = 100
  @State boxHeight: number = 100
  @State bgColor: Color = Color.Red
  @State flag: boolean = false
  build(){
    Stack({
      alignContent: Alignment.BottomEnd // 设置Stack层叠组件按钮居于最下方
    }){
      Column(){
        Row(){
        }
        .width(this.boxWidth)
        .height(this.boxHeight)
        .animation({
          duration: 1000
        })
        .backgroundColor(this.bgColor)
      }
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)

      Button(){
        Text('动画')
          .fontSize(20)
          .fontColor(Color.White)
      }
      .width(60)
      .height(60)
      .margin({ bottom: 10, right: 10 })
      .onClick(()=>{
        if (this.flag) {
          this.boxWidth = 100
          this.boxHeight = 100
          this.bgColor = Color.Red
        }else {
          this.boxWidth = 200
          this.boxHeight = 200
          this.bgColor = Color.Green
        }
        this.flag = !this.flag
      })
    }
  }
}

最终呈现的效果如下: 

给个下面的案例加深一下印象吧,使用translate改变一下元素的位置:

// 公共样式
@Extend(Text) function actionSheet() {
  .fontSize(18)
  .fontColor('#666')
  .width('100%')
  .height(20)
  .textAlign(TextAlign.Center)
}
@Entry
@Component

struct test {
  @State boxPosition: number = 220
  @State flag: boolean = false
  build(){
    Stack({
      alignContent: Alignment.BottomEnd
    }){
      Column(){
        Button(){
          Text('show actionSheet')
            .fontSize(30)
            .fontColor(Color.White)
        }
        .onClick(()=>{
          animateTo({
            duration: 500
          },()=>{
            if(this.flag){
              this.boxPosition = 220
            }else {
              this.boxPosition = 0
            }
            this.flag = !this.flag
          })
        })
        .width('80%')
        .height(60)
      }
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)

      Column(){
        List({ space: 44 }){
          ListItem(){
            Text('相册选择')
              .actionSheet()
          }
          ListItem(){
            Text('相机拍照')
              .actionSheet()
          }
          ListItem(){
            Text('取消')
              .actionSheet()
          }
        }
        .divider({ strokeWidth: 1, color: Color.Gray })
      }
      .width('100%')
      .height(220)
      .justifyContent(FlexAlign.Center)
      .backgroundColor('#efefef')
      .translate({
        y: this.boxPosition
      })
    }
  }
}

结果如下:

显示动画

闭包内的变化均会触发动画,包括由数据变化引起的组件的增删、组件属性的变化等,可以做较为复杂的动画,其显示动画的接口为如下函数,第一个参数指定动画参数,第二个参数为动画的闭包函数。

animateTo(value: AnimateParam, event: () => void): void

以下给出显示动画执行的范例:

@Entry
@Component

struct test {
  @State boxWidth: number = 100
  @State boxHeight: number = 100
  @State flag: boolean = false
  build(){
    Stack({
      alignContent: Alignment.BottomEnd // 设置Stack层叠组件按钮居于最下方
    }){
      Column(){
        Row(){
        }
        .width(this.boxWidth)
        .height(this.boxHeight)
        .backgroundColor(Color.Red)
      }
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)

      Button(){
        Text('动画')
          .fontSize(20)
          .fontColor(Color.White)
      }
      .width(60)
      .height(60)
      .margin({ bottom: 10, right: 10 })
      .onClick(()=>{
        animateTo({
          duration: 1000, // 动画的执行时间
        },()=>{
          if (this.flag) {
            this.boxWidth = 100
            this.boxHeight = 100
          }else {
            this.boxWidth = 200
            this.boxHeight = 200
          }
          this.flag = !this.flag
        })
      })
    }
  }
}

最终呈现的效果如下:

当然我们也可以给显示动画函数加一些动画参数,使动画效果更有视觉性,如下:

组件转场动画

组件的插入、删除过程即为组件本身的转场过程,组件的插入、删除动画称为组件内转场动画。通过组件内转场动画,可以定义组件出现、消失的效果。transition函数的入参为组件内转场的效果,可以定义平移、透明度、旋转、缩放这几种转场样式的单个或者组合的转场效果,必须和animateTo一起使用才能产生组件的转场效果。组件内转场动画的接口为如下代码:

transition(value: TransitionOptions)

以下给出组件转场动画执行的范例:

@Entry
@Component

struct test {
  @State flag: boolean = false
  build(){
    Stack({
      alignContent: Alignment.BottomEnd
    }){
      Column(){
        if(this.flag){
          Row(){
            Image("https://img-blog.csdnimg.cn/direct/b4ef01bec5c54a25b07024e76c8e6b0d.jpeg")
              .width(200)
              .height(200)
          }
          .width(200)
          .height(200)
          .justifyContent(FlexAlign.Center)
          .transition({
            type: TransitionType.Insert, // 显示执行动画
            opacity: 0,
            translate: { x: 300, y: 200 },
            scale: { x: 0, y: 0 }
          })
          .transition({
            type: TransitionType.Delete, // 删除执行动画
            opacity: 0,
            translate: { x: -300, y: 200 },
            scale: { x: 0, y: 0 }
          })
        }
      }
      .width('100%')
      .height('100%')
      .padding(20)
      .alignItems(HorizontalAlign.Center)
      .justifyContent(FlexAlign.Center)

      Button(){
        Text(this.flag ? '隐藏' : '显示')
          .fontColor(Color.White)
          .fontSize(20)
          .onClick(()=>{
            animateTo({
              duration: 1000
            },()=>{
              this.flag = !this.flag
            })
          })
      }
      .width(80)
      .height(80)
      .margin({ bottom: 10, right: 10 })
    }
  }
}

最终呈现的效果如下: 

弹簧曲线动画

ArkUI提供了预置动画曲线,指定了动画属性从起始值到终止值的变化规律,如Linear、Ease、EaseIn等。同时ArKUI也提供了由弹簧振子物理模型产生的弹簧曲线。通过弹簧曲线,开发者可以设置超过设置的终止值,在终止值附近震荡,直至最终停下来的效果。弹簧曲线的动画效果比其他曲线具有更强的互动性、可玩性。

弹簧曲线的接口包括两类,一类是springCurve,另一类是springMotion和responsiveSpringMotion,这两种方式都可以产生弹簧曲线,这里主要给大家讲解一下springCurve实现登录界面抖动动画,其springCurve的接口函数如下:

构造函数包括初速度(velocity)、弹簧系统的质量(mass)、刚度(stiffness)、阻尼(damping)。构建springCurve时,可指定质量为1,根据springCurve中的参数说明,调节刚度、阻尼两个参数,达到想要的震荡效果。

springCurve(velocity: number, mass: number, stiffness: number, damping: number)

以下给出弹簧曲线动画执行的范例: 

import curves from '@ohos.curves'
@Entry
@Component

struct test {
  @State translateX: number = 0

  jumpWidthSpeed(velocity: number) {
    this.translateX = 10 // 起始位置
    animateTo({
      duration: 1000,
      curve: curves.springCurve(velocity, 1, 1, 1)
    },()=>{
      this.translateX = 0 // 最终的位置
    })
  }
  build(){
      Column(){
        Row(){
          Text("登录框")
            .width('100%')
            .fontSize(40)
            .textAlign(TextAlign.Center)
        }
        .width(200)
        .height(200)
        .margin({ top: 20 })
        .backgroundColor(Color.Gray)
        .translate({
          x: this.translateX
        })

        Row(){
          Button("jump 10")
            .fontSize(14)
            .onClick(()=>{
              this.jumpWidthSpeed(10) // 以初速度10的弹簧曲线进行平移
            })
          Button("jump 200")
            .fontSize(14)
            .onClick(()=>{
              this.jumpWidthSpeed(200) // 以初速度200的弹簧曲线进行平移
            })
        }
        .width('100%')
        .margin({ top: 40 })
        .justifyContent(FlexAlign.SpaceAround)
      }
    .width('100%')
    .height('100%')
  }
}

最终呈现的效果如下: 

路径动画

通过路径动画也可以实现弹簧曲线,使用路径动画我们可以自定义我们元素的运动轨迹,以下是路径动画实现的接口函数:

path表示位移动画的运动路径;from表示运动路径的起点;to表示运动路径的终点;rotatable表示是否跟随路径进行旋转。

motionPath({path: string, from?: number, to?: number, rotatable?: boolean})

以下给出路径动画执行的范例:  

@Entry
@Component

struct test {
  @State flag: boolean = true
  build(){
    Column(){
      Row(){
        Text('路径曲线')
          .fontSize(20)
          .width('100%')
          .textAlign(TextAlign.Center)
      }
      .width(100)
      .height(100)
      .backgroundColor(Color.Gray)
      // 按照我们设置的坐标(10,600)、(400,600)、(400,500)这三个点进行运动
      .motionPath({ path: 'Mstart.x start.y L10 600 L400 600 L400 500 Lend.x end.y', from: 0.0, to: 1.0, rotatable: false })
      .margin(10)
      .onClick(()=>{
        animateTo({
          duration: 4000
        },()=>{
          this.flag = !this.flag
        })
      })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Start)
    .alignItems(this.flag ? HorizontalAlign.Start : HorizontalAlign.End)
  }
}

最终呈现的效果如下:  

共享元素转场动画

在不同页面间,有使用相同的元素(例如同一幅图)的场景,可以使用共享元素转场动画衔接。为了突出不同页面间相同元素的关联性,可为它们添加共享转场动画。如果相同元素在不同页面间的大小有差异,即可达到放大缩小视图的效果,共享元素转场的接口函数如下:

其中根据sharedTransitionOptions中的type参数,共享元素转场分为Exchange类型的共享元素转场和Static类型的共享元素转场。

sharedTransition(id: string, options?: sharedTransitionOptions)

Exchange类型的共享元素转场:交换型的共享元素转场,需要两个页面中存在通过sharedTransition函数配置为相同id的组件,它们称为共享元素。这种类型的共享元素转场适用于两个页面间相同元素的衔接,会从起始页共享元素的位置,大小过渡到目标页的共享元素的位置、大小。如果不指定type,默认为Exchange类型的共享元素转场,这也是最常见的共享元素转场的方式。使用Exchange类型的共享元素转场时,共享元素转场的动画参数由目标页options中的动画参数决定。以下给出实现的基本步骤:

1)我们在首页设置图片的默认转场,通过点击事件进行路由的跳转:

2)在另一个页面中我们通过sharedTransition函数配置为相同id的组件,将图片放大,然后点击图片后再回退到原始页面

实现的效果如下:

Static类型的共享元素转场:静态型的共享元素转场通常用于页面跳转时,标题逐渐出现或隐藏的场景,只需要在一个页面中有Static的共享元素,不能再两个页面中出现相同id的Static类型的共享元素。在跳转到该页面(即目标页)时,配置Static类型sharedTransition的组件做透明度从0到该组件设定的透明度的动画,位置保持不变。在该页面(即起始页)消失时,做透明度逐渐变为0的动画,位置保持不变。以下给出实现的基本步骤:

1)原页面还是通过Exchange给出图片的转场效果

2)另一个页面除了有图片之外,还有其他内容,比如text,这里设置文本内容为Static,有一个淡入淡出的效果实现

实现的效果如下:

结合共享元素转场动画效果,接下来我们实现朋友圈预览图片的效果案例 :

首先我们在主页面设置默认的Exchange转场效果:

import router from '@ohos.router'
interface ImageListData {
  uniqueId: string,
  author: string,
  imageUrl: string
}

@Entry
@Component
struct FriendPage {
  listData: ImageListData[] = [
    {
      "uniqueId": 'Candy-Shop',
      "author": 'Mohamed Chahin',
      "imageUrl": 'https://www.itying.com/images/flutter/1.png',
    },
    {
      "uniqueId": 'Childhood',
      "author": 'Google',
      "imageUrl": 'https://www.itying.com/images/flutter/2.png',
    },
    {
      "uniqueId": 'Alibaba-Shop',
      "author": 'Alibaba',
      "imageUrl": 'https://www.itying.com/images/flutter/3.png',
    },
    {
      "uniqueId": 'Alibaba-Shop1',
      "author": 'Alibaba1',
      "imageUrl": 'https://www.itying.com/images/flutter/4.png',
    },
    {
      "uniqueId": 'Alibaba-Shop2',
      "author": 'Alibaba2',
      "imageUrl": 'https://www.itying.com/images/flutter/5.png',
    },
    {
      "uniqueId": 'Alibaba-Shop3',
      "author": 'Alibaba3',
      "imageUrl": 'https://www.itying.com/images/flutter/6.png',
    },
  ]
  build() {
    Column(){
      Grid(){
        ForEach(this.listData,(item: ImageListData)=>{
          GridItem(){
            Image(item.imageUrl)
              .objectFit(ImageFit.Auto)
              .sharedTransition(item.uniqueId,{
                duration: 300
              })
          }
          .onClick(()=>{
            router.pushUrl({
              url: "pages/FriendImagePage",
              params: {
                uniqueId: item.uniqueId,
                imgUrl: item.imageUrl
              }
            })
          })
        })
      }
      .columnsTemplate(`1fr 1fr`)
      .columnsGap(10)
      .rowsGap(10)
    }
    .padding(10)
    .width('100%')
    .height('100%')


  }

  // 去掉页面转场动画
  pageTransition(){
    PageTransitionEnter({ type: RouteType.None, duration: 0 })
    PageTransitionExit({ type: RouteType.None, duration: 0 })
  }
}

接下来我们在预览效果的页面设置Exchange转场和Static转场效果:

import router from '@ohos.router'
interface ImageListData {
  uniqueId: string,
  author: string,
  imageUrl: string
}

@Entry
@Component
struct FriendImagePage {
  @State uniqueId: string = ''
  @State imageIndex: number = 0
  listData: ImageListData[] = [
    {
      "uniqueId": 'Candy-Shop',
      "author": 'Mohamed Chahin',
      "imageUrl": 'https://www.itying.com/images/flutter/1.png',
    },
    {
      "uniqueId": 'Childhood',
      "author": 'Google',
      "imageUrl": 'https://www.itying.com/images/flutter/2.png',
    },
    {
      "uniqueId": 'Alibaba-Shop',
      "author": 'Alibaba',
      "imageUrl": 'https://www.itying.com/images/flutter/3.png',
    },
    {
      "uniqueId": 'Alibaba-Shop1',
      "author": 'Alibaba1',
      "imageUrl": 'https://www.itying.com/images/flutter/4.png',
    },
    {
      "uniqueId": 'Alibaba-Shop2',
      "author": 'Alibaba2',
      "imageUrl": 'https://www.itying.com/images/flutter/5.png',
    },
    {
      "uniqueId": 'Alibaba-Shop3',
      "author": 'Alibaba3',
      "imageUrl": 'https://www.itying.com/images/flutter/6.png',
    },
  ]
  // build方法执行之前触发
  aboutToAppear(){
    // 获取上一个页面的传值
    let params: object = router.getParams()
    this.uniqueId = params["uniqueId"]
    // 获取轮播图默认选中的索引值
    for (let index = 0; index < this.listData.length; index++) {
      if (this.listData[index].uniqueId == this.uniqueId) {
        this.imageIndex = index
      }
    }
  }
  // build方法执行完毕后触发
  onPageShow(){

  }
  // 页面销毁时触发
  onPageHide(){

  }
  build() {
    Row() {
      Column() {
        Swiper(){
          ForEach(this.listData,(item: ImageListData)=>{
            Image(item.imageUrl)
              .width('100%')
              .objectFit(ImageFit.Auto)
              .sharedTransition(item.uniqueId,{
                duration: 300
              })
          })
        }
        .height('100%')
        .index(this.imageIndex)
      }
      .width('100%')
      .height('100%')
      .backgroundColor(Color.Black)
      .justifyContent(FlexAlign.Center)
      .sharedTransition('bg',{ // 设置淡入淡出的效果
        duration: 300,
        type: SharedTransitionEffectType.Static
      })
      .onClick(()=>{
        router.back()
      })
    }
  }

  // 去掉页面转场动画
  pageTransition(){
    PageTransitionEnter({ type: RouteType.None, duration: 0 })
    PageTransitionExit({ type: RouteType.None, duration: 0 })
  }
}

最终呈现的效果如下:

页面转场动画

两个页面发生跳转,一个页面消失,另一个页面出现,这时可以配置各自页面的页面转场参数实现自定义的页面转场效果。页面转场效果写在pageTransition函数中,通过PageTransitionEnter和PageTransitionExit指定页面进入和退出的动画效果。

PageTransitionEnter的接口为:

PageTransitionEnter({type?: RouteType, duration?: number, curve?: Curve | string, delay?:number)

PageTransitionExit的接口为:

PageTransitionExit({type?: RouteType, duration?: number, curve?: Curve|string, delay?:number})

上述接口定义了PageTransitionEnter和PageTransitionExit组件,可通过slide、 translate、 scale opacity属性定义不同的页面转场效果。对于PageTransitionEnter而言,这些效果表示入场时起点值,对于PageTransitionExit而言,这些效果表示退场的终点值,这一点与组件转场transition配置方法类似。此外,PageTransitionEnter提供了onEnter接口进行自定义页面入场动画的回调,PageTransitionExit提供了onExit接口进行自定义页面退场动画的回调。根据这个函数我们可以设置如下效果:

// 自定义动画
pageTransition(){
  PageTransitionEnter({
    duration: 1000,
    type: RouteType.None // 界面中的所有动画效果中,优先执行该自定义动画效果
  }).onEnter((type: RouteType, progress: number) => {

  }).slide(SlideEffect.Bottom) // 动画效果从下向上执行

  PageTransitionExit({
    duration: 1000,
    type: RouteType.None // 界面中的所有动画效果中,优先执行该自定义动画效果
  }).onExit((type: RouteType, progress: number) => {

  }).slide(SlideEffect.Right) // 动画效果从上向下执行
}

拿之前的朋友圈预览图片的案例进行举例,如下:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

echarts柱状图加单位,底部文本溢出展示

刚开始设置了半天都不展示单位&#xff0c;后来发现是被挡住了&#xff0c;需要调高top值 // 基于准备好的dom&#xff0c;初始化echarts实例var myChart echarts.init(document.getElementById("echartD"));rankOption {// backgroundColor: #00265f,tooltip: {…

让企业的招投标文件、生产工艺、流程配方、研发成果、公司计划、员工信息、客户信息等核心数据更安全。

PC端访问地址1&#xff1a;www.drhchina.com PC端访问地址2&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 全方位立体式防护  让数据泄密无处遁形 信息防泄漏是一项系统的整体部署工程&#xff0c;加密监控已成为多数企事业单…

个人优势能力测评 (多元智能版)

个人优势能力测评也叫多元智能测评&#xff0c;有美国心理学家加德纳Gardner博士在1983年提出多元智能理论&#xff0c;多元智能是相对于智商来说的&#xff0c;多元智能理论认为人类的智商不是单一的&#xff0c;而是多种类型的组合&#xff0c;每个人都是组合了不同的优势&am…

如何在CentOS安装SQL Server数据库并通过内网穿透工具实现公网访问

文章目录 前言1. 安装sql server2. 局域网测试连接3. 安装cpolar内网穿透4. 将sqlserver映射到公网5. 公网远程连接6.固定连接公网地址7.使用固定公网地址连接 前言 简单几步实现在Linux centos环境下安装部署sql server数据库&#xff0c;并结合cpolar内网穿透工具&#xff0…

第四站:指针的进阶-(二级指针,函数指针)

目录 二级指针 二级指针的用途 多级指针的定义和使用 指针和数组之间的关系 存储指针的数组(指针数组:保存地址值) 指向数组的指针(数组指针) 传参的形式(指针) 数组传参时会退化为指针 void类型的指针 函数指针 定义: 调用:两种方式:(*指针名)(参数地址) 或者 指针…

JavaScript的闭包、执行上下文、到底是怎么回事?还有必要学吗?

在上一课&#xff0c;我们了解了 JavaScript 执行中最粗粒度的任务&#xff1a;传给引擎执行的代码段。并且&#xff0c;我们还根据“由 JavaScript 引擎发起”还是“由宿主发起”&#xff0c;分成了宏观任务和微观任务&#xff0c;接下来我们继续去看一看更细的执行粒度。 一…

数据结构与算法-栈-移掉K位数字

移掉K位数字 给你一个以字符串表示的非负整数 num 和一个整数 k &#xff0c;移除这个数中的 k **位数字&#xff0c;使得剩下的数字最小。请你以字符串形式返回这个最小的数字。 示例 1 &#xff1a; 输入&#xff1a;num "1432219", k 3 输出&#xff1a;&quo…

葡萄酒的独特国度智利

葡萄酒的独特国度智利 一、独特的智利葡萄酒 智利葡萄酒是在20世纪90年代以后才逐渐走向了世界&#xff0c;由于低税、口味独特等优点&#xff0c;深受大众喜爱。因为智利独待的气候其生产的葡萄别有风味&#xff0c;为其产出优质葡萄酒奠定了基础&#xff0c;再加上欧洲古老的…

电口模块SFP-GE-T:实现光口与电口之间的转换

电口模块是一种用于实现光口转电口功能的设备&#xff0c;在网络通信中起到重要作用。电口模块没有光电转换的过程&#xff0c;只是传输电信号。本文介绍电口模块的作用、分类、以及使用方法。 一、什么是电口模块 电口模块又被称为光转电模块&#xff0c;它是一种支持热插拔…

vue3+echarts应用——深度遍历html的dom结构并用树图进行可视化

文章目录 ⭐前言&#x1f496;vue3系列文章 ⭐html数据解析&#x1f496; html字符串转为html对象&#x1f496; 深度遍历html对象内容 ⭐echarts 树图的渲染&#x1f496; 处理html内容为树状结构&#x1f496; 渲染树状图&#x1f496; inscode代码块 ⭐总结⭐结束 ⭐前言 大…

程序员必知!备忘录模式的实战应用与案例分析

备忘录模式允许在不破坏封装性下捕获并在外部保存对象状态&#xff0c;支持状态恢复&#xff0c;常用于撤销、历史记录等功能。例如在线文档编辑器的撤销操作&#xff0c;编辑器作为原发起人记录状态并提供保存与恢复方法&#xff0c;历史记录或撤销为管理者&#xff0c;保存备…

Vue3打印插件Print.js的使用

文章目录 一、Print.js二、安装2.1、 js文件2.2、npm2.3、CDN 三、使用3.1、网页&#xff08;HTML&#xff09;打印3.2、PDF 打印3.3、图像打印3.4、JSON 打印 四、最后 一、Print.js 在使用 Print.js 插件之前&#xff0c;我们可以通过下面的链接先了解和认识一下这个 JavaScr…

Linux下Redis6下载、安装和配置教程-2024年1月5日

Linux下Redis6下载、安装和配置教程-2024年1月5日 一、下载二、安装三、启动四、设置开机自启五、Redis的客户端1.Redis命令行客户端2.windows上的图形化桌面客户端 一、下载 1.Redis的官方下载&#xff1a;https://redis.io/download/ 2.网盘下载&#xff1a; 链接&#xff…

基于FPGA的万兆以太网学习(1)

万兆(10G) 以太网测速视频:FPGA 实现UDP万兆以太网的速度测试 1 代码结构 2 硬件需求 SFP+屏蔽笼可以插入千兆或万兆光模块。SFP+信号定义与 SFP 一致。 3 Xilinx IP 10 Gigabit Ethernet Subsystem IP说明 文章链接: Xilinx IP 10 Gigabit Ethernet Subsystem IP 4 E…

【ZooKeeper高手实战】ZooKeeper常用命令及客户端工具Curator核心功能

&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308; 欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术 的推送 发送 资料 可领取 深入理…

答疑解惑:核技术利用辐射安全与防护考核

前言 最近通过了《核技术利用辐射安全与防护考核》&#xff0c;顺利拿到了合格证。这是从事与辐射相关行业所需要的一个基本证书&#xff0c;考试并不难&#xff0c;在此写篇博客记录一下主要的知识点。 需要这个证书的行业常见的有医疗方面的&#xff0c;如放疗&#xff0c;…

寒假护眼台灯哪款更好?五款高品质护眼台灯推荐

近年来学生近视的现象越来越严重了&#xff0c;而且近视的年龄也越来越小了&#xff0c;不少还没开始上小学的孩子&#xff0c;就已经戴上了厚厚的近视眼镜。而那些高年级的学生更是近视的重灾区&#xff0c;不仅每天需要高强度的学习和长时间用眼&#xff0c;甚至晚上都还需要…

Java内存模型(JMM)是基于多线程的吗

Java内存模型&#xff08;JMM&#xff09;是基于多线程的吗 这个问题按我的思路转换了下&#xff0c;其实就是在问&#xff1a;为什么需要Java内存模型 总结起来可以由几个角度来看待「可见性」、「有序性」和「原子性」 面试官&#xff1a;今天想跟你聊聊Java内存模型&#…

【pdf密码】pdf打印密码强制解除

正常的PDF文件是可以打印的&#xff0c;如果PDF文件打开之后发现文件不能打印&#xff0c;我们需要先查看一下自己的打印机是否能够正常运行&#xff0c;如果打印机是正常的&#xff0c;我们再查看一下&#xff0c;文件中的打印功能按钮是否是灰色的状态。 如果PDF中的大多数功…

LED电平显示驱动电路图

LB1409九位LED电平显示驱动电路 如图所示为LBl409九位LED电平显示驱动电路。图&#xff08;a&#xff09;是用LB1409做电平显示驱动电路&#xff0c;图&#xff08;b&#xff09;是应用基准电压电平显示驱动电路。LB1409是日本东京互洋电机株式会社生产的产品&#xff0c;与其…