123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- <template>
- <view ref="u-index-list" class="u-index-list">
-
- <list
- :scrollTop="scrollTop"
- enable-back-to-top
- :offset-accuracy="1"
- :style="{
- maxHeight: addUnit(scrollViewHeight)
- }"
- @scroll="scrollHandler"
- ref="u-index-list__scroll-view"
- class="u-index-list__scroll-view"
- >
- <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: addUnit(scrollViewHeight)
- }"
- scroll-y
- @scroll="scrollHandler"
- ref="u-index-list__scroll-view"
- class="u-index-list__scroll-view"
- >
- <view class="u-index-list__header" v-if="$slots.header">
- <slot name="header" />
- </view>
- <slot />
- <view class="u-index-list__footer" v-if="$slots.footer">
- <slot name="footer" />
- </view>
- </scroll-view>
-
- <view
- class="u-index-list__letter"
- ref="u-index-list__letter"
- :style="{top: addUnit(letterInfo.top) , transform: 'translateY(-50%)'}"
- @touchstart.prevent="touchStart"
- @touchmove.prevent="touchMove"
- @touchend.prevent="touchEnd"
- @touchcancel.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: 'absolute',
- right: '50px',
- top: addUnit(indicatorTop, 'px'),
- zIndex: 3
- }"
- >
- <view
- class="u-index-list__indicator"
- :class="['u-index-list__indicator--show']"
- :style="{
- height: addUnit(indicatorHeight),
- width: 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';
- import { mpMixin } from '../../libs/mixin/mpMixin';
- import { mixin } from '../../libs/mixin/mixin';
- import { addUnit, getWindowInfo, sleep, getPx } from '../../libs/function/index';
-
-
- const dom = uni.requireNativePlugin('dom')
-
-
-
- export default {
- name: 'u-index-list',
- mixins: [mpMixin, 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: {},
- scrolling: false,
- scrollIntoView: '',
- pageY: 0,
- topOffset: 0
- }
- },
- computed: {
-
- uIndexList() {
- return this.indexList.length ? this.indexList : indexList()
- },
-
- indicatorTop() {
- const {
- top,
- height,
- itemHeight
- } = this.letterInfo
- return Math.floor(top - (height / 2) + itemHeight * this.activeIndex + itemHeight - 70 / 2)
- }
- },
- watch: {
-
- uIndexList: {
- immediate: false,
- handler() {
- sleep(30).then(() => {
- this.setIndexListLetterInfo()
- })
- }
- }
- },
- created() {
- this.children = []
- this.anchors = []
- this.sys = getWindowInfo()
- },
- mounted() {
- this.init()
- sleep(50).then(() => {
- this.setIndexListLetterInfo()
- })
- },
- methods: {
- addUnit,
- init() {
-
-
-
- let customNavHeight = getPx(this.customNavHeight)
-
- this.getIndexListRect().then(async sizeScroll => {
- this.scrollViewHeight = sizeScroll.height ? sizeScroll.height : this.sys.windowHeight - customNavHeight
- this.topOffset = this.sys.windowHeight - this.scrollViewHeight
-
-
- })
- },
-
- touchStart(e) {
-
- const touchStartData = e.changedTouches[0]
- if (!touchStartData) return
- this.touching = true
- const {
- pageY,
- screenY
- } = touchStartData
-
-
-
- const currentIndex = this.getIndexListLetter(screenY - 68)
-
-
- 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,
- screenY
- } = touchMove
-
-
- const currentIndex = this.getIndexListLetter(screenY - 68)
-
-
- const currentIndex = this.getIndexListLetter(pageY)
-
- this.setValueForTouch(currentIndex)
- },
-
- touchEnd(e) {
-
- 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)
- })
-
- })
- },
- getIndexListScrollViewRect() {
- return new Promise(resolve => {
-
-
- this.$uGetRect('.u-index-list__scroll-view').then(size => {
- resolve(size)
- })
-
-
- const ref = this.$refs['u-index-list__scroll-view']
- dom.getComponentRect(ref, res => {
- resolve(res.size)
- })
-
- })
- },
- getIndexListRect() {
- return new Promise(resolve => {
-
-
- this.$uGetRect('.u-index-list').then(size => {
- resolve(size)
- })
-
-
- const ref = this.$refs['u-index-list']
- dom.getComponentRect(ref, res => {
- resolve(res.size)
- })
-
- })
- },
-
- setIndexListLetterInfo() {
- this.getIndexListLetterRect().then(size => {
-
- const {
- height
- } = size
- const sysData = getWindowInfo()
- const windowHeight = sysData.windowHeight
- let customNavHeight = 0
-
- if (this.customNavHeight == 0) {
-
- customNavHeight = sysData.windowTop
-
-
-
- customNavHeight = -(sysData.statusBarHeight + 44)
-
- } else {
- customNavHeight = getPx(this.customNavHeight)
- }
- this.getIndexListScrollViewRect().then(sizeScroll => {
-
- this.letterInfo = {
- height,
-
- top: sizeScroll.height / 2,
-
- itemHeight: Math.floor(height / this.uIndexList.length)
- }
- })
- })
- },
-
- getIndexListLetter(pageY) {
- this.pageY = pageY
- let {
- top,
- height,
- itemHeight
- } = this.letterInfo
- let index = this.currentIndex;
-
-
-
-
-
-
-
- top = top - (height / 2)
- pageY = pageY - this.topOffset
-
-
-
-
-
-
- if (pageY < top) {
- index = 0
- } else if (pageY >= top + height) {
-
- index = this.uIndexList.length - 1
- } else {
-
- index = Math.floor((pageY - top) / itemHeight)
- }
-
- return index
- },
-
- async setValueForTouch(currentIndex) {
-
- if (currentIndex === this.activeIndex) return
- this.activeIndex = currentIndex
-
-
- this.scrollIntoView = `u-index-item-${this.uIndexList[currentIndex].charCodeAt(0)}`
-
-
-
- const customNavHeight = this.customNavHeight
-
- const header = await this.getHeaderRect()
-
- let top = header.height
-
- const anchors = this.anchors
-
- let children = this.children.map((item, index) => {
- const child = {
- height: item.height,
- top: top
- }
-
- top = top + item.height
-
-
- top = top + anchors[index].height
-
- return child
- })
-
- if (children[currentIndex]?.top) {
- this.scrollTop = children[currentIndex].top - getPx(customNavHeight)
- }
-
-
-
- const anchor = `u-index-anchor-${this.uIndexList[currentIndex]}`
-
- dom.scrollToElement(this.anchors[currentIndex].$refs[anchor], {
- offset: 0,
- animated: false
- })
-
- },
- getHeaderRect() {
-
- return new Promise(resolve => {
- if (!this.$slots.header) {
- resolve({
- width: 0,
- height: 0
- })
- }
-
- this.$uGetRect('.u-index-list__header').then(size => {
- resolve(size)
- })
-
-
- let headerRef = this.$refs.header
- if (!headerRef) {
- resolve({
- width: 0,
- height: 0
- })
- }
- dom.getComponentRect(headerRef, res => {
- resolve(res.size)
- })
-
- })
- },
-
- async scrollHandler(e) {
- if (this.touching || this.scrolling) return
-
- this.scrolling = true
- sleep(10).then(() => {
- this.scrolling = false
- })
- let scrollTop = 0
- const len = this.children.length
- let children = this.children
-
-
- let sys = getWindowInfo()
- scrollTop = Math.abs(e.contentOffset.y) / 10
-
-
-
- const header = await this.getHeaderRect()
-
- let top = header.height
- const anchors = this.anchors
-
- children = this.children.map((item, index) => {
- const child = {
- height: item.height,
- top: top
- }
-
- top = top + item.height
-
-
- top = top + anchors[index].height
-
- return child
- })
-
-
-
- scrollTop = e.detail.scrollTop
-
-
-
- scrollTop = scrollTop + getPx(this.customNavHeight)
- 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: absolute;
- right: 0;
- text-align: center;
- z-index: 3;
- padding: 0 6px;
- width: 30px;
- &__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>
|