123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- <template>
- <nav
- class="toc"
- :class="{ 'toc--fixed': fixed }"
- :style="{ 'top': offset + 'px' }">
- <ol class="group">
- <el-toc-item
- v-for="item in toc"
- class="title"
- :active="active"
- :item="item">
- </el-toc-item>
- </ol>
- </nav>
- </template>
- <script>
- import ElTocItem from './toc-item';
- import E from 'oui-dom-events';
- /**
- * 浮动在右边的目录
- */
- export default {
- name: 'toc',
- components: {
- ElTocItem
- },
- props: {
- main: {
- type: String,
- required: true
- }
- },
- data() {
- return {
- toc: [],
- offset: 0,
- fixed: false,
- active: 0
- };
- },
- events: {
- ['element.example.reload']() {
- this.$nextTick(() => {
- this.toc = [];
- this.anchors = this.findAllAnchors();
- this.scrollWrap = document.querySelector('body');
- this.currentHeaderHeight = document.querySelector('.app__main:first-of-type').offsetTop;
- this.fixedOffsetTop = document.querySelector('.app__menu__label').offsetTop;
- this.updateFixed();
- this.generateTOC(this.anchors, this.toc);
- E.off(this.scrollWrap, 'mousewheel.toc.activecurrent');
- this.scrollActiveCurrent();
- });
- }
- },
- ready() {
- this.scrollAutoFixed();
- },
- methods: {
- /**
- * @see https://github.com/chjj/marked/issues/334
- */
- generateTOC(anchors, toc) {
- if (document.querySelector('.no-toc')) {
- return;
- }
- let cache = {};
- for (let key in anchors) {
- const current = this.fetchAnchorData(anchors[key]);
- const level = current.level || 1;
- const last = cache[level - 1];
- current.id = Number(key);
- if (last) {
- last.children = (last.children || []).concat(current);
- } else {
- cache[level] = current;
- toc.push(current);
- }
- }
- },
- fetchAnchorData(anchor) {
- const head = anchor.parentNode;
- const level = head.tagName.match(/\d/g)[0];
- const heading = head.getAttribute('id');
- return { level, heading };
- },
- updateFixed() {
- if (this.scrollWrap.scrollTop <= this.fixedOffsetTop / 2) {
- this.offset = this.currentHeaderHeight;
- this.fixed = false;
- return;
- }
- if (this.fixed) return;
- this.offset = this.fixedOffsetTop;
- this.fixed = true;
- },
- scrollAutoFixed() {
- E.on(document, 'mousewheel.toc.autofixed', this.updateFixed);
- },
- findAllAnchors() {
- return Array.prototype.slice.call(document.querySelectorAll('.anchor'));
- },
- scrollActiveCurrent() {
- const anchors = this.findAllAnchors();
- const body = this.scrollWrap;
- const distances = [];
- let current;
- let index = 0;
- let max;
- for (let anchor of anchors) {
- distances.push(anchor.offsetTop);
- }
- current = distances[index];
- max = distances.length - 1;
- this.active = index;
- // 记录当前滚动档位
- // 判断当前滚动和下个档位
- E.on(this.scrollWrap, 'mousewheel.toc.activecurrent', e => {
- const scrollTop = body.scrollTop;
- while (index < max && scrollTop > current) {
- current = distances[++index];
- this.active = index;
- }
- while (index && scrollTop < current) {
- current = distances[--index];
- this.active = index;
- }
- });
- }
- },
- beforeDestroy() {
- E.off(this.scrollWrap, 'mousewheel.toc.autofixed');
- E.off(this.scrollWrap, 'mousewheel.toc.activecurrent');
- }
- };
- </script>
- <style lang="css">
- .toc {
- color: #999;
- margin-right: 40px;
- margin-top: -14px;
- position: absolute;
- right: 0;
- top: 0;
- width: 160px;
- &.toc--fixed {
- position: fixed;
- }
- .group {
- font-size: 13px;
- list-style-type: none;
- margin: 0;
- padding-left: 12px;
- }
- .item {
- margin: 14px 0;
- .toc__link:hover,
- .toc__link.active {
- color: #216fc1;
- }
- .group {
- border-left: 1px solid rgba(31, 109, 191, .5);
- }
- }
- .title {
- margin-bottom: 18px;
- }
- }
- </style>
|