uniapp 实现微信小程序电影选座功能

拖动代码

  /**
     * 获取点击或触摸事件对应的座位位置
     * 通过事件对象获取座位的行列信息
     * @param {Event|TouchEvent} event - 点击或触摸事件对象
     * @returns {Object} 返回座位位置对象,包含行(row)和列(col)信息,若未找到有效位置则返回 {row: -1, col: -1}
     */
    getSeatPosition(event) {
      // 统一处理触摸事件和点击事件
      // 触摸事件时从 touches 数组获取第一个触摸点
      // 点击事件时直接使用事件对象
      const touch = event.touches ? event.touches[0] : event;

      // 获取触摸/点击的坐标位置
      // clientX/Y 用于标准事件,x/y 用于某些特殊环境
      const x = touch.clientX || touch.x;
      const y = touch.clientY || touch.y;

      // 创建查询对象,用于获取 DOM 信息(当前未使用)
      const query = uni.createSelectorQuery();

      // 从事件目标的数据集中获取座位信息
      // 使用 HTML5 data-* 属性存储的行列信息
      if (event.target && event.target.dataset) {
        const dataset = event.target.dataset;
        // 检查数据集中是否包含有效的行列信息
        if (dataset.row !== undefined && dataset.col !== undefined) {
          // 返回解析后的座位位置
          // parseInt 确保返回数值类型
          return {
            row: parseInt(dataset.row),
            col: parseInt(dataset.col)
          };
        }
      }

      // 如果无法获取有效的座位信息
      // 返回表示无效位置的对象
      return { row: -1, col: -1 };
    },

    /**
     * 处理触摸开始事件
     * 用于初始化拖拽和缩放的起始状态
     * @param {TouchEvent} event - 触摸事件对象,包含触摸点信息
     */
    onTouchStart(event) {
      // 记录触摸开始的时间戳,用于后续判断是点击还是拖动
      this.touchStartTime = Date.now();
      // 重置移动标志,初始状态下未发生移动
      this.isMoved = false;

      // 单指触摸 - 处理拖动初始化
      if (event.touches.length === 1) {
        const touch = event.touches[0];
        // 记录当前触摸点作为上一次触摸位置,用于计算移动距离
        this.lastTouch = { x: touch.clientX, y: touch.clientY };
        // 记录触摸起始位置,用于计算总移动距离
        this.touchStartPos = { x: touch.clientX, y: touch.clientY };
      }
      // 双指触摸 - 处理缩放初始化
      else if (event.touches.length === 2) {
        // 计算两个触摸点之间的初始距离,用于后续计算缩放比例
        this.startDistance = this.getDistance(event.touches[0], event.touches[1]);
      }
    },

    // 处理触摸移动事件
    onTouchMove(event) {
      // 标记已经发生移动,用于区分点击和拖动
      this.isMoved = true;

      // 单指触摸 - 处理拖动
      if (event.touches.length === 1) {
        const touch = event.touches[0];
        // 计算相对于上一次触摸位置的偏移量
        const deltaX = touch.clientX - this.lastTouch.x;
        const deltaY = touch.clientY - this.lastTouch.y;

        // 根据当前缩放比例调整位移距离
        // 缩放比例越大,移动距离越小,保证移动体验一致
        this.position.x += deltaX / this.scale;
        this.position.y += deltaY / this.scale;

        // 更新最后一次触摸位置
        this.lastTouch = { x: touch.clientX, y: touch.clientY };
      }
      // 双指触摸 - 处理缩放
      else if (event.touches.length === 2) {
        // 计算当前两个触摸点之间的距离
        const currentDistance = this.getDistance(event.touches[0], event.touches[1]);
        // 根据距离变化计算新的缩放比例
        let newScale = this.scale * (currentDistance / this.startDistance);
        // 限制缩放范围在 minScale 和 maxScale 之间
        newScale = Math.max(this.minScale, Math.min(this.maxScale, newScale));
        this.scale = newScale;
        // 更新起始距离,用于下一次计算
        this.startDistance = currentDistance;
      }

      // 检查并限制移动边界,防止内容移出可视区域
      this.checkBoundaries();
    },

    // 处理手势结束
    onTouchEnd() {
      // 可以在这里处理手势结束后的逻辑
    },

    /**
     * 计算两个触摸点之间的距离
     * @param {Object} touch1 - 第一个触摸点,包含 clientX 和 clientY 坐标
     * @param {Object} touch2 - 第二个触摸点,包含 clientX 和 clientY 坐标
     * @returns {number} 两点之间的欧几里得距离
     */
    getDistance(touch1, touch2) {
      // 计算 X 轴方向的距离差
      const dx = touch1.clientX - touch2.clientX;
      // 计算 Y 轴方向的距离差
      const dy = touch1.clientY - touch2.clientY;
      // 使用勾股定理计算两点之间的直线距离
      // distance = √(dx² + dy²)
      return Math.sqrt(dx * dx + dy * dy);
    },

    /**
     * 检查并限制座位区域的移动边界
     * 防止用户将座位区域拖动到视图之外
     */
    checkBoundaries() {
      // 定义最大可移动距离(像素)
      const maxX = 200; // X轴最大移动距离,可根据实际座位区域大小调整
      const maxY = 200; // Y轴最大移动距离,可根据实际座位区域大小调整

      // 限制X轴移动范围:[-maxX, maxX]
      // Math.min 确保不会超过右边界
      // Math.max 确保不会超过左边界
      this.position.x = Math.max(-maxX, Math.min(maxX, this.position.x));

      // 限制Y轴移动范围:[-maxY, maxY]
      // Math.min 确保不会超过下边界
      // Math.max 确保不会超过上边界
      this.position.y = Math.max(-maxY, Math.min(maxY, this.position.y));
    },

计算总价代码

 

    /**
     * 获取指定座位在所有已选座位中的序号
     * @param {number} row - 要查询的座位行号
     * @param {number} col - 要查询的座位列号
     * @returns {number} 返回该座位是第几个被选中的座位(从1开始计数)
     * 
     * 使用场景:
     * 1. 用于确定座位的选中顺序
     * 2. 可用于显示座位的选中序号
     * 3. 帮助用户了解座位的选择顺序
     */
    getSelectedIndex(row, col) {
      // 初始化计数器,从1开始计数
      let count = 1;

      // 遍历所有座位
      for (let i = 0; i < this.seatMap.length; i++) {
        for (let j = 0; j < this.seatMap[i].length; j++) {
          // 检查当前遍历到的座位是否被选中
          if (this.seatMap[i][j].selected) {
            // 如果找到目标座位,返回当前计数
            if (i === row && j === col) return count;
            // 如果不是目标座位,计数器加1
            count++;
          }
        }
      }
      return count;
    },

    // 获取已选座位列表
    getSelectedSeats() {
      const selectedSeats = [];
      this.seatMap.forEach((row, rowIndex) => {
        row.forEach((seat, colIndex) => {
          if (seat.selected) {
            selectedSeats.push({
              row: rowIndex,
              col: colIndex,
              type: seat.type
            });
          }
        });
      });
      return selectedSeats;
    },

    /**
     * 计算所有已选座位的总价
     * @returns {string} 返回格式化后的总价字符串,保留两位小数
     * 
     * 使用场景:
     * 1. 显示确认选座按钮上的总价
     * 2. 提交订单时计算支付金额
     * 3. 更新用户选座时实时显示价格
     */
    getTotalPrice() {
      // 定义不同类型座位的价格映射
      const prices = {
        pink: 40,   // 粉色座位(VIP座)价格
        orange: 38, // 橙色座位(情侣座)价格
        blue: 35    // 蓝色座位(普通座)价格
      };

      // 使用 reduce 方法计算总价
      // 1. 获取所有已选座位列表
      // 2. 根据每个座位的类型获取对应价格
      // 3. 累加所有座位的价格
      return this.getSelectedSeats().reduce((total, seat) => {
        // total: 累计总价
        // seat: 当前座位信息,包含 type 属性
        return total + prices[seat.type];
      }, 0).toFixed(2); // 初始值为0,结果保留两位小数
    },

    /**
     * 获取指定类型座位的单价
     * @param {string} type - 座位类型('pink'|'orange'|'blue')
     * @returns {string} 返回格式化后的价格字符串,保留两位小数
     * 
     * 使用场景:
     * 1. 显示单个座位的价格
     * 2. 在已选座位列表中显示每个座位的单价
     */
    getSeatPrice(type) {
      // 定义不同类型座位的价格映射
      const prices = {
        pink: 40,   // 粉色座位(VIP座)价格
        orange: 38, // 橙色座位(情侣座)价格
        blue: 35    // 蓝色座位(普通座)价格
      };

      // 返回格式化后的价格,保留两位小数
      return prices[type].toFixed(2);
    },

    /**
     * 处理确认选座操作
     * 验证选座状态并进行后续处理
     * 
     * 使用场景:
     * 1. 用户点击确认选座按钮时触发
     * 2. 验证是否已选择座位
     * 3. 进行下一步订单处理
     */
    confirmSeats() {
      // 检查是否有选中的座位
      if (this.selectedSeatsCount === 0) {
        // 如果没有选择座位,显示提示信息
        uni.showToast({
          title: '请先选择座位',
          icon: 'none'
        });
        return;
      }

      // TODO: 处理确认选座逻辑
      // 可以添加以下操作:
      // 1. 获取选中的座位信息
      // 2. 调用后端API锁定座位
      // 3. 跳转到订单确认页面
      // 4. 处理支付流程等
      console.log('确认选座', this.getSelectedSeats());
    }

完整代码





你可能感兴趣的:(uni-app,微信小程序,javascript,vue.js,前端,小程序)