123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- <template>
- <view ref="page" class="page">
- <text class="tip">半屏弹窗,演示了弹出层内scroll-view滚动到顶时由滚变拖。本效果是通过监听TouchEvent实现,当半屏窗口移动时禁用scroll-view的滚动,避免两者的冲突。</text>
- <button class="bottomButton" @click="switchHalfScreen(true)">打开弹窗</button>
- <view ref="halfScreen" class="halfScreen" @touchstart="onHalfTouchStart" @touchmove="onHalfTouchMove"
- @touchend="onHalfTouchEnd">
- <view class="halfTitle">半屏弹窗标题</view>
- <scroll-view ref="halfScroll" class="halfScroll" bounces="true" :direction="scrollDirection">
- <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 UniElement | null,
- scrollNode: null as UniElement | null,
- scrollDirection: 'vertical'
- }
- },
- methods: {
- onHalfTouchStart(_ : TouchEvent) {
- this.halfNode?.style?.setProperty('transition-duration', 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;
- // #ifdef APP
- //this.scrollNode?.setAttribute('scroll-y', 'false');
- this.scrollNode?.setAttribute('direction', 'none');
- // #endif
- // #ifdef WEB || MP
- this.scrollDirection = 'none';
- // #endif
- 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;
- }
- // #ifdef APP
- //this.scrollNode?.setAttribute('scroll-y', 'true');
- this.scrollNode?.setAttribute('direction', 'vertical');
- // #endif
- // #ifdef WEB || MP
- this.scrollDirection = 'vertical';
- // #endif
- this.halfNode?.style?.setProperty('transform', 'translateY(' + offset.toFixed(2) + 'px)');
- this.halfOffset = offset;
- }
- }
- // #ifdef WEB
- e.preventDefault();
- // #endif
- },
- 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('quick click button!!!');
- return;
- }
- this.halfMove = false;
- // #ifdef APP
- //this.scrollNode?.setAttribute('scroll-y', 'true');
- this.scrollNode?.setAttribute('direction', 'vertical');
- // #endif
- // #ifdef WEB || MP
- this.scrollDirection = 'vertical';
- // #endif
- 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('transition-timing-function', 'ease-in-out');
- } else {
- this.halfNode?.style?.setProperty('transition-timing-function', 'linear');
- time *= (this.halfHeight / this.totalHeight); //计算关闭动画时间
- }
- this.halfNode?.style?.setProperty('transition-duration', time.toFixed(0)+'ms');
- this.halfNode?.style?.setProperty('transition-property', 'top');
- this.halfNode?.style?.setProperty('top', top.toFixed(2)+'px');
- setTimeout(() => {
- if (!show) {
- this.halfNode?.style?.setProperty('visibility', 'hidden');
- this.halfNode?.style?.setProperty('transition-duration', 0);
- this.halfNode?.style?.setProperty('transform', '');
- }
- this.halfNode?.style?.setProperty('transition-property', 'none');
- this.bAnimation = false;
- }, time)
- this.bAnimation = true;
- },
- resumeHalfScreen() {
- let time = 300;//(500*this.halfOffset/this.halfHeight).toFixed(0); //回弹动画时间
- 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');
- this.halfNode?.style?.setProperty('transform', 'translateY(0px)');
- this.halfMove = false;
- // #ifdef APP
- //this.scrollNode?.setAttribute('scroll-y', 'true');
- this.scrollNode?.setAttribute('direction', 'vertical');
- // #endif
- // #ifdef WEB || MP
- this.scrollDirection = 'vertical';
- // #endif
- this.halfScreenY = 0;
- this.halfOffset = 0;
- setTimeout(() => {
- this.bAnimation = false;
- this.halfNode?.style?.setProperty('transition-property', 'none');
- }, time)
- this.bAnimation = true;
- }
- },
- onReady() {
- this.halfNode = this.$refs['halfScreen'] as UniElement;//uni.getElementById('halfScreen');
- this.scrollNode = this.$refs['halfScroll'] as UniElement;//uni.getElementById('halfScroll');
- this.halfNode!.getBoundingClientRectAsync()!.then((rect: DOMRect) => {
- this.halfHeight = rect.height
- });
- (this.$refs['page'] as UniElement).getBoundingClientRectAsync()!.then((rect: DOMRect) => {
- this.totalHeight = rect.height
- this.halfNode?.style?.setProperty('top', this.totalHeight.toFixed(2)+'px');
- });
- },
- onResize() {
- this.halfNode?.getBoundingClientRectAsync()!.then((rect: DOMRect) => {
- this.halfHeight = rect.height
- });
- this.totalHeight = uni.getWindowInfo().windowHeight;
- this.halfNode?.style?.setProperty('top', this.totalHeight.toFixed(2)+'px');
- this.halfNode?.style?.setProperty('visibility', 'hidden');
- },
- onBackPress(): boolean {
- // #ifndef MP
- if('visible' == this.halfNode?.style?.getPropertyValue('visibility')){
- this.switchHalfScreen(false);
- return true;
- }
- return false;
- // #endif
- }
- }
- </script>
- <style>
- /* #ifdef MP */
- page {
- overflow: hidden
- }
- /* #endif */
- .page {
- flex: 1;
- background-color: darkgrey;
- }
- .tip {
- margin: 10px
- }
- .bottomButton {
- position: absolute;
- width: 100%;
- bottom: 0px;
- /* #ifdef APP */
- padding-bottom: env(safe-area-inset-bottom, 0px);
- /* #endif */
- }
- .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;
- transition-duration: 0ms;
- visibility: hidden;
- }
- .halfTitle {
- align-items: center;
- justify-content: center;
- height: 48px;
- background-color: ghostwhite;
- border-radius: 10px 10px 0 0;
- }
- .halfScroll {
- background-color: white;
- flex: 1;
- }
- .item {
- height: 100px;
- }
- </style>
|