1# 如何实现内容下拉变化 2## 场景介绍 3组件开发过程中经常遇到组件堆叠,上层组件下拉,下层组件随之变化的场景。常见的如朋友圈背景的下拉放大,内容的下拉刷新等。本例将为大家介绍如何实现上述下拉变化的场景。 4## 效果呈现 5效果图如下: 6 7 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