index.vue 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. <template>
  2. <transition :name="transitionName">
  3. <div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop">
  4. <svg
  5. width="16"
  6. height="16"
  7. viewBox="0 0 17 17"
  8. xmlns="http://www.w3.org/2000/svg"
  9. class="Icon Icon--backToTopArrow"
  10. aria-hidden="true"
  11. style="height:16px;width:16px"
  12. >
  13. <path
  14. d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z"
  15. />
  16. </svg>
  17. </div>
  18. </transition>
  19. </template>
  20. <script>
  21. export default {
  22. name: "BackToTop",
  23. props: {
  24. visibilityHeight: {
  25. type: Number,
  26. default: 400
  27. },
  28. backPosition: {
  29. type: Number,
  30. default: 0
  31. },
  32. customStyle: {
  33. type: Object,
  34. default: function() {
  35. return {
  36. right: "50px",
  37. bottom: "50px",
  38. width: "40px",
  39. height: "40px",
  40. "border-radius": "4px",
  41. "line-height": "45px",
  42. background: "#e7eaf1"
  43. };
  44. }
  45. },
  46. transitionName: {
  47. type: String,
  48. default: "fade"
  49. }
  50. },
  51. data() {
  52. return {
  53. visible: false,
  54. interval: null,
  55. isMoving: false
  56. };
  57. },
  58. mounted() {
  59. window.addEventListener("scroll", this.handleScroll);
  60. },
  61. beforeDestroy() {
  62. window.removeEventListener("scroll", this.handleScroll);
  63. if (this.interval) {
  64. clearInterval(this.interval);
  65. }
  66. },
  67. methods: {
  68. handleScroll() {
  69. this.visible = window.pageYOffset > this.visibilityHeight;
  70. },
  71. backToTop() {
  72. if (this.isMoving) return;
  73. const start = window.pageYOffset;
  74. let i = 0;
  75. this.isMoving = true;
  76. this.interval = setInterval(() => {
  77. const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500));
  78. if (next <= this.backPosition) {
  79. window.scrollTo(0, this.backPosition);
  80. clearInterval(this.interval);
  81. this.isMoving = false;
  82. } else {
  83. window.scrollTo(0, next);
  84. }
  85. i++;
  86. }, 16.7);
  87. },
  88. easeInOutQuad(t, b, c, d) {
  89. if ((t /= d / 2) < 1) return (c / 2) * t * t + b;
  90. return (-c / 2) * (--t * (t - 2) - 1) + b;
  91. }
  92. }
  93. };
  94. </script>
  95. <style scoped>
  96. .back-to-ceiling {
  97. position: fixed;
  98. display: inline-block;
  99. text-align: center;
  100. cursor: pointer;
  101. }
  102. .back-to-ceiling:hover {
  103. background: #d5dbe7;
  104. }
  105. .fade-enter-active,
  106. .fade-leave-active {
  107. transition: opacity 0.5s;
  108. }
  109. .fade-enter,
  110. .fade-leave-to {
  111. opacity: 0;
  112. }
  113. .back-to-ceiling .Icon {
  114. fill: #9aaabf;
  115. background: none;
  116. }
  117. </style>