Объемный график на CSS или JS
Подскажите как сделать такой график, в интернете не могу найти что-то похожее на это
Есть много библиотек для построения графиков на JS, но я не знаю какая из них может это
И можно ли это сделать на чистом CSS?
Ответы (1 шт):
И можно ли это сделать на чистом CSS?
Ну, совсем без скриптов нельзя. Но, большая часть вполне реализуема посредством стилей (смотреть лучше развернув на весь экран):
let oJson = JSON.parse(`{
"Понедельник": [47,230,1,164,20,219,72,235,145,209,25,205],
"Вторник": [175,38,13,151,185,237,85,126,103,125,153,165],
"Среда": [112,19,183,201,21,64,60,35,193,236,73,207],
"Четверг": [50,192,211,148,198,86,217,32,49,113,218,62],
"Пятница": [69,54,210,74,7,222,78,92,77,142,162,23],
"Суббота": [101,39,212,157,131,139,178,149,206,147,71,122],
"Воскресенье": [46,11,109,26,43,158,28,227,34,90,121,129]
}`);
function fFillChart() {
let oChart = document.querySelector(".girder-chart");
let nItems = 0;
let nQ = Math.max(...Object.values(oJson).flat()) / 100;
for (let k in oJson) {
let oRow = document.createElement("div");
oChart.append(oRow);
oJson[k].forEach((el) => {
let nValue = el / nQ;
nItems++;
oRow.insertAdjacentHTML("beforeend",
`
<div
class="girder"
data-grp="${Math.floor(nValue / 25)}"
style="
transition-delay:${nItems / 100}s;
--girder-height: ${nValue};
filter:
drop-shadow(0 2px 3px #0008)
hue-rotate(${(100 - nValue) * 3 - 45}deg)
saturate(${(nValue / 100) * 0.75});
opacity:${nValue / 110 + 0.1};
">
<div class="girder_l"></div>
<div class="girder_r"></div>
<div class="girder_t" data-val="${el}"></div>
</div>
`
);
});
}
}
fFillChart();
body {
margin: 0;
min-height: 100vh;
background-image: radial-gradient(#181100, #000);
background-position: 0% 110%;
background-repeat: no-repeat;
background-size: auto;
display: flex;
flex-flow: column nowrap;
justify-content: space-around;
align-items: center;
}
.girder-wrap {
display: flex;
flex-flow: row wrap;
justify-content: center;
align-items: center;
width: min-content;
color: #fff;
text-shadow: -1px -1px 3px #000, -1px 1px 3px #000, 1px -1px 3px #000, 1px 1px 3px #000;
}
.girder-wrap>input:not(:first-child) {
margin-left: 2em;
}
hr {
margin: 20px 40px 80px;
width: 100%;
}
.girder-chart {
--girder-think: 50;
font-size: calc(var(--girder-think, 100) * 1px) !important;
position: relative;
display: inline-flex;
flex-flow: column nowrap;
justify-content: flex-start;
align-items: flex-start;
padding: 1em 2em;
}
.girder-chart::before {
content: "";
position: absolute;
top: 0em; left: 0.23em;
height: 100%; width: calc(100% - 0.49em);
background-image: repeating-linear-gradient( 210.9deg, #0000 0 calc(0.49em - 1px), #8888 0.49em, #0000 calc(0.49em + 1px) 0.514em), repeating-linear-gradient( -210.9deg, #0000 0 calc(0.24em - 1px), #8888 0.24em, #0000 calc(0.24em + 1px) 0.514em);
-webkit-mask-image: radial-gradient(ellipse closest-side, #0000, #000, #0000);
mask-image: radial-gradient(ellipse closest-side, #0000, #000, #0000);
pointer-events: none;
}
.girder-chart>div {
display: flex;
flex-flow: row nowrap;
justify-content: flex-start;
align-items: flex-end;
height: 0.3em;
}
.girder-chart>div:nth-child(odd) { margin-right: calc(var(--girder-think) / 2 * 1px); }
.girder-chart>div:nth-child(even) { margin-left: calc(var(--girder-think) / 2 * 1px); }
.girder {
transform: translate3d(0, 0, 0);
position: relative;
display: flex;
justify-content: center;
align-items: flex-start;
will-change: height, opacity;
height: calc(var(--girder-height, 0) * 1px + 0.3em) !important; width: 1em;
filter: drop-shadow(0 3px 1px #000a) saturate(100%);
transition: height 0.4s cubic-bezier(0.65, 0.45, 0.33, 1.18), opacity 0.5s ease-in-out 0s;
}
.girder_t {
position: relative;
height: 0.68em; width: 0.68em;
border-radius: 0.066em 0.066em 0.066em 0.033em;
transform-origin: 50% 50%;
transform: rotate(-45deg) skew(15deg, 15deg) translate(48%, -48%) scale(0.87);
background-image: linear-gradient(135deg, #fa0f, #fa0d);
box-shadow: inset -0.1em -0.155em 0.45em -0.01em #fa0f, -0.02em 0.02em 0.033em -0.01em #fa0a;
}
/* Плашка со значением */
.girder_t::before,
.girder_t::after {
position: absolute;
bottom: 50%; left: 100%; z-index: 1;
font-size: initial !important;
opacity: 0;
transition: transform 0.4s cubic-bezier(0.65, 0.45, 0.33, 1.18), opacity 0.4s linear 0s;
pointer-events: none;
}
.girder_t::before {
content: "";
height: 2em;
transform: rotate(45deg) skew(0deg, 0deg) translate(-50%, 0%) scale(1, 1.5);
box-shadow: 0 0 0 1px #f0f;
}
.girder_t::after {
content: attr(data-val);
padding: 0.5em 0.7em;
border-radius: 0.3em;
transform: rotate(45deg) skew(0deg, 0deg) translate(-33%, -1em) scale(0.1, 1.5);
font-family: sans-serif;
text-align: center;
background-color: #eee;
box-shadow: inset 0 0 0 2px #f0f;
}
.girder:hover .girder_t::before,
.girder:hover .girder_t::after {
opacity: 1;
transition: transform 0.4s cubic-bezier(0.65, 0.45, 0.33, 1.18) 1s, opacity 0.4s linear 1s;
}
.girder:hover .girder_t::after {
transform: rotate(45deg) skew(0deg, 0deg) translate(-33%, -1em) scale(1, 1.5);
}
.girder_l,
.girder_r {
position: absolute;
height: calc(100% - 0.3em); width: calc(50% + 0.015em);
}
.girder_l {
left: 0;
border-radius: 0 0 0.055em 0.033em;
transform-origin: 0% 0%; transform: skewy(30deg);
background-image: linear-gradient(90deg, #fa0f, #805500, #0001);
box-shadow: inset -0.34em -0.1em 0.25em -0.2em #000e, inset 0.84em 0.54em 0.95em -0.4em #fa08;
}
.girder_r {
right: 0;
border-radius: 0 0 0.033em 0.055em;
transform-origin: 100% 0%; transform: skewy(-30deg);
background-image: linear-gradient(90deg, #000c, #805500);
box-shadow: inset -0.125em 0.1em 0.425em -0.225em #fa0c;
}
input[name="grp"][value=""]:checked~.girder-chart:hover>div>div:not(:hover) {
height: 0.3em !important;
transition: height 1s ease-in-out, opacity 0.5s ease-in-out;
transition-delay: 0s !important;
}
input[name="grp"][value="0"]:checked~.girder-chart>div>div:not([data-grp="0"]),
input[name="grp"][value="1"]:checked~.girder-chart>div>div:not([data-grp="1"]),
input[name="grp"][value="2"]:checked~.girder-chart>div>div:not([data-grp="2"]),
input[name="grp"][value="3"]:checked~.girder-chart>div>div:not([data-grp="3"]) {
height: 0.3em !important;
opacity: 0 !important;
pointer-events: none;
transition: height 1s ease-in-out, opacity 0.5s ease-in-out;
transition-delay: 0s !important;
}
.girder-chart:hover>div>div:hover {
opacity: 1 !important;
transition-delay: 0s !important;
}
<div class="girder-wrap">
<input type="radio" name="grp" value="" checked>Все
<input type="radio" name="grp" value="0">0-25%
<input type="radio" name="grp" value="1">26-50%
<input type="radio" name="grp" value="2">51-75%
<input type="radio" name="grp" value="3">76-100%
<hr>
<div class="girder-chart"></div>
</div>
Функция лишь раскидывает входящие данные по создаваемым элементам, одновременно управляя внешним видом столбцов. Ненужные эффекты (разный цвет, прозрачность) можно закомментировать или удалить.
Как правильно заметили в комментариях под вопросом - загвоздка в формате входящих данных и их парсинге (просто от этого нужно изначально отталкиваться).
