gpt4 book ai didi

javascript - 如何在不相互重叠的情况下散布div

转载 作者:太空狗 更新时间:2023-10-29 16:04:52 25 4
gpt4 key购买 nike

我正在构建一个事件调度程序,但我遇到了一个问题,我无法找到一种方法来传播事件而不相互重叠。 (它可能同时有事件并且没有限制。只要有可能,请使用 100% 的可用 width)

这是这个场景的图片。 enter image description here

一些注意事项:

  • 事件被包裹在一个带有 position:relative 的 div 中,所有事件都有 position:absolute

  • 使用 javascript,我必须弄清楚 top left widthheight 的值需要什么 每个“div 事件”的动态。

  • 事件是一组对象,如下面的代码:

    { 开始时间:“12:00:30”, 结束于:“13:00:00”, 描述:“evt1”, 编号:'00001'

  • 我正在使用 Vue.js 开发这个元素。但如果你不了解 Vue,这不是问题。我已经使用 jsbin 构建了一个小元素,因此您可以随意使用 javascript 函数。

在线代码:https://jsbin.com/bipesoy/

我哪里有问题?

我找不到一个算法来动态计算top left widthheight在一系列事件上。

关于jsbin代码的一些注意事项:

  • 查找上述 4 个属性的所有代码都在函数 parsedEvents
  • parsedEvents 中,您可以使用以下方法访问事件数组:this.events
  • parsedEvents 的工作是遍历事件数组并将样式属性添加到每个事件,然后返回带有样式对象的新事件数组。
  • 每30分钟的高度为40px;

关于如何完成它或更好的解决方案有什么想法吗?

最佳答案

在尝试这个挑战一段时间后,我认为是时候放弃这个想法了。可以对安排事件的许多可能场景进行编程,但是当你深入挖掘时,你会意识到甚至很难准确地写出你想要完成的事情,即使你做到了,你的一些决定在现场看起来也不太好屏幕。这仅适用于宽度扩展事件。定位甚至重新定位它们以填补空白已解决并且不太困难。

代码片段在这里(如果您愿意,也可以是 JSBin:http://jsbin.com/humiyi/99/edit?html,js,console,output)。

绿色事件被检测为“可扩展”。灰色不能扩展。为了阐明扩展逻辑的问题,举一些例子:

  • evt 1 和 evt3?它可以是这样的,也可以是 evt3 向右,它们都展开了
  • evt7 和 evt12?有很多方法可以扩展这个...如何定义规则?
  • 想象 evt7 和 evt11 合并成一个大事件。如何扩展 evt10、evt7/11 和 evt 12?...现在尝试编写规则以始终如一地回答以上 3 个问题(以及本示例中未包含的更多可能场景)

我的结论是编写规则并开发它是不值得的。 UI 可用性不会得到太多。在某些情况下,它们甚至会松散,即某些事件在视觉上会更大,只是因为它们有空间,而不是因为它们更重要。

我建议布局与示例中的相似或完全相同。事件只是不扩大。我不知道你期望多少日常事件,现实生活中的场景是什么,但我可能做的唯一升级是在不同的区域垂直分割日历 - 不与任何事件重叠的时间点,比如 evt7 和 evt7 之间的线例如 evt11。然后在每个区域独立运行相同的脚本。这将重新计算每个区域的垂直插槽,因此具有 evt10 和 evt11 的区域将只有 2 个垂直插槽,每个填充空间 50%。如果您的日历上没有几个拥挤的时间并且之后/之前只有少数事件,那么这可能是值得的。这将解决当天晚些时候事件范围太窄的问题,而无需花费太多时间。但是,如果事件遍布一天并且重叠很多,我认为这不值得。

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>

关于javascript - 如何在不相互重叠的情况下散布div,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41594519/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com