mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
200 lines
5.2 KiB
Vue
200 lines
5.2 KiB
Vue
<template>
|
|
<teleport to="#content">
|
|
<div id="navmap">
|
|
<nav>
|
|
<a
|
|
class="top"
|
|
href="#top"
|
|
>
|
|
<i class="fa fa-fw fa-square" />
|
|
<span>{{ $t('nav.top') }}</span>
|
|
</a>
|
|
<item
|
|
v-for="item of items"
|
|
:key="item.key"
|
|
:item="item"
|
|
:step="step"
|
|
/>
|
|
</nav>
|
|
</div>
|
|
</teleport>
|
|
</template>
|
|
|
|
<script>
|
|
import Item from './StickyNav/Item.vue';
|
|
|
|
export default {
|
|
name: "StickyNav",
|
|
components: {
|
|
Item
|
|
},
|
|
data() {
|
|
return {
|
|
header: document.querySelector("header nav.navbar"),
|
|
bannerName: document.querySelector("#header-accompanying_course-name"),
|
|
bannerDetails: document.querySelector("#header-accompanying_course-details"),
|
|
container: null,
|
|
heightSum: null,
|
|
stickyNav: null,
|
|
limit: 25,
|
|
anchors: null,
|
|
items: [],
|
|
}
|
|
},
|
|
computed: {
|
|
accompanyingCourse() {
|
|
return this.$store.state.accompanyingCourse;
|
|
},
|
|
step() {
|
|
return this.accompanyingCourse.step;
|
|
},
|
|
top() {
|
|
return parseInt(window.getComputedStyle(this.stickyNav).getPropertyValue('top').slice(0, -2));
|
|
},
|
|
},
|
|
mounted() {
|
|
this.ready();
|
|
window.addEventListener('scroll', this.handleScroll);
|
|
},
|
|
unmounted() {
|
|
window.removeEventListener('scroll', this.handleScroll);
|
|
},
|
|
methods: {
|
|
ready() {
|
|
|
|
// load datas DOM when mounted ready
|
|
this.container = document.querySelector("#content");
|
|
this.stickyNav = document.querySelector('#navmap');
|
|
this.anchors = document.querySelectorAll("h2 a[id^='section']");
|
|
this.initItemsMap();
|
|
|
|
// TODO resizeObserver not supports IE !
|
|
// Listen when elements change size, then recalculate heightSum and initItemsMap
|
|
const resizeObserver = new ResizeObserver(() => {
|
|
this.refreshPos();
|
|
});
|
|
|
|
resizeObserver.observe(this.header);
|
|
resizeObserver.observe(this.bannerName);
|
|
resizeObserver.observe(this.bannerDetails);
|
|
resizeObserver.observe(this.container);
|
|
},
|
|
initItemsMap() {
|
|
|
|
this.anchors.forEach(anchor => {
|
|
this.items.push({
|
|
pos: null,
|
|
active: false,
|
|
key: parseInt(anchor.id.slice(8).slice(0, -1)),
|
|
id: '#' + anchor.id
|
|
})
|
|
});
|
|
},
|
|
refreshPos() {
|
|
|
|
//console.log('refreshPos');
|
|
this.heightSum = this.header.offsetHeight + this.bannerName.offsetHeight + this.bannerDetails.offsetHeight;
|
|
|
|
this.anchors.forEach((anchor, i) => {
|
|
this.items[i].pos = this.findPos(anchor)['y'];
|
|
});
|
|
},
|
|
findPos(element) {
|
|
|
|
let posX = 0, posY = 0;
|
|
do {
|
|
posX += element.offsetLeft;
|
|
posY += element.offsetTop;
|
|
element = element.offsetParent;
|
|
}
|
|
while( element != null );
|
|
|
|
let pos = [];
|
|
pos['x'] = posX;
|
|
pos['y'] = posY;
|
|
|
|
return pos;
|
|
},
|
|
handleScroll(event) {
|
|
|
|
let pos = this.findPos(this.stickyNav);
|
|
let top = this.heightSum + this.top - window.scrollY;
|
|
//console.log(window.scrollY);
|
|
|
|
if (top > this.limit) {
|
|
this.stickyNav.style.position = 'absolute';
|
|
this.stickyNav.style.left = '10px';
|
|
} else {
|
|
this.stickyNav.style.position = 'fixed';
|
|
this.stickyNav.style.left = pos['x'] + 'px';
|
|
}
|
|
|
|
this.switchActive();
|
|
},
|
|
switchActive() {
|
|
|
|
this.items.forEach((item, i) => {
|
|
let next = (this.items[i+1]) ? this.items[i+1].pos : '100000';
|
|
item.active =
|
|
(window.scrollY >= item.pos & window.scrollY < next) ? true : false;
|
|
}, this);
|
|
|
|
// last item never switch active because scroll reach bottom of page
|
|
if (document.body.scrollHeight == window.scrollY + window.innerHeight) {
|
|
this.items[this.items.length-1].active = true;
|
|
this.items[this.items.length-2].active = false;
|
|
} else {
|
|
this.items[this.items.length-1].active = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
div#content {
|
|
position: relative;
|
|
|
|
div#navmap {
|
|
position: absolute;
|
|
top: 30px;
|
|
left: 10px; //-10%;
|
|
nav {
|
|
font-size: small;
|
|
a {
|
|
display: block;
|
|
box-sizing: border-box;
|
|
margin-bottom: -3px;
|
|
color: #71859669;
|
|
text-decoration: none;
|
|
&.top {
|
|
color: #718596;
|
|
}
|
|
span {
|
|
display: none;
|
|
}
|
|
&:hover,
|
|
&.active {
|
|
span {
|
|
display: inline;
|
|
padding-left: 8px;
|
|
}
|
|
}
|
|
&:hover {
|
|
color: #718596b5;
|
|
}
|
|
&.active {
|
|
color: #e2793d; //orange
|
|
//color: #eec84a; //jaune
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@media only screen and (max-width: 768px) {
|
|
div#navmap {
|
|
display: none;
|
|
}
|
|
}
|
|
</style>
|