dgex-tantan.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <template>
  2. <view class="tantan-slide" :style="{
  3. width: winWidth + 'px',
  4. height: winHeigh + 'px',
  5. }">
  6. <view @touchstart.capture="touchStart($event,currentIndex)"
  7. @touchmove.stop.capture="touchMove($event,currentIndex)"
  8. @touchend.capture="touchEnd(currentIndex)" class="tantan-slide-box">
  9. <template v-for="(item, index) in list">
  10. <view class="tantan-slide-box-item"
  11. :key="index"
  12. v-if="currentIndex + visible >= index"
  13. @click.stop="clickImage"
  14. :style="[cardTransform(item, index), {
  15. 'zIndex': list.length - index,
  16. 'opacity': currentIndex + visible - 1 >= index && currentIndex <= index ? 1 : 0,
  17. 'transform': 'rotate(' + ((item.x || 0) / 30 ) + 'deg) translate3d(' + (item.x || 0) + 'px,' + (item.y || 0) + 'px, '+ 0 +'px)'
  18. }]">
  19. <!-- 加载图片会闪屏 双if避免 -->
  20. <template v-if="currentIndex + visible >= index && currentIndex <= index">
  21. <image class="tantan-slide-img" mode="aspectFill" :src="item.image" ></image>
  22. <view class="tantan-slide-box-item-bg">
  23. <view class="tantan-slide-box-info">
  24. <view class="title" v-if="item.title" v-text="item.title"></view>
  25. <view class="desc" v-if="item.desc" v-text="item.desc"></view>
  26. <view class="tags" v-if="item.tags && item.tags.length > 0" >
  27. <text class="tag" v-for="(item1, index1) in item.tags" :key="index1" v-text="item1"></text>
  28. </view>
  29. </view>
  30. </view>
  31. <view v-if="index === currentIndex">
  32. <view class="tantan-slide-box-icon tantan-slide-box-dislike"
  33. :style="{
  34. opacity: dislike * 1.5,
  35. transform: 'scale('+ (dislike + 1 > 2 ? 2 : dislike + 1 ) +')',
  36. }">
  37. <image style="width: 30rpx;height: 30rpx;" src="/static/dgex-tantan/close.png"></image>
  38. </view>
  39. <view class="tantan-slide-box-icon tantan-slide-box-love" :style="{
  40. opacity: love * 1.5,
  41. transform: 'scale('+ (love + 1 > 2 ? 2 : love + 1 ) +')',
  42. }">
  43. <image style="width: 30rpx;height: 30rpx;" src="/static/dgex-tantan/like.png"></image>
  44. </view>
  45. </view>
  46. </template>
  47. </view>
  48. </template>
  49. </view>
  50. </view>
  51. </template>
  52. <script>
  53. export default {
  54. name: "slide",
  55. props: {
  56. list: {
  57. type: Array,
  58. default: () => []
  59. }
  60. },
  61. data() {
  62. return {
  63. winWidth: 0,
  64. winHeigh: 0,
  65. /*记录x y轴*/
  66. x: {
  67. start: 0,
  68. move: 0,
  69. end: 0
  70. },
  71. y: {
  72. start: 0,
  73. move: 0,
  74. end: 0
  75. },
  76. visible: 3,
  77. /*下标*/
  78. currentIndex: 0,
  79. /*滑动*/
  80. swipering: false,
  81. /*滑动中*/
  82. slideing: false,
  83. love: 0,
  84. dislike: 0,
  85. }
  86. },
  87. mounted() {
  88. const res = uni.getSystemInfoSync()
  89. this.winWidth = res.windowWidth
  90. this.winHeigh = res.windowHeight
  91. },
  92. methods: {
  93. cardTransform(item, index) {
  94. let css = {};
  95. if (index === this.currentIndex) {
  96. if (this.slideing) {
  97. css["transitionDuration"] = `${!this.swipering ? 1000 : 0}ms`;
  98. } else {
  99. css["transitionDuration"] = `${!this.swipering ? 300 : 0}ms`;
  100. }
  101. }
  102. return css
  103. },
  104. touchStart(e, index) {
  105. if (this.slideing) return;
  106. if (typeof this.list[index].x === 'undefined' && typeof this.list[index].y === 'undefined') {
  107. this.$set(this.list[index], 'y', 0)
  108. this.$set(this.list[index], 'x', 0)
  109. }
  110. this.swipering = true;
  111. this.x.start = e.touches[0].pageX;
  112. this.y.start = e.touches[0].pageY;
  113. },
  114. touchMove(e, index) {
  115. if (this.slideing) return
  116. // 滑动状态/最后一个就不滑动
  117. if (this.list.length == index + 1) {
  118. return;
  119. }
  120. this.x.move = e.touches[0].pageX;
  121. this.y.move = e.touches[0].pageY;
  122. this.list[index].x = this.x.move - this.x.start
  123. this.list[index].y = this.y.move - this.y.start
  124. if (Number.parseInt(this.list[index].x) > 0) {
  125. this.love = Number.parseInt(this.list[index].x) / (100 * 2)
  126. } else {
  127. this.dislike = Math.abs(Number.parseInt(this.list[index].x) / (100 * 2))
  128. }
  129. },
  130. touchEnd(index) {
  131. if (this.slideing) return
  132. this.swipering = false;
  133. if (this.list.length == index + 1) {
  134. return;
  135. }
  136. if (
  137. this.list[index].x > 0 &&
  138. this.list[index].x > this.winWidth / 2 - this.winWidth / 5
  139. ) {
  140. this.touchEndNext(index);
  141. } else if (
  142. this.list[index].x < 0 &&
  143. this.list[index].x < -this.winWidth / 2 + this.winWidth / 5
  144. ) {
  145. this.touchEndNext(index);
  146. } else {
  147. this.list[index].x = 0;
  148. this.list[index].y = 0;
  149. this.slideing = false;
  150. this.love = 0;
  151. this.dislike = 0;
  152. }
  153. },
  154. touchEndNext(index) {
  155. this.slideing = true;
  156. this.list[index].x = this.list[index].x * 5;
  157. this.list[index].y = this.list[index].y * 5;
  158. this.touchEndDone()
  159. },
  160. touchEndDone() {
  161. return new Promise((resolve) => {
  162. setTimeout(() => {
  163. this.slideing = false
  164. this.$emit('onChange', {
  165. currentIndex: this.currentIndex,
  166. currentItem: this.list[this.currentIndex],
  167. type: this.love !== 0 ? 'love' : 'dislike'
  168. })
  169. this.currentIndex++
  170. this.x.move = 0
  171. this.y.move = 0
  172. this.slideing = false
  173. this.btnClickType = false
  174. this.love = 0
  175. this.dislike = 0
  176. resolve()
  177. }, 300);
  178. })
  179. },
  180. footerBtnClick(type) {
  181. if (this.btnClickType) {
  182. return
  183. }
  184. this.btnClickType = true
  185. let w = 0
  186. if (type === 'love') {
  187. w = this.winWidth * 1.5
  188. this.love = 1
  189. } else if (type === 'dislike') {
  190. w = -this.winWidth * 1.5
  191. this.dislike = 1
  192. }
  193. this.$set(this.list[this.currentIndex], 'x', w)
  194. this.touchEndDone()
  195. },
  196. clickImage() {
  197. this.$emit('onClickImage', {
  198. type: 'click',
  199. currentIndex: this.currentIndex,
  200. currentItem: this.list[this.currentIndex],
  201. })
  202. }
  203. }
  204. };
  205. </script>
  206. <style>
  207. /* fa547c f8ba35 */
  208. .tantan-slide {
  209. /* background-color: #2196f3; */
  210. width: 100%;
  211. height: 100%;
  212. display: flex;
  213. justify-content: center;
  214. align-items: center;
  215. overflow: hidden;
  216. }
  217. .tantan-slide-box {
  218. position: relative;
  219. width: 95%;
  220. height: 90%;
  221. perspective: 2100rpx;
  222. perspective-origin: 50% -30%;
  223. transform-style: preserve-3d;
  224. margin: auto;
  225. }
  226. .tantan-slide-box-item {
  227. transform-style: preserve-3d;
  228. display: flex;
  229. width: 100%;
  230. height: 100%;
  231. border-radius: 24rpx;
  232. position: absolute;
  233. opacity: 0;
  234. transform: translate3d(0px, 0px, 0px) rotate(0deg);
  235. transition: 300ms;
  236. color: #fff;
  237. /* display: none; */
  238. }
  239. .tantan-slide-box-item.on {
  240. /* opacity: 1; */
  241. display: block;
  242. }
  243. .tantan-slide-box-item-bg {
  244. height: 380rpx;
  245. background-image: linear-gradient(to bottom, transparent, #000000 70%);
  246. position: absolute;
  247. left: 0;
  248. right: 0;
  249. bottom: 0;
  250. margin: auto;
  251. z-index: 1;
  252. border-bottom-right-radius: 40rpx;
  253. border-bottom-left-radius: 40rpx;
  254. }
  255. .tantan-slide-box-icon {
  256. position: absolute;
  257. width: 70rpx;
  258. height: 70rpx;
  259. top: 45rpx;
  260. border-radius: 100%;
  261. background-color: #fff;
  262. z-index: 1;
  263. opacity: 0;
  264. transition: 100ms;
  265. display: flex;
  266. align-items: center;
  267. justify-content: center;
  268. }
  269. .tantan-slide-box-love {
  270. left: 50rpx;
  271. }
  272. .tantan-slide-box-dislike {
  273. right: 50rpx;
  274. }
  275. .tantan-slide-img {
  276. position: relative;
  277. will-change: transform;
  278. width: 100%;
  279. height: 100%;
  280. border-radius: 40rpx;
  281. }
  282. .tantan-slide-img.on{
  283. transform: scale(1);
  284. width: 50rpx;
  285. height: 50rpx;
  286. }
  287. .tantan-slide-box-info{
  288. padding: 0 32rpx;
  289. color: #ffffff;
  290. }
  291. .tantan-slide-box-info .title{
  292. font-size: 64rpx;
  293. line-height: 1.2;
  294. text-shadow: 0 0 10rpx rgb(0, 0, 0, 0.5);
  295. }
  296. .tantan-slide-box-info .desc{
  297. font-size: 32rpx;
  298. line-height: 1.2;
  299. margin-top: 30rpx;
  300. text-shadow: 0 0 10rpx rgb(0, 0, 0, 0.5);
  301. }
  302. .tantan-slide-box-info .tags{
  303. margin-top: 24rpx;
  304. }
  305. .tantan-slide-box-info .tag{
  306. height: 48rpx;
  307. font-size: 24rpx;
  308. padding: 0 16rpx;
  309. line-height: 48rpx;
  310. background-color: rgba(255, 255,255,0.3);
  311. border-radius: 10rpx;
  312. }
  313. </style>