利用Javascript開發(fā)一個二維周視圖日歷

前言

創(chuàng)新互聯(lián)建站長期為1000+客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為霍林郭勒企業(yè)提供專業(yè)的成都網(wǎng)站制作、做網(wǎng)站,霍林郭勒網(wǎng)站改版等技術(shù)服務(wù)。擁有十年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。

本文給大家介紹了Javascript開發(fā)二維周視圖日歷的相關(guān)內(nèi)容,即之前實現(xiàn)了一個月視圖日歷,我們今天來實現(xiàn)一個二維周視圖的日歷。

以下進(jìn)行分析其中的關(guān)鍵部分。

結(jié)構(gòu)準(zhǔn)備

不同之處在于其在日歷的基礎(chǔ)上還有一個分類軸,用于展示不同的類目,主要用于一周內(nèi)的日程安排、會議安排等。

二維則和之前單獨(dú)的有所不同,二維日歷再切換日期時不用全部重新渲染,分類是不用變的,僅僅改變顯示的日期即可。

而且由于是二維的,插入的內(nèi)容必定是同時屬于一個分類和一個時間段的,內(nèi)容肯定是可以跨越時間(即日期軸)的,因此不能直接將插入的內(nèi)容像開始的日歷一樣直接放置在日歷的格子中。而要進(jìn)行單獨(dú)的處理。

另外,只要分類不變,日期和分類構(gòu)成的網(wǎng)格是不用重繪的。

考慮到以上情況,插入內(nèi)容的和網(wǎng)格是需要分開來的,我將現(xiàn)成的日歷弄成一下3D效果示意:

利用Javascript開發(fā)一個二維周視圖日歷

即插入內(nèi)容的層是單獨(dú)放置在時間和分類構(gòu)成的網(wǎng)格上方的。

基于以上分析,先構(gòu)建如下基本結(jié)構(gòu):

<div class="ep-weekcalendar border">
 <!-- 頭部 -->
 <div class="ep-weekcalendar-header">
 <div class="ep-weekcalendar-header-left"></div>
 <div class="ep-weekcalendar-header-center">
 <span class="ep-weekcalendar-header-btn ep-weekcalendar-header-btn-prev"></span>
 <span class="ep-weekcalendar-title">2017年12月04日 - 10日</span>
 <span class="ep-weekcalendar-header-btn ep-weekcalendar-header-btn-next"></span>
 </div>
 <div class="ep-weekcalendar-header-right"></div>
 </div>
 <!-- 主體 -->
 <div class="ep-weekcalendar-body">
 <!-- 分類區(qū)域 -->
 <div class="ep-weekcalendar-category-area">
 <div class="ep-weekcalendar-category-header">
 <span class="ep-weekcalendar-category-title">車輛</span>
 </div>
 <ul class="ep-weekcalendar-category-list">
 </ul>
 </div>
 <!-- 內(nèi)容區(qū)域 -->
 <div class="ep-weekcalendar-time-area">
 <!-- 每周日期渲染區(qū)域。切換日期時重新繪制內(nèi)容 -->
 <div class="ep-weekcalendar-weeks"></div>
 <div class="ep-weekcalendar-main">
 <!-- 分類和內(nèi)容構(gòu)建的網(wǎng)格區(qū)域,僅在分類改變時進(jìn)行調(diào)整 -->
 <div class="ep-weekcalendar-grid"> </div>
 <!-- 可插入任意內(nèi)容的區(qū)域,日期切換時清空,根據(jù)使用需求插入內(nèi)容 -->
 <div class="ep-weekcalendar-content"></div>
 </div>
 </div>
 </div>
 <!-- 底部 -->
 <div class="ep-weekcalendar-body"></div>
</div>

結(jié)構(gòu)如上,實現(xiàn)代碼就不用展示了。

繪制實現(xiàn)

初始好了必要的結(jié)構(gòu),我們接著進(jìn)行日歷的繪制工作。

分類繪制

首先要處理的是分類,周視圖中,一周的天數(shù)是固定的,確定好分類才能繪制出主體部分的網(wǎng)格。

對于分類,暫時考慮如下必要數(shù)據(jù)格式:

{
 id: 'cate-1', // 分類ID
 name: '法拉利', // 分類名稱
 content: '蘇E00000' // 分類的具體描述
}

實現(xiàn)如下:

{
 // 設(shè)置分類數(shù)據(jù)
 setCategory: function (data) {
 if (!(data instanceof Array)) {
 this.throwError('分類數(shù)據(jù)必須是一個數(shù)組');
 return;
 }
 this._categoryData = data;

 // 繪制分類
 this._renderCatagories();
 // 繪制其他需要改變的部分
 this._renderChanged();
 },
 // 左側(cè)分類渲染
 _renderCatagories: function () {
 this._categoryListEl.innerHTML = '';
 var i = 0,
 data = this._categoryData,
 node = document.createElement('li'),
 cataEl;
 node.className = 'ep-weekcalendar-category';
 // 用行作為下標(biāo)記錄當(dāng)前分類id集合
 this._categoryIndexs = [];
 // id為鍵記錄索引
 this._categoryReocrds = {};
 while (i < data.length) {
 this._categoryIndexs.push(data[i].id);
 this._categoryReocrds[data[i].id] = i;
 cataEl = node.cloneNode(true);
 this._rendercategory(data[i], cataEl);
 i++;
 }
 },
 _rendercategory: function (cate, cateEl) {
 cateEl.setAttribute('data-cateid', cate.id);
 var titleEl = document.createElement('span'),
 contentEl = document.createElement('span');
 titleEl.className = 'title';
 contentEl.className = 'content';
 titleEl.innerHTML = cate.name;
 contentEl.innerHTML = cate.content;
 cateEl.appendChild(titleEl);
 cateEl.appendChild(contentEl);
 this.fire('categoryRender', {
 categoryEl: cateEl,
 titleEl: titleEl,
 contentEl: contentEl
 });
 this._categoryListEl.appendChild(cateEl);
 this.fire('agterCategoryRender', {
 categoryEl: cateEl,
 titleEl: titleEl,
 contentEl: contentEl
 });
 }
}

上面通過設(shè)置分類數(shù)據(jù) setCategory 作為入口,調(diào)用繪制分類方法,其中還調(diào)用了 _renderChanged 此方法用于重新繪制日歷的可變部分,如標(biāo)題、日期和其中的內(nèi)容,會在之后進(jìn)行介紹。

日期繪制

上面已經(jīng)準(zhǔn)備好了分類軸,還需要繪制出日期軸,對于周視圖而言,一周的實現(xiàn)就非常簡單了,根據(jù)一周的開始日期,依次渲染7天即可。 注意在繪制過程中提供日期的必要信息給相應(yīng)事件,一遍使用者能夠在事件中進(jìn)行個性化處理。

{
 // 渲染日歷的星期
 _renderWeeks: function () {
 this._weeksEl.innerHTML = '';
 var i = 0,
 currDate = this._startDate.clone(),
 node = document.createElement('div'),
 week;
 node.className = 'ep-weekcalendar-week';
 // 單元格列作為下標(biāo)記錄日期
 this._dateRecords = [];
 while (i++ < 7) {
 // 更新記錄日期
 this._dateRecords.push(currDate.clone());
 week = node.cloneNode(true);
 this._renderWeek(currDate, week);
 currDate.add(1, 'day');
 }
 // 切換日期 需要重繪內(nèi)容區(qū)域
 this._rednerContent();
 },
 _renderWeek: function (date, node) {
 var dateText = date.format('YYYY-MM-DD'),
 day = date.isoWeekday();
 if (day > 5) {
 node.className += ' weekend';
 }
 if (date.isSame(this.today, 'day')) {
 node.className += ' today';
 }
 node.setAttribute('data-date', dateText);
 node.setAttribute('date-isoweekday', day);
 var ev = this.fire('dateRender', {
 // 當(dāng)前完整日期
 date: dateText,
 // iso星期
 isoWeekday: day,
 // 顯示的文本
 dateText: '周' + this._WEEKSNAME[day - 1] + ' ' + date.format('MM-DD'),
 // classname
 dateCls: node.className,
 // 日歷el
 el: this.el,
 // 當(dāng)前el
 dateEl: node
 });
 // 處理事件的修改
 node.innerHTML = ev.dateText;
 node.className = ev.dateCls;
 this._weeksEl.appendChild(node);
 this.fire('afterDateRender', {
 // 當(dāng)前完整日期
 date: dateText,
 // iso星期
 isoWeekday: day,
 // 顯示的文本
 dateText: node.innerHTML,
 // classname
 dateCls: node.className,
 // 日歷el
 el: this.el,
 // 當(dāng)前el
 dateEl: node
 });
 }
}

網(wǎng)格和內(nèi)容

上面已經(jīng)準(zhǔn)備好了二維視圖中的兩個軸,接著進(jìn)行網(wǎng)格和內(nèi)容層的繪制即可。

網(wǎng)格

此處以分類為Y方向(行),日期為X方向(列)來進(jìn)行繪制:

{
 // 右側(cè)網(wǎng)格
 _renderGrid: function () {
 this._gridEl.innerHTML = '';
 var rowNode = document.createElement('div'),
 itemNode = document.createElement('span'),
 rowsNum = this._categoryData.length,
 i = 0,
 j = 0,
 row, item;
 rowNode.className = 'ep-weekcalendar-grid-row';
 itemNode.className = 'ep-weekcalendar-grid-item';
 while (i < rowsNum) {
 row = rowNode.cloneNode();
 row.setAttribute('data-i', i);
 j = 0;
 while (j < 7) {
 item = itemNode.cloneNode();
 // 周末標(biāo)識
 if (this.dayStartFromSunday) {
  if (j === 0 || j === 6) {
  item.className += ' weekend';
  }
 } else {
  if (j > 4) {
  item.className += ' weekend';
  }
 }
 item.setAttribute('data-i', i);
 item.setAttribute('data-j', j);
 row.appendChild(item);

 j++;
 }
 this._gridEl.appendChild(row);

 i++;
 }
 rowNode = itemNode = row = item = null;
 }
}

內(nèi)容

理論上來說,二維要支持跨行、跨列兩種情況,即內(nèi)容區(qū)域應(yīng)該為一整塊元素。但是結(jié)合到實際情況,跨時間的需求普遍存在(一個東西在一段時間內(nèi)被連續(xù)使用)??绶诸惒]有多大的實際意義,本來就要分開以分類來管理,再跨分類,又變得復(fù)雜了。而且即使一定要實現(xiàn)一段時間內(nèi)同時在使用多個東西,也是可以直接實現(xiàn)的(分類A在XX時間段內(nèi)被使用,B在XX時間段內(nèi)被使用,只是此時XX正好相同而已)。

因此此處僅處理跨時間情況,可將內(nèi)容按行即分類進(jìn)行繪制,這樣在插入內(nèi)容部件時,可以簡化很多計算。

{
 // 右側(cè)內(nèi)容
 _rednerContent: function () {
 this._contentEl.innerHTML = '';
 var i = 0,
 node = document.createElement('div'),
 row;
 node.className = 'ep-weekcalendar-content-row';
 while (i < this._categoryData.length) {
 row = node.cloneNode();
 row.setAttribute('data-i', i);

 this._contentEl.appendChild(row);
 ++i;
 }
 row = node = null;
 },
 // 日期切換時清空內(nèi)容
 _clearContent: function () {
 var rows = this._contentEl.childNodes,
 i = 0;
 while (i < rows.length) {
 rows[i].innerHTML && (rows[i].innerHTML = '');
 ++i;
 }
 // 部件數(shù)據(jù)清空
 this._widgetData = {};
 }
}

如果一定要實現(xiàn)跨行跨列的情況,直接將內(nèi)容繪制成一整塊元素即可,但是在點(diǎn)擊事件和插入內(nèi)容部件時,需要同時計算對應(yīng)的分類和日期時間。

難點(diǎn)實現(xiàn)

內(nèi)容部件插入

我們實現(xiàn)這個二維周視圖日歷的主要目的就是要支持插入任意的內(nèi)容,上面已經(jīng)準(zhǔn)備好了插入內(nèi)容的dom元素,這里要做的就是將數(shù)據(jù)繪制成dom放置在合適的位置。

考慮必要的內(nèi)容部件數(shù)據(jù)結(jié)構(gòu)如下:

{
 id: '數(shù)據(jù)標(biāo)識',
 categoryId: '所屬分類標(biāo)識',
 title: '名稱',
 content: '內(nèi)容',
 start: '開始日期時間'
 end: '結(jié)束日期時間'
 bgColor: '展示的背景色'
}

由于上面在內(nèi)容區(qū)域是直接按照分類作為繪制的,因此拿到數(shù)據(jù)后,對應(yīng)的分類就已經(jīng)存在了。重點(diǎn)要根據(jù)指定的開始和結(jié)束時間計算出開始和結(jié)束位置。

考慮如下:

  • 考慮響應(yīng)式,位置計算按照百分比計算
  • 一周的總時間是固定的,開始日期時間和這周開始日期時間的差額占總時間的百分比即開始位置的百分比
  • 結(jié)束日期時間和開始時間的差額占總時間的百分比即為結(jié)束時間距離最左側(cè)的百分比
  • 注意處理開始和結(jié)束時間溢出本周的情況

因此關(guān)于位置計算可以用如下代碼處理:

{
 // 日期時間分隔符 默認(rèn)為空 對應(yīng)格式為 '2017-11-11 20:00'
 // 對于'2017-11-11T20:00' 這樣的格式務(wù)必指定正確的日期和時間之間的分隔符T
 _dateTimeSplit:' ',
 // 一周分鐘數(shù)
 _WEEKMINUTES: 7 * 24 * 60,
 // 一周秒數(shù)
 _WEEKSECONDS: 7 * 24 * 3600,
 // 一天的分鐘數(shù)秒數(shù)
 _DAYMINUTES: 24 * 60,
 _DAYSCONDS: 24 * 3600,
 // 計算位置的精度 取值second 或 minute
 posUnit: 'second',
 // 計算指定日期的分鐘或秒數(shù)
 _getNumByUnits: function (dateStr) {
 var temp = dateStr.split(this._dateTimeSplit),
  date = temp[0];
 // 處理左側(cè)溢出
 if (this._startDate.isAfter(date, 'day')) {
  // 指定日期在開始日期之前
  return 0;
 }
 // 右側(cè)溢出直接算作第7天即可
 var times = (temp[1] || '').split(':'),
  days = (function (startDate) {
  var currDate = startDate.clone(),
   i = 0,
   d = moment(date, 'YYYY-MM-DD');
  while (i < 7) {
   if (currDate.isSame(d, 'day')) {
   return i;
   } else {
   currDate.add(1, 'day');
   ++i;
   }
  }
  console && console.error && console.error('計算天數(shù)時出錯!');
  return i;
  }(this._startDate)),
  hours = parseInt(times[0], 10) || 0,
  minutes = parseInt(times[1], 10) || 0,
  seconds = parseInt(times[2], 10) || 0,
  // 對應(yīng)分鐘數(shù)
  result = days * this._DAYMINUTES + hours * 60 + minutes;
 return this.posUnit == 'minute' ? result : (result * 60 + seconds);
 },
 // 計算日期時間的百分比位置
 _getPos: function (dateStr) {
 var p = this._getNumByUnits(dateStr) / (this.posUnit == 'minute' ? this._WEEKMINUTES : this._WEEKSECONDS);
 return p > 1 ? 1 : p;
 }
}

上面就拿到了一個數(shù)據(jù)所對應(yīng)的開始位置和結(jié)束位置。基本上是已經(jīng)完成了,但是還需要再處理一個情況:相同分類下的時間沖突問題。

考慮以如下方式進(jìn)行:

  • 沒添加一個就記錄下其數(shù)據(jù)
  • 新增的如果和當(dāng)前分類下已有的存在時間重疊,則認(rèn)為沖突。

實現(xiàn)如下:

{
 /**
 * 檢查是否發(fā)生重疊
 *
 * @param {Object} data 當(dāng)前要加入的數(shù)據(jù)
 * @returns false 或 和當(dāng)前部件重疊的元素數(shù)組
 */
 _checkOccupied: function (data) {
 if (!this._widgetData[data.categoryId]) {
  return false;
 }
 var i = 0,
  cate = this._widgetData[data.categoryId],
  len = cate.length,
  result = false,
  occupied = [];
 for (; i < len; ++i) {
  // 判斷時間是否存在重疊
  if (data.start < cate[i].end && data.end > cate[i].start) {
  occupied.push(cate[i]);
  result = true;
  }
 }
 return result ? occupied : false;
 }
}

完成以上兩步就可以往我們的內(nèi)容區(qū)域中插入了

{
 // 緩存widget數(shù)據(jù)
 _cacheWidgetData: function (data) {
 if (!this._widgetData[data.categoryId]) {
  this._widgetData[data.categoryId] = [];
 }
 // 記錄當(dāng)前的
 this._widgetData[data.categoryId].push(data);
 },
 // 新增一個小部件
 addWidget: function (data) {
 var row = this._contentEl.childNodes[this._categoryReocrds[data.categoryId]];
 if (!row) {
  this.throwError('對應(yīng)分類不存在,添加失敗');
  return false;
 }
 // 先查找是否含有
 var $aim = jQuery('.ep-weekcalendar-content-widget[data-id="' + data.id + '"]', row);
 if ($aim.length) {
  // 已經(jīng)存在則不添加
  return $aim[0];
 }
 // 創(chuàng)建部件
 var widget = document.createElement('div'),
  title = document.createElement('span'),
  content = document.createElement('p'),
  startPos = this._getPos(data.start),
  endPos = this._getPos(data.end),
  _data = {
  categoryId: data.categoryId,
  id: data.id,
  start: startPos,
  end: endPos,
  el: widget,
  data: data
  };
 widget.className = 'ep-weekcalendar-content-widget';
 title.className = 'ep-weekcalendar-content-widget-title';
 content.className = 'ep-weekcalendar-content-widget-content';
 widget.appendChild(title);
 widget.appendChild(content);
 // 通過絕對定位,指定其left和right來拉開寬度的方式來處理響應(yīng)式
 // 可以通過樣式設(shè)置一個最小寬度,來避免時間段過小時其中文本無法顯示的問題
 widget.style.left = startPos * 100 + '%';
 widget.style.right = (1 - endPos) * 100 + '%';
 data.bgColor && (widget.style.backgroundColor = data.bgColor);
 data.id && widget.setAttribute('data-id', data.id);
 widget.setAttribute('data-start', data.start);
 widget.setAttribute('data-end', data.end);
 title.innerHTML = data.title;
 data.content && (content.innerHTML = data.content);
 widget.title = data.title;
 // 檢查是否發(fā)生重疊
 var isoccupied = this._checkOccupied(_data);
 if (isoccupied) {
  // 觸發(fā)重疊事件
  var occupiedEv = this.fire('widgetoccupied', {
  occupiedWidgets: (function () {
   var arr = [];
   for (var i = 0, l = isoccupied.length; i < l; ++i) {
   arr.push(isoccupied[i].el);
   }
   return arr;
  })(),
  currWidget: widget,
  widgetData: data
  });
  // 取消后續(xù)執(zhí)行
  if (occupiedEv.cancel) {
  return false;
  }
 }
 // 緩存數(shù)據(jù)
 this._cacheWidgetData(_data);
 var addEv = this.fire('widgetAdd', {
  widgetId: data.id,
  categoryId: data.categoryId,
  start: data.start,
  end: data.end,
  startPos: startPos,
  endPos: endPos,
  widgetEl: widget
 });
 if (addEv.cancel) {
  return false;
 }
 row.appendChild(widget);
 this.fire('afterWidgetAdd', {
  widgetId: data.id,
  categoryId: data.categoryId,
  start: data.start,
  end: data.end,
  startPos: startPos,
  endPos: endPos,
  widgetEl: widget
 });
 return widget;
 },
}

點(diǎn)擊事件和范圍選擇

此控件不僅用于結(jié)果展示,還要可用于點(diǎn)擊進(jìn)行添加,需要處理其點(diǎn)擊事件,但是由于要展示內(nèi)容,內(nèi)容是覆蓋在分類和日期構(gòu)成的網(wǎng)格之上的,用戶的點(diǎn)擊是點(diǎn)擊不到網(wǎng)格元素的,必須要根據(jù)點(diǎn)擊的位置進(jìn)行計算來獲取所點(diǎn)擊的日期和所在分類。

同時,由于展示的部件都是時間范圍的,因此點(diǎn)擊返回某天和某個分類是不夠的,還需要能夠支持鼠標(biāo)按下拖動再松開,來直接選的一段時間。

考慮到以上需求,點(diǎn)擊事件不能直接使用 click 來實現(xiàn),考慮使用 mousedown 和 mouseup 來處理點(diǎn)擊事件,同時需要在 mousemove 中實時給出用戶響應(yīng)。

{
 _initEvent: function () {
 var me = this;
 // 點(diǎn)擊的行索引
 var row,
  // 開始列索引
  columnStart,
  // 結(jié)束列索引
  columnEnd,
  // 是否在按下、移動、松開的click中
  isDurringClick = false,
  // 是否移動過 用于處理按下沒有移動直接松開的過程
  isMoveing = false,
  $columns,
  // 網(wǎng)格左側(cè)寬度
  gridLeft,
  // 每列的寬度
  columnWidth
 jQuery(this.el)
  // 按下鼠標(biāo) 記錄分類和開始列
  .on('mousedown.weekcalendar', '.ep-weekcalendar-content-row', function (e) {
  isDurringClick = true;
  gridLeft = jQuery(me._gridEl).offset().left;
  columnWidth = jQuery(me._gridEl).width() / 7;
  jQuery(me._gridEl).find('.ep-weekcalendar-grid-item').removeClass(me._selectedCls);
  row = this.getAttribute('data-i');
  $columns = jQuery(me._gridEl).find('.ep-weekcalendar-grid-row').eq(row).children();

  columnStart = (e.pageX - gridLeft) / columnWidth >> 0;
  });
 // 移動和松開 松開鼠標(biāo) 記錄結(jié)束列 觸發(fā)點(diǎn)擊事件 
 // 不能直接綁定在日期容器上 否則鼠標(biāo)移出日歷后,松開鼠標(biāo),實際點(diǎn)擊已經(jīng)結(jié)束,但是日歷上處理不到。
 jQuery('body')
  // 點(diǎn)擊移動過程中 實時響應(yīng)選中狀態(tài)
  .on('mousemove.weekcalendar', function (e) {
  if (!isDurringClick) {
   return;
  }
  isMoveing = true;
  // 當(dāng)前列索引
  var currColumn;
  // mousemoveTimer = setTimeout(function () {
  currColumn = (e.pageX - gridLeft) / columnWidth >> 0;
  // 修正溢出
  currColumn = currColumn > 6 ? 6 : currColumn;
  currColumn = currColumn < 0 ? 0 : currColumn;
  $columns.removeClass(me._selectedCls);
  // 起止依次選中
  var start = Math.min(columnStart, currColumn),
   end = Math.max(columnStart, currColumn);
  do {
   $columns.eq(start).addClass(me._selectedCls);
  } while (++start <= end);
  })
  // 鼠標(biāo)松開
  .on('mouseup.weekcalendar', function (e) {
  if (!isDurringClick) {
   return;
  }
  var startIndex = -1,
   endIndex = -1;
  columnEnd = (e.pageX - gridLeft) / columnWidth >> 0;
  columnEnd = columnEnd > 6 ? 6 : columnEnd;
  // 沒有移動過時
  if (!isMoveing) {
   startIndex = endIndex = columnEnd;
   // 直接down up 沒有move的過程則只會有一個選中的,直接以結(jié)束的作為處理即可
   $columns.eq(columnEnd).addClass(me._selectedCls)
   .siblings().removeClass(me._selectedCls);
  } else {
   startIndex = Math.min(columnStart, columnEnd);
   endIndex = Math.max(columnStart, columnEnd);
  }
  // 觸發(fā)點(diǎn)擊事件
  me.fire('cellClick', {
   // 分類id 
   categoryId: me._categoryIndexs[row],
   // 時間1
   startDate: me._dateRecords[startIndex].format('YYYY-MM-DD'),
   // 日期2
   endDate: me._dateRecords[endIndex].format('YYYY-MM-DD'),
   // 行索引
   rowIndex: row,
   // 列范圍
   columnIndexs: (function (i, j) {
   var arr = [];
   while (i <= j) {
    arr.push(i++);
   }
   return arr;
   }(startIndex, endIndex))
  });
  row = columnStart = columnEnd = isMoveing = isDurringClick = false;
  });
 }
}

此過程要注意的問題是:mousedown 必須綁定在日歷上,而 mouseup 和 mousemove 則不能綁定在日歷上,具體原因已經(jīng)寫在上面代碼注釋中了。

另外需要注意,由于范圍點(diǎn)擊選擇使用了 mousedown 和 mouseup 來模擬,那么日歷內(nèi)容區(qū)域中插入的數(shù)據(jù)部件的點(diǎn)擊事件也要用 mousedown 和 mouseup 來模擬,因為 mouseup 觸發(fā)比 click 早,如果使用 click ,會導(dǎo)致先觸發(fā)日歷上的日期點(diǎn)擊或日期范圍點(diǎn)擊。

使用

此日歷實現(xiàn)基于一個控件基類擴(kuò)展而來,其必要功能僅為一套事件機(jī)制,可參考實現(xiàn)一套自定義事件機(jī)制

實測一下效果吧:

<div id="week-calendar" ></div>
<script>
var calendar = epctrl.init('WeekCalendar', {
 el: '#week-calendar',
 categoryTitle: '車輛',
 category: [{
 id: 'cate-1',
 name: '法拉利',
 content: '蘇E00000'
 }, {
 id: 'cate-2',
 name: 'Lamborghini',
 content: '蘇E00001'
 }, {
 id: 'cate-3',
 name: '捷豹',
 content: '蘇E00002'
 }, {
 id: 'cate-4',
 name: '賓利',
 content: '蘇E00003'
 }, {
 id: 'cate-5',
 name: 'SSC',
 content: '蘇E00004'
 }],
 events: {
 // 日期變化時觸發(fā)
 dateChanged: function (e) {
  var data = {
  start: e.startDate,
  end: e.endDate,
  };
  // 獲取數(shù)據(jù)并逐個添加到日歷上
  getData(data).done(function (data) {
  $.each(data, function (i, item) {
   calendar.addWidget(item);
  });
  });
 },
 // 部件重疊時觸發(fā)
 widgetOccupied: function (e) {
  // 沖突時禁止繼續(xù)添加
  console.error(e.widgetData.categoryId + '分類下id為' + e.widgetData.id + '的部件和現(xiàn)有部件有重疊,取消添加');
  e.cancel = true;
 }
 }
});
calendar.on('dateClick', function (e) {
 alert(JSON.stringify({
 '開始時間': e.startDate,
 '結(jié)束時間': e.endDate,
 '分類id': e.categoryId,
 '行索引': e.rowIndex,
 '列索引范圍': e.columnIndexs
 }, 0, 4));
});
</script>

源碼下載:

github

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對創(chuàng)新互聯(lián)的支持。

文章標(biāo)題:利用Javascript開發(fā)一個二維周視圖日歷
本文鏈接:http://muchs.cn/article42/geshhc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器外貿(mào)建站、品牌網(wǎng)站建設(shè)、App設(shè)計網(wǎng)頁設(shè)計公司、定制開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)