Familiewaaier
by PhilQ
HTML
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
<div id="familiewaaier"></div>
SCSS
*, :before, :after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
}
body {
background: #20262E;
padding: 20px;
font-family: Helvetica, sans-serif;
}
@import url('https://fonts.googleapis.com/css2?family=Jost:ital,wght@0,100..900;1,100..900&display=swap');
:root {
--e-global-color-primary: #782A24;
--e-global-color-secondary: #B89349;
--e-global-color-text: #615956;
--e-global-color-accent: #EBB54F;
--e-global-color-69c5e9e: #EBB54F;
--e-global-color-b2b2f2c: #674E22;
--e-global-color-19f3416: #E34439;
--e-global-color-3504911: #331A0F;
--e-global-color-63f8136: #F2D987;
--e-global-color-0b40fbb: #F5EFD0;
--e-global-color-8a491fc: #9F987A;
--e-global-color-9eb4bcd: #615956;
--e-global-color-10c6832: #222222;
--e-global-color-94148c1: #FFFFFF;
--e-global-typography-primary-font-family: "Eczar";
--e-global-typography-primary-font-weight: 600;
--e-global-typography-secondary-font-family: "Eczar";
--e-global-typography-secondary-font-weight: 400;
--e-global-typography-text-font-family: "Jost";
--e-global-typography-text-font-weight: 400;
--e-global-typography-accent-font-family: "Jost";
--e-global-typography-accent-font-weight: 500;
}
#familiewaaier {
position: relative;
display: flex;
justify-content: stretch;
align-items: stretch;
background: #fff;
border-radius: 4px;
margin: 0 auto;
width: fit-content;
height: fit-content;
min-width: 70%;
max-width: 80vw;
max-height: 90vh;
overflow: clip;
&:fullscreen {
width: 100vw;
height: 100vh;
}
.inner {
position: relative;
overflow: auto;
min-width: 100%;
min-height: 100%;
svg {
z-index: 1;
}
}
.controls {
position: absolute;
top: 1em;
right: 1em;
z-index: 2;
display: flex;
gap: 1em;
button {
display: flex;
justify-content: center;
align-items: center;
font-family: var(--e-global-typography-accent-font-family), Sans-serif;
font-weight:...
JavaScript
class VHW_FamilyTree {
// Data and objects
// NOTE: ordered by date of birth
// {
// id,
// name,
// parent_id,
// *gen,
// *offspring count,
// *parent offspring count offset,
// }
// * = will be set by script
persons = [];
items = [];
gens = [[]];
child_of_classes = {};
parent_of_classes = {};
max_gen_width = 0;
wrapper = null;
svg = null;
w = 200;
h = 200;
x = 0;
y = 0;
cx = 0;
cy = 0;
zoom = 1.0;
// Settings
r_per_gen = 100;
margin = 50;
rotate = -90;
constructor(selector, persons) {
this.wrapper = document.querySelector(selector);
if (!this.wrapper || this.wrapper.classList.contains('loaded')) {
return;
}
// Normalize rotation to positive multitude of 90
this.rotate = Math.round(
(
(
(this.rotate % 360) + 360
) % 360
) / 90
) * 90;
this.persons = persons;
this.prepareItems();
this.prepareAncestorClasses();
if (this.items.length) {
const inner = document.createElement('div');
inner.classList.add('inner');
this.max_gen_width = this.items[0][4];
this.svg = this.createTag('svg');
this.setSize();
// this.updateViewbox();
inner.appendChild(this.svg);
this.insertItems(this.svg, this.items[0]);
this.addControls();
this.wrapper.appendChild(inner);
this.wrapper.classList.add('loaded');
// window.addEventListener('resize', () => this.updateViewbox() );
}
}
prepareItems() {
// Group names by parent
let ids = new Set([0]);
let parent_of = {};
let grouped_by_parent = {};
for (const person of this.persons) {
person[3] = 0; // gen
person[4] = 0; // offspring count
person[5] = 0; // parent offspring count offset
let id = `${person[0]}`;
let parent_id = `${person[2]}`;
parent_of[id] = parent_id;
ids.add(id);
grouped_by_parent[parent_id] =...