index.vue 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. <script setup>
  2. const props = defineProps({
  3. dataSource: { type: Array, required: true },
  4. itemHeight: { type: Number, required: false, default: 80 },
  5. })
  6. const transformY = ref()
  7. const transformStyle = computed(() => {
  8. return `transform: translateY(${transformY.value}px)`
  9. })
  10. const scrollerContainerRef = ref()
  11. const scrollerContainerRefHeight = computed(() => {
  12. return scrollerContainerRef.value ? scrollerContainerRef.value.offsetHeight : 0
  13. })
  14. const itemCount = computed(() => {
  15. return Math.ceil(scrollerContainerRefHeight.value / props.itemHeight) + 1
  16. })
  17. const start = ref(0)
  18. const end = computed(() => {
  19. return start.value + itemCount.value
  20. })
  21. const Data = ref()
  22. const pillarHeight = computed(() => {
  23. if (Data.value?.length)
  24. return props.itemHeight * Data.value?.length
  25. return void 0
  26. })
  27. const renderData = computed(() => {
  28. const _start = Math.max(0, start.value)
  29. const _end = Math.min(end.value, Data.value.length)
  30. return Data.value?.slice(_start, _end)
  31. })
  32. function init() {
  33. if (!props.dataSource) {
  34. const res = Array.from({ length: 1e4 })
  35. res.forEach((_, i) => {
  36. res[i] = i
  37. })
  38. Data.value = res
  39. }
  40. else {
  41. Data.value = props.dataSource
  42. }
  43. }
  44. function handleScroll(e) {
  45. const scrollTop = e.target.scrollTop
  46. start.value = Math.floor(scrollTop / props.itemHeight)
  47. transformY.value = start.value * props.itemHeight
  48. }
  49. onMounted(() => {
  50. init()
  51. })
  52. </script>
  53. <template>
  54. <div v-if="Data" class="list-container">
  55. <div ref="scrollerContainerRef" class="scroller-container scrollbar" @scroll="handleScroll">
  56. <div class="pillar" :style="{ height: `${pillarHeight}px` }" />
  57. <div class="list" :style="transformStyle">
  58. <div v-for="(item, index) in renderData" :key="index" class="item" :style="{ height: `${itemHeight}px` }">
  59. <slot name="renderItem" :item="item" />
  60. </div>
  61. </div>
  62. </div>
  63. </div>
  64. </template>
  65. <style scoped lang="less">
  66. .scrollbar {
  67. &::-webkit-scrollbar {
  68. width: 5px;
  69. height: 10px;
  70. }
  71. &::-webkit-scrollbar-thumb {
  72. border-radius: 5px;
  73. -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
  74. background: rgba(190, 190, 190, 0.2);
  75. }
  76. &::-webkit-scrollbar-track {
  77. -webkit-box-shadow: inset 0 0 5px rgba(227, 227, 227, 0.2);
  78. border-radius: 0;
  79. background: rgba(0, 0, 0, 0.1);
  80. }
  81. }
  82. .list-container {
  83. height: 350px;
  84. width: 100%;
  85. background-color: var(--bg-color);
  86. .scroller-container {
  87. position: relative;
  88. width: 100%;
  89. height: 100%;
  90. overflow: auto;
  91. // 处理ios滚动卡顿
  92. --webkit-overflow-scrolling: touch;
  93. .pillar {
  94. position: absolute;
  95. left: 0;
  96. top: 0;
  97. right: 0;
  98. z-index: -1;
  99. }
  100. .list {
  101. position: absolute;
  102. top: 0;
  103. left: 0;
  104. right: 0;
  105. .item {
  106. box-sizing: border-box;
  107. display: flex;
  108. align-items: center;
  109. width: 100%;
  110. height: 50px;
  111. padding: 0 20px;
  112. border-bottom: 1px solid var(--bg-color-container);
  113. :deep(.ant-list-item) {
  114. display: flex;
  115. justify-content: space-between;
  116. align-items: center;
  117. width: 100%;
  118. .ant-list-item-action {
  119. margin-top: 18px;
  120. }
  121. & > div:nth-child(1) {
  122. flex: 1;
  123. }
  124. }
  125. :deep(.ant-list-item-meta) {
  126. display: flex;
  127. .ant-list-item-meta-avatar {
  128. margin-right: 15px;
  129. }
  130. }
  131. }
  132. }
  133. }
  134. }
  135. </style>