Skip to content

Commit

Permalink
Feat nested scroll (didi#245)
Browse files Browse the repository at this point in the history
* feat: nested scrolls

* docs(scroll): add nest scroll introduce
  • Loading branch information
tank0317 authored and dolymood committed Oct 10, 2018
1 parent cbd038a commit 9027e6a
Show file tree
Hide file tree
Showing 10 changed files with 826 additions and 23 deletions.
103 changes: 102 additions & 1 deletion document/components/docs/en-US/scroll.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ So for the Scroll component, The length of `.cube-scroll-content`, the scroll-co

### Example

Five sample code to quickly understand how to use the Scroll component.
Seven sample code to quickly understand how to use the Scroll component.

- **1. Basic usage - Default**

Expand Down Expand Up @@ -304,6 +304,106 @@ Scroll components can meet the scrolling needs of most mobile applications. In t

> **Note:** In this example, the `pullDownRefresh` configuration item does not have a `stop` value, but it is still able to bounce back to the correct location after the pulldown. The reason is that when the Scroll component is initialized, the pulldown height will be used as the `stop` default value when `beforePullDown === false && isPullingDown === true`.
**6. Vertical nested scrolls - Vertical Scrolls**

The `Scroll` component also supports nested scenes (currently only supports two levels of nesting). It's worth celebrating that you don't need to do any work, just use the `Scroll` component as usual. The `Scroll` component will determine if there is a nesting situation and handle nested scrolling issues. By default, nested `Scroll` has the same scrolling behavior as the browser's native nested scene. The complete sample code is [here](https://github.com/didi/cube-ui/blob/master/example/pages/scroll/vertical-scrolls.vue).

```html
<cube-scroll
ref="scroll1"
class="scroll-list-outer-wrap">
...
<cube-scroll
ref="scroll2"
class="scroll-list-outer-wrap">
<ul class="cube-scroll-list">
<li class="cube-scroll-item border-bottom-1px"
v-for="(item, index) in items2"
:key="index">{{item}}</li>
</ul>
</cube-scroll>
...
</cube-scroll>
```

**7. Horizontal nested scrolls - Horizontal Scrolls**

You can also implement horizontal nested scrolling. In this example, we also set `nestMode` to `free`. Different from `native` mode, in `free` mode, as long as the boundary is triggered during the inner scrolling process, the outer scroll will be started. In the `native` mode, it is only when the scrolling starts to determine whether it reaches the boundary, which is consistent with the browser's native nested scrolling. The complete sample code is [here](https://github.com/didi/cube-ui/blob/master/example/pages/scroll/horizontal-scrolls.vue).

```html
<cube-scroll
ref="scroll"
:data="items1"
direction="horizontal"
class="outer-horizontal-scroll">
<ul class="list-wrapper">
<li v-for="item in items1" class="list-item">{{ item }}</li>
<li class="list-item inner-horizontal-scroll">
<cube-scroll
ref="scroll"
:data="items2"
direction="horizontal"
nest-mode="free">
<ul class="list-wrapper">
<li v-for="item in items2" class="list-item">{{ item }}</li>
</ul>
</cube-scroll>
</li>
<li v-for="item in items1" class="list-item">{{ item }}</li>
</ul>
</cube-scroll>
```

<!-- **8. Textarea within scroll - Textarea**
Sometimes we need to include the teatarea input box in the `Scroll` component. However, since we disabled the default behavior of the browser 'touch' event when using `Scroll`, we were unable to use the browser's native scrolling in the textarea input box.
Now through this example, we hope to introduce two ways to solve this problem. The core is to take advantage of `Scroll` to support nesting. We wrap the internal input box with `Scroll` and simulate the scrolling behavior with `Scroll`. But there is a requirement that the input box content area must be highly adaptive, ie the height increases or decreases with the content.
1)using div to simulate textarea to achieve content area's height adaptation.
2)using js and textarea to achieve content area's height adaptation.
Finally, we need some extra work to ensure that the cursor is always in line of sight and consistent with the behavior of the native input box during the input process. The complete sample code is [here](https://github.com/didi/cube-ui/blob/master/example/pages/scroll/textarea.vue)
```html
<cube-scroll
ref="scrollOuter"
:options="optionsOuter"
class="scroll-outer">
...
<div class="editable-div-wrapper" :class="{'editable-div_active': isFocusDiv}">
<cube-scroll
ref="divWrapScroll"
:options="options">
<div ref="editablediv" contenteditable="true" class="editable-div"
@focus="onFocusDiv"
@blur="onBlurDiv"
@input="onInputDiv">
</div>
</cube-scroll>
<span class="editable-div-indicator">{{divValueCount}}</span>
</div>
<div class="cube-textarea-wrapper" :class="{'cube-textarea_active': isFocusNative}">
<cube-scroll
ref="nativeWrapScroll"
:options="options">
<textarea
ref="textarea"
v-model="textareaValue"
@input="onInputNative"
@focus="onFocusNative"
@blur="onBlurNative"
:placeholder="placeholder"
class="cube-textarea">
</textarea>
</cube-scroll>
<span class="cube-textarea-indicator">{{textareaValueCount}}</span>
</div>
...
</cube-scroll>
``` -->

### Props configuration

| Attribute | Description | Type | Accepted Values | Default |
Expand All @@ -315,6 +415,7 @@ Scroll components can meet the scrolling needs of most mobile applications. In t
| listenScroll | whether to dispatch scroll event. `Deprecated`, please use the property `scroll-events` instead. | Boolean | true/false | false |
| listenBeforeScroll | whether to dispatch before-scroll-start event. `Deprecated`, please use the property `scroll-events` instead. | Boolean | true/false | false |
| refreshDelay | the delay of scroll refresh after `data` updating | Number | - | 20 |
| nestMode | Nested scroll mode, the default is `native` mode, only to determine whether to reach the boundary and start the outer scroll when starting scrolling, consistent with the browser's native nested scrolling. In the `free` mode, as long as the boundary is triggered during the inner scrolling process, the outer scrolling is turned on. | String | 'native', 'free' | 'native' |

In `options`, there are three frequently-used options, `scrollbar``pullDownRefresh``pullUpLoad`, which could set as `Boolean`(`false` to disable the feature, `true` to enable the feature and use default sub configuration), or `Object` to enable the feature and customize the sub configuration.

Expand Down
105 changes: 103 additions & 2 deletions document/components/docs/zh-CN/scroll.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
### 示例

5 个示例代码快速了解如何使用 Scroll 组件。
7 个示例代码快速了解如何使用 Scroll 组件。

- **1. 基本使用 - Default**

Expand Down Expand Up @@ -305,6 +305,106 @@

> 在本例中,`pullDownRefresh`配置项没有传入`stop`值,但是下拉后依然能够回弹到正确位置,原因是 Scroll 组件初始化时会将 `beforePullDown === false && isPullingDown === true` 时下拉内容高度作为 `stop` 默认值。
- **6. 嵌套纵向滚动 - Vertical Scrolls**

`Scroll`组件还支持嵌套的场景(目前只支持两层嵌套)。值得庆祝的是,对于你不需要做任何工作,只需要像平时使用`Scroll`组件一样即可。`Scroll`组件会自行判断是否有嵌套情况,同时处理嵌套滚动问题。默认情况下,嵌套`Scroll`与浏览器原生嵌套场景的滚动行为相同。下面是`Scroll`组件实现纵向嵌套滚动的例子。完整的示例代码在这里[这里](https://github.com/didi/cube-ui/blob/master/example/pages/scroll/vertical-scrolls.vue)

```html
<cube-scroll
ref="scroll1"
class="scroll-list-outer-wrap">
...
<cube-scroll
ref="scroll2"
class="scroll-list-inner-wrap">
<ul class="cube-scroll-list">
<li class="cube-scroll-item border-bottom-1px"
v-for="(item, index) in items2"
:key="index">{{item}}</li>
</ul>
</cube-scroll>
...
</cube-scroll>
```

- **7. 嵌套横向滚动 - Horizontal Scrolls**

你还可以实现横向的嵌套滚动。这里同时设置`nestMode``free`,与`native`模式不同的是,`free`模式下,内层滚动过程中只要触发边界,便会开启外层滚动。而`native`模式下,只在开始滚动时判断是否到达边界,与浏览器原生的嵌套滚动保持一致。完整的示例代码在[这里](https://github.com/didi/cube-ui/blob/master/example/pages/scroll/horizontal-scrolls.vue)

```html
<cube-scroll
ref="scroll"
:data="items1"
direction="horizontal"
class="outer-horizontal-scroll">
<ul class="list-wrapper">
<li v-for="item in items1" class="list-item">{{ item }}</li>
<li class="list-item inner-horizontal-scroll">
<cube-scroll
ref="scroll"
:data="items2"
direction="horizontal"
nest-mode="free">
<ul class="list-wrapper">
<li v-for="item in items2" class="list-item">{{ item }}</li>
</ul>
</cube-scroll>
</li>
<li v-for="item in items1" class="list-item">{{ item }}</li>
</ul>
</cube-scroll>
```

<!-- - **8. Scroll 中嵌套 textarea - Textarea**
有时候我们需要在 `Scroll` 组件中包含 teatarea 输入框。然而由于我们在使用 `Scroll` 时禁用了浏览器 'touch' 事件的默认行为,因此我们无法在 textarea 输入框中使用浏览器的原生滚动。
现在我们希望通过这个例子,介绍两种解决这个问题的方法。核心都是利用了 `Scroll` 支持嵌套的能力,我们将内部的输入框用 `Scroll` 进行包装,通过 `Scroll` 去模拟滚动行为。但是有一个要求是,输入框内容区域必须是高度自适应,即高度随内容增加或减少。
1)利用 div 标签模拟 textarea,实现内容区域高度自适应。
2)利用 textarea 配合 js,实现高度自适应。
最后,我们还需要一些额外的工作保证输入过程中,光标能始终在视线内,保持与原生输入框的行为一致。完整的示例代码在[这里](https://github.com/didi/cube-ui/blob/master/example/pages/scroll/textarea.vue)
```html
<cube-scroll
ref="scrollOuter"
:options="optionsOuter"
class="scroll-outer">
...
<div class="editable-div-wrapper" :class="{'editable-div_active': isFocusDiv}">
<cube-scroll
ref="divWrapScroll"
:options="options">
<div ref="editablediv" contenteditable="true" class="editable-div"
@focus="onFocusDiv"
@blur="onBlurDiv"
@input="onInputDiv">
</div>
</cube-scroll>
<span class="editable-div-indicator">{{divValueCount}}</span>
</div>
<div class="cube-textarea-wrapper" :class="{'cube-textarea_active': isFocusNative}">
<cube-scroll
ref="nativeWrapScroll"
:options="options">
<textarea
ref="textarea"
v-model="textareaValue"
@input="onInputNative"
@focus="onFocusNative"
@blur="onBlurNative"
:placeholder="placeholder"
class="cube-textarea">
</textarea>
</cube-scroll>
<span class="cube-textarea-indicator">{{textareaValueCount}}</span>
</div>
...
</cube-scroll>
``` -->

### Props 配置

| 参数 | 说明 | 类型 | 可选值 | 默认值 |
Expand All @@ -316,6 +416,7 @@
| listenScroll | 是否派发 scroll 事件。`即将废弃`,推荐使用 `scroll-events` 属性 | Boolean | true/false | false |
| listenBeforeScroll | 是否派发 before-scroll-start 事件。`即将废弃`,推荐使用 `scroll-events` 属性 | Boolean | true/false | false |
| refreshDelay | data属性的数据更新后,scroll 的刷新延时 | Number | - | 20 |
| nestMode | 嵌套滚动模式,默认是`native`模式,只在开始滚动时判断是否到达边界并开启外层滚动,与浏览器原生的嵌套滚动保持一致。`free`模式下,内层滚动过程中只要触发边界,便会开启外层滚动。| String | 'native', 'free' | 'native' |

`options`中 better-scroll 的几个常用配置项,`scrollbar``pullDownRefresh``pullUpLoad`这三个配置即可设为 `Boolean``false` 关闭该功能,`true` 开启该功能,并使用默认子配置),也可设为`Object`,开启该功能并具体定制其子配置项。

Expand Down Expand Up @@ -375,4 +476,4 @@

| 属性名 | 说明 |
| - | - |
| scroll | 可以通过该属性获得内部实现滚动核心的 BScoll 实例,从而获得更多 BScoll 的底层能力,如监听`touchEnd`事件,获得滚动中的中间状态等,具体可查看[ better-scroll 文档](http://ustbhuangyi.github.io/better-scroll/doc/zh-hans/) |
| scroll | 可以通过该属性获得内部实现滚动核心的 BScoll 实例,从而获得更多 BScoll 的底层能力,如监听`touchEnd`事件,获得滚动中的中间状态等,具体可查看[ better-scroll 文档](http://ustbhuangyi.github.io/better-scroll/doc/zh-hans/) |
82 changes: 82 additions & 0 deletions example/pages/scroll/horizontal-scrolls.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<cube-page type="scroll-view" title="Scroll" class="option-demo">
<div slot="content" class="scroll-wrapper">
<div class="demo">
<cube-scroll
ref="scroll"
:data="items1"
direction="horizontal"
class="outer-horizontal-scroll">
<ul class="list-wrapper">
<li v-for="item in items1" class="list-item">{{ item }}</li>
<li class="list-item inner-horizontal-scroll">
<cube-scroll
ref="scroll"
:data="items2"
nestMode="free"
direction="horizontal">
<ul class="list-wrapper">
<li v-for="item in items2" class="list-item">{{ item }}</li>
</ul>
</cube-scroll>
</li>
<li v-for="item in items1" class="list-item">{{ item }}</li>
</ul>
</cube-scroll>
</div>
</div>
</cube-page>
</template>

<script type="text/ecmascript-6">
import CubePage from '../../components/cube-page.vue'
const _data1 = [
'👈🏻 outer 👉🏻 ',
'🙂 🤔 😄 🤨 😐 🙃 '
]
const _data2 = [
'👈🏻 inner 👉🏻 ',
'🙂 🤔 😄 🤨 😐 🙃 ',
'👈🏻 inner 👉🏻 ',
'😔 😕 🙃 🤑 😲 ☹️ ',
'👈🏻 inner 👉🏻 ',
'🐣 🐣 🐣 🐣 🐣 🐣 ',
'👈🏻 inner 👉🏻 ',
'🐥 🐥 🐥 🐥 🐥 🐥 '
]
export default {
data() {
return {
items1: _data1,
items2: _data2
}
},
components: {
CubePage
}
}
</script>

<style lang="stylus" rel="stylesheet/stylus">
.inner-horizontal-scroll
vertical-align: top
width: 200px
.outer-horizontal-scroll
.inner-horizontal-scroll
border: 1px solid rgba(0, 0, 0, 0.1)
border-radius: 5px
transform: rotate(0deg) // fix 子元素超出边框圆角部分不隐藏的问题
position: relative
.cube-scroll-content
display: inline-block
vertical-align: top
.list-wrapper
padding: 0 10px
line-height: 60px
white-space: nowrap
.list-item
display: inline-block
</style>
33 changes: 15 additions & 18 deletions example/pages/scroll/horizontal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@
<div slot="content" class="scroll-wrapper">
<div class="demo">
<div class="title">horizontal Demo</div>
<div class="horizontal-scroll-list-wrap">
<cube-scroll
ref="scroll"
:data="items"
direction="horizontal">
<ul class="list-wrapper">
<li v-for="item in items" class="list-item">{{ item }}</li>
</ul>
</cube-scroll>
</div>
<cube-scroll
ref="scroll"
:data="items"
direction="horizontal"
class="horizontal-scroll-list-wrap">
<ul class="list-wrapper">
<li v-for="item in items" class="list-item">{{ item }}</li>
</ul>
</cube-scroll>
</div>
</div>
</cube-page>
Expand Down Expand Up @@ -59,14 +58,12 @@ export default {
.horizontal-scroll-list-wrap
border: 1px solid rgba(0, 0, 0, 0.1)
border-radius: 5px
transform: rotate(0deg) // fix 子元素超出边框圆角部分不隐藏的问题
position: relative
.cube-scroll-content
display: inline-block
.list-wrapper
padding: 0 10px
line-height: 60px
white-space: nowrap
.list-item
display: inline-block
.list-wrapper
padding: 0 10px
line-height: 60px
white-space: nowrap
.list-item
display: inline-block
</style>
3 changes: 3 additions & 0 deletions example/pages/scroll/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
<cube-button @click="goTo('config')"><span class="scroll-example">3. Customized</span></cube-button>
<cube-button @click="goTo('jd')"><span class="scroll-example">4. JD</span></cube-button>
<cube-button @click="goTo('toutiao')"><span class="scroll-example">5. Toutiao</span></cube-button>
<cube-button @click="goTo('v-scrolls')"><span class="scroll-example">6. Vertical scrolls</span></cube-button>
<cube-button @click="goTo('h-scrolls')"><span class="scroll-example">7. Horizontal scrolls</span></cube-button>
<!-- <cube-button @click="goTo('textarea')"><span class="scroll-example">8. Textarea</span></cube-button> -->
</cube-button-group>
<cube-view></cube-view>
</div>
Expand Down
Loading

0 comments on commit 9027e6a

Please sign in to comment.