Подтвердить что ты не робот

Вертикально выравнивать div, но сохраняя неизменное горизонтальное положение

Из базы данных я вытягиваю временную шкалу Div с определенной начальной точкой и определенной конечной точкой. Некоторые из них перекрываются, некоторые из них могут быть установлены рядом друг с другом. from Database

В конечном счете я хочу объединить их вместе, чтобы он был настолько компактным, насколько это возможно: what we want to accomplish

Я сомневаюсь, как подойти к этой проблеме: через серверную часть (php) script или с помощью некоторого javascript плавающего script thingy. Или, конечно, совершенно другой подход

Может кто-нибудь подтолкнет меня в правильном направлении?

Edit:: Важно, поскольку это временная шкала, что горизонтальное положение div остается неизменным. Таким образом, плавающие все divs влево или встроенные блоки не являются опцией:)

Моя настройка базы данных:

id | name | start | end  
1  | a    | 2     | 7  
2  | b    | 5     | 10  
etc
4b9b3361

Ответ 1

ваш вопрос звучит очень наивно, но на самом деле он включает в себя некоторые сложные элементы, если это необходимо для решения наиболее оптимальным способом.

быстрый ответ того, что я, вероятно, сделал бы для создания вашего дисплея -

  • используйте предоставленную функцию, которая добавляет номер строки в вашу таблицу.
  • используйте свой код PHP для создания контейнера DIV с style = "display: block" для каждой строки

  • внутри строки генерируется соответствующий размер DIV (шкала конечного старта *), с style = "display: inline-block; float: left; display: relative" и (EDIT:) добавить прозрачные элементы DIV, чтобы компенсировать требуемые пробелы. (то есть от 0 до начала и от конца до начала следующего DIV)

  • добавить поле имени внутри элемента DIV

use mySchema; drop procedure if exists tileItems;

DELIMITER $$
CREATE PROCEDURE tileItems ()
BEGIN
DECLARE p_id, p_start, p_end, p_row int;
DECLARE done INT DEFAULT FALSE;
DECLARE cur1 CURSOR FOR SELECT id, start, end FROM tasks order by start, id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

drop temporary table if exists tiles;

create temporary table tiles (
row int(11) NOT NULL,
id int(11) NOT NULL,
end int(11) NOT NULL
);
-- row field will indicates the row number the task should apear
OPEN cur1;
next_task: LOOP

FETCH cur1 into p_id, p_start, p_end;
  IF (done) THEN
    LEAVE next_task;
  END IF;
select min(row) from (select row, max(end) me from tiles t2 group by row) t1 
  where me < p_start
  into p_row;


-- take care of row numbering  
IF (p_row IS NULL) then
  select max(row) from tiles
    into p_row;
    IF (p_row IS NULL) then
      SET p_row = 0;
    END IF;
    SET p_row=p_row+1;
END IF;

insert into tiles (id, row, end)
  values (p_id,p_row,p_end);

END LOOP;

-- CLOSE cur1;
-- here is your output, on the PHP/.Net code you should loop on the row 
select tasks.*, tiles.row from tasks
inner join tiles
 on tasks.id = tiles.id
order by tiles.row, tasks.start;


END $$
DELIMITER ;   

вот таблица, которую я использовал для проверки -

CREATE TABLE `tasks` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  `start` int(11) NOT NULL,
  `end` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=11 ;


INSERT INTO `tasks` (`id`, `name`, `start`, `end`) VALUES
(1, 'A', 2, 6),
(2, 'B', 5, 7),
(3, 'C', 8, 10),
(4, 'D', 1, 5),
(5, 'E', 6, 7);

несколько слов об оптимизации (один из моих любимых тем:) - в этом коде нет оптимизации, это означает, что задача будет выделена первой доступной строке. чтобы минимизировать количество возможных линий (но потребуется некоторое время) для создания функции, которая использует метод Heuristic для решения этой проблемы.

выход:

id  name    start   end row
4   D   1   5   1
5   E   6   7   1
3   C   8   10  1
1   A   2   6   2
2   B   5   7   3

Ответ 2

<!DOCTYPE html>
<html>
<!--
  Created using jsbin.com
  Source can be edited via http://jsbin.com/udofoq/26/edit
-->
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
<style id="jsbin-css">div.blue {
    background-color: #a4dcdf;
}

div.orange {
    background-color: #fd9226;
}

div.green {
    background-color: #88b37e;
}

div.yellow {
    background-color: #d8d03f;
}

div.red {
    background-color: #c16558;
}
div.grey {
    background-color: #cdcdcd;
}
div.hours1{
  top: 0px;
  left: 10px;
  width: 100px;//(110-10)
}
div.hours2{
  top: 30px;
  left: 80px;
  width: 50px;
}
div.hours3{
  top: 60px;
  left: 120px;
  width: 50px;
}

div.hours4{
  top: 90px;
  left: 5px;
  width: 70px;
}

div.hours5{
  top: 120px;
  left: 110px;
  width: 30px;
}
div.hours6{
  top: 150px;
  left: 130px;
  width: 70px;
}
div.hours {
  position: absolute;
  height:20px;
  color: white;
  text-align:center;
  border:white;
  -webkit-box-shadow: 3px 3px 6px 2px rgba(00, 00, 00, .2);
  box-shadow: 3px 3px 6px 2px rgba(00, 00, 00, .2);
  font: bold 18px Arial, Helvetica, Geneva, sans-serif;
  line-height:20px;     
}

button{
    position:static;
    margin-top:200px;
}
.collapse,
.overlap1,
.overlap2,
.overlap3,
reset{
    float:left;
}
</style></head>
<body>
  <div class="hours hours1 orange">A</div>
<div class="hours hours2 yellow">B</div>
<div class="hours hours3 blue">C</div>
<div class="hours hours4 green">D</div>
<div class="hours hours5 red">E</div>
<div class="hours hours6 grey">F</div>
<button class="collapse">collapse</button>
<button class="overlap1">sort</button>
<button class="reset">reset</button>

<script>
data1 = [
  [1, 10, 110],
  [2, 80, 130],
  [3, 120, 170],
  [4, 5, 70],
  [5, 110, 140],
  [6, 130, 180]
];

//just added for console output not needed
var divider="";
for (var i = 0; i < 80; i++) {
  divider += "_";
}

console.log(divider);
console.log("ORIGINAL ARRAY DATA1:", data1);


//add a column to keep track of the row, to start set it to  row 1
data1 = $.each(data1, function(index, value) {
  value[3] = 0;
});

console.log(divider);
console.log("ORIGINAL dataA WITH ADDED COLUMN:", data1);

function timelinesort(dataA){

//make a new Array to store the elements in with their new row number
var dataB = dataA.slice(0, 1);

console.log(divider);
console.log("INITIALIZED dataB WITH FIRST ELEMENT FROM dataA:", dataB);


//initialize the counter
var counter = 0;

console.log(divider);
console.log("INITIALIZED ROUNDS COUNTER:", counter);


dataA = $.map(dataA, function(value1, index1) {

//increment counter with 1 
counter++;

console.log(divider);
console.log("INCREMENTED ROUNDS COUNTER:", counter);


  dataA = $.map(dataA, function(value2, index2) {


    //exclude comparing an element with itself
    if(value2 != dataB[0]) {
      //check to see if elements overlap
      if(value2[2] >= dataB[0][1] && value2[1] <= dataB[0][2]) {
        console.log(divider);
        console.log("Round " + counter  + " from dataA: [" + value2 + "] overlaps with " + dataB[0] + " incrementing row counter with 1");
        //increment the value in column 3 (row counter) of the array
        value2[3]++;
        console.log(divider);
        console.log("Now the dataA has changed to this:", dataA);
        console.log("Meanwhile data1 has changed to this:", data1);
      } else {
        //if no overlap occurs check if the element is not already in the dataB array and if not check if it doesn't overlap with the existing elements
        if($.inArray(value2, dataB) == -1) {
          $.each(dataB, function(index3, value3) {
            if(value3[2] >= value2[1] && value3[1] <= value2[2]) {
              console.log(divider);
              console.log("Round " + counter + " from dataA: [" + value2 + "] overlaps with " + value3 + " incrementing row counter with 1");
              dataB.pop();
              //increment the value in column 3 (row counter) of the array
              value2[3]++;
            } else {
              //if no overlap occurs add the value to dataB
              dataB.push(value2);
              console.log(divider);
              console.log("Added [" + value2 + "] to dataB and now dataB has changed to this: ", dataB);
            }
          });
        } else {
          dataB.push(value2);
          console.log("Added [" + value2 + "] to dataB and now dataB has changed to this: ", dataB);
        }
      }
    }
    return [value2];
  });
  dataA = jQuery.grep(dataA, function(item) {
    return jQuery.inArray(item, dataB) < 0;
  });
  if(dataA.length >= 1) {
    dataB.unshift(dataA[0]);
    dataB = dataB.splice(0, 1);
  } else {
    dataA = [];
  }

});

}
//run the function
timelinesort(data1);

console.log(divider);
console.log("Finally the data1 has changed to this:", data1);


$(".collapse").click(function() {
  $.each(data1, function(index, value) {
    $("div.hours" + (index + 1)).animate({
      "top": 0
    }, "slow");
  });

});

$(".overlap1").click(function() {
  $.each(data1, function(index, value) {
    console.log("div.hours" + (index + 1) + ":" + (value[3]) * 26);
    $("div.hours" + (index + 1)).animate({
      "top": (value[3]) * 26
    }, "slow");
  });

});

$(".reset").click(function() {
  $.each(data1, function(index, value) {
    $("div.hours" + (index + 1)).removeAttr('style');
  });

});
</script>
</body>
</html>

То, что я сделал, это свернуть все строки в первой строке, затем проверить, какие из них накладываются на оригиналы этой строки, и если так увеличивать число строк над перекрывающимися, затем перейдите к следующей строке и повторите процесс до тех пор, пока все элементы аккуратно сложены.

Вам все равно нужно очистить материал javascript/jquery и поместить его в хорошую функцию. Но как доказательство концепции, похоже, работает

рабочий пример:


http://jsbin.com/udofoq/26/watch

или

http://jsfiddle.net/stofke/7VP5U/

Ответ 3

Ознакомьтесь с моей скрипкой здесь. Я думаю, что он делает то, что вам нужно, с неограниченным количеством блоков. Данные блоков берутся из таблицы HTML.

JS:

var data = [],
    rows = [],
    chart = $('.wrapper-inner');


function DataItem(id, name, start, end){
     this.id = id;
     this.name = name;
     this.start = start;
     this.end = end;
}

$('.data tr').each(function() {
    var $this = $(this),
        item = new DataItem( $this.find('td:eq(0)').text(),
                             $this.find('td:eq(1)').text(),
                             $this.find('td:eq(2)').text(),
                             $this.find('td:eq(3)').text() );
        data.push(item);
});

function addRow(){
    var row = {
        el : $('<div class="row"></div>').appendTo(chart),
        positions: []
    };

    rows.push( row );
}

function checkRow(rowId, item){        
    var isRowAvailible = true;

    for (var i = 0; i < +item.end - +item.start; i++){
        if (rows[rowId].positions[+item.start + i]){
            isRowAvailible = false;
            break;
        }
    }

    return isRowAvailible;
}

function markRowPositions(rowId, item){


    for (var i = 0; i < item.end - item.start; i++){
        rows[rowId].positions[+item.start + i] = true;
    }

}

function addItems(){
    for (var i = 0; i < data.length; i++){
        (function(i){
            setTimeout(function() {addItem(data[i])}, 100 * i);
        })(i)
    }
}

function addItem(item){
    var rowToAdd = false,
        itemEl = $('<div class="item"></div>');

    for (var i = 0; i < rows.length; i++){
        if ( checkRow(i, item) ){
            rowToAdd = i;
            break;    
        }
    }

    if (rowToAdd === false){
        addRow();
        rowToAdd = rows.length - 1;
    }

    rows[ rowToAdd ].el.append(itemEl);

        console.log(itemEl.css('opacity'))

    itemEl.css({
        'left': item.start * 30,
        'opacity' : 1,
        'width': ( ( item.end - item.start ) * 30 ) - 2
    });


    markRowPositions(rowToAdd, item);



}

addItems();

Ответ 4

Это самое близкое, что я мог бы получить. Он получает json_string, анализирует и генерирует результаты. Это полностью динамично. Элементы затухают в том порядке, в котором они находятся в массиве json.

Рабочая демонстрация

Надеюсь, что это хепс:

HTML:

    <div id="divHolder">
    </div>
    <input type="button" onclick="loadChart(json_data1);" value="Load Data 1" />
    <input type="button" onclick="loadChart(json_data2);" value="Load Data 2" />
    <input type="button" onclick="loadChart(json_data3);" value="Load Data 3" />

JS:

var json_data1 = '[{"id":1,"name":"A","start":2,"end":7},{"id":2,"name":"B","start":6,"end":9},{"id":3,"name":"C","start":8,"end":12},{"id":4,"name":"D","start":0,"end":5},{"id":5,"name":"E","start":7,"end":9}]';

var json_data2 = '[{"id":1,"name":"A","start":5,"end":7},{"id":2,"name":"B","start":6,"end":9},{"id":3,"name":"C","start":2,"end":6},{"id":4,"name":"D","start":2,"end":12},{"id":5,"name":"E","start":1,"end":9}, {"id":6,"name":"F","start":7,"end":11}, {"id":7,"name":"G","start":8,"end":12}, {"id":8,"name":"H","start":2,"end":4} ]';

var json_data3 = '[{"id":1,"name":"A","start":8,"end":12},{"id":2,"name":"B","start":4,"end":10},{"id":3,"name":"C","start":2,"end":4},{"id":4,"name":"D","start":0,"end":7},{"id":5,"name":"E","start":7,"end":11}, {"id":6,"name":"F","start":5,"end":7}]';

function loadChart(json_data){
    var data = JSON.parse(json_data);
    var divHolder = $('#divHolder');
    var maxWidth = $(document).width() - 200;
    var maxHeight = $(document).height();
    $(divHolder).empty();
    var maxEnd = 0;
    var minStart = 0;
    var widthUnit = 0;
    $(data).each(function(){
        if(this.end > maxEnd)
            maxEnd = this.end;
        if(this.start < minStart)
            minStart = this.start;
    });
    widthUnit = maxWidth / (maxEnd - minStart) ;
    var maxItemUnit = maxEnd;
    var rows = new Array();
    $(data).each(function(){
        var added = false;
        var currentObj = this;
        var i;
        for(i=0;i<rows.length;i++){
            var toAdd = true;
            //if(widthSum> maxItemUnit              
            var widthSum = 0;
            $(rows[i]).each(function(){
                widthSum += this.end - this.start;
                if(this.end < currentObj.start ||  this.start > currentObj.end){
                    if((widthSum + currentObj.end - currentObj.start) < maxItemUnit) 
                        toAdd = true;
                }                               
                else{
                    toAdd=false;
                    return false;
                }
            });
            if(toAdd){
                rows[i].push(currentObj);
                added = true;
                break;
            }
        }
        if(!added){
            rows[i] = new Array();
            rows[i].push(currentObj);
        }
    });

    $(rows).each(function(){
        var current = this;
        var divMain = $('<div></div>').css('display','block').css('width', maxWidth).css('height', 50)
        $(current).each(function(){
            var div = $('<div></div>');
            div.addClass('item').html('<span class="item-content">'+this.name+'</span>');
            var diff = this.end - this.start;
            div.attr('id', this.id).css('opacity','0').css('left', widthUnit * this.start).width(widthUnit * diff);
            if(diff < (maxItemUnit/3))
                div.addClass('small');
            else if (diff < (maxItemUnit/2))
                div.addClass('medium');
            else
                div.addClass('large');
            divMain.append(div);
        });
        $(divHolder).append(divMain);
    });
    var delayVar = 0;
    $(data).each(function(){
        $('#'+this.id).fadeTo(delayVar, 1);
        delayVar += 300;
    });
}

CSS

.item{
    display:inline-block;
    position:absolute;
    color: #ffffff;
    border: 1px solid white;
}
.item-content{
    margin-left: 50%;
}
.small{
    background-color:#E66C3D;
}
.medium{
    background-color:#DDD634;
}
.large{
    background-color:#3EC043;
}

Ответ 6

Попробуйте следующее:

function collides($block1,$block2) {
  return ( ($block1['start'] >= $block2['start']) and ($block1['start'] <  $block2['end']) ) or
         ( ($block1['end']   >  $block2['start']) and ($block1['end']   <= $block2['end']) ) or
         ( ($block1['start'] <= $block2['start']) and ($block1['end']   => $block2['end']) );   
}

function FitsInRow ($row,$block) {
    $fits=true;
    foreach ($row as $block1) 
        if (collides($block,$block1))
            $fits=false;
    return $fits;
}

$rows=array();

// $blocks like that:
$blocks[0]['start']=0;
$blocks[0]['end']=10;
$blocks[0]['name']='A';
$blocks[0]['color']='#F00';

$blocks[1]['start']=5;
$blocks[1]['end']=20;
$blocks[1]['name']='B';
$blocks[1]['color']='#0F0';
//etc


foreach ($blocks as $block) {
    $i=0;
    while (isset($rows[$i]) && !FitsInRow($block,$rows[$i]))
        $i++;
    $rows[$i][]=$block;
}

echo '<div class="block_outer" style="height: '.(count($rows)*20).'px;">';
foreach ($rows as $nr=>$row)
    foreach ($row as $block)
        echo '<div class="block" style="left:'.$block['start'].'px; width:'.($block['end']-$block['start']).'px; top:'.($nr*20).'px; background-color:'.$block['color'].';">'.$block['name'].'</div>';
echo '</div>';

Со следующим css:

.block {
  position:absolute;
  height:16px;
  /* ... */
}
.block_outer {
  position:relative;
  width:100%;
  overflow:auto;
}

Я еще не тестировал его.

Изменить: я изменил имена, чтобы он соответствовал указанной настройке базы данных.

Другое редактирование: теперь я добавил внешний div, поэтому абсолютные позиции внутренних divs не нарушают макет страницы.

Ответ 7

Вы пытались показать их inline с помощью CSS?

div.inline {
    display: inline-block;
}

Если ваши div имеют класс inline.

Ответ 8

Это выглядит как хороший случай:

div.hours {
  position: absolute;
  top: 100px;
  left: 155px;
  width: 100px;
}

Конечно, замените 100px, 155px, 100px вашими значениями.

В качестве альтернативы вы можете попробовать использовать теги для баров, внутри блоков, с позициями: относительные и левые/значения ширины.

Ответ 9

Нет простого способа сделать это, вам нужно будет использовать position: absolute и рассчитать, где события будут видны на странице, например: один день полный день имеет 10px height и 10px width, и нам нужно добавьте событие в day 1, затем мы проверим, есть ли line 1 is empty или имеет событие, если оно пустое, а затем put the event there, else go to the next line and repeat, пока оно не будет выделено в строке.

Ответ 10

Вам нужно искать алгоритмы топологическую сортировку и либо реализовать их самостоятельно (в php или JavaScript), либо попытаться найти подходящую готовность -деланная реализация.

Ни свойства CSS, ни макет таблицы не решают эту проблему автоматически и безупречно.