mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-23 18:00:27 +08:00
feat(Tab): add scrollspy prop (#5273)
This commit is contained in:
parent
9cd06f3b20
commit
4603e1633c
@ -5,8 +5,8 @@ exports[`renders demo correctly 1`] = `
|
|||||||
<div class="van-tabs van-tabs--line">
|
<div class="van-tabs van-tabs--line">
|
||||||
<div class="van-tabs__wrap van-hairline--top-bottom">
|
<div class="van-tabs__wrap van-hairline--top-bottom">
|
||||||
<div role="tablist" class="van-tabs__nav van-tabs__nav--line" style="border-color: #1989fa;">
|
<div role="tablist" class="van-tabs__nav van-tabs__nav--line" style="border-color: #1989fa;">
|
||||||
<div role="tab" aria-selected="true" class="van-tab van-tab--active"><span class="van-ellipsis">基础用法</span></div>
|
<div role="tab" aria-selected="true" class="van-tab van-tab--active van-ellipsis"><span class="van-tab__text">基础用法<!----></span></div>
|
||||||
<div role="tab" class="van-tab"><span class="van-ellipsis">自定义索引列表</span></div>
|
<div role="tab" class="van-tab van-ellipsis"><span class="van-tab__text">自定义索引列表<!----></span></div>
|
||||||
<div class="van-tabs__line" style="background-color: rgb(25, 137, 250); width: 0px; transform: translateX(0px) translateX(-50%);"></div>
|
<div class="van-tabs__line" style="background-color: rgb(25, 137, 250); width: 0px; transform: translateX(0px) translateX(-50%);"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,8 +5,8 @@ exports[`renders demo correctly 1`] = `
|
|||||||
<div class="van-tabs van-tabs--line">
|
<div class="van-tabs van-tabs--line">
|
||||||
<div class="van-tabs__wrap van-hairline--top-bottom">
|
<div class="van-tabs__wrap van-hairline--top-bottom">
|
||||||
<div role="tablist" class="van-tabs__nav van-tabs__nav--line">
|
<div role="tablist" class="van-tabs__nav van-tabs__nav--line">
|
||||||
<div role="tab" aria-selected="true" class="van-tab van-tab--active"><span class="van-ellipsis">基础用法</span></div>
|
<div role="tab" aria-selected="true" class="van-tab van-tab--active van-ellipsis"><span class="van-tab__text">基础用法<!----></span></div>
|
||||||
<div role="tab" class="van-tab"><span class="van-ellipsis">错误提示</span></div>
|
<div role="tab" class="van-tab van-ellipsis"><span class="van-tab__text">错误提示<!----></span></div>
|
||||||
<div class="van-tabs__line" style="width: 0px; transform: translateX(0px) translateX(-50%);"></div>
|
<div class="van-tabs__line" style="width: 0px; transform: translateX(0px) translateX(-50%);"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -170,6 +170,18 @@ In swipeable mode, you can switch tabs with swipe gestrue in the content
|
|||||||
</van-tabs>
|
</van-tabs>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Scrollspy
|
||||||
|
|
||||||
|
In scrollspy mode, the list of content will be tiled
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-tabs v-model="active" scrollspy sticky>
|
||||||
|
<van-tab v-for="index in 10" :title="'tab ' + index">
|
||||||
|
content {{ index }}
|
||||||
|
</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
```
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### Tabs Props
|
### Tabs Props
|
||||||
@ -193,6 +205,7 @@ In swipeable mode, you can switch tabs with swipe gestrue in the content
|
|||||||
| sticky | Whether to use sticky mode | *boolean* | `false` | - |
|
| sticky | Whether to use sticky mode | *boolean* | `false` | - |
|
||||||
| swipeable | Whether to switch tabs with swipe gestrue in the content | *boolean* | `false` | - |
|
| swipeable | Whether to switch tabs with swipe gestrue in the content | *boolean* | `false` | - |
|
||||||
| lazy-render | Whether to enable tab content lazy render | *boolean* | `true` | - |
|
| lazy-render | Whether to enable tab content lazy render | *boolean* | `true` | - |
|
||||||
|
| scrollspy | Whether to use scrollspy mode | *boolean* | `false` | - |
|
||||||
|
|
||||||
### Tab Props
|
### Tab Props
|
||||||
|
|
||||||
|
@ -174,6 +174,18 @@ export default {
|
|||||||
</van-tabs>
|
</van-tabs>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 滚动导航
|
||||||
|
|
||||||
|
通过`scrollspy`属性可以开启滚动导航模式,该模式下,内容将会平铺展示
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-tabs v-model="active" scrollspy sticky>
|
||||||
|
<van-tab v-for="index in 10" :title="'选项 ' + index">
|
||||||
|
内容 {{ index }}
|
||||||
|
</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
```
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### Tabs Props
|
### Tabs Props
|
||||||
@ -197,6 +209,7 @@ export default {
|
|||||||
| sticky | 是否使用粘性定位布局 | *boolean* | `false` | - |
|
| sticky | 是否使用粘性定位布局 | *boolean* | `false` | - |
|
||||||
| swipeable | 是否开启手势滑动切换 | *boolean* | `false` | - |
|
| swipeable | 是否开启手势滑动切换 | *boolean* | `false` | - |
|
||||||
| lazy-render | 是否开启标签页内容延迟渲染 | *boolean* | `true` | - |
|
| lazy-render | 是否开启标签页内容延迟渲染 | *boolean* | `true` | - |
|
||||||
|
| scrollspy | 是否开启滚动导航 | *boolean* | `false` | - |
|
||||||
|
|
||||||
### Tab Props
|
### Tab Props
|
||||||
|
|
||||||
|
@ -93,6 +93,14 @@
|
|||||||
</van-tab>
|
</van-tab>
|
||||||
</van-tabs>
|
</van-tabs>
|
||||||
</demo-block>
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block :title="$t('title10')">
|
||||||
|
<van-tabs scrollspy sticky>
|
||||||
|
<van-tab :title="$t('tab') + index" v-for="index in 10" :key="index">
|
||||||
|
{{ $t('content') }} {{ index }}
|
||||||
|
</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
</demo-block>
|
||||||
</demo-section>
|
</demo-section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -109,6 +117,7 @@ export default {
|
|||||||
title7: '自定义标签',
|
title7: '自定义标签',
|
||||||
title8: '切换动画',
|
title8: '切换动画',
|
||||||
title9: '滑动切换',
|
title9: '滑动切换',
|
||||||
|
title10: '滚动导航',
|
||||||
disabled: ' 已被禁用',
|
disabled: ' 已被禁用',
|
||||||
matchByName: '通过名称匹配'
|
matchByName: '通过名称匹配'
|
||||||
},
|
},
|
||||||
@ -123,6 +132,7 @@ export default {
|
|||||||
title7: 'Custom Tab',
|
title7: 'Custom Tab',
|
||||||
title8: 'Switch Animation',
|
title8: 'Switch Animation',
|
||||||
title9: 'Swipeable',
|
title9: 'Swipeable',
|
||||||
|
title10: 'Scrollspy',
|
||||||
disabled: ' is disabled',
|
disabled: ' is disabled',
|
||||||
matchByName: 'Match By Name'
|
matchByName: 'Match By Name'
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,8 @@ export default createComponent({
|
|||||||
|
|
||||||
render(h) {
|
render(h) {
|
||||||
const { slots, isActive } = this;
|
const { slots, isActive } = this;
|
||||||
const shouldRender = this.inited || !this.parent.lazyRender;
|
const shouldRender = this.inited || this.parent.scrollspy || !this.parent.lazyRender;
|
||||||
|
const show = this.parent.scrollspy || isActive;
|
||||||
const Content = shouldRender ? slots() : h();
|
const Content = shouldRender ? slots() : h();
|
||||||
|
|
||||||
if (this.parent.animated) {
|
if (this.parent.animated) {
|
||||||
@ -62,7 +63,7 @@ export default createComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div vShow={isActive} role="tabpanel" class={bem('pane')}>
|
<div vShow={show} role="tabpanel" class={bem('pane')}>
|
||||||
{Content}
|
{Content}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -275,5 +275,60 @@ exports[`renders demo correctly 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="van-tabs van-tabs--line">
|
||||||
|
<div>
|
||||||
|
<div class="van-sticky">
|
||||||
|
<div class="van-tabs__wrap van-tabs__wrap--scrollable van-hairline--top-bottom">
|
||||||
|
<div role="tablist" class="van-tabs__nav van-tabs__nav--line">
|
||||||
|
<div role="tab" aria-selected="true" class="van-tab van-tab--active van-ellipsis" style="flex-basis: 22%;"><span class="van-tab__text">标签 1<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis" style="flex-basis: 22%;"><span class="van-tab__text">标签 2<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis" style="flex-basis: 22%;"><span class="van-tab__text">标签 3<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis" style="flex-basis: 22%;"><span class="van-tab__text">标签 4<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis" style="flex-basis: 22%;"><span class="van-tab__text">标签 5<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis" style="flex-basis: 22%;"><span class="van-tab__text">标签 6<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis" style="flex-basis: 22%;"><span class="van-tab__text">标签 7<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis" style="flex-basis: 22%;"><span class="van-tab__text">标签 8<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis" style="flex-basis: 22%;"><span class="van-tab__text">标签 9<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis" style="flex-basis: 22%;"><span class="van-tab__text">标签 10<!----></span></div>
|
||||||
|
<div class="van-tabs__line" style="width: 0px; transform: translateX(0px) translateX(-50%);"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-tabs__content">
|
||||||
|
<div role="tabpanel" class="van-tab__pane">
|
||||||
|
内容 1
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">
|
||||||
|
内容 2
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">
|
||||||
|
内容 3
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">
|
||||||
|
内容 4
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">
|
||||||
|
内容 5
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">
|
||||||
|
内容 6
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">
|
||||||
|
内容 7
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">
|
||||||
|
内容 8
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">
|
||||||
|
内容 9
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">
|
||||||
|
内容 10
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -223,6 +223,50 @@ exports[`render nav-left & nav-right slot 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`scrollspy 1`] = `
|
||||||
|
<div class="van-tabs van-tabs--line">
|
||||||
|
<div>
|
||||||
|
<div class="van-sticky">
|
||||||
|
<div class="van-tabs__wrap van-hairline--top-bottom">
|
||||||
|
<div role="tablist" class="van-tabs__nav van-tabs__nav--line">
|
||||||
|
<div role="tab" aria-selected="true" class="van-tab van-tab--active van-ellipsis"><span class="van-tab__text">title1<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis"><span class="van-tab__text">title2<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis"><span class="van-tab__text">title3<!----></span></div>
|
||||||
|
<div class="van-tabs__line" style="width: 0px; transform: translateX(0px) translateX(-50%);"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-tabs__content">
|
||||||
|
<div role="tabpanel" class="van-tab__pane">Text</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">Text</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">Text</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`scrollspy 2`] = `
|
||||||
|
<div class="van-tabs van-tabs--line">
|
||||||
|
<div>
|
||||||
|
<div class="van-sticky">
|
||||||
|
<div class="van-tabs__wrap van-hairline--top-bottom">
|
||||||
|
<div role="tablist" class="van-tabs__nav van-tabs__nav--line">
|
||||||
|
<div role="tab" class="van-tab van-ellipsis"><span class="van-tab__text">title1<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-ellipsis"><span class="van-tab__text">title2<!----></span></div>
|
||||||
|
<div role="tab" class="van-tab van-tab--active van-ellipsis" aria-selected="true"><span class="van-tab__text">title3<!----></span></div>
|
||||||
|
<div class="van-tabs__line" style="width: 0px; transform: translateX(0px) translateX(-50%); transition-duration: 0.3s;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-tabs__content">
|
||||||
|
<div role="tabpanel" class="van-tab__pane">Text</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">Text</div>
|
||||||
|
<div role="tabpanel" class="van-tab__pane">Text</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`swipe to switch tab 1`] = `
|
exports[`swipe to switch tab 1`] = `
|
||||||
<div class="van-tabs van-tabs--line">
|
<div class="van-tabs van-tabs--line">
|
||||||
<div class="van-tabs__wrap van-hairline--top-bottom">
|
<div class="van-tabs__wrap van-hairline--top-bottom">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Tab from '..';
|
import Tab from '..';
|
||||||
import Tabs from '../../tabs';
|
import Tabs from '../../tabs';
|
||||||
import { mount, later, triggerDrag } from '../../../test';
|
import { mount, later, triggerDrag, mockScrollTop } from '../../../test';
|
||||||
|
|
||||||
Vue.use(Tab);
|
Vue.use(Tab);
|
||||||
Vue.use(Tabs);
|
Vue.use(Tabs);
|
||||||
@ -263,3 +263,33 @@ test('info prop', () => {
|
|||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('scrollspy', async () => {
|
||||||
|
const onChange = jest.fn();
|
||||||
|
window.scrollTo = jest.fn();
|
||||||
|
|
||||||
|
const wrapper = mount({
|
||||||
|
template: `
|
||||||
|
<van-tabs scrollspy sticky @change="onChange">
|
||||||
|
<van-tab name="a" title="title1">Text</van-tab>
|
||||||
|
<van-tab name="b" title="title2">Text</van-tab>
|
||||||
|
<van-tab name="c" title="title3">Text</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
`,
|
||||||
|
methods: {
|
||||||
|
onChange
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await later();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
|
||||||
|
const tabs = wrapper.findAll('.van-tab');
|
||||||
|
tabs.at(2).trigger('click');
|
||||||
|
expect(onChange).toHaveBeenCalledWith('c', 'title3');
|
||||||
|
|
||||||
|
await later();
|
||||||
|
mockScrollTop(100);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
expect(onChange).toHaveBeenCalledWith('c', 'title3');
|
||||||
|
});
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { createNamespace, isDef, addUnit } from '../utils';
|
import { createNamespace, isDef, addUnit } from '../utils';
|
||||||
import { scrollLeftTo } from './utils';
|
import { scrollLeftTo, scrollTopTo } from './utils';
|
||||||
import { route } from '../utils/router';
|
import { route } from '../utils/router';
|
||||||
import { isHidden } from '../utils/dom/style';
|
import { isHidden } from '../utils/dom/style';
|
||||||
|
import { on, off } from '../utils/dom/event';
|
||||||
import { ParentMixin } from '../mixins/relation';
|
import { ParentMixin } from '../mixins/relation';
|
||||||
import { BindEventMixin } from '../mixins/bind-event';
|
import { BindEventMixin } from '../mixins/bind-event';
|
||||||
import { BORDER_TOP_BOTTOM } from '../utils/constant';
|
import { BORDER_TOP_BOTTOM } from '../utils/constant';
|
||||||
import { setRootScrollTop, getElementTop } from '../utils/dom/scroll';
|
import { setRootScrollTop, getElementTop, getVisibleHeight, getVisibleTop } from '../utils/dom/scroll';
|
||||||
import Title from './Title';
|
import Title from './Title';
|
||||||
import Content from './Content';
|
import Content from './Content';
|
||||||
import Sticky from '../sticky';
|
import Sticky from '../sticky';
|
||||||
@ -17,6 +18,9 @@ export default createComponent({
|
|||||||
ParentMixin('vanTabs'),
|
ParentMixin('vanTabs'),
|
||||||
BindEventMixin(function(bind) {
|
BindEventMixin(function(bind) {
|
||||||
bind(window, 'resize', this.resize, true);
|
bind(window, 'resize', this.resize, true);
|
||||||
|
if (this.scrollspy) {
|
||||||
|
bind(window, 'scroll', this.onScrollspyScroll, true);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -29,6 +33,7 @@ export default createComponent({
|
|||||||
sticky: Boolean,
|
sticky: Boolean,
|
||||||
animated: Boolean,
|
animated: Boolean,
|
||||||
swipeable: Boolean,
|
swipeable: Boolean,
|
||||||
|
scrollspy: Boolean,
|
||||||
background: String,
|
background: String,
|
||||||
lineWidth: [Number, String],
|
lineWidth: [Number, String],
|
||||||
lineHeight: [Number, String],
|
lineHeight: [Number, String],
|
||||||
@ -97,6 +102,13 @@ export default createComponent({
|
|||||||
if (activeTab) {
|
if (activeTab) {
|
||||||
return activeTab.computedName;
|
return activeTab.computedName;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollOffset() {
|
||||||
|
if (this.sticky) {
|
||||||
|
return this.offsetTop + this.tabHeight;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -123,9 +135,17 @@ export default createComponent({
|
|||||||
this.setLine();
|
this.setLine();
|
||||||
|
|
||||||
// scroll to correct position
|
// scroll to correct position
|
||||||
if (this.stickyFixed) {
|
if (this.stickyFixed && !this.scrollspy) {
|
||||||
setRootScrollTop(Math.ceil(getElementTop(this.$el) - this.offsetTop));
|
setRootScrollTop(Math.ceil(getElementTop(this.$el) - this.offsetTop));
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollspy(val) {
|
||||||
|
if (val) {
|
||||||
|
on(window, 'scroll', this.onScrollspyScroll, true);
|
||||||
|
} else {
|
||||||
|
off(window, 'scroll', this.onScrollspyScroll);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -147,6 +167,7 @@ export default createComponent({
|
|||||||
onShow() {
|
onShow() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.inited = true;
|
this.inited = true;
|
||||||
|
this.tabHeight = getVisibleHeight(this.$refs.wrap);
|
||||||
this.scrollIntoView(true);
|
this.scrollIntoView(true);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -236,6 +257,7 @@ export default createComponent({
|
|||||||
this.$emit('disabled', computedName, title);
|
this.$emit('disabled', computedName, title);
|
||||||
} else {
|
} else {
|
||||||
this.setCurrentIndex(index);
|
this.setCurrentIndex(index);
|
||||||
|
this.scrollToCurrentContent();
|
||||||
this.$emit('click', computedName, title);
|
this.$emit('click', computedName, title);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -258,6 +280,44 @@ export default createComponent({
|
|||||||
onScroll(params) {
|
onScroll(params) {
|
||||||
this.stickyFixed = params.isFixed;
|
this.stickyFixed = params.isFixed;
|
||||||
this.$emit('scroll', params);
|
this.$emit('scroll', params);
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollToCurrentContent() {
|
||||||
|
if (this.scrollspy) {
|
||||||
|
this.clickedScroll = true;
|
||||||
|
const instance = this.children[this.currentIndex];
|
||||||
|
const el = instance && instance.$el;
|
||||||
|
if (el) {
|
||||||
|
const to = Math.ceil(getElementTop(el)) - this.scrollOffset;
|
||||||
|
scrollTopTo(to, this.duration, () => {
|
||||||
|
this.clickedScroll = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onScrollspyScroll() {
|
||||||
|
if (this.scrollspy && !this.clickedScroll) {
|
||||||
|
const index = this.getCurrentIndexOnScroll();
|
||||||
|
this.setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getCurrentIndexOnScroll() {
|
||||||
|
let i;
|
||||||
|
|
||||||
|
for (i = 0; i < this.children.length; i++) {
|
||||||
|
const top = getVisibleTop(this.children[i].$el);
|
||||||
|
|
||||||
|
if (top > this.scrollOffset) {
|
||||||
|
if (i === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i - 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { raf } from '../utils/dom/raf';
|
import { raf } from '../utils/dom/raf';
|
||||||
|
import { getRootScrollTop, setRootScrollTop } from '../utils/dom/scroll';
|
||||||
|
|
||||||
export function scrollLeftTo(el: HTMLElement, to: number, duration: number) {
|
export function scrollLeftTo(el: HTMLElement, to: number, duration: number) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
@ -15,3 +16,28 @@ export function scrollLeftTo(el: HTMLElement, to: number, duration: number) {
|
|||||||
|
|
||||||
animate();
|
animate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function scrollTopTo(to: number, duration: number, cb: Function) {
|
||||||
|
let current = getRootScrollTop();
|
||||||
|
const toDown = current < to;
|
||||||
|
const frames = duration === 0 ? 1 : Math.round((duration * 1000) / 16);
|
||||||
|
const pxPerFrames = (to - current) / frames;
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
current += pxPerFrames;
|
||||||
|
|
||||||
|
if ((toDown && current > to) || (!toDown && current < to)) {
|
||||||
|
current = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRootScrollTop(current);
|
||||||
|
|
||||||
|
if ((toDown && current < to) || (!toDown && current > to)) {
|
||||||
|
raf(animate);
|
||||||
|
} else {
|
||||||
|
cb && cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animate();
|
||||||
|
}
|
||||||
|
@ -59,3 +59,7 @@ export function getVisibleHeight(element: ScrollElement) {
|
|||||||
? element.innerHeight
|
? element.innerHeight
|
||||||
: (<HTMLElement>element).getBoundingClientRect().height;
|
: (<HTMLElement>element).getBoundingClientRect().height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getVisibleTop(element: ScrollElement) {
|
||||||
|
return element === window ? 0 : (<HTMLElement>element).getBoundingClientRect().top;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user