未验证 提交 42ea48d9 编写于 作者: O openharmony_ci 提交者: Gitee

!18122 新增案例【不需要翻译】

Merge pull request !18122 from duangavin123/master
......@@ -13,6 +13,8 @@
- [如何为同一组件在不同场景下绑定不同的业务逻辑](different-operations-for-one-component.md)
- [如何实现列表项滑动显示快捷菜单](listitem-slide-to-display-menu.md)
- [如何在网格组件中通过拖拽交换子组件的位置](griditem-drag-and-drop.md)
- [如何实现逐帧动画](how-to-develop-frame-animation.md)
- [如何实现抽屉式导航](navigation-drawer.md)
### 网络管理
- [如何请求并加载网络图片](how-to-load-images-from-internet.md)
......
# 如何实现逐帧动画
## 场景说明
逐帧动画是常见的一种动画呈现形式,本例就为大家介绍如何通过translate(),setInterval(),clearAllInterval()等方法实现逐帧动画。
## 效果呈现
本例最终效果如下:
- 点击“run”按钮,火柴人开始走动。
- 点击“stop”按钮,火柴人停止走动。
![frameanimation](figures/frameanimation.gif)
## 运行环境
- IDE:DevEco Studio 3.1 Beta1
- SDK:Ohos_sdk_public 3.2.11.9 (API Version 9 Release)
## 实现思路
本例的实现有两个关键点:
- 将连续走动的火柴人拆分为多帧静态图像,在固定的时间间隔内逐帧将图像移动到动画窗口,间隔时间要小于肉眼可察觉的时间。循环上述动作,就可以实现火柴人的走动动画。
火柴人静态图像如下:
![man](figures/man.png)
- 将背景图片以固定速度相对于火柴人走动方向反方向移动,从而实现火柴人向前走动的效果。
背景图如下:
![background](figures/background.png)
本例使用translate()控制火柴人的移动,用backgroundImagePosition()控制背景图的移动。另外,通过setInterval()设置火柴人移动的时间间隔,通过clearAllInterval()清除移动。
## 开发步骤
1. 搭建UI框架。
使用两个Row组件分别呈现背景图和火柴人,第二个Row组件作为第一个Row组件的子组件,父Row组件的背景设置为背景图,子Row组件中添加Image组件用来呈现火柴人单帧图像。
```ts
@Entry
@Component
export default struct frameAnimation {
build() {
Column() {
// 父Row组件
Row() {
// 子Row组件
Row() {
// 通过Image组件显示火柴人图像
Image($r("app.media.man")).height(60).width(545.16)
}.width(100)
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Top)
// 截取显示与背景同等大小的区域,控制单个火柴人显示在画面中
.clip(true)
}
// 添加背景图像
.backgroundImage($r("app.media.background"))
// 保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。
.backgroundImageSize(ImageSize.Cover)
.width('100%')
.height(130)
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Bottom)
Row() {
// 添加跑动按钮
Button('run')
.margin({ right: 10 })
.type(ButtonType.Normal)
.width(75)
.borderRadius(5)
// 添加停止按钮
Button('stop')
.type(ButtonType.Normal)
.borderRadius(5)
.width(75)
.backgroundColor('#ff0000')
}.margin({ top: 30, bottom: 10 })
}.width('100%').width('100%').padding({ top: 30 })
}
}
```
2. 添加火柴人和背景图片的移动逻辑。
通过状态变量设定火柴人和背景图片的位置,位置变化时可以实时刷新UI界面。
```ts
// 火柴人位置变量
@State manPostion: {
x: number,
y: number
} = { x: 0, y: 0 }
// 背景图位置变量
@State treePosition: {
x: number,
y: number
} = { x: 0, y: 0 }
```
给火柴人和背景图片添加位置属性。
```ts
Row() {
Row() {
Image($r("app.media.man"))
.height(60)
.width(545.16)
// 通过translate实现火柴人的位移。绑定manPosition,用来改变火柴人位置。
.translate(this.manPostion)
}
...
}
.backgroundImage($r("app.media.background"))
.backgroundImageSize(ImageSize.Cover)
// 通过backgroundImagePosition实现背景图片的位移。绑定treePosition,用来改变背景图片的位置。
.backgroundImagePosition(this.treePosition)
...
```
3. 为''run''按钮和"stop"按钮绑定控制逻辑。
构建火柴人和背景图片移动的方法,用来设定火柴人和背景图片每次移动的距离。这里要注意火柴人每次移动的距离等于两个火柴人之间的间隔距离(像素值)。
```ts
// 火柴人移动方法
manWalk() {
if (this.manPostion.x <= -517.902) {
this.manPostion.x = 0
} else {
// 每次移动的距离为火柴人静态图像之间的间隔距离
this.manPostion.x -= 129.69
}
}
// 背景移动方法
treesMove() {
if (this.treePosition.x <= -1215) {
this.treePosition.x = 0
} else {
this.treePosition.x -= 20
}
}
```
创建doAnimation()方法调用上述两个方法,以便在后续的定时器中使用。
```ts
doAnimation() {
this.manWalk()
this.treesMove()
}
```
通过setInterval为“run”按钮绑定走动逻辑。
```ts
Button('run')
.margin({ right: 10 })
.type(ButtonType.Normal)
.width(75)
.borderRadius(5)
.onClick(() => {
this.clearAllInterval()
// 创建定时器,调用doAnimation方法,启动动画
let timer = setInterval(this.doAnimation.bind(this), 100)
this.timerList.push(timer)
})
```
通过clearAllInterval为“stop”按钮绑定停止逻辑。
```ts
Button('stop')
.type(ButtonType.Normal)
.borderRadius(5)
.width(75)
.backgroundColor('#ff0000')
.onClick(() => {
// 清理定时器,停止动画
this.clearAllInterval()
})
```
## 完整代码
本例完整代码如下:
```ts
@Entry
@Component
export default struct frameAnimation {
// 火柴人位置变量
@State manPostion: {
x: number,
y: number
} = { x: 0, y: 0 }
// 背景图位置变量
@State treePosition: {
x: number,
y: number
} = { x: 0, y: 0 }
// 定时器列表,当列表清空时,动画停止
private timerList: number[] = []
// 火柴人移动方法
manWalk() {
if (this.manPostion.x <= -517.902) {
this.manPostion.x = 0
} else {
this.manPostion.x -= 129.69
}
}
// 背景移动方法
treesMove() {
if (this.treePosition.x <= -1215) {
this.treePosition.x = 0
} else {
this.treePosition.x -= 20
}
}
// 销毁所有定时器
clearAllInterval() {
this.timerList.forEach((timer: number) => {
clearInterval(timer)
})
this.timerList = []
}
doAnimation() {
this.manWalk()
this.treesMove()
}
build() {
Column() {
// 父Row组件
Row() {
// 子Row组件
Row() {
// 通过Image组件显示火柴人图像
Image($r("app.media.man"))
.height(60)
.width(545.16)
// 通过translate实现火柴人的位移。绑定manPosition变量,用来改变火柴人位置。
.translate(this.manPostion)
}
.width(100)
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Top)
// 截取显示与背景同等大小的区域,控制单个火柴人显示在画面中
.clip(true)
}
// 添加背景图像
.backgroundImage($r("app.media.background"))
// 保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。
.backgroundImageSize(ImageSize.Cover)
// 通过backgroundImagePosition实现背景图片的位移。绑定treePosition,用来改变背景图片的位置。
.backgroundImagePosition(this.treePosition)
.width('100%')
.height(130)
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Bottom)
Row() {
// 添加跑动按钮
Button('run')
.margin({ right: 10 })
.type(ButtonType.Normal)
.width(75)
.borderRadius(5)
.onClick(() => {
this.clearAllInterval()
let timer = setInterval(this.doAnimation.bind(this), 100)
this.timerList.push(timer)
})
// 添加停止按钮
Button('stop')
.type(ButtonType.Normal)
.borderRadius(5)
.width(75)
.backgroundColor('#ff0000')
.onClick(() => {
this.clearAllInterval()
})
}.margin({ top: 30, bottom: 10 })
}.width('100%').width('100%').padding({ top: 30 })
}
}
```
## 参考
- [Timer (定时器)](../application-dev/reference/apis/js-apis-timer.md)
- [图形变换](../application-dev/reference/arkui-ts/ts-universal-attributes-transformation.md)
- [背景设置](../application-dev/reference/arkui-ts/ts-universal-attributes-background.md)
# 如何实现抽屉式导航
## 场景介绍
由于用户所需功能逐渐增多,传统的标签式导航在个别场景已经无法满足用户需求。当导航栏的空间放不下过多页签时,可以采用抽屉式导航,本例将为大家介绍如何通过SideBarContainer组件实现抽屉式导航。
## 效果呈现
本例最终实现效果如下:
![navigation-drawer](figures/navigation-drawer.gif)
## 运行环境
- IDE:DevEco Studio 3.1 Beta1
- SDK:Ohos_sdk_public 3.2.11.9 (API Version 9 Release)
## 实现思路
- 通过SideBarContainer组件提供容器,通过子组件定义侧边栏和内容区,第一个子组件表示侧边栏,第二个子组件表示内容区。
- 调用showSideBar属性来设置不显示侧边栏,controlButton属性来控制完成侧栏的展示/收起。
![](figures/navigation-drawer1.PNG)![](figures/navigation-drawer2.png)
## 开发步骤
1. 创建内容区域文本组件。
首先创建内容区,具体代码块如下:
```ts
...
// 内容区
Column() {
Text("内容区域")
.width("100%")
.height("100%")
.fontSize(30)
.textAlign(TextAlign.Center)
}
.width("100%")
.height("100%")
.backgroundColor("#bbaacc")
...
```
2. 通过SideBarContainer所支持的showSideBar属性来设置不显示侧边栏,controlButton属性来控制完成侧栏的展示/收起。
具体代码块如下:
```ts
...
.showSideBar(false) //默认不展示侧边栏,展示icon,用户点击调出
.controlButton({
left: 10, // 图标距离左侧宽度
top: 20, // 图标距离顶部高度
height: 30, // 图标高度
width: 30, // 图标宽度
icons: {
shown: $r('app.media.back'), // 侧边栏展示时图标
hidden: $r('app.media.sidebar_shown'), // 侧边栏收起时图标
switching: $r('app.media.sidebar_shown') // 侧边栏切换过程图标
}
})
...
```
3. 创建侧边栏文本组件。
具体代码如下:
```ts
...
struct SideBarContainerExample {
@ State navList: Array<string> = ["我的会员", "我的收藏", "我的相册", "我的文件",]
build() {
SideBarContainer(SideBarContainerType.Embed) {
// 侧边栏内容
Column() {
ForEach(this.navList, (item) => {
Text(item)
.width("100%")
.fontSize(20)
.textAlign(TextAlign.Start)
.padding({ top: 20 })
})
}
.height("100%")
.padding({ top: 60, left: 50 })
.backgroundColor("#aabbcc")
}
...
}
}
```
## 完整代码
示例完整代码如下:
```ts
@Entry
@Component
struct SideBarContainerExample {
@State navList: Array<string> = ["我的会员", "我的收藏", "我的相册", "我的文件",]
build() {
// Embed:侧边栏占据内容空间 Overlay:侧边栏悬浮于内容之上
SideBarContainer(SideBarContainerType.Embed) {
// 侧边栏内容
Column() {
ForEach(this.navList, (item) => {
Text(item)
.width("100%")
.fontSize(20)
.textAlign(TextAlign.Start)
.padding({ top: 20 })
})
}
.height("100%")
.padding({ top: 60, left: 50 })
.backgroundColor("#aabbcc")
// 内容区
Column() {
Text("内容区域")
.width("100%")
.height("100%")
.fontSize(30)
.textAlign(TextAlign.Center)
}
.width("100%")
.height("100%")
.backgroundColor("#bbaacc")
}
// 默认不展示侧边栏,展示icon,用户点击调出
.showSideBar(false)
.controlButton({
// 图标距离左侧宽度
left: 10,
// 图标距离顶部高度
top: 20,
// 图标高度
height: 30,
// 图标宽度
width: 30,
icons: {
// 侧边栏展示时图标
shown: $r('app.media.back'),
// 侧边栏收起时图标
hidden: $r('app.media.sidebar_shown'),
// 侧边栏切换过程图标
switching: $r('app.media.sidebar_shown')
}
})
// 侧边栏宽度
.sideBarWidth(200)
.width('100%')
.height('100%')
}
}
```
**注意**:模拟机与真机的预览有区别,在SideBarContainerType.Embed情况下,真机中内容区域是压缩,模拟器中内容区域是缺失。
## 总结
[Tabs组件](../application-dev/reference/arkui-ts/ts-container-tabs.md): 适用于导航栏固定在页面上下左右侧,入口分类数目不多,可以控制在5个以内,且用户需要频繁切换每一个页签的应用,比如微信、QQ等。
[Navigation组件](../application-dev/reference/arkui-ts/ts-basic-components-navigation.md): 同样可以实现Tabs组件中导航栏位于底部的场景,根据需要显示隐藏导航栏,提供标题,菜单,返回等选项,使用户是使用时更加灵活。
[sideBarContainer组件](../application-dev/reference/arkui-ts/ts-container-sidebarcontainer.md):主要的功能和内容都在一个页面里面,只是一些低频操作内容需要显示在其他页面里,可以把这些辅助功能放在抽屉栏里。屏幕较小时导航栏不占用空间。比如QQ,开发指导文档等。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册