half-screen.uvue 7.2 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
<template>
  <view id="page" class="page">
    <text
      style="margin: 10px">半屏弹窗,演示了弹出层内scroll-view滚动到顶时由滚变拖。本效果是通过监听TouchEvent实现,当半屏窗口移动时禁用scroll-view的滚动,避免两者的冲突。</text>
    <button class="bottomButton" @click="switchHalfScreen(true)">打开弹窗</button>
    <view id="halfScreen" class="halfScreen" @touchstart="onHalfTouchStart" @touchmove="onHalfTouchMove"
      @touchend="onHalfTouchEnd">
      <view class="halfTitle">半屏弹窗标题</view>
      <scroll-view id="halfScroll" class="halfScroll" rebound="true">
        <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,	//是否动画
        halfNode: null as Element | null,
        scrollNode: null as Element | null
      }
    },
    methods: {
      onHalfTouchStart(_ : TouchEvent) {
37
        this.halfNode?.style?.setProperty('transition-duration', 0);
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
      },
      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) {//向下滚动
            this.halfMove = true;
            this.scrollNode?.setAttribute('scroll-y', 'false');
            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;
              this.scrollNode?.setAttribute('scroll-y', 'true');
            }
            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'))) {//容错处理
          console.log('qucik click button!!!');
          return;
        }
        this.halfMove = false;
        this.scrollNode?.setAttribute('scroll-y', 'true');
        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');
101
          this.halfNode?.style?.setProperty('transition-timing-function', 'ease-in-out');
102
        } else {
103
          this.halfNode?.style?.setProperty('transition-timing-function', 'linear');
104 105
          time *= (this.halfHeight / this.totalHeight);		//计算关闭动画时间
        }
106 107
        this.halfNode?.style?.setProperty('transition-duration', time.toFixed(0)+"ms");
        this.halfNode?.style?.setProperty('transition-property', 'top');
108 109 110 111
        this.halfNode?.style?.setProperty('top', top.toFixed(2));
        setTimeout(() => {
          if (!show) {
            this.halfNode?.style?.setProperty('visibility', 'hidden');
112
            this.halfNode?.style?.setProperty('transition-duration', 0);
113 114
            this.halfNode?.style?.setProperty('transform', '');
          }
115
          this.halfNode?.style?.setProperty('transition-property', '');
116 117 118 119 120 121
          this.bAnimation = false;
        }, time)
        this.bAnimation = true;
      },
      resumeHalfScreen() {
        let time = 300;//(500*this.halfOffset/this.halfHeight).toFixed(0); //回弹动画时间
122 123 124
        this.halfNode?.style?.setProperty('transition-duration', time.toFixed(0)+"ms");
        this.halfNode?.style?.setProperty('transition-timing-function', 'ease-in-out');
        this.halfNode?.style?.setProperty('transition-property', 'transform');
125 126 127 128 129 130 131
        this.halfNode?.style?.setProperty('transform', 'translateY(0px)');
        this.halfMove = false;
        this.scrollNode?.setAttribute('scroll-y', 'true');
        this.halfScreenY = 0;
        this.halfOffset = 0;
        setTimeout(() => {
          this.bAnimation = false;
132
          this.halfNode?.style?.setProperty('transition-property', '');
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
        }, time)
        this.bAnimation = true;
      }
    },
    onReady() {
      this.halfNode = uni.getElementById('halfScreen');
      this.scrollNode = uni.getElementById('halfScroll');

      this.halfHeight = this.halfNode!.getBoundingClientRect().height;
      this.totalHeight = uni.getElementById('page')?.getBoundingClientRect()?.height ?? 0;//uni.getWindowInfo().windowHeight
      this.halfNode?.style?.setProperty('top', this.totalHeight.toFixed(2));
    },
    onResize() {
      this.halfHeight = this.halfNode?.getBoundingClientRect()?.height ?? 0;
      this.totalHeight = uni.getWindowInfo().windowHeight;
      this.halfNode?.style?.setProperty('top', this.totalHeight.toFixed(2));
      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;
张磊 已提交
174
    transition-duration: 0ms;
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
    visibility: hidden;
  }

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

  .halfScroll {
    background-color: white;
  }

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