美文网首页深入解读JavaScript
小程序实现在线选座实战(中)

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

作者: 悟C | 来源:发表于2019-03-24 16:37 被阅读0次

    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);
        }
    

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

    相关文章

      网友评论

        本文标题:小程序实现在线选座实战(中)

        本文链接:https://www.haomeiwen.com/subject/gjoumqtx.html