123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- <template>
- <view class="u-index-list">
-
- <list
- :scrollTop="scrollTop"
- enable-back-to-top
- :offset-accuracy="1"
- :style="{
- maxHeight: $u.addUnit(scrollViewHeight)
- }"
- @scroll="scrollHandler"
- ref="uList"
- >
- <cell
- v-if="$slots.header"
- ref="header"
- >
- <slot name="header" />
- </cell>
- <slot />
- <cell v-if="$slots.footer">
- <slot name="footer" />
- </cell>
- </list>
-
-
- <scroll-view
- :scrollTop="scrollTop"
- :scrollIntoView="scrollIntoView"
- :offset-accuracy="1"
- :style="{
- maxHeight: $u.addUnit(scrollViewHeight)
- }"
- scroll-y
- @scroll="scrollHandler"
- ref="uList"
- >
- <view v-if="$slots.header">
- <slot name="header" />
- </view>
- <slot />
- <view v-if="$slots.footer">
- <slot name="footer" />
- </view>
- </scroll-view>
-
- <view
- class="u-index-list__letter"
- ref="u-index-list__letter"
- :style="{ top: $u.addUnit(letterInfo.top || 100) }"
- @touchstart="touchStart"
- @touchmove.stop.prevent="touchMove"
- @touchend.stop.prevent="touchEnd"
- @touchcancel.stop.prevent="touchEnd"
- >
- <view
- class="u-index-list__letter__item"
- v-for="(item, index) in uIndexList"
- :key="index"
- :style="{
- backgroundColor: activeIndex === index ? activeColor : 'transparent'
- }"
- >
- <text
- class="u-index-list__letter__item__index"
- :style="{color: activeIndex === index ? '#fff' : inactiveColor}"
- >{{ item }}</text>
- </view>
- </view>
- <u-transition
- mode="fade"
- :show="touching"
- :customStyle="{
- position: 'fixed',
- right: '50px',
- top: $u.addUnit(indicatorTop),
- zIndex: 2
- }"
- >
- <view
- class="u-index-list__indicator"
- :class="['u-index-list__indicator--show']"
- :style="{
- height: $u.addUnit(indicatorHeight),
- width: $u.addUnit(indicatorHeight)
- }"
- >
- <text class="u-index-list__indicator__text">{{ uIndexList[activeIndex] }}</text>
- </view>
- </u-transition>
- </view>
- </template>
- <script>
- const indexList = () => {
- const indexList = [];
- const charCodeOfA = 'A'.charCodeAt(0);
- for (let i = 0; i < 26; i++) {
- indexList.push(String.fromCharCode(charCodeOfA + i));
- }
- return indexList;
- }
- import props from './props.js';
-
-
- const dom = uni.requireNativePlugin('dom')
-
-
-
- export default {
- name: 'u-index-list',
- mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
-
-
- options: {
- virtualHost: true
- },
-
- data() {
- return {
-
- activeIndex: -1,
- touchmoveIndex: 1,
-
- letterInfo: {
- height: 0,
- itemHeight: 0,
- top: 0
- },
-
- indicatorHeight: 50,
-
-
-
- touching: false,
-
- scrollTop: 0,
-
- scrollViewHeight: 0,
-
- sys: uni.$u.sys(),
- scrolling: false,
- scrollIntoView: '',
- }
- },
- computed: {
-
- uIndexList() {
- return this.indexList.length ? this.indexList : indexList()
- },
-
- indicatorTop() {
- const {
- top,
- itemHeight
- } = this.letterInfo
- return Math.floor(top + itemHeight * this.activeIndex + itemHeight / 2 - this.indicatorHeight / 2)
- }
- },
- watch: {
-
- uIndexList: {
- immediate: true,
- handler() {
- uni.$u.sleep().then(() => {
- this.setIndexListLetterInfo()
- })
- }
- }
- },
- created() {
- this.children = []
- this.anchors = []
- this.init()
- },
- mounted() {
- this.setIndexListLetterInfo()
- },
- methods: {
- init() {
-
-
-
- this.scrollViewHeight = this.sys.windowHeight - this.customNavHeight
- },
-
- touchStart(e) {
-
- const touchStart = e.changedTouches[0]
- if (!touchStart) return
- this.touching = true
- const {
- pageY
- } = touchStart
-
- const currentIndex = this.getIndexListLetter(pageY)
- this.setValueForTouch(currentIndex)
- },
-
- touchMove(e) {
-
- let touchMove = e.changedTouches[0]
- if (!touchMove) return;
-
- if (!this.touching) {
- this.touching = true
- }
- const {
- pageY
- } = touchMove
- const currentIndex = this.getIndexListLetter(pageY)
- this.setValueForTouch(currentIndex)
- },
-
- touchEnd(e) {
-
- uni.$u.sleep(300).then(() => {
- this.touching = false
- })
- },
-
- getIndexListLetterRect() {
- return new Promise(resolve => {
-
-
- this.$uGetRect('.u-index-list__letter').then(size => {
- resolve(size)
- })
-
-
- const ref = this.$refs['u-index-list__letter']
- dom.getComponentRect(ref, res => {
- resolve(res.size)
- })
-
- })
- },
-
- setIndexListLetterInfo() {
- this.getIndexListLetterRect().then(size => {
- const {
- height
- } = size
- const sys = uni.$u.sys()
- const windowHeight = sys.windowHeight
- let customNavHeight = 0
-
- if (this.customNavHeight == 0) {
-
- customNavHeight = sys.windowTop
-
-
-
- customNavHeight = -(sys.statusBarHeight + 44)
-
- } else {
- customNavHeight = uni.$u.getPx(this.customNavHeight)
- }
- this.letterInfo = {
- height,
-
- top: (windowHeight - height) / 2 + customNavHeight / 2,
- itemHeight: Math.floor(height / this.uIndexList.length)
- }
- })
- },
-
- getIndexListLetter(pageY) {
- const {
- top,
- height,
- itemHeight
- } = this.letterInfo
-
-
- pageY += uni.$u.sys().windowTop
-
-
- if (pageY < top) {
- return 0
- } else if (pageY >= top + height) {
-
- return this.uIndexList.length - 1
- } else {
-
- return Math.floor((pageY - top) / itemHeight);
- }
- },
-
- setValueForTouch(currentIndex) {
-
- if (currentIndex === this.activeIndex) return
- this.activeIndex = currentIndex
-
-
- this.scrollIntoView = `u-index-item-${this.uIndexList[currentIndex].charCodeAt(0)}`
-
-
-
- this.scrollTop = this.children[currentIndex].top
-
-
-
- const anchor = `u-index-anchor-${this.uIndexList[currentIndex]}`
- dom.scrollToElement(this.anchors[currentIndex].$refs[anchor], {
- offset: 0,
- animated: false
- })
-
- },
- getHeaderRect() {
-
- return new Promise(resolve => {
- dom.getComponentRect(this.$refs.header, res => {
- resolve(res.size)
- })
- })
- },
-
- async scrollHandler(e) {
- if (this.touching || this.scrolling) return
-
- this.scrolling = true
- uni.$u.sleep(10).then(() => {
- this.scrolling = false
- })
- let scrollTop = 0
- const len = this.children.length
- let children = this.children
- const anchors = this.anchors
-
-
- scrollTop = Math.abs(e.contentOffset.y)
-
- const header = await this.getHeaderRect()
-
- let top = header.height
-
- children = this.children.map((item, index) => {
- const child = {
- height: item.height,
- top
- }
-
- top += item.height + anchors[index].height
- return child
- })
-
-
-
- scrollTop = e.detail.scrollTop
-
- for (let i = 0; i < len; i++) {
- const item = children[i],
- nextItem = children[i + 1]
-
- if (scrollTop <= children[0].top || scrollTop >= children[len - 1].top + children[len -
- 1].height) {
- this.activeIndex = -1
- break
- } else if (!nextItem) {
-
- this.activeIndex = len - 1
- break
- } else if (scrollTop > item.top && scrollTop < nextItem.top) {
- this.activeIndex = i
- break
- }
- }
- },
- },
- }
- </script>
- <style lang="scss" scoped>
- @import "../../libs/css/components.scss";
- .u-index-list {
- &__letter {
- position: fixed;
- right: 0;
- text-align: center;
- z-index: 3;
- padding: 0 6px;
- &__item {
- width: 16px;
- height: 16px;
- border-radius: 100px;
- margin: 1px 0;
- @include flex;
- align-items: center;
- justify-content: center;
- &--active {
- background-color: $u-primary;
- }
- &__index {
- font-size: 12px;
- text-align: center;
- line-height: 12px;
- }
- }
- }
- &__indicator {
- width: 50px;
- height: 50px;
- border-radius: 100px 100px 0 100px;
- text-align: center;
- color: #ffffff;
- background-color: #c9c9c9;
- transform: rotate(-45deg);
- @include flex;
- justify-content: center;
- align-items: center;
- &__text {
- font-size: 28px;
- line-height: 28px;
- font-weight: bold;
- color: #fff;
- transform: rotate(45deg);
- text-align: center;
- }
- }
- }
- </style>
|