将碰撞元素放在一起

时间:2014-10-07 12:09:48

标签: php html

我正在创建某种日历/日程表,以显示特定日期的事件。每个事件在垂直小时网格中显示为HTML元素。可能同时存在多个(“碰撞”)事件,并且在这些情况下,元素应该彼此相邻放置,水平放置,并且具有相等的宽度。例如。四个碰撞事件得到列值4,这样宽度为25%。

棘手的部分是这些碰撞事件。我以为我解决了它,但有些元素的列数错误。

可能有更好的方法来计算列数和位置 - 我愿意接受建议。

当前(错误)结果的示例图片: enter image description here

相关代码:

<?php
    class Calendar {
        const ROW_HEIGHT = 24;

        public $events = array();
        public $blocks = array();


        public function calculate_blocks() {
            foreach($this->events as $event) {

                // Calculate the correct height and vertical placement
                $top = $this->time_to_pixels($event->_event_start_time);
                $bottom = $this->time_to_pixels($event->_event_end_time);
                $height = $bottom - $top;

                // Abort if there's no height
                if(!$height) continue;

                $this->blocks[] = array(
                    'id' => $event->ID,
                    'columns' => 1,
                    'placement' => 0, // Column order, 0 = first
                    'css' => array(
                        'top' => $top,
                        'bottom' => $bottom, // bottom = top + height
                        'height' => $height
                    )
                );
            }

            $done = array();

            // Compare all the blocks with each other
            foreach($this->blocks as &$block) {
                foreach($this->blocks as &$sub) {

                    // Only compare two blocks once, and never compare a block with itself
                    if($block['id'] == $sub['id'] || (isset($done[$block['id']]) && in_array($sub['id'], $done[$block['id']])) || (isset($done[$sub['id']]) && in_array($block['id'], $done[$sub['id']]))) continue;
                    $done[$block['id']][] = $sub['id'];

                    // If the blocks are colliding
                    if(($sub['css']['top'] >= $block['css']['top'] && $sub['css']['top'] < $block['css']['bottom'])
                    || ($sub['css']['bottom'] >= $block['css']['top'] && $sub['css']['bottom'] < $block['css']['bottom'])
                    || ($sub['css']['top'] <= $block['css']['top'] && $sub['css']['bottom'] >= $block['css']['bottom'])) {

                        // Increase both blocks' columns and sub-block's placement
                        $sub['columns'] = ++$block['columns'];
                        $sub['placement']++;
                    }
                }
            }
        }


        private function time_to_int($time) {

            // H:i:s (24-hour format)
            $hms = explode(':', $time);
            return ($hms[0] + ($hms[1] / 60) + ($hms[2] / 3600));
        }


        private function time_to_pixels($time) {
            $block = $this->time_to_int($time);

            return (int)round($block * self::ROW_HEIGHT * 2);
        }
    }
?>

1 个答案:

答案 0 :(得分:1)

试试这个:

public function calculate_blocks()
{
    $n          = count($events);
    $collumns   = array();
    $placements = array();

    // Set initial values.
    for ($i = 0; $i < $n; $i++)
    {
        $collumns[$i]   = 1;
        $placements[$i] = 0;
    }
    // Loop over all events.
    for ($i = 0; $i < $n; $i++)
    {
        $top1           = $this->time_to_pixels($events[$i]->_event_start_time);
        $bottom1        = $this->time_to_pixels($events[$i]->_event_end_time);

        // Check for collisions with events with higher indices.
        for ($j = $i + 1; $j < $n; $j++)
        {
            $top2     = $this->time_to_pixels($events[$k]->_event_start_time);
            $bottom2  = $this->time_to_pixels($events[$k]->_event_end_time);
            $collides = $top1 < $bottom2 && $top2 < $bottom1;

            // If there is a collision, increase the collumn count for both events and move the j'th event one place to the right.
            if ($collides)
            {
                $collumns[$i]++;
                $collumns[$j]++;
                $placements[$j]++;
            }
        }

        $this->blocks[] = array(
            'id'        => $events[$i]->ID,
            'columns'   => $collumns[$i],
            'placement' => $placements[$i],
            'css'       => array(
                'top'    => $top1,
                'bottom' => $bottom1,
                'height' => $bottom1 - $top1;
            )
        );
    }
}

我无法对它进行实际测试,但我认为应该为您提供正确的数组。

编辑1:似乎没有产生所需的结果,请参阅下面的评论。

编辑2:我认为这是完全相同的问题:Visualization of calendar events. Algorithm to layout events with maximum width。有人用C#解决了它,但是将这个答案移植到PHP来解决你的问题应该相对容易。