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 37 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 101 102 103 104 105
<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) {
        this.halfNode?.style?.setProperty('transitionDuration', 0);
      },
      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');
          this.halfNode?.style?.setProperty('transitionTimingFunction', 'ease-in-out');
        } else {
          this.halfNode?.style?.setProperty('transitionTimingFunction', 'linear');
          time *= (this.halfHeight / this.totalHeight);		//计算关闭动画时间
        }
张磊 已提交
106
        this.halfNode?.style?.setProperty('transitionDuration', time.toFixed(0)+"ms");
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
        this.halfNode?.style?.setProperty('transitionProperty', 'top');
        this.halfNode?.style?.setProperty('top', top.toFixed(2));
        setTimeout(() => {
          if (!show) {
            this.halfNode?.style?.setProperty('visibility', 'hidden');
            this.halfNode?.style?.setProperty('transitionDuration', 0);
            this.halfNode?.style?.setProperty('transform', '');
          }
          this.halfNode?.style?.setProperty('transitionProperty', '');
          this.bAnimation = false;
        }, time)
        this.bAnimation = true;
      },
      resumeHalfScreen() {
        let time = 300;//(500*this.halfOffset/this.halfHeight).toFixed(0); //回弹动画时间
张磊 已提交
122
        this.halfNode?.style?.setProperty('transitionDuration', time.toFixed(0)+"ms");
123 124 125 126 127 128 129 130 131 132 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
        this.halfNode?.style?.setProperty('transitionTimingFunction', 'ease-in-out');
        this.halfNode?.style?.setProperty('transitionProperty', 'transform');
        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;
          this.halfNode?.style?.setProperty('transitionProperty', '');
        }, 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>