Временнáя диаграмма в виде линии с ветками-событиями
Я хочу сделать хронологическую диаграмму в виде горизонтальной линии, от которой под углом отходят ветки с событиями (слегка похоже на карту ветки метро в вагонах поездов):
Я написал это:
.chrono-line-window {
width: 100%;
height: 400px;
border-radius: 15px;
box-shadow: 0 0 15px 5px rgba(0, 0, 0, 0.25) inset;
overflow-x: auto;
padding: 0 70px 0 70px;
display: flex;
align-items: center;
}
.chrono-line {
flex: 0 0 1000px;
height: 10px;
background-color: #333;
display: flex;
align-items: center;
}
.sub-line {
width: 200px;
height: 5px;
background: #333;
margin: 3px;
}
.sub-line:before {
content: "";
display: block;
width: 20px;
height: 20px;
border-radius: 50%;
background: #333;
}
.top-branch .sub-line:before {
transform: translate(-50%, -40%);
}
.bottom-branch .sub-line:before {
transform: translate(-45%, -35%);
}
.edge-circle {
width: 25px;
height: 25px;
border-radius: 50%;
background: #333;
}
.edge-circle:first-child {
transform: translate(-50%, 0px);
}
.edge-circle:last-child {
margin-left: auto;
transform: translate(50%, 0px);
}
.branch-container {
width: 200px;
}
.branch {
text-align: center;
transform-origin: left center;
}
.top-branch {
transform: translateY(-1.5px) rotate(45deg);
}
.bottom-branch {
transform: translateY(3px) rotate(-45deg);
}
.branch p {
font-size: 16px;
margin-left: 40px;
margin-right: 20px;
}
.branch p:first-child {
font-weight: 600;
}
.branch p:last-child {
font-weight: 300;
}
<div class="chrono-line-window">
<div class="chrono-line">
<div class="edge-circle"></div>
<div class="branch-container top-branch-container">
<div class="branch top-branch">
<p>Name of procedure</p>
<div class="sub-line"></div>
<p>01.01.2000</p>
</div>
</div>
<div class="branch-container">
<div class="branch bottom-branch">
<p>Name of procedure</p>
<div class="sub-line"></div>
<p>01.01.2000</p>
</div>
</div>
<div class="branch-container top-branch-container">
<div class="branch top-branch">
<p>Name of procedure</p>
<div class="sub-line"></div>
<p>01.01.2000</p>
</div>
</div>
<div class="branch-container">
<div class="branch bottom-branch">
<p>Name of procedure</p>
<div class="sub-line"></div>
<p>01.01.2000</p>
</div>
</div>
</div>
</div>
Но так как у .chrono-line установлено display: flex, то линии (.branch-container) генерируются строго одна за другой по горизонтали, а я хочу вручную устанавливать X-координату.
Конечно, можно высчитывать margin'ы, но в дальнейшем планирую генерировать ветки в JS и хотелось бы устанавливать их позицию по X в соответствии с датой события на ветке одной строкой вроде margin-left: N px, где N=ширина_ветки*(дата_события-дата_начала_линии)/(дата_конец_линии-дата_начала_линии)
Ответы (2 шт):
Вариант на Flexbox.
Из минусов, чтобы вращение и положение линии .chrono-item-inner было взаимосвязанно, пришлось ограничить по высоте элемент .chrono-item-date и плясать уже от него :)
Тут между линиями есть отступ, он фиксированный, за счёт использования gap правила у селектора .chrono-inner.
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
min-height: 100vh;
font-family: sans-serif;
color: #333;
overflow: hidden auto;
margin: 0;
}
.chrono {
display: block;
width: 100%;
max-width: 600px;
height: 350px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5) inset;
overflow: auto hidden;
}
.chrono-inner {
display: flex;
justify-content: flex-start;
align-items: center;
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
height: 100%;
background-image:
radial-gradient(circle, #304e87 67.5%, transparent calc(67.5% + 1px)),
linear-gradient(#304e87, #304e87);
background-repeat: no-repeat;
background-position: 40px center, 60px center;
background-size: 30px 30px, 100% 4px;
gap: 40px;
padding: 0 160px 0 120px;
box-sizing: border-box;
margin: 0;
position: relative;
list-style: none;
}
.chrono-item {
display: block;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #304e87;
position: relative;
}
.chrono-item-inner {
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
gap: 4px;
background: linear-gradient(#304e87, #304e87) no-repeat 0 calc(100% - 22px) / 100% 2px;
white-space: nowrap;
position: absolute;
padding-left: 60px;
text-align: center;
box-sizing: border-box;
left: 50%;
bottom: calc(50% - 22px);
transform-origin: left calc(100% - 22px);
}
.chrono-item:nth-child(odd) .chrono-item-inner {
transform: rotate(-45deg);
}
.chrono-item:nth-child(even) .chrono-item-inner {
transform: rotate(45deg);
}
.chrono-item-title {
font-weight: 700;
}
.chrono-item-date {
height: 20px;
}
<div class="chrono">
<ul class="chrono-inner">
<li class="chrono-item">
<div class="chrono-item-inner">
<div class="chrono-item-title">Название события</div>
<div class="chrono-item-date">01.01.2000</div>
</div>
</li>
<li class="chrono-item">
<div class="chrono-item-inner">
<div class="chrono-item-title">Название события<br>Новая строка</div>
<div class="chrono-item-date">02.01.2000</div>
</div>
</li>
<li class="chrono-item">
<div class="chrono-item-inner">
<div class="chrono-item-title">Название события</div>
<div class="chrono-item-date">01.01.2001</div>
</div>
</li>
</ul>
</div>
а я хочу вручную устанавливать X-координату
Не особо понятно каким образом вы будете их выставлять, относительно чего?
Например, чтобы визуально показать, что между А и Б точками прошло много времени, предлагаю лучше добавить для Б margin-left какой-то величины.
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
min-height: 100vh;
font-family: sans-serif;
color: #333;
overflow: hidden auto;
margin: 0;
}
.chrono {
display: block;
width: 100%;
max-width: 600px;
height: 350px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5) inset;
overflow: auto hidden;
}
.chrono-inner {
display: flex;
justify-content: flex-start;
align-items: center;
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
height: 100%;
background-image:
radial-gradient(circle, #304e87 67.5%, transparent calc(67.5% + 1px)),
linear-gradient(#304e87, #304e87);
background-repeat: no-repeat;
background-position: 40px center, 60px center;
background-size: 30px 30px, 100% 4px;
gap: 40px;
padding: 0 160px 0 120px;
box-sizing: border-box;
margin: 0;
position: relative;
list-style: none;
}
.chrono-item {
display: block;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #304e87;
position: relative;
}
.chrono-item-inner {
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
gap: 4px;
background: linear-gradient(#304e87, #304e87) no-repeat 0 calc(100% - 22px) / 100% 2px;
white-space: nowrap;
position: absolute;
padding-left: 60px;
text-align: center;
box-sizing: border-box;
left: 50%;
bottom: calc(50% - 22px);
transform-origin: left calc(100% - 22px);
}
.chrono-item:nth-child(odd) .chrono-item-inner {
transform: rotate(-45deg);
}
.chrono-item:nth-child(even) .chrono-item-inner {
transform: rotate(45deg);
}
.chrono-item-title {
font-weight: 700;
}
.chrono-item-date {
height: 20px;
}
<div class="chrono">
<ul class="chrono-inner">
<li class="chrono-item">
<div class="chrono-item-inner">
<div class="chrono-item-title">Название события</div>
<div class="chrono-item-date">01.01.2000</div>
</div>
</li>
<li class="chrono-item">
<div class="chrono-item-inner">
<div class="chrono-item-title">Название события<br>Новая строка</div>
<div class="chrono-item-date">02.01.2000</div>
</div>
</li>
<li class="chrono-item" style="margin-left: 80px">
<div class="chrono-item-inner">
<div class="chrono-item-title">Название события</div>
<div class="chrono-item-date">01.01.2001</div>
</div>
</li>
</ul>
</div>
ассортимента ради, альтернатива на псевдо-верстке с гридами
не сильно симпатично конечно, но украшательств тут как бы и нету.
зато бонусом доступно сразу несколько диаграм в одном врапере(можно не только месяцы но и недели разные по разным диаграммам раскидывать. ну чтоб без треша с перекрытием ближайших дат было)
или на первой то что еще лишь в планах(в т.ч. с просроченным дедлайном), а во второй то что уже выполнено.
#r, #t, #x > *{position: absolute; width: 90%;}
#x>p{grid-row: 1;}
#x>div{grid-row: 2;}
#r, #t {z-index: 5;}
#r{top: calc(50vh + 1.3em);}
#t{top: 150vh;}
#x {
display: grid;
position: relative;
grid-template-columns: repeat(31, 1fr);
grid-template-rows: 1px 1px;
grid-row-gap: 100vh;
top: 50vh;
}
#x>* {
border-top: 1px solid black;
text-align: center;
width: 150px;
transform-origin: 0 50%;}
#x>*:before {
display: block;
content: attr(data-name);
font: 1.5em bold;}
#x>*:hover {background: white;}
#x>*:hover:after{content: attr(data-text);}
#x>*:nth-child(odd) {transform: rotateZ(45deg);}
#x>*:nth-child(even) {transform: rotateZ(-45deg);}
<input id='r' type='range' min='0' max='28'>
<input id='t' type='range' min='0' max='31'>
<div id='x'>
<p style='grid-column: 2; /* это типа дата такая */'
data-name='днюха'
data-text='чья-то'></p>
<p style='grid-column: 5;'
data-name='пятое'
data-text='февраля'></p>
<p style='grid-column: 14;'
data-name='<3'
data-text='ййййййй'>
aaaaa
</p>
<p style='grid-column: 15;'
data-name='йцукен'
data-text='ййййййййй!'></p>
<p style='grid-column: 16;'
data-name='----'
data-text='!!1'></p>
<div style='grid-column: 1;'>1 марта</div>
<div style='grid-column: 9;'>hhhhh</div>
<div style='grid-column: 15;'>bgbgbgb</div>
<div style='grid-column: 16;'>vvvvv</div>
</div>
впрочем... почему бы вместо плясок с позиционированием не повернуть весь грид целиком? :3
#x, #y{
display: inline-grid;
grid-auto-rows: 1fr;
border-left: 20px solid #bbb; /* чем это не timeline? */
margin: 1em;
}
#x>*, #y>*{display: list-item;}
#x>*:after, #y>*:after{content: ' ' attr(style);}
#y{transform: rotateZ(-90deg); transform-origin: 100% 0;}
#y>*{transform: rotateZ(45deg);}
#y>*:nth-child(odd){transform: rotateZ(135deg); transform-origin: 0 0;}
<div id='x'>
<b style='grid-row: 1'>x</b>
<b style='grid-row: 2'>x</b>
<b style='grid-row: 5'>x</b>
<b style='grid-row: 7'>x</b>
<b style='grid-row: 15'>x</b>
<b style='grid-row: 14'>x</b>
<b style='grid-row: 28'>x</b>
</div>
<div id='y'>
<b style='grid-row: 1'>y</b>
<b style='grid-row: 2'>y</b>
<b style='grid-row: 5'>y</b>
<b style='grid-row: 7'>y</b>
<b style='grid-row: 15'>y</b>
<b style='grid-row: 14'>y</b>
<b style='grid-row: 28'>y</b>
</div>
