1# 如何实现内容下拉变化
2## 场景介绍
3组件开发过程中经常遇到组件堆叠,上层组件下拉,下层组件随之变化的场景。常见的如朋友圈背景的下拉放大,内容的下拉刷新等。本例将为大家介绍如何实现上述下拉变化的场景。
4## 效果呈现
5效果图如下:
6
7![](figures/collapsibleeffect.gif)
8
9## 运行环境
10本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
11- IDE: DevEco Studio 3.1 Beta2
12- SDK: Ohos_sdk_public 3.2.11.9(API Version 9 Release)
13## 实现思路
14本例的3个关键特性及实现方案如下:
15- 界面不同背景的展示:通过Stack堆叠容器实现界面之间的覆盖
16- 中间子组件的下拉和回弹:通过ontouch事件判断heightValue的大小去设置可滑动的中层组件的动画效果
17- 底层子组件的文本变化:判断heightValuede来决定文本的变化
18## 开发步骤
191. 通过Stack堆叠容器创建各层子组件和文本
20    具体代码块如下:
21    ```ts
22    // 底层子组件
23    ...
24    Stack({ alignContent: Alignment.Bottom }) {
25      build() {
26        Stack({ alignContent: Alignment.Bottom }) { //底部对齐
27          Column() {
28            Row() {
29              Image($r('app.media.back')).width(25).height(25)
30                .onClick(() => {
31                  router.back();
32                })
33            ...
34            Image($r("app.media.sharew")) //分享
35                .width(25)
36                .height(25)
37            Image($r('app.media.more')) //更多
38                .width(25)
39                .height(25)
40            }.width('100%').padding(20).margin({ top: 20 })
41            ...
42          }
43          .height(this.ImHeight)
44          .width(this.ImWidth)
45          .backgroundImage($r('app.media.images')) //底层背景图
46          .backgroundImageSize(ImageSize.Cover)
47        ...
48        }
49      }
50    }
51    ...
52    //中层子组件
53    ...
54          Column() {
55        Row() {
56          Text('当点击此场景卡片时:').fontSize(16)
57          Blank()
58          Image($r('app.media.bl_harm')).width(30).height(30)
59        }.width('100%').margin({ top: 10, left: 10, right: 10, bottom: 20 })
60      }.borderRadius(35)
61    ...
62    //颜色渲染,线性渐变
63        .linearGradient(
64          {
65            direction: GradientDirection.Top,
66            angle: 180,
67            colors: [['#c7e2eb', 0.01], ["#F7F7F7", 0.05], ["#F7F7F7", 1]]
68          })
69    //底层子组件
70    ...
71    Column() {
72      Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) {
73        Button({ type: ButtonType.Capsule, stateEffect: true }) {
74          Row() {
75            Text('查看更多').fontSize(16).fontColor(Color.Blue).margin({ left: 5, right: 12 })
76          }.alignItems(VerticalAlign.Center)
77        }.height(40).borderRadius(25).backgroundColor('#EBEBEB').width('45%')
78        Button({ type: ButtonType.Capsule, stateEffect: true }) {
79          Row() {
80            Text('执行').fontSize(16).fontColor(Color.Blue).margin({ left: 5, right: 12 })
81          }.alignItems(VerticalAlign.Center)
82        }.height(40).borderRadius(25).backgroundColor('#EBEBEB').width('45%')
83      }
84    }.height('10%').width('100%').align(Alignment.Top).backgroundColor('#F7F7F7')
85    ...
86    ```
872.  通过ontouch事件判断heightValue的大小去设置可滑动的中层组件的动画效果
88    具体代码块如下:
89    ```ts
90    ...
91    .onTouch((event: TouchEvent) => { //触摸事件
92      if (event.type === TouchType.Down) {
93        this.duration = 1000;
94      }
95      if (event.type === TouchType.Up) { // 结束,回弹,回弹动画更快
96        this.heightValue = '88%'
97        this.duration = 500;
98        this.ImHeight = "100%"
99        this.ImWidth = "100%"
100      }
101      if (event.type === TouchType.Move) {
102      // 根据滑动距离确定组件高度,最多拖动140的,此时组件最小高度为68%
103        if (event.touches[0].y <= 140) {
104          this.heightValue = 88 - event.touches[0].y / 7 + '%'
105          this.ImHeight = "200%"
106          this.ImWidth = "200%"
107        } else {
108          this.heightValue = '68%'
109        }
110      }
111      console.info('垂直方向滑动距离' + event.touches[0].y)
112    })
113    .height(this.heightValue)
114    .animation({
115      duration: this.duration, // 动画时长
116      curve: Curve.FastOutSlowIn, // 动画曲线
117      delay: 0, // 动画延迟
118      iterations: 1, // 播放次数
119      playMode: PlayMode.Normal // 动画模式
120    })
121
122    ```
1233. 判断可滑动的中层组件的高度变化来体现标题栏的变化
124    具体代码块如下:
125    ```ts
126    ...
127    Row(){
128      //可滑动的组件高度大于整体界面的70%,显示test1
129      Text(this.heightValue >= '70%' ? 'test1' : '')
130        .fontColor(Color.Black)
131        .fontSize(20)
132        .margin({ left: 5 })
133    }
134    ...
135    Row() {
136      Text(this.heightValue < '70%' ? 'test2' : '') //可滑动的组件高度小于于整体界面的70%,显示test2
137        .fontColor(Color.Red)
138        .fontSize(30)
139        .margin({ left: 5 })
140    }
141    ...
142    ```
143## 完整代码
144示例完整代码如下:
145```ts
146import router from '@ohos.router'
147
148@Entry
149@Component
150struct DetailExample {
151  @State heightValue: string = '88%'
152  @State duration: number = 1000
153  @State ImHeight: string = '100%'
154  @State ImWidth: string = '100%'
155
156  build() {
157    Stack({ alignContent: Alignment.Bottom }) {
158      // 底层子组件
159      Column() {
160        Row() {
161          Image($r('app.media.back')).width(25).height(25)
162            .onClick(() => {
163              router.back();
164            })
165          Text(this.heightValue >= '70%' ? 'test1' : '')
166            .fontColor(Color.Black)
167            .fontSize(20)
168            .margin({ left: 5 })
169          Blank()
170          Image($r("app.media.sharew"))
171            .width(25)
172            .height(25)
173          Image($r('app.media.more'))
174            .width(25)
175            .height(25)
176        }.width('100%').padding(20).margin({ top: 20 })
177
178        Row() {
179          Text(this.heightValue < '70%' ? 'test2' : '')
180            .fontColor(Color.Red)
181            .fontSize(30)
182            .margin({ left: 5 })
183        }
184        .width("100%")
185        .height(35)
186        .alignItems(VerticalAlign.Center)
187        .justifyContent(FlexAlign.Start)
188      }
189      .height(this.ImHeight)
190      .width(this.ImWidth)
191      .backgroundImage($r('app.media.images'))
192      .backgroundImageSize(ImageSize.Cover)
193
194      // 可拖动中层子组件,动画,圆弧
195      Column() {
196        Row() {
197          Text('当点击此场景卡片时:').fontSize(16)
198          Blank()
199          Image($r('app.media.bl_harm')).width(30).height(30)
200        }.width('100%').margin({ top: 10, left: 10, right: 10, bottom: 20 })
201      }.borderRadius(35)
202      .padding(20)
203      .onTouch((event: TouchEvent) => {
204        if (event.type === TouchType.Down) {
205          this.duration = 1000;
206        }
207        if (event.type === TouchType.Up) {
208          this.heightValue = '88%'
209          this.duration = 500;
210          this.ImHeight = "100%"
211          this.ImWidth = "100%"
212        }
213        if (event.type === TouchType.Move) {
214          if (event.touches[0].y <= 140) {
215            this.heightValue = 88 - event.touches[0].y / 7 + '%'
216            this.ImHeight = "200%"
217            this.ImWidth = "200%"
218          } else {
219            this.heightValue = '68%'
220          }
221        }
222        console.info('垂直方向滑动距离' + event.touches[0].y)
223      })
224      .height(this.heightValue)
225      .animation({
226        duration: this.duration,
227        curve: Curve.FastOutSlowIn,
228        delay: 0,
229        iterations: 1,
230        playMode: PlayMode.Normal
231      })
232      .linearGradient(
233        {
234          direction: GradientDirection.Top,
235          angle: 180,
236          colors: [['#c7e2eb', 0.01], ["#F7F7F7", 0.05], ["#F7F7F7", 1]]
237        })
238
239      // 最上层子组件
240      Column() {
241        Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) {
242          Button({ type: ButtonType.Capsule, stateEffect: true }) {
243            Row() {
244              Text('查看更多').fontSize(16).fontColor(Color.Blue).margin({ left: 5, right: 12 })
245            }.alignItems(VerticalAlign.Center)
246          }
247          .height(40).borderRadius(25).backgroundColor('#EBEBEB').width('45%')
248          Button({ type: ButtonType.Capsule, stateEffect: true }) {
249            Row() {
250              Text('执行').fontSize(16).fontColor(Color.Blue).margin({ left: 5, right: 12 })
251            }.alignItems(VerticalAlign.Center)
252          }
253          .height(40).borderRadius(25).backgroundColor('#EBEBEB').width('45%')
254        }
255      }.height('10%').width('100%').align(Alignment.Top).backgroundColor('#F7F7F7')
256    }
257  }
258}
259```
260## 参考
261[Stack堆叠容器](../application-dev/reference/apis-arkui/arkui-ts/ts-container-stack.md)
262
263[Toggle](../application-dev/reference/apis-arkui/arkui-ts/ts-basic-components-toggle.md)
264
265[Flex](../application-dev/reference/apis-arkui/arkui-ts/ts-container-flex.md)
266