waterfall-card.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <template>
  2. <view class="waterfall">
  3. <view class="left" v-if="leftList[0]">
  4. <view v-for="(item, index) in leftList" :key="index" @click="emits('click',item)">
  5. <u-transition :show="true" mode="fade-left" :duration="transitionIndex">
  6. <view class="waterfall-item" :style="{backgroundColor: bgColor}">
  7. <view class="tags">{{item.city}}</view>
  8. <image :src="item.cover" mode="widthFix" lazy-load @load="onImageLoad">
  9. </image>
  10. <view class="mt20 column">
  11. <view class="align-center" v-if="item.user">
  12. <u-avatar :src="item.user.head_img" size="48rpx"></u-avatar>
  13. <text class="ellipsis ml10 fs28 bold">{{item.user.nickname}}</text>
  14. </view>
  15. <view class="column mt10">
  16. <text class="bold mb10 ellipsis">{{item.title}}</text>
  17. <text class="base-color-gray ellipsis lines-2">{{item.content}}</text>
  18. </view>
  19. <view class="align-center mt20" @click.stop.prevent>
  20. <view class="align-center" @click="emits('onLike',item)">
  21. <u-icon :name="item.is_collent==1?'heart-fill':'heart'"
  22. :color="item.is_collent==1?'#f44':''"></u-icon>
  23. <text class="ml10" :style="{color:item.is_collent==1?'#f44':''}">{{item.like_num}}</text>
  24. </view>
  25. <view class="align-center ml30">
  26. <u-icon name="eye"></u-icon>
  27. <text class="ml10">{{item.browse_num}}</text>
  28. </view>
  29. </view>
  30. </view>
  31. </view>
  32. </u-transition>
  33. </view>
  34. </view>
  35. <view class="right" v-if="rightList[0]">
  36. <view v-for="(item, index) in rightList" :key="index" @click="emits('click',item)">
  37. <u-transition :show="true" mode="fade-right" :duration="transitionIndex">
  38. <view class="waterfall-item" :style="{backgroundColor: bgColor}">
  39. <view class="tags">{{item.city}}</view>
  40. <image :src="item.cover" mode="widthFix" lazy-load @load="onImageLoad">
  41. </image>
  42. <view class="mt20 column">
  43. <view class="align-center" v-if="item.user">
  44. <u-avatar :src="item.user.head_img" size="48rpx"></u-avatar>
  45. <text class="ellipsis ml10 fs28 bold">{{item.user.nickname}}</text>
  46. </view>
  47. <view class="column mt10">
  48. <text class="bold mb10 ellipsis">{{item.title}}</text>
  49. <text class="base-color-gray ellipsis lines-2">{{item.content}}</text>
  50. </view>
  51. <view class="align-center mt20" @click.stop.prevent>
  52. <view class="align-center" @click="emits('onLike',item)">
  53. <u-icon :name="item.is_collent==1?'heart-fill':'heart'"
  54. :color="item.is_collent==1?'#f44':''"></u-icon>
  55. <text class="ml10" :style="{color:item.is_collent==1?'#f44':''}">{{item.like_num}}</text>
  56. </view>
  57. <view class="align-center ml30">
  58. <u-icon name="eye"></u-icon>
  59. <text class="ml10">{{item.browse_num}}</text>
  60. </view>
  61. </view>
  62. </view>
  63. </view>
  64. </u-transition>
  65. </view>
  66. </view>
  67. </view>
  68. </template>
  69. <script setup>
  70. import {
  71. watch
  72. } from 'vue';
  73. name: 'waterfall-card'
  74. import {
  75. onLoad,
  76. } from "@dcloudio/uni-app";
  77. import {
  78. ref
  79. } from "vue";
  80. const emits = defineEmits(['click', 'onLike'])
  81. const props = defineProps({
  82. list: {
  83. type: Array,
  84. default: () => []
  85. },
  86. bgColor: {
  87. type: String,
  88. default: '#fff'
  89. }
  90. })
  91. const transitionIndex = ref(100) //动画延时
  92. const leftList = ref([]) //左边列表
  93. const rightList = ref([]) //右边列表
  94. const itemIndex = ref(0)
  95. const leftHeight = ref(0)
  96. const rightHeight = ref(0)
  97. //第一张图片入栈
  98. leftList.value = [props.list[0]];
  99. watch(() => props.list, (n, o) => {
  100. // console.log('=====watch list=====', n, o);
  101. let ol = o.length;
  102. let nl = n.length;
  103. // console.log(ol);
  104. if (nl > ol) {
  105. if (leftHeight.value > rightHeight.value) {
  106. rightList.value.push(props.list[ol]);
  107. } else {
  108. leftList.value.push(props.list[ol]);
  109. }
  110. onImageLoad();
  111. }
  112. })
  113. const onImageLoad = (e) => {
  114. props.list.forEach((item, index) => {
  115. let i = index
  116. transitionIndex.value = (i + 1) * 30
  117. if (i > 10) i = 0
  118. })
  119. if (!e) {
  120. // console.log('无图片!!!!');
  121. return;
  122. }
  123. let imgH = (e.detail.height / e.detail.width) * 345 + 110 + 20; //图片显示高度加上下面的文字的高度110rpx,加上margin-bottom20rpx
  124. if (itemIndex.value === 0) {
  125. leftHeight.value += imgH; //第一张图片高度加到左边
  126. itemIndex.value++;
  127. rightList.value.push(props.list[itemIndex.value]); //第二张图片先入栈
  128. } else {
  129. itemIndex.value++;
  130. //再加高度
  131. if (leftHeight.value > rightHeight.value) {
  132. rightHeight.value += imgH;
  133. } else {
  134. leftHeight.value += imgH;
  135. }
  136. if (itemIndex.value < props.list.length) {
  137. //下一张图片入栈
  138. if (leftHeight.value > rightHeight.value) {
  139. rightList.value.push(props.list[itemIndex.value]);
  140. } else {
  141. leftList.value.push(props.list[itemIndex.value]);
  142. }
  143. }
  144. }
  145. }
  146. </script>
  147. <style lang="scss">
  148. .waterfall {
  149. width: 100%;
  150. display: grid;
  151. grid-template-columns: 1fr 1fr;
  152. grid-gap: 20rpx;
  153. .left,
  154. .right {
  155. .waterfall-item {
  156. width: 100%;
  157. margin-bottom: 20rpx;
  158. box-sizing: border-box;
  159. border-radius: 20rpx;
  160. overflow: hidden;
  161. font-size: 24rpx;
  162. background: #fff;
  163. padding: 30rpx 25rpx;
  164. position: relative;
  165. image {
  166. width: 100%;
  167. display: block;
  168. border-radius: 10rpx;
  169. }
  170. .tags {
  171. position: absolute;
  172. top: 36rpx;
  173. right: 37rpx;
  174. width: 86rpx;
  175. height: 33rpx;
  176. line-height: 33rpx;
  177. color: #fff;
  178. background: rgba(255, 255, 255, .45);
  179. z-index: 10;
  180. border-radius: 20rpx;
  181. text-align: center;
  182. font-size: 22rpx
  183. }
  184. }
  185. }
  186. }
  187. </style>