Javascript 日历事件的可视化。用最大宽度布局事件的算法
我需要你的算法帮助(它将在客户端用javascript开发,但并不重要,我主要对算法本身感兴趣)安排日历事件,以便每个事件框具有最大宽度。请看下图: Y轴是时间。因此,如果“测试事件”在中午开始(例如),并且没有更多的东西与它相交,它将占用整个100%的宽度。“每周回顾”与“翻滚的基督教青年会”和“安娜/阿米莉亚”相交,但后两个没有相交,所以它们都占了50%。Test3、Test4和Test5都是相交的,因此每个的最大宽度为33.3%。但是Test7是66%,因为Test3是33%固定的(见上文),所以它占用了所有可用空间,即66% 我需要一个算法来解决这个问题 提前谢谢Javascript 日历事件的可视化。用最大宽度布局事件的算法,javascript,algorithm,calendar,visualization,Javascript,Algorithm,Calendar,Visualization,我需要你的算法帮助(它将在客户端用javascript开发,但并不重要,我主要对算法本身感兴趣)安排日历事件,以便每个事件框具有最大宽度。请看下图: Y轴是时间。因此,如果“测试事件”在中午开始(例如),并且没有更多的东西与它相交,它将占用整个100%的宽度。“每周回顾”与“翻滚的基督教青年会”和“安娜/阿米莉亚”相交,但后两个没有相交,所以它们都占了50%。Test3、Test4和Test5都是相交的,因此每个的最大宽度为33.3%。但是Test7是66%,因为Test3是33%固定的(见上
///选择每个事件的左右位置,这样就不会有重叠。
///算法的第三步。
无效布局事件(IEnumerable事件)
{
var columns=新列表();
DateTime?lastEventEnding=null;
foreach(events.OrderBy中的var ev(ev=>ev.Start)。然后by(ev=>ev.End))
{
如果(ev.Start>=lastEventEnding)
{
填料(柱);
columns.Clear();
lastEventEnding=null;
}
bool-placed=false;
foreach(列中的变量列)
{
如果(!col.Last().collizeswith(ev))
{
列加(ev);
放置=真;
打破
}
}
如果(!放置)
{
添加(新列表{ev});
}
if(lastEventEnding==null | | ev.End>lastEventEnding.Value)
{
lastEventEnding=ev.End;
}
}
如果(columns.Count>0)
{
填料(柱);
}
}
///设置所连接组中每个事件的左右位置。
///算法的第4步。
无效包装事件(列表列)
{
float numColumns=columns.Count;
int-iColumn=0;
foreach(列中的变量列)
{
foreach(列中的var ev)
{
int colSpan=ExpandEvent(ev、iColumn、columns);
ev.Left=i列/num列;
ev.Right=(iColumn+colSpan)/numColumn;
}
iColumn++;
}
}
///检查事件可以扩展到多少列而不与之冲突
///其他活动。
///算法的第5步。
int ExpandEvent(事件ev、int iColumn、列表列)
{
int colSpan=1;
foreach(列中的变量col.Skip(iColumn+1))
{
foreach(列中的var ev1)
{
如果(ev1.与(ev)碰撞)
{
返回colSpan;
}
}
colSpan++;
}
返回colSpan;
}
编辑:现在对事件进行排序,而不是假设它们已排序
Edit2:如果有足够的空间,现在将事件向右展开。接受的答案描述了一个包含5个步骤的算法。在接受答案的注释中链接的示例实现仅实现步骤1到4。第5步是确保最右边的事件使用所有可用空间。参见OP提供的图片中的事件7 我通过添加所述算法的步骤5扩展了给定的实现:
$( document ).ready( function( ) {
var column_index = 0;
$( '#timesheet-events .daysheet-container' ).each( function() {
var block_width = $(this).width();
var columns = [];
var lastEventEnding = null;
// Create an array of all events
var events = $('.bubble_selector', this).map(function(index, o) {
o = $(o);
var top = o.offset().top;
return {
'obj': o,
'top': top,
'bottom': top + o.height()
};
}).get();
// Sort it by starting time, and then by ending time.
events = events.sort(function(e1,e2) {
if (e1.top < e2.top) return -1;
if (e1.top > e2.top) return 1;
if (e1.bottom < e2.bottom) return -1;
if (e1.bottom > e2.bottom) return 1;
return 0;
});
// Iterate over the sorted array
$(events).each(function(index, e) {
// Check if a new event group needs to be started
if (lastEventEnding !== null && e.top >= lastEventEnding) {
// The latest event is later than any of the event in the
// current group. There is no overlap. Output the current
// event group and start a new event group.
PackEvents( columns, block_width );
columns = []; // This starts new event group.
lastEventEnding = null;
}
// Try to place the event inside the existing columns
var placed = false;
for (var i = 0; i < columns.length; i++) {
var col = columns[ i ];
if (!collidesWith( col[col.length-1], e ) ) {
col.push(e);
placed = true;
break;
}
}
// It was not possible to place the event. Add a new column
// for the current event group.
if (!placed) {
columns.push([e]);
}
// Remember the latest event end time of the current group.
// This is later used to determine if a new groups starts.
if (lastEventEnding === null || e.bottom > lastEventEnding) {
lastEventEnding = e.bottom;
}
});
if (columns.length > 0) {
PackEvents( columns, block_width );
}
});
});
// Function does the layout for a group of events.
function PackEvents( columns, block_width )
{
var n = columns.length;
for (var i = 0; i < n; i++) {
var col = columns[ i ];
for (var j = 0; j < col.length; j++)
{
var bubble = col[j];
var colSpan = ExpandEvent(bubble, i, columns);
bubble.obj.css( 'left', (i / n)*100 + '%' );
bubble.obj.css( 'width', block_width * colSpan / n - 1 );
}
}
}
// Check if two events collide.
function collidesWith( a, b )
{
return a.bottom > b.top && a.top < b.bottom;
}
// Expand events at the far right to use up any remaining space.
// Checks how many columns the event can expand into, without
// colliding with other events. Step 5 in the algorithm.
function ExpandEvent(ev, iColumn, columns)
{
var colSpan = 1;
// To see the output without event expansion, uncomment
// the line below. Watch column 3 in the output.
//return colSpan;
for (var i = iColumn + 1; i < columns.length; i++)
{
var col = columns[i];
for (var j = 0; j < col.length; j++)
{
var ev1 = col[j];
if (collidesWith(ev, ev1))
{
return colSpan;
}
}
colSpan++;
}
return colSpan;
}
$(文档).ready(函数(){
var列_指数=0;
$('#时间表事件.daysheet容器')。每个(函数(){
var block_width=$(this).width();
var列=[];
var lastEventEnding=null;
//创建所有事件的数组
var events=$('.bubble_selector',this).map(函数(索引,o){
o=$(o);
var top=o.偏移量().top;
返回{
“obj”:o,
"顶":顶,,
“底部”:顶部+o.高度()
};
}).get();
//按开始时间排序,然后按结束时间排序。
events=events.sort(函数(e1、e2){
如果(e1.tope2.top)返回1;
if(e1.bottome2.bottom)返回1;
返回0;
});
//迭代排序的数组
$(事件)。每个(功能)(索引,e){
//检查是否需要启动新的事件组
if(lastEventEnding!==null&&e.top>=lastEventEnding){
//最新事件晚于中的任何事件
//当前组。没有重叠。输出当前组
//事件组并启动新的事件组。
填料通风孔(柱、块体宽度);
columns=[];//这将启动新的事件组。
lastEventEnding=null;
}
//尝试将事件放置在现有列中
var=false;
对于(var i=0;ilastEventEnding){
lastEventEnding=e.bottom;
}
});
if(columns.length)