uniapp小程序实现手动向上滑动窗口

实现下图所示,手动向上滑,窗口滑出:

javascript 复制代码
//  ---data定义
      // 面板当前位置 (px)
      panelPosition: 620,
      // 面板完全展开的位置
      panelUpPosition: 100,
      // 面板收起的位置
      panelDownPosition: 400,
      // 触摸起始点
      startY: 0,
      // 记录滑动过程中的位移
      moveY: 0,
      // 控制遮罩显示
      isPanelVisible: false,
      // 控制scroll-view的滚动位置
      scrollTop: 0,
      // 标记面板当前状态
      panelState: 'down',
      videoContext: null,
javascript 复制代码
// -----布局
<view
      class="detail-mask"
      :class="{ 'mask-show': isPanelVisible }"
      @tap="hideDetailPanel"
    ></view>

    <view
      class="detail-panel"
      :style="{ transform: `translateY(${panelPosition}px)` }"
      @touchstart="onTouchStart"
      @touchmove="onTouchMove"
      @touchend="onTouchEnd"
    >
      <view class="panel-handle">
        <view class="handle-bar"></view>
      </view>

      <scroll-view
        class="panel-content"
        scroll-y="true"
        :scroll-top="scrollTop"
        style="height: 2000rpx"
      >
        <view class="content-section">
          <view class="top-box">
            <view class="section-title">查看数据</view>
            <view class="iconfont icon-zhaoxiangji1" @click="toPhoto"></view>
          </view>

          <view class="realtime-section" v-if="cameraData.length > 0">
            <image
              class="content-image"
              :src="snapshotImg"
              mode=""
              v-if="snapshotImg"
            ></image>

            <view class="sub-title" @click="toViewVideo">
              <view class="iconfont icon-shipinchakan"></view>
            </view>
          </view>
        </view>

        <view class="next-box">
          <view class="content-section">
            <view class="section-box" v-if="cameraData.length > 0">
              <view class="section-title set-top">长势图</view>
              <view class="section-history" @click="viewHistory">历史数据</view>
            </view>

            <view class="comparison-box" v-if="snapshotImgArr.length > 0">
              <view class="item-mid">
                <view class="two-step">
                  <view class="two-line">
                    <view class="step-right">
                      <view class="line-content">
                        <view class="right-a">
                          <view class="r-name set-color">{{ getTime() }}</view>
                        </view>
                        <view class="grouth-box">
                          <view
                            class="img-box"
                            v-for="item in snapshotImgArr"
                            :key="item"
                          >
                            <image
                              style="width: 290rpx; height: 200rpx"
                              :src="item.imagePath"
                              mode=""
                            ></image>
                            <view class="img-text">{{ item.imageDate }}</view>
                          </view>
                        </view>
                        <view class="content">
                          <view class="section">
                            <view class="markdown-container">
                              <uMarkdown
                                :source="textInfo.comparisonResult"
                              ></uMarkdown>
                            </view>
                          </view>

                          <view class="section">
                            <view class="markdown-container">
                              <uMarkdown
                                :source="textInfo.recommendations"
                              ></uMarkdown>
                            </view>
                          </view>
                        </view>
                      </view>
                    </view>
                  </view>
                </view>
              </view>
            </view>
          </view>
        </view>
      </scroll-view>
    </view>
javascript 复制代码
// ------函数方法
// 隐藏详情面板
    hideDetailPanel () {
      this.panelPosition = 620
      this.panelState = 'down'
      // 动画结束后隐藏遮罩
      setTimeout(() => {
        this.isPanelVisible = false
        this.scrollTop = 0
      }, 300)
    },
    handlePhoneTouchStart (e) {
      uni.makePhoneCall({
        phoneNumber: '13122222222'
      })
      e.stopPropagation()
    },
    // 触摸开始
    onTouchStart (e) {
      if (
        e.target.dataset.type === 'phoneBtn' ||
        e.target.dataset.type === 'mointorBtn'
      ) {
        return
      }
      // e.stopPropagation()
      this.startY = e.touches[0].clientY
      this.isPanelVisible = true
      this.$nextTick(() => {
        setTimeout(() => {
          this.panelPosition = this.panelUpPosition
          this.panelState = 'up'
        }, 50)
      })
    },

    // 触摸移动
    onTouchMove (e) {
      if (!this.isPanelVisible) return
      let currentY = e.touches[0].clientY
      this.moveY = currentY - this.startY

      // 阻止向下滚动时页面整体滚动
      if (this.moveY > 0 && this.scrollTop <= 0) {
        e.preventDefault()
      }

      // 计算新的面板位置
      let newPosition = this.panelUpPosition + this.moveY

      // 限制拖动范围:不能向上推得比完全展开更高,向下不能过低
      newPosition = Math.max(this.panelUpPosition - 50, newPosition) // 允许稍微拉过一点
      newPosition = Math.min(newPosition, this.panelDownPosition + 50)
      this.panelPosition = newPosition
    },

    // 触摸结束
    onTouchEnd (e) {
      console.log('onTouchEnd')

      if (!this.isPanelVisible) return
      // e.stopPropagation()
      const moveDistance = this.moveY
      const panelHeight = this.panelDownPosition - this.panelUpPosition

      // 根据滑动距离和速度决定最终状态
      if (moveDistance > panelHeight * 0.3 || moveDistance > 100) {
        // 下滑超过阈值,收起
        this.hideDetailPanel()
      } else {
        // 否则,恢复展开状态
        this.panelPosition = this.panelUpPosition
        this.panelState = 'up'
      }

      // 重置移动距离
      this.moveY = 0
    },
javascript 复制代码
css样式
.detail-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  visibility: hidden;
  transition: all 0.3s ease;
  z-index: 998;
}
.mask-show {
  opacity: 1;
  visibility: visible;
}

.detail-panel {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 95vh;
  background: #fff;
  border-top-left-radius: 30rpx;
  border-top-right-radius: 30rpx;
  z-index: 999;
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* 平滑的动画曲线 */
  box-shadow: 0 -5rpx 20rpx rgba(0, 0, 0, 0.15);
  display: flex;
  flex-direction: column;
}
.handle-bar {
  width: 100rpx;
  height: 8rpx;
  background-color: #ddd;
  border-radius: 4rpx;
}
.panel-content {
  flex: 1;
  padding: 0 30rpx;
  // width: 700rpx;
  box-sizing: border-box;
}

.realtime-section {
  position: relative;
}
.sub-title {
  position: absolute;
  bottom: 12rpx;
  left: 0;
  width: 100%;
  height: 68rpx;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  border-radius: 10rpx;
  text-align: right;

  line-height: 68rpx;
  .text-bold {
    font-size: 30rpx;
    margin-left: 16rpx;
    margin-top: 16rpx;
  }
  .icon-shipinchakan {
    margin-right: 40rpx;
  }
}
.icon-fangda04 {
  position: absolute;
  top: 32rpx;
  right: 40rpx;
  color: #fff;
}
.section-box {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.section-title {
  font-size: 32rpx;
  font-weight: bold;
  margin-bottom: 20rpx;
}
.section-history {
  font-size: 28rpx;
  color: #767676;
  // width: 160rpx;
  height: 40rpx;
}
.content-image {
  width: 100%;
  border-radius: 15rpx;
  margin-top: 15rpx;
  height: 385rpx;
}
/* 全屏视频样式 */
.fullscreen-video {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 9999;
  background-color: #000;
}

.video-close-btn {
  position: absolute;
  top: 60rpx;
  right: 30rpx;
  z-index: 10000;
  width: 60rpx;
  height: 60rpx;
  background-color: rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 40rpx;
}
相关推荐
学传打活5 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续3)
微信公众平台·1024程序员节·汉字·昆仑正义文化
学传打活10 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续1)
微信公众平台·1024程序员节·汉字·昆仑正义文化
fangzhanpeng16818 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心20 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u0103055271 个月前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u0103055271 个月前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝1 个月前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u0103055271 个月前
Java图像处理实战指南
人工智能·1024程序员节
u0103055271 个月前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u0103055271 个月前
Java实用类应用精讲
人工智能·1024程序员节