2017-01-11 3 views
5

私はイベントスケジューラを構築していますが、互いに重なり合うことなくイベントを広める方法を見つけられないという点に気付きました。可能であれば、利用可能なものの100%を使用してくださいdivsを互いに重ならないように広げる方法

ここでは、このシナリオの写真です。 enter image description here

いくつかの注意事項:

  • イベントはposition: relativeでのdiv内にラップされ、すべてのイベントがposition:absoluteを持っています。

  • JavaScriptを使用して、私はtopleftwidthとオンザフライそれぞれ「DIVイベント」のheightの値にする必要があるかを把握する必要があります。

    {startAt: "12時〇〇分30秒"、 endsAt:

  • イベントは、以下のコードのようなオブジェクトの配列である "午前13時○○分00秒"、 説明: "EVT1"、 id: '00001'

  • 私はこのプロジェクトを開発するためにVue.jsを使用しています。しかし、あなたがVueを知らないなら、これは問題ではありません。私はjsbinを使用して小さなプロジェクトを構築したので、ちょうどjavascript関数を使って遊ぶことができます。

ライブコード:私は問題を抱えているhttps://jsbin.com/bipesoy/

私はイベントの配列に基づいてオンザフライでtopleftwidthheightを計算するためのアルゴリズムを見つけることができません。 jsbinコードについて

いくつかの注意事項:this.events

  • は、すべてのコードは、上記の4つのプロパティを見つけるためには、あなたが使用してイベントの配列にアクセスすることができますparsedEventsインサイドparsedEvents
  • 関数内で
  • parsedEventsのジョブは、イベントの配列をループし、それぞれにスタイルプロパティを追加し、スタイルオブジェクトでイベントの新しい配列を返します。
  • 各30分の高さは40ピクセルです。

どのようなアイデアを達成するか、より良い解決策をお探しですか?

+0

私の頭の上から、私はイベントを通じたが30分ずつ、各30分のスパンで秋のイベントのカウント数をループではないと思います。次にthruoghアイテムをループさせ、1)30分増分がこのアイテムと重なっているかどうかを調べる、2)重複した30分増分の最大カウントを見つけ、それはあなたの幅です(カウント3では33%、2は50%等。)。私はそれがアイテムの持続時間/ 0.5h x 40 pxの倍数であるので、高さは問題ではないと思います –

+0

@MirkoVukušićを約束しているような音。私はそれを試してみましょうと後で知らせる:) –

+0

@MirkoVukušić実際には、幅に問題があると思います。写真でevt2とevt7を見てください。彼らの幅は何ですか? –

答えて

3

この挑戦をしばらくしてから、アイデアをあきらめるときだと思います。イベントをアレンジする可能性のある多くのシナリオをプログラムすることは可能ですが、深く掘り下げたときに、正確に何をしたいのかを書くことは非常に難しく、管理していても、画面。これは、幅のイベントを展開する場合にのみ適用されます。ギャップを埋めるためにそれらを配置したり、再配置することさえも解決され、それほど難しくはありません。

スニペットはこちら(またはご希望の場合はJSBin:http://jsbin.com/humiyi/99/edit?html,js,console,output)。

緑色のイベントは、「展開可能」として検出されます。グレーは展開できません。拡張の問題を明確にするために、いくつかの例があります:

  • evt1とevt3?それはこのようなことができますか、evt3がうまくいって、彼らは両方とも展開します
  • evt7とevt12?これを拡張する多くの方法...ルールを定義する方法?
  • evt7とevt11 arが1つの大きなイベントに統合されたと想像してください。 evt10、evt7/11、およびevt12を拡張する方法は? は...今

私の結論は、ルールを書いて、これを開発する努力の価値はないということです一貫し(この例では、多くのより多くの可能なシナリオではない)3の上方に答えるためにルールを記述してみてください。 UIのユーザビリティはあまり得られません。それらはいくつかのシナリオでは緩んでしまいます。つまり、いくつかのイベントはスペースがあるだけで視覚的に大きくなります。

レイアウトは、例と似ていますが、まったく同じです。イベントは展開されません。実際のライフ・シナリオはどれくらいの日々の出来事が予想されているのか分かりませんが、アップグレードは唯一可能性があります。別の地域で縦方向のカレンダーを分割することです - evt7と例えばevt11。次に、この同じスクリプトを領域ごとに独立して実行します。リージョンごとに垂直スロットが再計算されるので、evt10 i evt11のリージョンには、それぞれ50%のスペースを占める垂直スロットが2つしかありません。あなたのカレンダーに混み合った時間が少なくても、あと数回のイベントしかない場合には、これは価値があるかもしれません。これは、あまりにも時間がかかりすぎることなく、後日、あまりにも狭いイベントの問題を修正するでしょう。しかし、イベントが一日中終わり、重複していれば、それはそれが価値があるとは思わない。

let events = [ 
 
    { startAt: "00:00", endsAt: "01:00", description: "evt1", id: '00001' }, 
 
    { startAt: "01:30", endsAt: "08:00", description: "evt2", id: '00002' }, 
 
    { startAt: "01:30", endsAt: "04:00", description: "evt3", id: '00003' }, 
 
    { startAt: "00:30", endsAt: "02:30", description: "evt3", id: '00013' }, 
 
    { startAt: "00:00", endsAt: "01:00", description: "evt3", id: '00014' }, 
 
    { startAt: "03:00", endsAt: "06:00", description: "evt4", id: '00004' }, 
 
    { startAt: "01:30", endsAt: "04:30", description: "evt5", id: '00005' }, 
 
    { startAt: "01:30", endsAt: "07:00", description: "evt6", id: '00006' }, 
 
    { startAt: "06:30", endsAt: "09:00", description: "evt7", id: '00007' }, 
 
    { startAt: "04:30", endsAt: "06:00", description: "evt8", id: '00008' }, 
 
    { startAt: "05:00", endsAt: "06:00", description: "evt9", id: '00009' }, 
 
    { startAt: "09:00", endsAt: "10:00", description: "evt10", id: '00010' }, 
 
    { startAt: "09:00", endsAt: "10:30", description: "evt11", id: '00011' }, 
 
    { startAt: "07:00", endsAt: "08:00", description: "evt12", id: '00012' } 
 
] 
 

 
console.time() 
 

 
// will store counts of events in each 30-min chunk 
 
// each element represents 30 min chunk starting from midnight 
 
// ... so indexOf * 30 minutes = start time 
 
// it will also store references to events for each chunk 
 
// each element format will be: { count: <int>, eventIds: <array_of_ids> } 
 
let counter = [] 
 

 
// helper to convert time to counter index 
 
time2index = (time) => { 
 
    let splitTime = time.split(":") 
 
    return parseInt(splitTime[0]) * 2 + parseInt(splitTime[1])/30 
 
} 
 

 
// loop through events and fill up counter with data 
 
events.map(event => { 
 
    for (let i = time2index(event.startAt); i < time2index(event.endsAt); i++) { 
 
    if (counter[i] && counter[i].count) { 
 
     counter[i].count++ 
 
     counter[i].eventIds.push(event.id) 
 
    } else { 
 
     counter[i] = { count: 1, eventIds: [event.id] } 
 
    } 
 
    } 
 
}) 
 

 
//find chunk with most items. This will become number of slots (vertical spaces) for our calendar grid 
 
let calSlots = Math.max(...counter.filter(c=>c).map(c=>c.count)) // filtering out undefined elements 
 
console.log("number of calendar slots: " + calSlots) 
 

 
// loop through events and add some more props to each: 
 
// - overlaps: all overlapped events (by ref) 
 
// - maxOverlapsInChunk: number of overlapped events in the most crowded chunk 
 
// (1/this is maximum number of slots event can occupy) 
 
// - pos: position of event from left (in which slot it starts) 
 
// - expandable: if maxOverlapsInChunk = calSlot, this event is not expandable for sure 
 
events.map(event => { 
 
    let overlappedEvents = events.filter(comp => { 
 
    return !(comp.endsAt <= event.startAt || comp.startAt >= event.endsAt || comp.id === event.id) 
 
    }) 
 
    event.overlaps = overlappedEvents //stores overlapped events by reference! 
 
    event.maxOverlapsInChunk = Math.max(...counter.filter(c=>c).map(c=>c.eventIds.indexOf(event.id) > -1 ? c.count : 0)) 
 
    event.expandable = event.maxOverlapsInChunk !== calSlots 
 
    event.pos = Math.max(...counter.filter(c=>c).map(c => { 
 
    let p = c.eventIds.indexOf(event.id) 
 
    return p > -1 ? p+1 : 1 
 
    })) 
 
}) 
 

 
// loop to move events leftmost possible and fill gaps if any 
 
// some expandable events will stop being expandable if they fit gap perfectly - we will recheck those later 
 
events.map(event => { 
 
    if (event.pos > 1) { 
 
    //find positions of overlapped events on the left side 
 
    let vertSlotsTakenLeft = event.overlaps.reduce((result, cur) => { 
 
     if (result.indexOf(cur.pos) < 0 && cur.pos < event.pos) result.push(cur.pos) 
 
     return result 
 
    }, []) 
 
    
 
    // check if empty space on the left 
 
    for (i = 1; i < event.pos; i++) { 
 
     if (vertSlotsTakenLeft.indexOf(i) < 0) { 
 
     event.pos = i 
 
     console.log("moving " + event.description + " left to pos " + i) 
 
     break 
 
     } 
 
    } 
 
    } 
 
}) 
 

 
// fix moved events if they became non-expandable because of moving 
 
events.filter(event=>event.expandable).map(event => { 
 
    let leftFixed = event.overlaps.filter(comp => { 
 
    return event.pos - 1 === comp.pos && comp.maxOverlapsInChunk === calSlots 
 
    }) 
 
    let rightFixed = event.overlaps.filter(comp => { 
 
    return event.pos + 1 === comp.pos && comp.maxOverlapsInChunk === calSlots 
 
    }) 
 
    event.expandable = (!leftFixed.length || !rightFixed.length) 
 
}) 
 

 
//settings for calendar (positioning events) 
 
let calendar = {width: 300, chunkHeight: 30} 
 

 
// one more loop through events to calculate top, left, width and height 
 
events.map(event => { 
 
    event.top = time2index(event.startAt) * calendar.chunkHeight 
 
    event.height = time2index(event.endsAt) * calendar.chunkHeight - event.top 
 
    //event.width = 1/event.maxOverlapsInChunk * calendar.width 
 
    event.width = calendar.width/calSlots // TODO: temporary width is 1 slot 
 
    event.left = (event.pos - 1) * calendar.width/calSlots 
 
}) 
 

 
console.timeEnd() 
 

 
// TEST drawing divs 
 
events.map(event => { 
 
    $("body").append(`<div style="position: absolute; 
 
    top: ${event.top}px; 
 
    left: ${event.left}px; 
 
    width: ${event.width}px; 
 
    height: ${event.height}px; 
 
    background-color: ${event.expandable ? "green" : "grey"}; 
 
    border: solid black 1px; 
 
    ">${event.description}</div>`) 
 
}) 
 

 
//console.log(events)
<!DOCTYPE html> 
 
<html> 
 
<head> 
 
    <script src="https://code.jquery.com/jquery-3.1.0.js"></script> 
 
    <meta charset="utf-8"> 
 
    <meta name="viewport" content="width=device-width"> 
 
    <title>JS Bin</title> 
 
</head> 
 
<body> 
 

 
</body> 
 
</html>

+0

Uau @MirkoVukušićそうです。通常、あまり重複しないイベントです。あなたが提案したとおりにプロジェクトを終了します。偉大な答えをありがとう! :) –

0

これについてさらに考えていました。私は幅、高さ、上をカバーしました。しかし、leftの計算に問題があるかもしれません。ですから、30分ごとにカウンターを作成するという少しの変更...目標はまだ残っていますが、増分や項目のフィルタリングを繰り返すのではなく、逆も同様です。項目をループし、そのカウンタオブジェクトをそこから埋める。それはまたより速くなります。そのループでは、counterオブジェクトを格納するだけでなく、itemIDを同じオブジェクトの別のプロパティ(同じインクリメント)にプッシュします。したがって、各増分は{span: "17:00"、counter:2、items:[135、148]}のように表すことができます。このitems配列は、後で各アイテムの位置をleftと決定し、水平方向の重なりを避けるのに役立ちます。 最大値アイテムのpoistionはitemsアイテムのすべてのインクリメントにわたって配列=左からカレンダー上のアイテムの位置です。幅(-1)と掛け合わせると、leftになります。

関連する問題