card.uvue 7.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
<template>
    <view class="card" ref="card" @touchstart="touchstart($event as TouchEvent)"
      @touchmove="touchmove($event as TouchEvent)" @touchend="touchend" @touchcancel="touchend">
      <image class="card-img" ref="card-img" :src="img"></image>
      <view class="state">
        <image class="state-icon like" ref="state-icon-like" src="/static/template/drop-card/like.png" mode="widthFix">
        </image>
        <image class="state-icon dislike" ref="state-icon-dislike" src="/static/template/drop-card/dislike.png"
          mode="widthFix"></image>
          <!-- cardIndex:{{cardIndex}} -->
      </view>
    </view>
</template>
<script>
  let sX : number = 0,
    sY : number = 0,
    screenWidth : number = 1,
    screenHeight : number = 1,
    floating : boolean = false,
    touchstartAfter : boolean = false;
  export default {
    data() {
      return {
        $nodeMap:new Map<string, INode>(),
        x: 0 as number,
        y: 0 as number,
        // 飘走的卡片计数
        floatCount:0 as number
      }
    },
    props: {
      img: {
        type: String,
        default: "/static/template/drop-card/1.jpg"
      },
      cardIndex:{
        type:Number,
        default:0
      }
    },
    computed: {
      movePercent() : number {
        return Math.abs(this.x) / (screenWidth / 2 * 3)
      },
      likeOpacity() : number {
        return this.x < 0 ? 0 : this.movePercent * 100
      },
      dislikeOpacity() : number {
        return this.x > 0 ? 0 : this.movePercent * 100
      }
    },
    mounted() {
      uni.getSystemInfo({
        success: (e) => {
          screenWidth = e.screenWidth;
          screenHeight = e.screenHeight;
        }
      })
59

DCloud_JSON's avatar
DCloud_JSON 已提交
60
      // TODO 需要延迟设置才能生效
61 62 63 64
      setTimeout(()=>{
        this.setINodeStyle('card','height', screenHeight * 0.7 + 'px');
        this.setINodeStyle('card-img','height', screenHeight * 0.7 + 'px');
        this.initCardStyle()
DCloud_JSON's avatar
DCloud_JSON 已提交
65
      },200)
66

67 68 69 70
      uni.$on('uni-drop-card-float',()=>{
        this.floatCount ++
        this.initCardStyle()
      })
71

72 73 74 75 76
    },
    methods: {
      initCardStyle(){
        let _index = (this.cardIndex + this.floatCount)%3
        // console.log('~~~~~~_index:'+_index + ' cardIndex:'+this.cardIndex+' floatCount:'+this.floatCount);
DCloud_JSON's avatar
DCloud_JSON 已提交
77
        this.setINodeStyle('card','z-index', _index)
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
        this.setINodeStyle('card','margin-top', screenHeight * 0.15 - 30 * _index + 'px');
        this.setINodeStyle('card','transform', 'scale('+(0.9 + 0.05 * _index)+')')
      },
      // 工具方法,用于快速设置 INode 的 style
      setINodeStyle(refName:string,propertyName : string, propertyStyle : any) : void {
        let node : INode | null = this.$nodeMap.get(refName)
        if(node == null){
          node = this.$refs.get(refName) as INode;
          this.$nodeMap.set(refName,node)
        }else{
          // console.log('直接拿');
        }
        node?.style?.setProperty(propertyName, propertyStyle);
      },
      touchstart(e : TouchEvent) {
        // console.log('touchstart')
        if (floating) {
          return // 浮动动画进行中
        }
        sX = e.touches[0].screenX;
        sY = e.touches[0].screenY;
        this.x = 0
        this.y = 0

        touchstartAfter = true
      },
      touchmove(e : TouchEvent) {
        // console.log('touchmove')
        if (!touchstartAfter || floating) {
          return // floating:浮动动画进行中
        }
        this.x += e.touches[0].screenX - sX;
        this.y += e.touches[0].screenY - sY;
        sX = e.touches[0].screenX;
        sY = e.touches[0].screenY;
        this.moveCard()
      },
      touchend() {
        // console.log('touchend')
        touchstartAfter = false
        if(floating){
          return // 浮动动画进行中
        }
        floating = true
122

123 124 125 126 127 128 129
        // 设置释放之后飘走的方向 0回到坐标中心 1向右 2向左
        let k:number = 0;
        if (this.x > screenWidth / 10 ) {
          k = 1
        }else if(this.x < screenWidth * -1 / 10){
          k = -1
        }
130 131

        function cardTo(x:number,y:number,callback: () => void,speed:number = 10){
132
          let interval:number = 0
133
          let acceleration:number = 1
134
          interval = setInterval(()=>{
135
            // 加速度
136 137
            acceleration += 0.2

138
            const dx = x - this.x
139 140 141 142
            if(Math.abs(dx) < 1){
              this.x = x
            }else{
              this.x += dx/speed*acceleration
143
            }
144

145
            const dy = y - this.y
146 147 148 149
            if(Math.abs(dy) < 1){
              this.y = y
            }else{
              this.y += dy/speed*acceleration
150
            }
151

152
            this.moveCard()
153
            if( this.x == x && this.y == y){
154
              clearInterval(interval)
155
              callback()
156
            }
157 158
          },16)
        }
159

160 161 162 163 164
        if(k.toInt() != 0){
          cardTo(k * screenWidth * 1.3, this.y * 3,()=>{
            //  状态图标变回透明
            this.setINodeStyle("state-icon-like",'opacity', 0)
            this.setINodeStyle("state-icon-dislike",'opacity', 0)
165

166 167 168 169 170
            // 设置为透明,防止飘回时因为 margin-top 太高,露出来
            this.setINodeStyle("card",'opacity', 0)
            setTimeout(()=>{
              this.setINodeStyle("card",'opacity', 1)
            },300)
171

172
            // 执行卡片飘动后事件,注意uni.$emit是全局事件。其他卡片也会执行
173
            uni.$emit('uni-drop-card-float',null)
174 175
            floating = false
          },8)
176
        }else{
177 178 179 180 181 182 183 184 185 186
          const _x:number = this.x
          const _y:number = this.y
          cardTo( (_x*-0.05).toInt() , (_y*-0.05).toInt() ,()=>{
            cardTo(0,0,()=>{
             console.log('bounce')
             floating = false
            },30)
          })
        }
      },
187 188 189
      moveCard() {
        this.setINodeStyle("card",
          'transform',
DCloud_JSON's avatar
DCloud_JSON 已提交
190
          `translate(${this.x}px,${this.y}px) rotate(${this.x/-30}deg) scale(1)`
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
        )
        this.setINodeStyle("state-icon-like",'opacity', this.x < 0 ? 0 : movePercent * 10)
        this.setINodeStyle("state-icon-dislike",'opacity', this.x > 0 ? 0 : movePercent * 10)
      }
    }
  }
</script>

<style>
  .card {
    width: 700rpx;
    height: 750rpx;
    position: absolute;
    top: 0;
    left: 0;
    margin: 0 25rpx;
    margin-top: 50px;
    border-radius: 10px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
    background-color: #FFF;
DCloud_JSON's avatar
DCloud_JSON 已提交
211
    transition: margin-top 300ms;
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
    transition-timing-function: ease-in;
  }

  .card-img {
    width: 700rpx;
    height: 750rpx;
    border-radius: 10px;
  }

  .state {
    top: 20rpx;
    left: 20rpx;
    width: 650rpx;
    padding: 4px;
    position: absolute;
    flex-direction: row;
    justify-content: space-between;
  }

  .state-icon {
    width: 30px;
    height: 30px;
    border: 1px solid #FFF;
    background-color: #FFF;
    padding: 3px;
    border-radius: 100px;
    box-shadow: 0 0 1px #EBEBEB;
    opacity: 0;
  }
241
</style>