UNPKG

4.65 kBPlain TextView Raw
1<template>
2 <div class="mint-indexlist">
3 <ul class="mint-indexlist-content" ref="content" :style="{ 'height': currentHeight + 'px', 'margin-right': navWidth + 'px'}">
4 <slot></slot>
5 </ul>
6
7 <div class="mint-indexlist-nav" @touchstart="handleTouchStart" ref="nav">
8 <ul class="mint-indexlist-navlist">
9 <li class="mint-indexlist-navitem" v-for="section in sections">{{ section.index }}</li>
10 </ul>
11 </div>
12
13 <div class="mint-indexlist-indicator" v-if="showIndicator" v-show="moving">{{ currentIndicator }}</div>
14 </div>
15</template>
16
17<style lang="css">
18 @import "../../../src/style/var.css";
19
20 @component-namespace mint {
21 @component indexlist {
22 width: 100%;
23 position: relative;
24 overflow: hidden;
25
26 @descendent content {
27 margin: 0;
28 padding: 0;
29 overflow: auto;
30 }
31
32 @descendent nav {
33 position: absolute;
34 top: 0;
35 bottom: 0;
36 right: 0;
37 margin: 0;
38 background-color: #fff;
39 border-left: solid 1px #ddd;
40 text-align: center;
41 max-height: 100%;
42 display: flex;
43 flex-direction: column;
44 justify-content: center;
45 }
46
47 @descendent navlist {
48 padding: 0;
49 margin: 0;
50 list-style: none;
51 max-height: 100%;
52 display: flex;
53 flex-direction: column;
54 }
55
56 @descendent navitem {
57 padding: 2px 6px;
58 font-size: 12px;
59 user-select: none;
60 -webkit-touch-callout: none;
61 }
62
63 @descendent indicator {
64 position: absolute;
65 size: 50px;
66 top: 50%;
67 left: 50%;
68 transform: translate(-50%, -50%);
69 text-align: center;
70 line-height: 50px;
71 background-color: rgba(0, 0, 0, .7);
72 border-radius: 5px;
73 color: #fff;
74 font-size: 22px;
75 }
76 }
77 }
78</style>
79
80<script type="text/babel">
81 export default {
82 name: 'mt-index-list',
83
84 props: {
85 height: Number,
86 showIndicator: {
87 type: Boolean,
88 default: true
89 }
90 },
91
92 data() {
93 return {
94 sections: [],
95 navWidth: 0,
96 indicatorTime: null,
97 moving: false,
98 firstSection: null,
99 currentIndicator: '',
100 currentHeight: this.height,
101 navOffsetX: 0
102 };
103 },
104
105 watch: {
106 sections() {
107 this.init();
108 },
109 height(val) {
110 if (val) {
111 this.currentHeight = val;
112 }
113 }
114 },
115
116 methods: {
117 init() {
118 this.$nextTick(() => {
119 this.navWidth = this.$refs.nav.clientWidth;
120 });
121 let listItems = this.$refs.content.getElementsByTagName('li');
122 if (listItems.length > 0) {
123 this.firstSection = listItems[0];
124 }
125 },
126
127 handleTouchStart(e) {
128 if (e.target.tagName !== 'LI') {
129 return;
130 }
131 this.navOffsetX = e.changedTouches[0].clientX;
132 this.scrollList(e.changedTouches[0].clientY);
133 if (this.indicatorTime) {
134 clearTimeout(this.indicatorTime);
135 }
136 this.moving = true;
137 window.addEventListener('touchmove', this.handleTouchMove);
138 window.addEventListener('touchend', this.handleTouchEnd);
139 },
140
141 handleTouchMove(e) {
142 e.preventDefault();
143 this.scrollList(e.changedTouches[0].clientY);
144 },
145
146 handleTouchEnd() {
147 this.indicatorTime = setTimeout(() => {
148 this.moving = false;
149 this.currentIndicator = '';
150 }, 500);
151 window.removeEventListener('touchmove', this.handleTouchMove);
152 window.removeEventListener('touchend', this.handleTouchEnd);
153 },
154
155 scrollList(y) {
156 let currentItem = document.elementFromPoint(this.navOffsetX, y);
157 if (!currentItem || !currentItem.classList.contains('mint-indexlist-navitem')) {
158 return;
159 }
160 this.currentIndicator = currentItem.innerText;
161 let targets = this.sections.filter(section => section.index === currentItem.innerText);
162 let targetDOM;
163 if (targets.length > 0) {
164 targetDOM = targets[0].$el;
165 this.$refs.content.scrollTop = targetDOM.getBoundingClientRect().top - this.firstSection.getBoundingClientRect().top;
166 }
167 }
168 },
169
170 mounted() {
171 if (!this.currentHeight) {
172 window.scrollTo(0, 0);
173 requestAnimationFrame(()=>{
174 this.currentHeight = document.documentElement.clientHeight - this.$refs.content.getBoundingClientRect().top;
175 });
176 }
177 this.init();
178 }
179 };
180</script>