half-screen.uvue 8.2 KB
Newer Older
1 2 3 4 5
<template>
  <view id="page" class="page">
    <text
      style="margin: 10px">半屏弹窗,演示了弹出层内scroll-view滚动到顶时由滚变拖。本效果是通过监听TouchEvent实现,当半屏窗口移动时禁用scroll-view的滚动,避免两者的冲突。</text>
    <button class="bottomButton" @click="switchHalfScreen(true)">打开弹窗</button>
6
    <view ref="halfScreen" class="halfScreen" @touchstart="onHalfTouchStart" @touchmove="onHalfTouchMove"
7 8
      @touchend="onHalfTouchEnd">
      <view class="halfTitle">半屏弹窗标题</view>
9
      <scroll-view ref="halfScroll" class="halfScroll" rebound="true" :direction="scrollDirection">
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
        <view v-for="(item,index) in list" :key="index" class="item">
          half screen content-{{item}}
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<script>
  export default {
    data() {
      return {
        list: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15'],
        totalHeight: 0,		//总高度
        halfMove: false,	//是否Move,响应TouchMove
        halfScreenY: 0,		//响应TouchMove的起始点Y坐标
        halfOffset: 0,		//偏移的位置,translateY
        halfHeight: 0,		//高度
        lastY: 0,			//上次
        lastY2: 0,			//
        bAnimation: false,	//是否动画
31 32 33
        halfNode: null as UniElement | null,
        scrollNode: null as UniElement | null,
        scrollDirection: 'vertical'
34 35 36 37
      }
    },
    methods: {
      onHalfTouchStart(_ : TouchEvent) {
38
        this.halfNode?.style?.setProperty('transition-duration', 0);
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
      },
      onHalfTouchMove(e : TouchEvent) {
        if (this.bAnimation) {//容错处理
          return;
        }
        let top : number = this.scrollNode?.scrollTop ?? 0;
        let p = e.touches[0];
        this.lastY2 = this.lastY;
        this.lastY = p.screenY;
        if (top <= 0.01 || this.halfMove) {
          if (this.halfScreenY == 0) {
            this.halfScreenY = p.screenY;
          }
          let offset = p.screenY - this.halfScreenY;
          if (offset > 0) {//向下滚动
54 55 56 57 58 59 60 61
            this.halfMove = true;
            // #ifdef APP
            //this.scrollNode?.setAttribute('scroll-y', 'false');
            this.scrollNode?.setAttribute('direction', 'none');
            // #endif
            // #ifdef WEB
            this.scrollDirection = 'none';
            // #endif
62 63 64 65 66 67 68
            this.halfNode?.style?.setProperty('transform', 'translateY(' + offset.toFixed(2) + 'px)');
            this.halfOffset = offset;
          } else if (this.halfOffset > 0) {//向上滚动
            offset = this.halfScreenY - p.screenY;
            if (offset > this.halfOffset) {
              offset = 0;
              this.halfMove = false;
69 70 71 72 73 74 75 76
            }
            // #ifdef APP
            //this.scrollNode?.setAttribute('scroll-y', 'true');
            this.scrollNode?.setAttribute('direction', 'vertical');
            // #endif
            // #ifdef WEB
            this.scrollDirection = 'vertical';
            // #endif
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
            this.halfNode?.style?.setProperty('transform', 'translateY(' + offset.toFixed(2) + 'px)');
            this.halfOffset = offset;
          }
        }
      },
      onHalfTouchEnd(_ : TouchEvent) {
        this.halfScreenY = 0;
        if (this.bAnimation) {//容错处理
          return;
        }
        let top : number = this.scrollNode?.scrollTop ?? 0;
        let bHide = (this.halfHeight - this.halfOffset) < this.halfHeight / 4;
        if (bHide) {
          bHide = this.lastY2 > 0 && this.lastY2 <= this.lastY;
        } else if (top <= 0.01) {
          bHide = (this.lastY - this.lastY2) > 3;		//向下滑动计算加速度判断是否关闭,简单处理未考虑时间
        }
        if (bHide) {
          this.switchHalfScreen(false);
        } else if (this.halfOffset > 0) {
          this.resumeHalfScreen();
        }
      },
      switchHalfScreen(show : boolean) {
        if (show && ('visible' == this.halfNode?.style?.getPropertyValue('visibility'))) {//容错处理
102
          console.log('quick click button!!!');
103 104 105
          return;
        }
        this.halfMove = false;
106 107 108 109 110 111 112
        // #ifdef APP
        //this.scrollNode?.setAttribute('scroll-y', 'true');
        this.scrollNode?.setAttribute('direction', 'vertical');
        // #endif
        // #ifdef WEB
        this.scrollDirection = 'vertical';
        // #endif
113 114 115 116 117 118 119
        this.halfScreenY = 0;
        this.halfOffset = 0;
        let top = this.totalHeight;
        let time = 300;
        if (show) {
          top = this.totalHeight * 30 / 100;	//计算显示的位置
          this.halfNode?.style?.setProperty('visibility', 'visible');
120
          this.halfNode?.style?.setProperty('transition-timing-function', 'ease-in-out');
121
        } else {
122
          this.halfNode?.style?.setProperty('transition-timing-function', 'linear');
123 124
          time *= (this.halfHeight / this.totalHeight);		//计算关闭动画时间
        }
125
        this.halfNode?.style?.setProperty('transition-duration', time.toFixed(0)+'ms');
126
        this.halfNode?.style?.setProperty('transition-property', 'top');
127
        this.halfNode?.style?.setProperty('top', top.toFixed(2)+'px');
128 129 130
        setTimeout(() => {
          if (!show) {
            this.halfNode?.style?.setProperty('visibility', 'hidden');
131
            this.halfNode?.style?.setProperty('transition-duration', 0);
132 133
            this.halfNode?.style?.setProperty('transform', '');
          }
134
          this.halfNode?.style?.setProperty('transition-property', '');
135 136 137 138 139 140
          this.bAnimation = false;
        }, time)
        this.bAnimation = true;
      },
      resumeHalfScreen() {
        let time = 300;//(500*this.halfOffset/this.halfHeight).toFixed(0); //回弹动画时间
141
        this.halfNode?.style?.setProperty('transition-duration', time.toFixed(0)+'ms');
142 143
        this.halfNode?.style?.setProperty('transition-timing-function', 'ease-in-out');
        this.halfNode?.style?.setProperty('transition-property', 'transform');
144
        this.halfNode?.style?.setProperty('transform', 'translateY(0px)');
145 146 147 148 149 150 151 152
        this.halfMove = false;
        // #ifdef APP
        //this.scrollNode?.setAttribute('scroll-y', 'true');
        this.scrollNode?.setAttribute('direction', 'vertical');
        // #endif
        // #ifdef WEB
        this.scrollDirection = 'vertical';
        // #endif
153 154 155 156
        this.halfScreenY = 0;
        this.halfOffset = 0;
        setTimeout(() => {
          this.bAnimation = false;
157
          this.halfNode?.style?.setProperty('transition-property', '');
158 159 160 161 162
        }, time)
        this.bAnimation = true;
      }
    },
    onReady() {
163 164
      this.halfNode = this.$refs['halfScreen'] as UniElement;//uni.getElementById('halfScreen');
      this.scrollNode = this.$refs['halfScroll'] as UniElement;//uni.getElementById('halfScroll');
165 166 167

      this.halfHeight = this.halfNode!.getBoundingClientRect().height;
      this.totalHeight = uni.getElementById('page')?.getBoundingClientRect()?.height ?? 0;//uni.getWindowInfo().windowHeight
168
      this.halfNode?.style?.setProperty('top', this.totalHeight.toFixed(2)+'px');
169 170 171 172
    },
    onResize() {
      this.halfHeight = this.halfNode?.getBoundingClientRect()?.height ?? 0;
      this.totalHeight = uni.getWindowInfo().windowHeight;
173
      this.halfNode?.style?.setProperty('top', this.totalHeight.toFixed(2)+'px');
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
      this.halfNode?.style?.setProperty('visibility', 'hidden');
    }
  }
</script>

<style>
  .page {
    flex: 1;
    background-color: darkgrey;
  }

  .bottomButton {
    position: absolute;
    width: 100%;
    bottom: 0px;
  }

  .halfScreen {
    position: absolute;
    top: 100%;
    width: 100%;
    height: 70%;
    transition-timing-function: ease-in-out;
    /*ease ease-in ease-out ease-in-out linear step-start step-end*/
    transition-property: top;
张磊 已提交
199
    transition-duration: 0ms;
200 201 202 203 204 205 206 207 208 209 210 211
    visibility: hidden;
  }

  .halfTitle {
    align-items: center;
    justify-content: center;
    height: 48px;
    background-color: ghostwhite;
    border-radius: 10px 10px 0 0;
  }

  .halfScroll {
212 213
    background-color: white;
    flex: 1;
214 215 216 217 218
  }

  .item {
    height: 100px;
  }
雪洛's avatar
雪洛 已提交
219
</style>