深入解读JavaScript

小程序实现在线选座实战(中)

2019-03-24  本文已影响0人  悟C

hi~ 大家好,我叫内孤,一名web前端开发者/:B-,在小程序实现在线选座实战(中)里我们搭建完了layout,下面我们来实现最核心的选座。

在实现选座组件前,我们这里先介绍一下,我们需要的座位表数据结果

[
  {
    "id": 1,
    "price": 4,
    "x": 1,
    "y": 1
  }
]

其中x、y代表这个座位在整个座位表中的横轴和纵轴坐标,下面我们就针对这个数据结果展开实现这个选座组件

1. 首先创建Seat.js文件,并且初始化基本架子:

var Seat = (function(factory) {
  return factory.call();
}(function() {
  var __CORE__ = {
    init: function() {

    },
    render: function() {

    },
    setData: function() {

    }
  };

  return __CORE__;
}));

下面创建并且在init初始化模版:

    // __CORE__ 里面的方法
    init: function(options) {
      this.$el = document.querySelector(options.el);
      this.data = [];
      this.selectedData = [];

      this.$el.innerHTML = this._getDefaultTpl();
    },
    _getDefaultTpl: function() {
      return (
        '<div class="seatComponent">' +
          '<div class="screen">' +
            '<span class="title">舞台</span>' +
          '</div>' +
          '<div class="seat-container">' +
          '</div>' + 
        '</div>'        
      );
    },

这里还需要动态的计算seatComponentseat-container的大小

    // 添加计算容器的大小
    _computedContainerSize: function() {
      var seatContainer = this.$node.parentNode.getBoundingClientRect();
      return {
        width: seatContainer.width,
        height: seatContainer.height
      };
    }

// 修改init
    init: function(options) {
      this.$el = document.querySelector(options.el);
      this.data = [];
      this.selectedData = [];

      this.$el.innerHTML = this._getDefaultTpl();
      this.$node = this.$el.querySelector('.seatComponent');
      
      // 获取座位容器主要的区域
      var $seatViewContainer = this.$node.querySelector('.seat-container');
      this.layer = $seatViewContainer;

      // 初始化设置容器的大小
      var boxSize = this._computedContainerSize();
      this.$node.style.width = boxSize.width + 'px';
      this.$node.style.height = boxSize.height + 'px';

      $seatViewContainer.style.width = boxSize.width + 'px';
      // 42 是舞台模块写死的height+margin
      $seatViewContainer.style.height = boxSize.height - 42 + 'px';
    }

在渲染座位前,我们先写一个setData方法来注入座位信息

    setData: function(data) {
      this.data = data;
      this._renderSeat();
    },
    // 渲染座位表
    _renderSeat: function() {
      var me = this;
      var data = me.data;
      if (!data.length) return;

      var seatsList = this._createdSeat(data);
      this.layer.innerHTML = seatsList;
    },
    _createdSeat: function(data) {
      var me = this;
      var seatsList = '';
      var width = this.width - 20;     // 减去20为了给整个座位表添加一个padding
      var maxSize = this._getWrapperSize(data);
      // 计算一个座位的大小
      var seatWidth = parseInt(width / maxSize.x);
      // 计算整个座位表x轴占满后,剩余的宽度
      var overWidth = width - maxSize.x * seatWidth;
      // 计算左右可用padding
      var offsetLeft = Math.floor(overWidth / 2);

      for (var i = 0, len = data.length; i < len; i++) {
        var item = data[i];
        var _seatLeft = seatWidth * item.x + offsetLeft;
        var _seatTop = seatWidth * item.y;
        // -2 为了空出座位和座位之间的间隙
        var _seatWidth = seatWidth - 2;
        var _seatHeight = seatWidth - 2;
        var style = 'position: absolute; transform: matrix(1, 0, 0, 1,' + _seatLeft.toFixed(1) + ',' + _seatTop.toFixed(1) + '); width:' + _seatWidth.toFixed(1) + 'px;height:' +
        _seatHeight.toFixed(1) + 'px;background-color:' + (item.status === 0 ? '#fff' : '#989898') + ';';
        seatsList += '<div class="seat ' + 'seatId-' + item.id + '" data-index="' + i + '" data-type="seat" data-id="' + item.id + '" data-status="' + item.status + '" style="' + style + '"></div>';
      }

      return seatsList;
    },
    // 获取seat中最大的x和y
    _getWrapperSize: function(list) {
      var maxX = 0;
      var maxY = 0;

      if (!list) list = [];
      for (var i = 0, len = list.length; i < len; i++) {
        if (list[i].x > maxX) {
          maxX = list[i].x;
        }
        if (list[i].y > maxY) {
          maxY = list[i].y;
        }
      }

      return {
        x: maxX,
        y: maxY
      };
    }

通过_getWrapperSize方法算出最大x和y,然后根据容器的大小算出每一个座位占用的大小。绝对定位每一个座位,一个座位的left:“座位大小座位的x+偏移量”,top:“座位大小座位的y”,这样遍历整个座位列表我们就可以得到整个座位图:

image.png

接下去实现,拖动座位图和放大缩小功能:

    _onTouchLayer: function() {
      var me = this, 
          startX, 
          startY, 
          distance = {},
          origin,
          scale;

      me.isMove = false;
      me.isCanScale = false;
      me.scale = 1;
      
      me.$node.addEventListener('touchstart', function(e) {
        e.preventDefault();

        me.isMove = false;
        if (e.touches.length === 1) {
          startX = e.touches[0].clientX;
          startY = e.touches[0].clientY;
        } else if (e.touches.length > 1) {
          // 开始缩放
          me.isCanScale = true;

          distance.start = me._getDistance({
            x: e.touches[0].clientX,
            y: e.touches[0].clientY
          }, {
            x: e.touches[1].clientX,
            y: e.touches[1].clientY
          });
        }
      }, false);

      me.$node.addEventListener('touchmove', function(e) {
        e.preventDefault();
        var moveX, moveY, disX, disY;

        if (e.touches.length === 1) {
          moveX = e.touches[0].clientX;
          moveY = e.touches[0].clientY;

          disX = Math.round(moveX - startX);
          disY = Math.round(moveY - startY);

          if (Math.abs(disX) + Math.abs(disY) > 0) {
            me.isMove = true;
          }
          startX = moveX;
          startY = moveY;
          // 执行移动
          me._transformLayer(disX, disY);
        } else if (e.touches.length === 2) {
          origin = me._getOrigin({
            x: e.touches[0].clientX,
            y: e.touches[0].clientY
          }, {
            x: e.touches[1].clientX,
            y: e.touches[1].clientY
          });
          distance.stop = me._getDistance({
            x: e.touches[0].clientX,
            y: e.touches[0].clientY
          }, {
            x: e.touches[1].clientX,
            y: e.touches[1].clientY
          });

          scale = Math.ceil(distance.stop / distance.start + me.scale - 1);

          if (scale >= 2) {
            me.scale = 5;
          } else {
            me.scale = 1;
          }

          if (distance.stop - distance.start > 0) {
            // 放大
            me.scale = scale;     
            if (scale > 5) me.scale = 5;
          } else if (distance.stop - distance.start < 0) {
            // 缩小
            me.scale -= scale;
            if (scale < 5) me.scale = 1;
          }

          me.isMove = true;
          me._scaleLayer(origin, me.scale);
        }        

      }, false);

      me.$node.addEventListener('touchend', function(e) {
        e.preventDefault();
        me.isCanScale = false;
        if (me.scale === 1) {
          me._resetTransFormLayer();
        }
      }, false)
    },
    _scaleLayer: function(origin, scale) {
      var x, y;
      x = origin.x + (-origin.x) * scale;
      y = origin.y + (-origin.y) * scale;
      this.layer.style.transform = 'translate3d(' + this.layerLeft + 'px, ' + this.layerTop + 'px, 0) scale(' + scale + ')';
    },
    _transformLayer: function(disX, disY) {
      var me = this;
      // 如果正在缩放,则不进行移动
      if (me.isCanScale) true;

      if (this.layerTop > 100) {
        this.layerTop = 100;
      }

      if (me.scale === 5) {
        // doc.querySelector('.sureBtn').innerText = disY
        if (this.layerLeft >= 900 && disX > 0) {
          disX = 0;
        }

        if (this.layerLeft <= -900 && disX < 0) {
          disX = 0;
        }

        if (this.layerTop <= -1000 && disY < 0) {
          disY = 0;
        }
      }

      this.layerLeft += disX;
      this.layerTop += disY;
      // 开启3D加速移动位置
      this.layer.style.transform = 'translate3d(' + this.layerLeft + 'px, ' + this.layerTop + 'px, 0) scale(' + me.scale + ')';
    },
    _resetTransFormLayer: function() {
      this.layer.style.transform = 'translate3d(' + this.oldLayerLeft + 'px, ' + this.oldLayerTop + 'px, 0) scale(' + this.scale + ')';
      this.layerLeft = this.oldLayerLeft;
      this.layerTop = this.oldLayerTop;
    },
    _getOrigin: function(first, second) {
      return {
        x: (first.x + second.x) / 2,
        y: (first.y + second.y) / 2
      };
    },
    _getDistance: function(start, stop) {
      return Math.sqrt(Math.pow((stop.x - start.x), 2) + Math.pow((stop.y - start.y), 2));
    }

这里监听容器的touchstart 、touchmove 、touchend判断e.touches.length长度来判断指数,进行缩放或者移动的处理。

下面写监听点击了座位的事件,并抛出外部数据

 _onTouchSeat: function () {
      var me = this;
      this.layer.addEventListener('touchend', function (e) {
        e.preventDefault();
        if (me.isMove) return;

        var target = e.target;
        var type = target.getAttribute('data-type');
        var id = target.getAttribute('data-id');
        var status = target.getAttribute('data-status');
        var index = target.getAttribute('data-index');
        var data = me.data;

        if (type && type === 'seat') {
          // 如果状态为0, 则可以进行选择
          if (status == 0) {
            // 检测当前是取消还是选中
            if (target.className.indexOf('active') > -1) {
              // 取消
              target.className = target.className.replace('active', '');
              target.style.backgroundColor = '#fff';
              me._removeSelectedSeat(id, data[index], index);
            } else {
              // 选中
              target.className = target.className + ' active';
              target.style.backgroundColor = 'inherit';
              me._addSelectedSeat(id, data[index], index);
            }
          }
        }
      });
    },
    _addSelectedSeat: function(id, item, index) {
      item.index = index;
      this.selectedData.push(item);
      this._onChange();
    },
    _removeSelectedSeat: function(id, item) {
      var selectedData = this.selectedData;
      var index = 0;
      var i = 0;
      var len = selectedData.length;
      for (i; i < len; i++) {
        if (selectedData[i] === item.id) {
          index = i;
          break;
        }
      }
      selectedData.splice(index, 1);
      this._onChange();
    },
    _onChange: function() {
      var selectedData = this.selectedData;
      this.onChange(selectedData);
    }

以上基本已经完成了座位表的功能,不过有一个缺点,不能根据指定缩放位置缩放

上一篇下一篇

猜你喜欢

热点阅读