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>
|