我在您的方法中看到的问题之一是没有存储数据的结构。我以前用 Javascript 构建了一个日历,这并不容易。首先,确保您对日历事件有某种抽象。就像是:
function CalendarEvent(startDateTime, endDateTime) {
this.startDateTime = startDateTime;
this.endDateTime = endDateTime;
}
CalendarEvent.prototype.start = function() {
return this.startDateTime.getTime();
};
CalendarEvent.prototype.end = function() {
return this.endDateTime.getTime();
};
CalendarEvent.new = function(startDateTime, endDateTime) {
// This is a little factory method. It prevents calendar events
// from having end times that fall before the start time.
// USE THIS TO INSTANTIATE A NEW CALENDAR EVENT
if(endDateTime.getTime() < startDateTime.getTime()) {
throw new Error("End time falls before start time");
}
return new CalendarEvent(startDateTime, endDateTime);
};
CalendarEvent.compare = function(eventOne, eventTwo) {
// this is a class method to compare two events
// If used with sort it will sort by startDateTime
return eventOne.start() - eventTwo.start();
};
// ... add any other methods you need
接下来,您将要对日历事件进行排序。我会按开始时间排序。然后,一旦对它进行排序,您实际上可以在进行更改时重新渲染所有内容。只要您正确排序,确定日历事件是否发生冲突就这么简单:
CalendarEvent.prototype.intersects = function(otherEvent) {
// If the other event starts after this one ends
// then they don't intersect
if(otherEvent.start() > this.end()) {
return false;
}
// If the other event ends before this one starts
// then they don't intersect
if(otherEvent.end() < this.start()) {
return false;
}
// Everything else is true
return true;
};
因为数据已排序,所以您知道如果两个或更多日历事件相交,它们将不得不共享空间。当然,在划分空间时,您必须考虑一些事情。您是否想要一个简单的实现,您只需从左到右平均共享空间(左侧具有最早的开始时间)。如果是这样,如果它有 4 个共享空间的事件(每个块都是一个事件),您的视觉表示可能看起来像这样:
但是,如果您的事件具有奇怪的形状,它们可能会导致您的日历看起来很奇怪。考虑以下:
在这种情况下,事件 2 占用了大量垂直空间,并且事件 1 下方的所有空间都未使用。也许为了更好的用户体验,你不希望这种事情发生。如果是这样,您应该相应地设计您的渲染算法。请记住,在您遇到的每个更改上重新渲染可能是最容易的,但这完全取决于您如何存储数据。如果您不将数据存储在某种易于遍历的结构中,那么您将无法做这种事情。
但是要完成对您问题的回答,这是一个相当幼稚的示例。我还没有测试过它,所以这是一个很大的假设。它并不完全完整,您必须自己编辑渲染。这只是为了让您了解如何使其工作。它肯定看起来更漂亮:
function renderCalendarEvents(calendarEvents) {
// Sort the calendar events (assuming calendarEvents is an array)
var sortedEvents = calendarEvents.sort(CalendarEvent.compare);
var index = 0;
// renderEvents is an anonymous function that will be called every time
// you need to render an event
// it returns it's columnDivisor.
var renderEvent = function(position) {
var currentEvent = sortedEvents[index];
var nextEvent = sortedEvents[index + 1];
// The default column divisor is determined by
// the current x-position + 1
var columnDivisor = position + 1;
// Increment before any recursion
index += 1;
// Check if nextEvent even exists
if(nextEvent) {
// If the nextEvent intersects with the current event
// then recurse
if(currentEvent.intersects(nextEvent)) {
// We need to tell the next event that it starts at the
// column position that is immediately +1 to the current event
columnDivisor = renderEvent(position + 1);
}
}
// placeEvent() is some function you can call to actually place
// the calendar event element on the page
// The position is the x-position of the current event
// The columnDivisor is a count of the amount of events sharing this column
placeEvent(currentEvent, position, columnDivisor);
return columnDivisor;
};
while(true) {
// render events until we're done
renderEvent(0);
if(index >= sortedEvents.length) {
break;
}
}
}
本质上,这个特定算法的想法是,如果列表中的 nextEvent 存在并且该事件与 currentEvent 相交,那么我们需要拆分 currentEvent 的宽度。它继续递归,直到它找不到更多的交叉点,然后它会返回递归调用链。我跳过了实际的 DOM 操作逻辑,因为真正困难的部分是确定需要拆分多少实际列才能使这些事件适合。所以希望这一切都有意义。
编辑:
更清楚地说,为了将其添加到您现有的代码中,我将insertEvent
用类似的东西替换您的函数。我不会为你写下所有的逻辑,所以你必须自己写一些。但这只是乐趣的一半:-)。
function insertEvent(start, end) {
var newEvent = Calendar.new(start, end);
// you'll have to store the array somewhere.
// i'm just assuming some kind of global right now
eventsArray.push(newEvent);
// You'll want to destroy any event elements
destroyCurrentEventElements();
// Now run the rendering function
renderCalendarEvents(eventsArray);
}