back-to-top.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. $(document).ready(function () {
  2. var $button = $('#back-to-top');
  3. var $footer = $('footer.footer');
  4. var $mainColumn = $('.column-main');
  5. var $leftSidebar = $('.column-left');
  6. var $rightSidebar = $('.column-right');
  7. var lastScrollTop = 0;
  8. var rightMargin = 20;
  9. var bottomMargin = 20;
  10. var lastState = null;
  11. var state = {
  12. base: {
  13. classname: 'card has-text-centered',
  14. left: '',
  15. width: 64,
  16. bottom: bottomMargin,
  17. 'border-radius': 4
  18. }
  19. };
  20. state['desktop-hidden'] = Object.assign({}, state.base, {
  21. classname: state.base.classname + ' rise-up',
  22. });
  23. state['desktop-visible'] = Object.assign({}, state['desktop-hidden'], {
  24. classname: state['desktop-hidden'].classname + ' fade-in',
  25. });
  26. state['desktop-dock'] = Object.assign({}, state['desktop-visible'], {
  27. classname: state['desktop-visible'].classname + ' fade-in',
  28. width: 40,
  29. 'border-radius': '50%'
  30. });
  31. state['mobile-hidden'] = Object.assign({}, state.base, {
  32. classname: state.base.classname + ' fade-in',
  33. right: rightMargin
  34. });
  35. state['mobile-visible'] = Object.assign({}, state['mobile-hidden'], {
  36. classname: state['mobile-hidden'].classname + ' rise-up',
  37. });
  38. function isStateEquals(prev, next) {
  39. for (var prop in prev) {
  40. if (!next.hasOwnProperty(prop) || next[prop] !== prev[prop]) {
  41. return false;
  42. }
  43. }
  44. for (var prop in next) {
  45. if (!prev.hasOwnProperty(prop) || prev[prop] !== prev[prop]) {
  46. return false;
  47. }
  48. }
  49. return true;
  50. }
  51. function applyState(state) {
  52. if (lastState !== null && isStateEquals(lastState, state)) {
  53. return;
  54. }
  55. $button.attr('class', state.classname);
  56. for (let prop in state) {
  57. if (prop === 'classname') {
  58. continue;
  59. }
  60. $button.css(prop, state[prop]);
  61. }
  62. lastState = state;
  63. }
  64. function isDesktop() {
  65. return window.innerWidth >= 1078;
  66. }
  67. function isTablet() {
  68. return window.innerWidth >= 768 && !isDesktop();
  69. }
  70. function isScrollUp() {
  71. return $(window).scrollTop() < lastScrollTop && $(window).scrollTop() > 0;
  72. }
  73. function hasLeftSidebar() {
  74. return $leftSidebar.length > 0;
  75. }
  76. function hasRightSidebar() {
  77. return $rightSidebar.length > 0;
  78. }
  79. function getRightSidebarBottom() {
  80. if (!hasRightSidebar()) {
  81. return 0;
  82. }
  83. return Math.max.apply(null, $rightSidebar.find('.widget').map(function () {
  84. return $(this).offset().top + $(this).outerHeight(true);
  85. }));
  86. }
  87. function getScrollTop() {
  88. return $(window).scrollTop();
  89. }
  90. function getScrollBottom() {
  91. return $(window).scrollTop() + $(window).height();
  92. }
  93. function getButtonWidth() {
  94. return $button.outerWidth(true);
  95. }
  96. function getButtonHeight() {
  97. return $button.outerHeight(true);
  98. }
  99. function updateScrollTop() {
  100. lastScrollTop = $(window).scrollTop();
  101. }
  102. function update() {
  103. // desktop mode or tablet mode with only right sidebar enabled
  104. if (isDesktop() || (isTablet() && !hasLeftSidebar() && hasRightSidebar())) {
  105. var nextState;
  106. var padding = ($mainColumn.outerWidth() - $mainColumn.width()) / 2;
  107. var maxLeft = $(window).width() - getButtonWidth() - rightMargin;
  108. var maxBottom = $footer.offset().top + getButtonHeight() / 2 + bottomMargin;
  109. if (getScrollTop() == 0 || getScrollBottom() < getRightSidebarBottom() + padding + getButtonHeight()) {
  110. nextState = state['desktop-hidden'];
  111. } else if (getScrollBottom() < maxBottom) {
  112. nextState = state['desktop-visible'];
  113. } else {
  114. nextState = Object.assign({}, state['desktop-dock'], {
  115. bottom: getScrollBottom() - maxBottom + bottomMargin
  116. });
  117. }
  118. var left = $mainColumn.offset().left + $mainColumn.outerWidth() + padding;
  119. nextState = Object.assign({}, nextState, {
  120. left: Math.min(left, maxLeft)
  121. });
  122. applyState(nextState);
  123. } else {
  124. // mobile and tablet mode
  125. if (!isScrollUp()) {
  126. applyState(state['mobile-hidden']);
  127. } else {
  128. applyState(state['mobile-visible']);
  129. }
  130. updateScrollTop();
  131. }
  132. }
  133. update();
  134. $(window).resize(update);
  135. $(window).scroll(update);
  136. $('#back-to-top').on('click', function () {
  137. $('body, html').animate({ scrollTop: 0 }, 400);
  138. });
  139. });