BackToTop.vue 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. <template>
  2. <SimpleTransition name="back-to-top" :show="showState" :duration="2000">
  3. <template #show="{ classNames }">
  4. <view
  5. :class="[ 'nana-back-to-top', ...classNames ]"
  6. :style="{
  7. right: themeContext.resolveThemeSize(props.right),
  8. bottom: themeContext.resolveThemeSize(props.bottom),
  9. zIndex: props.zIndex,
  10. }"
  11. @click="onClick"
  12. >
  13. <slot>
  14. <IconButton
  15. :icon="props.icon"
  16. :size="40"
  17. :color="themeContext.resolveThemeColor(props.iconColor)"
  18. :buttonStyle="{
  19. ...buttonStyle,
  20. ...innerStyle,
  21. }"
  22. shape="round"
  23. />
  24. </slot>
  25. </view>
  26. </template>
  27. </SimpleTransition>
  28. </template>
  29. <script setup lang="ts">
  30. import { onPageScroll } from '@dcloudio/uni-app';
  31. import { useTheme, type ViewStyle } from '../theme/ThemeDefine';
  32. import IconButton from '../basic/IconButton.vue';
  33. import { ref, watch, type Ref } from 'vue';
  34. import { DynamicColor, DynamicSize, DynamicVar } from '../theme/ThemeTools';
  35. import SimpleTransition from '../anim/SimpleTransition.vue';
  36. const themeContext = useTheme();
  37. export interface BackToTopProps {
  38. /**
  39. * 组件距离右侧的距离
  40. * @default 40
  41. */
  42. right?: number|string;
  43. /**
  44. * 组件距离底部的距离
  45. * @default 40
  46. */
  47. bottom?: number|string;
  48. /**
  49. * 当容器滚动距离大于该值时显示组件(像素)
  50. * @default 200
  51. */
  52. showScrollValue?: number;
  53. /**
  54. * 组件的z-index
  55. * @default 100
  56. */
  57. zIndex?: number;
  58. /**
  59. * 是否自定义监听滚动容器,否则为监听页面滚动。
  60. * @default false
  61. */
  62. customScrollValue?: boolean;
  63. /**
  64. * 内部显示的图标。
  65. * @default 'arrow-up'
  66. */
  67. icon?: string;
  68. /**
  69. * 内部图标颜色
  70. * @default 'white'
  71. */
  72. iconColor?: string;
  73. /**
  74. * 内部图标样式
  75. */
  76. innerStyle?: ViewStyle;
  77. }
  78. export interface BackToTopInstance {
  79. /**
  80. * 滚动事件,用于自定义滚动容器,若要更换监听的滚动容器,将其绑定至scroll-view的滚动事件上即可:
  81. * ```
  82. * <scroll-view @scroll="onScroll"></scroll-view>
  83. * ```
  84. * @param e
  85. */
  86. onScroll: (e: any) => void;
  87. /**
  88. * 自定义滚动容器的滚动距离
  89. */
  90. customScrollTop: Ref<number>;
  91. }
  92. const emit = defineEmits([
  93. 'click',
  94. 'show',
  95. 'hide'
  96. ]);
  97. const props = withDefaults(defineProps<BackToTopProps>(), {
  98. showScrollValue: 200,
  99. zIndex: 100,
  100. icon: 'arrow-up-bold',
  101. iconColor: 'white',
  102. customScrollValue: false,
  103. right: 40,
  104. bottom: 40,
  105. });
  106. const buttonStyle = themeContext.useThemeStyle({
  107. width: DynamicSize('BackToTopButtonWidth', 80),
  108. height: DynamicSize('BackToTopButtonHeight', 80),
  109. borderRadius: '50%',
  110. boxShadow: DynamicVar('BackToTopButtonBoxShadow', '0 0 8px 0 rgba(0,0,0,0.2)'),
  111. backgroundColor: DynamicColor('BackToTopButtonBackgroundColor', 'primary'),
  112. });
  113. const customScrollTop = ref(0);
  114. const showState = ref(false);
  115. watch(showState, (v) => {
  116. if (v)
  117. emit('show');
  118. else
  119. emit('hide');
  120. });
  121. function onScroll(value: number) {
  122. customScrollTop.value = value;
  123. showState.value = value > props.showScrollValue;
  124. }
  125. function onClick() {
  126. if (props.customScrollValue) {
  127. customScrollTop.value = 0;
  128. } else {
  129. uni.pageScrollTo({
  130. scrollTop: 0,
  131. });
  132. }
  133. emit('click');
  134. }
  135. onPageScroll((e) => {
  136. if (props.customScrollValue)
  137. return;
  138. onScroll(e.scrollTop);
  139. });
  140. defineExpose<BackToTopInstance>({
  141. customScrollTop,
  142. onScroll(e) {
  143. console.log('onScroll', e);
  144. if (props.customScrollValue)
  145. onScroll(e.detail.scrollTop);
  146. },
  147. })
  148. defineOptions({
  149. options: {
  150. virtualHost: true,
  151. styleIsolation: "shared",
  152. },
  153. });
  154. </script>
  155. <style lang="scss">
  156. .nana-back-to-top {
  157. position: fixed;
  158. transition: all 0.3s ease-in-out;
  159. &.back-to-top-enter-active,
  160. &.back-to-top-leave-active {
  161. transform: scale(1);
  162. opacity: 1;
  163. }
  164. &.back-to-top-enter-from,
  165. &.back-to-top-leave-to {
  166. transform: scale(0);
  167. opacity: 0;
  168. }
  169. }
  170. </style>