【原生小程序使用webview添加横向滚动】

html 复制代码
<view class="container">
  <web-view wx:if="{{showWebview}}" src="{{templateUrl}}" bindload="onWebViewLoad">
    <cover-view class="floating-btn-container">
      <cover-view class="btn-content">
        <cover-view class="btn" bindtap="bindFile">
          <cover-image class="btn-icon" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/download_icon.png" />
          <cover-view class="btn-text">保存至附件</cover-view>
          <cover-view class="divider"></cover-view>
        </cover-view>
        <cover-view class="btn" bindtap="handleBack">
          <cover-image class="btn-icon" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/detail_icon.png" />
          <cover-view class="btn-text">更换样式</cover-view>
          <cover-view class="divider"></cover-view>
        </cover-view>
        <cover-view class="btn" bindtap="uploadFile">
          <cover-image class="btn-icon" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/upload_icon.png" />
          <cover-view class="btn-text">上传简历</cover-view>
          <cover-view class="divider"></cover-view>
        </cover-view>
        <cover-view class="btn btn-last" bindtap="goToEdit">
          <cover-image class="btn-icon" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/edit_icon.png" />
          <cover-view class="btn-text">编辑</cover-view>
        </cover-view>
      </cover-view>
    </cover-view>
  </web-view>

  <cover-view class="tpl-content" wx:if="{{show}}">
    <cover-view class="menu-overlay">
      <cover-view class="menu-content" style="transform: translateX({{translateX}}px)" bind:touchstart="onTouchStart" bind:touchmove="onTouchMove">
        <block wx:if="{{isIOS}}">
          <cover-view wx:for="{{pdfList}}" wx:key="{{item.id}}" class="items" data-id="{{item.id}}" data-imgUrl="{{item.img_src}}"  id="item-{{item.id}}" data-title="{{item.name}}" bind:tap="choose">
            <cover-image class="items-image" src="{{item.img_src}}" mode="" />
            <cover-view class="items-text">{{item.name}}</cover-view>
            <cover-image wx:if="{{item.id==id}}" class="choose-image" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/choose-icon.png" mode="" />
            <cover-image wx:else class="choose-image" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/no-choose-icon.png" mode="" />
          </cover-view>
        </block>
        <block wx:else>
          <cover-view wx:for="{{pdfList}}" wx:key="{{item.id}}" id="item-{{item.id}}" class="items {{item.id==id?'selected-box': 'un-selected-box'}}" data-id="{{item.id}}" data-imgUrl="{{item.img_src}}" data-title="{{item.name}}" bind:tap="choose">
            <cover-image class="items-image" src="{{item.img_src}}" mode="" />
            <cover-view class="items-text">{{item.name}}</cover-view>
          </cover-view>
        </block>
      </cover-view>
    </cover-view>
    <cover-view class="tpl-btn" bind:tap="chooseClose">使用此模版</cover-view>
  </cover-view>
</view>
css 复制代码
.mask {
  position: fixed;
  top: 0;
  left: 0;
  background: rgba(0, 0, 0, .3);
  width: 100%;
  height: 100%;
}

/* 容器样式 */
.container {
  height: 100vh;
  background: #f3f3f3;
}

/* 悬浮按钮容器 */
.floating-btn-container {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  z-index: 9999;
  background: #f3f3f3;
  padding: 30rpx 0;
}

/* 按钮内容区域 */
.btn-content {
  display: flex;
  justify-content: space-around;
  /* 横向均匀分布 */
  align-items: center;
  width: 90%;
  height: 120rpx;
  /* 增加高度容纳图标+文字 */
  margin: 0 auto;
  background: #fff;
  border-radius: 56rpx;
  box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.1);
  padding: 0 40rpx;
  box-sizing: border-box;
}


.btn {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 25%;
}

.btn-last {
  width: 20%;
}

/* 图标样式 */
.btn-icon {
  width: 48rpx;
  height: 48rpx;
  margin-bottom: 8rpx;
  /* 图标与文字间距 */
}

/* 文字样式 */
.btn-text {
  font-size: 24rpx;
  color: #333;
  white-space: nowrap;
  /* 防止文字换行 */
}

.divider {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 1rpx;
  height: 60%;
  /* 控制竖线高度为按钮区域的60% */
  background: #e5e5e5;
}

.items {
  flex-shrink: 0;
  /* 禁止收缩 */
  display: flex;
  flex-direction: column;
  position: relative;
  width: 240rpx;
  /* 与图片宽度一致 */
  margin-right: 10rpx;
}

.choose-image {
  width: 32rpx;
  height: 34rpx;
  position: absolute;
  z-index: 10002;
  right: 20rpx;
  top: 20rpx;
}

.selected-box {
  border: 2px solid #1787FB !important;
  border-radius: 8rpx;
}

.un-selected-box {
  border: 0px solid #1787FB !important;
  border-radius: 8rpx;
}

.ios-style {
  border: none;
  border-radius: 0;
}

.items-image {
  width: 240rpx;
  height: 360rpx;
}

.items-text {
  font-size: 24rpx;
  color: #333333;
  line-height: 34rpx;
  text-align: center;
}

.tpl-content {
  position: fixed;
  left: 0;
  bottom: 0;
  height: 610rpx;
  background: rgba(205, 231, 255, 1);
  z-index: 10000;
  width: 100%;
}

.tpl-btn {
  height: 88rpx;
  background: #1787FB;
  border-radius: 8rpx;
  width: 90%;
  font-weight: 500;
  font-size: 28rpx;
  color: #FFFFFF;
  line-height: 88rpx;
  text-align: center;
  position: fixed;
  bottom: 60rpx;
  left: 50%;
  transform: translate(-50%, 0);
  width: 90%;
  z-index: 10001;
}

/* 覆盖层容器 */
.menu-overlay {
  position: absolute;
  top: 0;
  visibility: visible !important;
  overflow: scroll;
  left: 0;
  height: 450rpx;
  padding: 30rpx 30rpx 0rpx 30rpx;
  z-index: 10001;
}

/* 菜单内容容器 */
.menu-content {
  display: flex;
  flex-direction: row;
  width: 100%;
  height: 400rpx;
  padding-bottom: 10rpx;
  transition: transform 0.3s;
  will-change: transform; /* 提前声明动画属性 */
  -webkit-overflow-scrolling: touch; /* 启用 iOS 弹性滚动 */
  transform: translateZ(0); /* 强制开启 GPU 加速 */
  /* 平滑动画 */
}
js 复制代码
var $ = getApp();
Page({

  /**
   * 页面的初始数据
   */
  data: {
    showWebview: true,
    resumeUrl: '',
    title: '',
    id: '0', //模版id
    resumeId: 0, //简历id
    loading: !1,
    pdfList: [],
    templateUrl: '',
    show: false,
    translateX: 0,
    maxScroll: 0,
    startX: 0,
    currentIndex: 0,
    lastUpdate: 0,
    isIOS: false,
    currentX: 0
  },
  onTouchStart(e) {
    this.setData({
      startX: this.data.isIOS ? e.touches[0].clientX : e.touches[0].pageX,
      currentX: this.data.translateX
    });
  },
  onTouchMove(e) {
    const deltaX = (this.data.isIOS ? e.touches[0].clientX : e.touches[0].pageX) - this.data.startX;
    let newTranslateX = this.data.currentX + deltaX;
    // 限制滚动范围(示例值需根据内容宽度调整)
    const maxScroll = -(140 * this.data.pdfList.length - 375)
    newTranslateX = Math.min(0, Math.max(maxScroll, newTranslateX))
    // 节流更新频率(每 16ms 更新一次)
    if (Date.now() - this.data.lastUpdate > 16) {
      this.setData({
        translateX: newTranslateX,
        lastUpdate: Date.now()
      })
    }
  },
  onLoad(options) {
    const systemInfo = wx.getSystemInfoSync()
    const isIOS = systemInfo.system.includes('iOS')
    this.setData({
      isIOS: isIOS
    })
    this.getMakePDF()
    if (options?.id) {
      this.setData({
        id: options.id
      })
    }
    if (options?.resumeId) {
      this.setData({
        resumeId: options.resumeId
      })
    }
    if (options?.title) {
      this.setData({
        title: options.title
      })
    }
    this.setData({
      templateUrl: url
    })
    this.getResumePdfUrl()
    // 动态计算高度(示例:屏幕高度 - 按钮区域高度)
    const buttonHeight = 60; // 根据实际按钮高度调整
    this.setData({
      webviewHeight: systemInfo.windowHeight - buttonHeight
    });
  },
  centerSelectedItem() {
    wx.createSelectorQuery()
      .select(`#item-${this.data.id}`)
      .boundingClientRect(res => {
        if (res) {
          const screenWidth = wx.getSystemInfoSync().screenWidth
          const translateX = screenWidth / 2 - res.left - res.width / 2
          this.setData({
            translateX
          })
        }
      }).exec()
  },
  getMakePDF(type) {
    $.http('home/resume_tpl/getTplList', (r) => {
      this.setData({
        pdfList: r.data
      }, () => {
        setTimeout(() => {
          // this.centerSelectedItem()
        }, 200)
      })
      if (type == 'init') {
        this.setData({
          show: true
        })
      }
    })
  },
  onWebViewLoad() {
    // 再次强制设置标题
    wx.setNavigationBarTitle({
      title: this.data.title + "简历模版-预览"
    });
  },
  getResumePdfUrl() {
    let params = {
        uid: $.visitor.uid,
        tpl_id: this.data.id
      },
      params2 = {
        id: this.data.resumeId,
        is_export: 0
      }
    let url = this.data.id == -1 ? 'home/resume/exportPdfByPhp' : 'home/poster_two/makePdf'
    if (this.data.id == -1) {
      wx.showLoading({
        title: '加载中...',
      })
    }
    $.http(url, this.data.id == -1 ? params2 : params, (r) => {
      if (this.data.id == -1) {
        wx.downloadFile({
          url: r.data.url, // 文件的本身url
          filePath: wx.env.USER_DATA_PATH + "/" + this.data.title, // 本地自定义的文件名
          success: function (res) {
            let filePath = res.filePath; // 微信临时文件路径(这里要使用自定义的名字文件名,否则打开的文件名是乱码)
            wx.openDocument({
              filePath: filePath,
              fileType: 'pdf',
              showMenu: true, // 是否显示右上角菜单按钮 默认为false(看自身需求,可要可不要。后期涉及到右上角分享功能)
              success: function () {
                wx.hideLoading()
              },
              fail: function (e) {
                wx.hideLoading()
                $.msg(0, '网络繁忙,请稍后重试~')
              }
            });
          },
          fail: function (r) {
            wx.hideLoading()
            $.msg(0, '网络繁忙,请稍后重试~')
          }
        });
        return
      }
      this.setData({
        resumeUrl: r.data
      })
    }, 'post')
  },
  choose(e) {
    let id = e.currentTarget.dataset.id,
      title = e.currentTarget.dataset.title
    wx.setNavigationBarTitle({
      title: title + "简历模版-预览"
    })

    this.setData({
      id,
      title
    }, () => {
      this.setData({})
    })
    console.log(this.data.id)
  },
  chooseClose() {
    this.setData({
      showWebview: false
    }, () => {
      setTimeout(() => {
        this.setData({
          show: false,
          showWebview: true,
          templateUrl: `https://www.hsolar.com/member/resume/template/index${this.data.id}?id=${$.visitor.uid}`
        })
      }, 200)
      this.getResumePdfUrl()
    })
  },
  // 换样式
  handleBack() {
    if (this.data.pdfList.length) {
      this.setData({
        show: true
      })
    } else {
      this.getMakePDF('init')
    }
  },
  // 跳转
  goToEdit() {
    wx.redirectTo({
      url: '/pages/personal/resume_edit/resume_edit',
    })
  },
  // 上传附件
  uploadFile() {
    wx.navigateTo({
      url: '/subPackages/pages/personal/resume_accessory/resume_accessory',
    })
  },
  detail(f) {
    $.http('personal/resume_two/detail', function (r) {
      let d = r.data
      f && f(d)
    })
  },
  bindFile() {
    wx.showLoading({
      title: '生成中',
    })
    this.setData({
      loading: !0
    })
    let that = this
    wx.downloadFile({
      url: this.data.resumeUrl, // 文件的本身url
      filePath: wx.env.USER_DATA_PATH + "/" + this.data.title, // 本地自定义的文件名
      success: function (res) {
        let filePath = res.filePath; // 微信临时文件路径(这里要使用自定义的名字文件名,否则打开的文件名是乱码)
        wx.openDocument({
          filePath: filePath,
          fileType: 'pdf',
          showMenu: true, // 是否显示右上角菜单按钮 默认为false(看自身需求,可要可不要。后期涉及到右上角分享功能)
          success: function () {
            wx.hideLoading()
            that.setData({
              loading: !1
            })
            $.msg(0, '生成成功')
          },
          fail: function (e) {
            wx.hideLoading()
            that.setData({
              loading: !1
            })
            $.msg(0, '网络繁忙,请稍后重试~')
          }
        });
      },
      fail: function (r) {
        wx.hideLoading()
        that.setData({
          loading: !1
        })
        $.msg(0, '网络繁忙,请稍后重试~')
      }
    });
  },
  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {
    // wx.onAppRoute((route) => {
    //   const pages = getCurrentPages();
    //   console.log(route, pages, '===')
    //   if (pages.length > 1) {
    //     // 覆盖左上角返回按钮行为
    //     // wx.enableAlertBeforeUnload({
    //     //   message: '是否直接返回上一页?',
    //     //   success: (res) => {
    //     //     console.log(res,999)
    //     //     if (res.errMsg=='enableAlertBeforeUnload:ok') {
    //     //       wx.redirectTo({
    //     //         url: '/subPackagesC/pages/resumeTetemplate/resumeTetemplate',
    //     //       })
    //     //     }
    //     //   },
    //     // });
    //   }
    // });
  }
})
相关推荐
说私域6 小时前
数字传播生态中开源链动模式与智能技术协同驱动的品牌认知重构研究——基于“开源链动2+1模式+AI智能名片+S2B2C商城小程序”的场景化传播实践
人工智能·小程序·重构·开源·零售
帅次15 小时前
Flutter TabBar / TabBarView 详解
android·flutter·ios·小程序·iphone·taro·reactnative
MaCa .BaKa1 天前
36-校园反诈系统(小程序)
java·spring boot·mysql·小程序·vue·maven·uniapp
始識1 天前
某团小程序mtgsig,_token 生成逻辑分析
爬虫·小程序
象骑士Hack1 天前
Uni-app小程序 hello world示例
小程序·uni-app
ice breaker1 天前
weapp-vite - 微信小程序工具链的另一种选择
微信小程序·小程序·notepad++