pisces.js 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. /* global NexT, CONFIG */
  2. var Affix = {
  3. init: function(element, options) {
  4. this.element = element;
  5. this.offset = options || 0;
  6. this.affixed = null;
  7. this.unpin = null;
  8. this.pinnedOffset = null;
  9. this.checkPosition();
  10. window.addEventListener('scroll', this.checkPosition.bind(this));
  11. window.addEventListener('click', this.checkPositionWithEventLoop.bind(this));
  12. window.matchMedia('(min-width: 992px)').addListener(event => {
  13. if (event.matches) {
  14. this.offset = NexT.utils.getAffixParam();
  15. this.checkPosition();
  16. }
  17. });
  18. },
  19. getState: function(scrollHeight, height, offsetTop, offsetBottom) {
  20. let scrollTop = window.scrollY;
  21. let targetHeight = window.innerHeight;
  22. if (offsetTop != null && this.affixed === 'top') {
  23. if (document.querySelector('.content-wrap').offsetHeight < offsetTop) return 'top';
  24. return scrollTop < offsetTop ? 'top' : false;
  25. }
  26. if (this.affixed === 'bottom') {
  27. if (offsetTop != null) return this.unpin <= this.element.getBoundingClientRect().top ? false : 'bottom';
  28. return scrollTop + targetHeight <= scrollHeight - offsetBottom ? false : 'bottom';
  29. }
  30. let initializing = this.affixed === null;
  31. let colliderTop = initializing ? scrollTop : this.element.getBoundingClientRect().top + scrollTop;
  32. let colliderHeight = initializing ? targetHeight : height;
  33. if (offsetTop != null && scrollTop <= offsetTop) return 'top';
  34. if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom';
  35. return false;
  36. },
  37. getPinnedOffset: function() {
  38. if (this.pinnedOffset) return this.pinnedOffset;
  39. this.element.classList.remove('affix-top', 'affix-bottom');
  40. this.element.classList.add('affix');
  41. return (this.pinnedOffset = this.element.getBoundingClientRect().top);
  42. },
  43. checkPositionWithEventLoop() {
  44. setTimeout(this.checkPosition.bind(this), 1);
  45. },
  46. checkPosition: function() {
  47. if (window.getComputedStyle(this.element).display === 'none') return;
  48. let height = this.element.offsetHeight;
  49. let { offset } = this;
  50. let offsetTop = offset.top;
  51. let offsetBottom = offset.bottom;
  52. let { scrollHeight } = document.body;
  53. let affix = this.getState(scrollHeight, height, offsetTop, offsetBottom);
  54. if (this.affixed !== affix) {
  55. if (this.unpin != null) this.element.style.top = '';
  56. let affixType = 'affix' + (affix ? '-' + affix : '');
  57. this.affixed = affix;
  58. this.unpin = affix === 'bottom' ? this.getPinnedOffset() : null;
  59. this.element.classList.remove('affix', 'affix-top', 'affix-bottom');
  60. this.element.classList.add(affixType);
  61. }
  62. if (affix === 'bottom') {
  63. this.element.style.top = scrollHeight - height - offsetBottom + 'px';
  64. }
  65. }
  66. };
  67. NexT.utils.getAffixParam = function() {
  68. const sidebarOffset = CONFIG.sidebar.offset || 12;
  69. let headerOffset = document.querySelector('.header-inner').offsetHeight;
  70. let footerOffset = document.querySelector('.footer').offsetHeight;
  71. document.querySelector('.sidebar').style.marginTop = headerOffset + sidebarOffset + 'px';
  72. return {
  73. top : headerOffset,
  74. bottom: footerOffset
  75. };
  76. };
  77. window.addEventListener('DOMContentLoaded', () => {
  78. Affix.init(document.querySelector('.sidebar-inner'), NexT.utils.getAffixParam());
  79. });