half-screen.uvue 8.4 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
            this.halfNode?.style?.setProperty('transform', 'translateY(' + offset.toFixed(2) + 'px)');
            this.halfOffset = offset;
          }
80
        }
81 82 83
        // #ifdef WEB
        e.preventDefault();
        // #endif
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
      },
      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'))) {//容错处理
105
          console.log('quick click button!!!');
106 107 108
          return;
        }
        this.halfMove = false;
109 110 111 112 113 114 115
        // #ifdef APP
        //this.scrollNode?.setAttribute('scroll-y', 'true');
        this.scrollNode?.setAttribute('direction', 'vertical');
        // #endif
        // #ifdef WEB
        this.scrollDirection = 'vertical';
        // #endif
116 117 118 119 120 121 122
        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');
123
          this.halfNode?.style?.setProperty('transition-timing-function', 'ease-in-out');
124
        } else {
125
          this.halfNode?.style?.setProperty('transition-timing-function', 'linear');
126 127
          time *= (this.halfHeight / this.totalHeight);		//计算关闭动画时间
        }
128
        this.halfNode?.style?.setProperty('transition-duration', time.toFixed(0)+'ms');
129
        this.halfNode?.style?.setProperty('transition-property', 'top');
130
        this.halfNode?.style?.setProperty('top', top.toFixed(2)+'px');
131 132 133
        setTimeout(() => {
          if (!show) {
            this.halfNode?.style?.setProperty('visibility', 'hidden');
134
            this.halfNode?.style?.setProperty('transition-duration', 0);
135 136
            this.halfNode?.style?.setProperty('transform', '');
          }
137
          this.halfNode?.style?.setProperty('transition-property', '');
138 139 140 141 142 143
          this.bAnimation = false;
        }, time)
        this.bAnimation = true;
      },
      resumeHalfScreen() {
        let time = 300;//(500*this.halfOffset/this.halfHeight).toFixed(0); //回弹动画时间
144
        this.halfNode?.style?.setProperty('transition-duration', time.toFixed(0)+'ms');
145 146
        this.halfNode?.style?.setProperty('transition-timing-function', 'ease-in-out');
        this.halfNode?.style?.setProperty('transition-property', 'transform');
147
        this.halfNode?.style?.setProperty('transform', 'translateY(0px)');
148 149 150 151 152 153 154 155
        this.halfMove = false;
        // #ifdef APP
        //this.scrollNode?.setAttribute('scroll-y', 'true');
        this.scrollNode?.setAttribute('direction', 'vertical');
        // #endif
        // #ifdef WEB
        this.scrollDirection = 'vertical';
        // #endif
156 157 158 159
        this.halfScreenY = 0;
        this.halfOffset = 0;
        setTimeout(() => {
          this.bAnimation = false;
160
          this.halfNode?.style?.setProperty('transition-property', '');
161 162 163 164 165
        }, time)
        this.bAnimation = true;
      }
    },
    onReady() {
166 167
      this.halfNode = this.$refs['halfScreen'] as UniElement;//uni.getElementById('halfScreen');
      this.scrollNode = this.$refs['halfScroll'] as UniElement;//uni.getElementById('halfScroll');
168 169 170

      this.halfHeight = this.halfNode!.getBoundingClientRect().height;
      this.totalHeight = uni.getElementById('page')?.getBoundingClientRect()?.height ?? 0;//uni.getWindowInfo().windowHeight
171
      this.halfNode?.style?.setProperty('top', this.totalHeight.toFixed(2)+'px');
172 173 174 175
    },
    onResize() {
      this.halfHeight = this.halfNode?.getBoundingClientRect()?.height ?? 0;
      this.totalHeight = uni.getWindowInfo().windowHeight;
176
      this.halfNode?.style?.setProperty('top', this.totalHeight.toFixed(2)+'px');
177
      this.halfNode?.style?.setProperty('visibility', 'hidden');
178 179 180 181 182 183 184
    },
    onBackPress(): boolean {
      if('visible' == this.halfNode?.style?.getPropertyValue('visibility')){
        this.switchHalfScreen(false);
        return true;
      }
      return false;
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
    }
  }
</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;
张磊 已提交
209
    transition-duration: 0ms;
210 211 212 213 214 215 216 217 218 219 220 221
    visibility: hidden;
  }

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

  .halfScroll {
222 223
    background-color: white;
    flex: 1;
224 225 226 227 228
  }

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