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

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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 《这辈子,我只宠你》 ——写给妈妈 我曾寄居在你的血肉里 我来到这个世界时 是这辈子 你最疼痛的记忆 此后的日子里...
    sunroot阅读 607评论 0 0
  • 姓名:巴桂成 公司:宁波大发化纤有限公司 宁波盛和塾《六项精进》235期学员 【日精进打卡第155天】 【知~学习...
    巴桂成_c6dd阅读 190评论 0 0
  • 今天我做作业盒子的时候,爸爸帮我分析问题的时候我不听,结果全错了,我以后一定要虚心,不能骄傲。
    韦金滢阅读 167评论 0 0
  • 缘知绝,散落天涯。 “宋维,如果从此不见,你会想我吗?” 宋维转过头安静地看着宇康,“康,怎么了?” “你先回答我...
    昕圜阅读 220评论 5 1
  • 这是李婷2018年读书计划的第2篇读书笔记 这是一本什么类型的书? 这不是一本虚拟的小说,而是一本论述性的书,更准...
    婷婷玉立水墨画阅读 588评论 0 0