Se vuoi inserire questo tool nel tuo sito web, puoi copiare e incollare il codice html qui sotto.
Se usi WordPress, ti basterà creare una nuova pagina, scegliere il blocco “HTML Personalizzato” e incollare il codice che trovi qui sotto.

Se invece vuoi caricare la pagina come a sé stante, ugualmente incolla il codice qui sotto ma inserisci “<html>” all’inizio e “</html>” alla fine.
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ruota della Vita | The Psychology Club</title>
<style>
/* ===== THE PSYCHOLOGY CLUB - RUOTA DELLA VITA ===== */
:root {
--tpc-tobacco: #5c4a3d;
--tpc-tobacco-soft: #6d5a4d;
--tpc-tobacco-muted: #8a7a6d;
--tpc-brick: #a63d2f;
--tpc-brick-deep: #8a3225;
--tpc-brick-glow: rgba(166, 61, 47, 0.1);
--tpc-beige: #d4c8b8;
--tpc-beige-light: #e8e0d4;
--tpc-beige-glow: rgba(212, 200, 184, 0.3);
--tpc-warm-gray: #a09080;
--tpc-cream: #f7f4ef;
--tpc-ivory: #faf8f4;
--tpc-paper: #fffdf8;
/* GDPR-SAFE FONTS - System only */
--tpc-font-display: Charter, 'Bitstream Charter', 'Sitka Text', Cambria, Georgia, serif;
--tpc-font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--tpc-font-body);
background: var(--tpc-cream);
color: var(--tpc-tobacco);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
overflow-x: hidden; /* guard anti scroll orizzontale */
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 40px 20px;
}
/* ───────────────────────────────────────────────────────────
ISOLAMENTO DAL TEMA
Il widget viene inserito dentro .inside-article / .entry-content
del blog, le cui regole (colore testo mobile, font serif,
sillabazione, max-width, centratura) "colpiscono" i <p>, gli
<a> e i titoli del widget. I selettori sotto sono prefissati
con .container per avere specificità sufficiente a vincere.
─────────────────────────────────────────────────────────── */
/* Header */
.header {
background: var(--tpc-tobacco);
border-radius: 12px;
padding: 50px 40px 40px;
margin-bottom: 40px;
text-align: left;
}
.container .header h1 {
font-family: var(--tpc-font-display);
color: var(--tpc-paper);
font-size: clamp(2em, 5vw, 2.8em);
margin: 0 0 15px;
max-width: none;
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.15;
}
.container .header-subtitle {
font-family: var(--tpc-font-body);
color: var(--tpc-beige-light);
font-size: clamp(1em, 2.5vw, 1.1em);
margin: 0 0 10px;
max-width: none;
padding: 0;
font-weight: 400;
hyphens: manual;
-webkit-hyphens: manual;
overflow-wrap: normal;
}
/* Intro */
.intro {
background: var(--tpc-beige-light);
padding: 30px;
border-radius: 12px;
margin-bottom: 40px;
border: 1px solid var(--tpc-beige);
}
.container .intro p {
font-family: var(--tpc-font-body);
font-size: 1em;
color: var(--tpc-tobacco);
margin: 0 0 10px;
max-width: none;
padding: 0;
line-height: 1.7;
hyphens: manual;
-webkit-hyphens: manual;
overflow-wrap: normal;
}
.container .intro p:last-child {
margin-bottom: 0;
}
/* Main Layout */
.main-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 40px;
}
/* Sliders Section */
.sliders-section {
background: var(--tpc-paper);
border: 1px solid var(--tpc-beige);
border-radius: 12px;
padding: 32px;
min-width: 0;
}
.container .section-title {
font-family: var(--tpc-font-display);
font-size: 1.5em;
font-weight: 500;
color: var(--tpc-tobacco);
margin: 0 0 24px;
max-width: none;
padding-bottom: 16px;
border-bottom: 1px solid var(--tpc-beige);
}
.area-slider {
margin-bottom: 24px;
}
.slider-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
gap: 12px;
}
.slider-label {
font-weight: 600;
color: var(--tpc-tobacco);
font-size: 0.95em;
min-width: 0;
}
.slider-emoji {
margin-right: 6px;
}
.slider-value {
font-family: var(--tpc-font-display);
font-size: 1.3em;
font-weight: 500;
color: var(--tpc-brick);
min-width: 40px;
text-align: right;
}
.importance-row {
display: flex;
align-items: center;
gap: 12px;
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid var(--tpc-beige-light);
}
.importance-label {
font-size: 0.8em;
color: var(--tpc-tobacco-muted);
font-weight: 500;
}
.importance-slider {
flex: 1;
height: 4px;
min-width: 0;
}
.importance-value {
font-size: 0.85em;
font-weight: 600;
color: var(--tpc-tobacco);
min-width: 60px;
text-align: right;
}
.stars {
color: var(--tpc-brick);
}
.slider {
width: 100%;
height: 8px;
border-radius: 4px;
background: var(--tpc-beige-light);
outline: none;
-webkit-appearance: none;
appearance: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--tpc-brick);
cursor: pointer;
border: 3px solid var(--tpc-paper);
box-shadow: 0 2px 4px rgba(0,0,0,0.15);
transition: all 0.2s;
}
.slider::-webkit-slider-thumb:hover {
transform: scale(1.15);
box-shadow: 0 3px 8px rgba(166, 61, 47, 0.3);
}
.slider::-moz-range-thumb {
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--tpc-brick);
cursor: pointer;
border: 3px solid var(--tpc-paper);
box-shadow: 0 2px 4px rgba(0,0,0,0.15);
transition: all 0.2s;
}
.slider::-moz-range-thumb:hover {
transform: scale(1.15);
box-shadow: 0 3px 8px rgba(166, 61, 47, 0.3);
}
.slider-range {
display: flex;
justify-content: space-between;
margin-top: 6px;
font-size: 0.8em;
color: var(--tpc-tobacco-muted);
gap: 8px;
}
/* Chart Section */
.chart-section {
background: var(--tpc-paper);
border: 1px solid var(--tpc-beige);
border-radius: 12px;
padding: 32px;
min-width: 0;
}
.chart-wrapper {
position: relative;
width: 100%;
max-width: 100%;
display: flex;
justify-content: center;
margin-bottom: 12px;
}
.chart-wrapper canvas {
display: block;
/* dimensioni gestite interamente in JS — no override CSS */
}
.container .chart-legend-note {
font-family: var(--tpc-font-body);
text-align: center;
font-size: 0.8em;
color: var(--tpc-tobacco-muted);
margin: 0 0 20px;
max-width: none;
padding: 0;
line-height: 1.6;
}
/* Area list rows */
.area-row {
display: flex;
align-items: center;
gap: 16px;
padding: 10px 0;
border-bottom: 1px solid var(--tpc-beige-light);
}
.area-row:last-child {
border-bottom: none;
}
.area-row-name {
flex: 1;
min-width: 0;
overflow-wrap: break-word;
font-size: 0.95em;
color: var(--tpc-tobacco);
font-weight: 500;
}
.area-row-stars {
font-size: 1em;
color: var(--tpc-brick);
letter-spacing: 1px;
min-width: 70px;
text-align: center;
}
.area-row-value {
font-family: var(--tpc-font-display);
font-size: 1.4em;
font-weight: 500;
color: var(--tpc-tobacco);
min-width: 52px;
text-align: right;
}
.area-row-value small {
font-size: 0.55em;
color: var(--tpc-tobacco-muted);
}
/* Insights */
.insights {
background: var(--tpc-beige-light);
border: 1px solid var(--tpc-beige);
border-radius: 12px;
padding: 32px;
margin-bottom: 24px;
}
.container .insights-title {
font-family: var(--tpc-font-display);
font-size: 1.3em;
font-weight: 500;
color: var(--tpc-tobacco);
margin: 0 0 20px;
max-width: none;
}
.insight-item {
background: var(--tpc-paper);
border: 1px solid var(--tpc-beige);
border-radius: 8px;
padding: 16px 20px;
margin-bottom: 12px;
}
.insight-item:last-child {
margin-bottom: 0;
}
.insight-item.insight-urgent {
border-color: #c0473a;
background: rgba(166, 61, 47, 0.05);
}
.insight-item.insight-moderate {
border-color: #c4973a;
background: rgba(196, 151, 58, 0.05);
}
.insight-item.insight-strong {
border-color: #7d9e7a;
background: rgba(125, 158, 122, 0.07);
}
.insight-header {
font-weight: 600;
color: var(--tpc-tobacco);
margin-bottom: 6px;
font-size: 0.95em;
}
.insight-text {
color: var(--tpc-tobacco-soft);
font-size: 0.9em;
line-height: 1.6;
}
.insight-icon {
margin-right: 6px;
}
/* Actions */
.actions {
display: flex;
gap: 16px;
justify-content: center;
flex-wrap: wrap;
}
.action-btn {
padding: 14px 32px;
font-size: 0.95em;
font-weight: 600;
border: none;
cursor: pointer;
transition: all 0.2s;
border-radius: 8px;
font-family: var(--tpc-font-body);
}
.btn-primary {
background: var(--tpc-brick);
color: var(--tpc-paper);
}
.btn-primary:hover {
background: var(--tpc-brick-deep);
}
.btn-secondary {
background: transparent;
color: var(--tpc-tobacco);
border: 1px solid var(--tpc-beige);
}
.btn-secondary:hover {
background: var(--tpc-beige-light);
}
/* Firma discreta — in fondo allo strumento */
.container .tool-credit {
font-family: var(--tpc-font-body);
text-align: center;
font-size: 0.8em;
color: var(--tpc-tobacco-muted);
max-width: none;
margin: 32px 0 0;
padding: 20px 0 0;
line-height: 1.6;
border-top: 1px solid var(--tpc-beige);
hyphens: manual;
-webkit-hyphens: manual;
}
.container .tool-credit a {
color: var(--tpc-tobacco-muted);
text-decoration: none;
font-weight: 600;
transition: color 0.2s;
}
.container .tool-credit a:hover {
color: var(--tpc-brick);
}
/* Responsive */
@media (max-width: 900px) {
.main-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 600px) {
.container {
padding: 20px 16px;
}
.header {
padding: 30px 24px 24px;
}
.sliders-section,
.chart-section,
.insights {
padding: 24px 20px;
}
.chart-wrapper {
min-height: 300px;
}
.container .intro p {
font-size: 0.9em;
}
}
.print-view {
display: none !important;
}
@media print {
@page {
size: A4 portrait;
margin: 1.2cm 1cm;
}
/* Nascondi tutto il cromo del tema GeneratePress */
.site-header,
.inside-header,
.main-navigation,
#site-navigation,
.nav-aligned-left,
.site-footer,
.footer-bar,
.footer-widgets,
.generate-back-to-top,
.site-info,
#wpadminbar,
.widget-area,
#right-sidebar,
.page-header,
.entry-header,
.breadcrumb-trail,
.aioseo-breadcrumbs {
display: none !important;
}
/* Nascondi il layout schermo del widget */
.container {
display: none !important;
}
/* Mostra solo il layout stampa */
.print-view {
display: block !important;
}
/* Azzera contenitori del tema così il print-view parte da pagina 1 */
body,
.site,
.site-content,
.content-area,
.site-main,
.entry-content,
.inside-article {
margin: 0 !important;
padding: 0 !important;
width: 100% !important;
max-width: 100% !important;
background: #fff !important;
display: block !important;
}
}
/* Stili interni del print-view */
.pv-header {
border: 2px solid #5c4a3d;
padding: 10px 14px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: baseline;
}
.pv-header-title {
font-family: Charter, Cambria, Georgia, serif;
font-size: 1.3em;
font-weight: 400;
color: #5c4a3d;
}
.pv-header-brand {
font-size: 0.7em;
color: #8a7a6d;
}
.pv-body {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-bottom: 10px;
}
/* Griglia valori aree */
.pv-values {
border: 1px solid #d4c8b8;
padding: 8px 10px;
}
.pv-section-label {
font-size: 0.65em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #8a7a6d;
margin-bottom: 6px;
padding-bottom: 4px;
border-bottom: 1px solid #e8e0d4;
}
.pv-area-row {
display: flex;
align-items: center;
gap: 6px;
padding: 3px 0;
border-bottom: 1px dotted #e8e0d4;
font-size: 0.75em;
color: #5c4a3d;
}
.pv-area-row:last-child {
border-bottom: none;
}
.pv-area-name {
flex: 1;
}
.pv-area-val {
font-family: Charter, Cambria, Georgia, serif;
font-weight: 600;
color: #a63d2f;
min-width: 28px;
text-align: right;
}
/* Canvas stampa */
.pv-chart {
border: 1px solid #d4c8b8;
padding: 8px;
display: flex;
flex-direction: column;
align-items: center;
}
.pv-chart canvas {
display: block;
}
.pv-chart-note {
font-size: 0.6em;
color: #8a7a6d;
text-align: center;
margin-top: 4px;
}
/* Lista per importanza */
.pv-list {
border: 1px solid #d4c8b8;
padding: 8px 10px;
}
.pv-list-row {
display: flex;
align-items: center;
gap: 10px;
padding: 4px 0;
border-bottom: 1px solid #f0ebe4;
font-size: 0.75em;
color: #5c4a3d;
}
.pv-list-row:last-child {
border-bottom: none;
}
.pv-list-name {
flex: 1;
}
.pv-list-stars {
color: #a63d2f;
letter-spacing: 1px;
min-width: 58px;
}
.pv-list-val {
font-family: Charter, Cambria, Georgia, serif;
font-size: 1.1em;
font-weight: 500;
min-width: 36px;
text-align: right;
}
.pv-list-val small {
font-size: 0.65em;
color: #8a7a6d;
}
.pv-footer {
text-align: center;
font-size: 0.65em;
color: #8a7a6d;
margin-top: 8px;
padding-top: 6px;
border-top: 1px solid #e8e0d4;
}
</style>
<div class="container">
<!-- Header -->
<div class="header">
<h1>🎯 Ruota della Vita</h1>
<p class="header-subtitle">Valuta il tuo equilibrio in 8 aree fondamentali della tua vita</p>
</div>
<!-- Intro -->
<div class="intro">
<p><strong>Come funziona:</strong> Valuta ogni area della tua vita da 1 (molto insoddisfatto) a 10 (pienamente soddisfatto). Poi indica quanto è <em>importante</em> per te quella specifica area (da 1 a 5 stelle).</p>
<p><strong>Perché la media pesata?</strong> Non tutte le aree hanno la stessa importanza per tutti. La media pesata tiene conto delle tue priorità personali, dandoti una visione più accurata del tuo benessere complessivo.</p>
<p>Non esistono risposte giuste o sbagliate: l'importante è essere onesti con te stesso. Usa questa valutazione come punto di partenza per identificare le aree su cui lavorare.</p>
</div>
<!-- Main Grid -->
<div class="main-grid">
<!-- Sliders -->
<div class="sliders-section">
<h2 class="section-title">Valuta ogni area</h2>
<div class="area-slider">
<div class="slider-header">
<span class="slider-label"><span class="slider-emoji">💪</span> Salute fisica</span>
<span class="slider-value" id="val-health">5</span>
</div>
<input type="range" class="slider" id="health" min="1" max="10" value="5">
<div class="slider-range">
<span>1 - Pessima</span>
<span>10 - Ottima</span>
</div>
<div class="importance-row">
<span class="importance-label">Importanza:</span>
<input type="range" class="slider importance-slider" id="imp-health" min="1" max="5" value="3">
<span class="importance-value"><span class="stars" id="star-health">★★★☆☆</span></span>
</div>
</div>
<div class="area-slider">
<div class="slider-header">
<span class="slider-label"><span class="slider-emoji">🧠</span> Crescita personale</span>
<span class="slider-value" id="val-growth">5</span>
</div>
<input type="range" class="slider" id="growth" min="1" max="10" value="5">
<div class="slider-range">
<span>1 - Stagnante</span>
<span>10 - In evoluzione</span>
</div>
<div class="importance-row">
<span class="importance-label">Importanza:</span>
<input type="range" class="slider importance-slider" id="imp-growth" min="1" max="5" value="3">
<span class="importance-value"><span class="stars" id="star-growth">★★★☆☆</span></span>
</div>
</div>
<div class="area-slider">
<div class="slider-header">
<span class="slider-label"><span class="slider-emoji">❤️</span> Relazioni & Amore</span>
<span class="slider-value" id="val-relationships">5</span>
</div>
<input type="range" class="slider" id="relationships" min="1" max="10" value="5">
<div class="slider-range">
<span>1 - Insoddisfacente</span>
<span>10 - Appagante</span>
</div>
<div class="importance-row">
<span class="importance-label">Importanza:</span>
<input type="range" class="slider importance-slider" id="imp-relationships" min="1" max="5" value="3">
<span class="importance-value"><span class="stars" id="star-relationships">★★★☆☆</span></span>
</div>
</div>
<div class="area-slider">
<div class="slider-header">
<span class="slider-label"><span class="slider-emoji">👨👩👧👦</span> Famiglia</span>
<span class="slider-value" id="val-family">5</span>
</div>
<input type="range" class="slider" id="family" min="1" max="10" value="5">
<div class="slider-range">
<span>1 - Conflittuale</span>
<span>10 - Armoniosa</span>
</div>
<div class="importance-row">
<span class="importance-label">Importanza:</span>
<input type="range" class="slider importance-slider" id="imp-family" min="1" max="5" value="3">
<span class="importance-value"><span class="stars" id="star-family">★★★☆☆</span></span>
</div>
</div>
<div class="area-slider">
<div class="slider-header">
<span class="slider-label"><span class="slider-emoji">💼</span> Carriera & Lavoro</span>
<span class="slider-value" id="val-career">5</span>
</div>
<input type="range" class="slider" id="career" min="1" max="10" value="5">
<div class="slider-range">
<span>1 - Insoddisfacente</span>
<span>10 - Realizzante</span>
</div>
<div class="importance-row">
<span class="importance-label">Importanza:</span>
<input type="range" class="slider importance-slider" id="imp-career" min="1" max="5" value="3">
<span class="importance-value"><span class="stars" id="star-career">★★★☆☆</span></span>
</div>
</div>
<div class="area-slider">
<div class="slider-header">
<span class="slider-label"><span class="slider-emoji">💰</span> Finanze</span>
<span class="slider-value" id="val-finances">5</span>
</div>
<input type="range" class="slider" id="finances" min="1" max="10" value="5">
<div class="slider-range">
<span>1 - Precarie</span>
<span>10 - Stabili</span>
</div>
<div class="importance-row">
<span class="importance-label">Importanza:</span>
<input type="range" class="slider importance-slider" id="imp-finances" min="1" max="5" value="3">
<span class="importance-value"><span class="stars" id="star-finances">★★★☆☆</span></span>
</div>
</div>
<div class="area-slider">
<div class="slider-header">
<span class="slider-label"><span class="slider-emoji">🏡</span> Ambiente & Casa</span>
<span class="slider-value" id="val-environment">5</span>
</div>
<input type="range" class="slider" id="environment" min="1" max="10" value="5">
<div class="slider-range">
<span>1 - Inadeguato</span>
<span>10 - Ideale</span>
</div>
<div class="importance-row">
<span class="importance-label">Importanza:</span>
<input type="range" class="slider importance-slider" id="imp-environment" min="1" max="5" value="3">
<span class="importance-value"><span class="stars" id="star-environment">★★★☆☆</span></span>
</div>
</div>
<div class="area-slider">
<div class="slider-header">
<span class="slider-label"><span class="slider-emoji">🎭</span> Svago & Divertimento</span>
<span class="slider-value" id="val-fun">5</span>
</div>
<input type="range" class="slider" id="fun" min="1" max="10" value="5">
<div class="slider-range">
<span>1 - Assente</span>
<span>10 - Abbondante</span>
</div>
<div class="importance-row">
<span class="importance-label">Importanza:</span>
<input type="range" class="slider importance-slider" id="imp-fun" min="1" max="5" value="3">
<span class="importance-value"><span class="stars" id="star-fun">★★★☆☆</span></span>
</div>
</div>
</div>
<!-- Chart -->
<div class="chart-section">
<h2 class="section-title">La tua ruota</h2>
<div class="chart-wrapper">
<canvas id="wheelCanvas"></canvas>
</div>
<p class="chart-legend-note">
🔵 Ampiezza spicchio = importanza | 📏 Raggio = soddisfazione | 🟢 verde ≥7.5 · 🟤 medio · 🔴 <5
</p>
</div>
</div>
<!-- Lista aree -->
<div class="insights">
<h3 class="insights-title">Aree per importanza</h3>
<div id="insights-container"></div>
</div>
<!-- Actions -->
<div class="actions">
<button class="action-btn btn-secondary" onclick="resetWheel()">↻ Azzera valutazione</button>
<button class="action-btn btn-primary" onclick="window.print()">🖨️ Stampa risultato</button>
</div>
<!-- Firma discreta -->
<p class="tool-credit">
Strumento di <a href="https://thepsychology.club" target="_blank" rel="noopener">The Psychology Club</a> · realizzato da <a href="https://maurizioiengo.it" target="_blank" rel="noopener">Maurizio Iengo</a>
</p>
</div><!-- fine .container -->
<!-- Layout dedicato per la stampa -->
<div class="print-view" id="printView">
<div class="pv-header">
<span class="pv-header-title">🎯 Ruota della Vita</span>
<span class="pv-header-brand">The Psychology Club · thepsychology.club</span>
</div>
<div class="pv-body">
<!-- Griglia valori -->
<div class="pv-values">
<div class="pv-section-label">Valutazioni</div>
<div id="pv-values-grid"></div>
</div>
<!-- Canvas stampa -->
<div class="pv-chart">
<div class="pv-section-label" style="width:100%">La tua ruota</div>
<canvas id="printCanvas"></canvas>
<p class="pv-chart-note">Ampiezza = importanza · Raggio = soddisfazione · 🟢≥7.5 · 🟤medio · 🔴<5</p>
</div>
</div>
<!-- Lista ordinata per importanza -->
<div class="pv-list">
<div class="pv-section-label">Aree per importanza</div>
<div id="pv-list"></div>
</div>
<div class="pv-footer">The Psychology Club | thepsychology.club</div>
</div>
<script>
const areas = [
{ id: 'health', label: 'Salute fisica', emoji: '💪', short: 'Salute' },
{ id: 'growth', label: 'Crescita personale', emoji: '🧠', short: 'Crescita' },
{ id: 'relationships', label: 'Relazioni & Amore', emoji: '❤️', short: 'Relazioni' },
{ id: 'family', label: 'Famiglia', emoji: '👨👩👧👦', short: 'Famiglia' },
{ id: 'career', label: 'Carriera & Lavoro', emoji: '💼', short: 'Carriera' },
{ id: 'finances', label: 'Finanze', emoji: '💰', short: 'Finanze' },
{ id: 'environment', label: 'Ambiente & Casa', emoji: '🏡', short: 'Casa' },
{ id: 'fun', label: 'Svago & Divert.', emoji: '🎭', short: 'Svago' }
];
// ─── COLORI ───────────────────────────────────────────
function sliceColor(value, alpha) {
if (value >= 7.5) return `rgba(125, 158, 122, ${alpha})`; // verde
if (value >= 5) return `rgba(92, 74, 61, ${alpha})`; // tobacco
return `rgba(166, 61, 47, ${alpha})`; // brick
}
// ─── GRAFICO CUSTOM ───────────────────────────────────
function drawWheel(values, weights) {
const canvas = document.getElementById('wheelCanvas');
const wrapper = canvas.parentElement;
const dpr = window.devicePixelRatio || 1;
// Canvas sempre QUADRATO — lato = min(larghezza wrapper, 440)
const side = Math.min(wrapper.clientWidth, 440);
canvas.style.width = side + 'px';
canvas.style.height = side + 'px';
canvas.width = side * dpr;
canvas.height = side * dpr;
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr);
ctx.clearRect(0, 0, side, side);
const pad = 64;
const cx = side / 2;
const cy = side / 2;
const maxR = side / 2 - pad;
const totalW = weights.reduce((a, b) => a + b, 0);
const sysFont = "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif";
// ── Cerchi concentrici di riferimento ──
[2, 4, 6, 8, 10].forEach(lvl => {
const r = (lvl / 10) * maxR;
ctx.beginPath();
ctx.arc(cx, cy, r, 0, 2 * Math.PI);
ctx.strokeStyle = lvl === 10 ? '#c4b8a8' : '#e0d8cc';
ctx.lineWidth = lvl === 10 ? 1.5 : 1;
ctx.setLineDash(lvl === 10 ? [] : [3, 4]);
ctx.stroke();
ctx.setLineDash([]);
// numeri sull'asse destro
ctx.fillStyle = '#a09080';
ctx.font = `10px ${sysFont}`;
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
ctx.fillText(lvl, cx + r + 3, cy - 5);
});
// ── Spicchi ──
let angle = -Math.PI / 2; // parte dall'alto
areas.forEach((area, i) => {
const v = values[i];
const w = weights[i];
const sweep = (w / totalW) * 2 * Math.PI;
const r = (v / 10) * maxR;
const mid = angle + sweep / 2;
// Fill spicchio
ctx.beginPath();
ctx.moveTo(cx, cy);
if (r > 0) ctx.arc(cx, cy, r, angle, angle + sweep);
ctx.closePath();
ctx.fillStyle = sliceColor(v, 0.82);
ctx.fill();
// Bordo spicchio
ctx.beginPath();
ctx.moveTo(cx, cy);
if (r > 0) ctx.arc(cx, cy, r, angle, angle + sweep);
ctx.closePath();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
// Linea radiale separatrice
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(cx + Math.cos(angle) * maxR, cy + Math.sin(angle) * maxR);
ctx.strokeStyle = '#e8e0d4';
ctx.lineWidth = 1;
ctx.stroke();
// ── Etichetta esterna ──
const labelR = maxR + 20;
const lx = cx + Math.cos(mid) * labelR;
const ly = cy + Math.sin(mid) * labelR;
const align = Math.cos(mid) > 0.1 ? 'left'
: Math.cos(mid) < -0.1 ? 'right' : 'center';
// emoji
ctx.font = `15px ${sysFont}`;
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
ctx.fillText(area.emoji, lx, ly);
// valore /10
ctx.font = `bold 11px ${sysFont}`;
ctx.fillStyle = sliceColor(v, 1);
ctx.textBaseline = 'top';
ctx.fillText(v + '/10', lx, ly + 1);
// nome abbreviato (piccolo, sotto il valore)
ctx.font = `10px ${sysFont}`;
ctx.fillStyle = '#8a7a6d';
ctx.textBaseline = 'top';
ctx.fillText(area.short, lx, ly + 14);
angle += sweep;
});
// Ultima linea radiale di chiusura
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(cx + Math.cos(angle) * maxR, cy + Math.sin(angle) * maxR);
ctx.strokeStyle = '#e8e0d4';
ctx.lineWidth = 1;
ctx.stroke();
// Centro: media pesata
const wAvg = calcWeightedAvg(values, weights);
ctx.beginPath();
ctx.arc(cx, cy, 28, 0, 2 * Math.PI);
ctx.fillStyle = '#fffdf8';
ctx.fill();
ctx.strokeStyle = '#d4c8b8';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.fillStyle = '#5c4a3d';
ctx.font = `bold 13px ${sysFont}`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(wAvg.toFixed(1), cx, cy - 5);
ctx.font = `9px ${sysFont}`;
ctx.fillStyle = '#a09080';
ctx.fillText('media', cx, cy + 7);
}
// ─── MEDIA PESATA (scala 1-10) ────────────────────────
function calcWeightedAvg(values, weights) {
const num = values.reduce((s, v, i) => s + v * weights[i], 0);
const den = weights.reduce((a, b) => a + b, 0);
return num / den;
}
// ─── UPDATE PRINCIPALE ────────────────────────────────
function updateChart() {
const values = areas.map(a => parseInt(document.getElementById(a.id).value));
const weights = areas.map(a => parseInt(document.getElementById('imp-' + a.id).value));
drawWheel(values, weights);
updateStats(values, weights);
updateInsights(values, weights);
}
function initChart() {
updateChart();
}
function getStars(count) {
const filled = '★'.repeat(count);
const empty = '☆'.repeat(5 - count);
return filled + empty;
}
function updateImportance(areaId) {
const weight = parseInt(document.getElementById('imp-' + areaId).value);
document.getElementById('star-' + areaId).textContent = getStars(weight);
}
function updateStats(values, weights) {
// stats card rimosse — nessun aggiornamento necessario
}
function updateInsights(values, weights) {
const sorted = areas
.map((area, i) => ({ area, value: values[i], weight: weights[i] }))
.sort((a, b) => b.weight - a.weight || b.value - a.value);
const rows = sorted.map(it => {
const stars = '★'.repeat(it.weight) + '☆'.repeat(5 - it.weight);
return `<div class="area-row">
<span class="area-row-name">${it.area.emoji} ${it.area.label}</span>
<span class="area-row-stars">${stars}</span>
<span class="area-row-value">${it.value}<small>/10</small></span>
</div>`;
}).join('');
document.getElementById('insights-container').innerHTML = rows;
}
function resetWheel() {
if (confirm('Sei sicuro di voler azzerare tutte le valutazioni?')) {
areas.forEach(a => {
document.getElementById(a.id).value = 5;
document.getElementById('val-' + a.id).textContent = 5;
document.getElementById('imp-' + a.id).value = 3;
updateImportance(a.id);
});
updateChart();
}
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
initChart();
// Add event listeners to all sliders
areas.forEach(area => {
// Value slider
const slider = document.getElementById(area.id);
const valueDisplay = document.getElementById('val-' + area.id);
slider.addEventListener('input', function() {
valueDisplay.textContent = this.value;
updateChart();
});
// Importance slider
const impSlider = document.getElementById('imp-' + area.id);
impSlider.addEventListener('input', function() {
updateImportance(area.id);
updateChart();
});
// Initialize stars
updateImportance(area.id);
});
// Initial update
updateChart();
// Ridisegna su resize finestra
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(updateChart, 100);
});
// Ridisegna prima della stampa con dimensioni corrette
// Popola e ridisegna per la stampa
window.addEventListener('beforeprint', () => {
const values = areas.map(a => parseInt(document.getElementById(a.id).value));
const weights = areas.map(a => parseInt(document.getElementById('imp-' + a.id).value));
// Griglia valori (2 colonne)
const valGrid = document.getElementById('pv-values-grid');
valGrid.innerHTML = areas.map(a => {
const v = parseInt(document.getElementById(a.id).value);
const w = parseInt(document.getElementById('imp-' + a.id).value);
const stars = '★'.repeat(w) + '☆'.repeat(5 - w);
return `<div class="pv-area-row">
<span class="pv-area-name">${a.emoji} ${a.label}</span>
<span style="font-size:0.8em;color:#a63d2f">${stars}</span>
<span class="pv-area-val">${v}<small>/10</small></span>
</div>`;
}).join('');
// Lista ordinata per importanza
const sorted = areas
.map((a, i) => ({ a, v: values[i], w: weights[i] }))
.sort((x, y) => y.w - x.w || y.v - x.v);
document.getElementById('pv-list').innerHTML = sorted.map(it => {
const stars = '★'.repeat(it.w) + '☆'.repeat(5 - it.w);
return `<div class="pv-list-row">
<span class="pv-list-name">${it.a.emoji} ${it.a.label}</span>
<span class="pv-list-stars">${stars}</span>
<span class="pv-list-val">${it.v}<small>/10</small></span>
</div>`;
}).join('');
// Disegna canvas stampa (quadrato 280px)
const printCanvas = document.getElementById('printCanvas');
const side = 280;
const dpr = window.devicePixelRatio || 1;
printCanvas.style.width = side + 'px';
printCanvas.style.height = side + 'px';
printCanvas.width = side * dpr;
printCanvas.height = side * dpr;
const ctx = printCanvas.getContext('2d');
ctx.scale(dpr, dpr);
ctx.clearRect(0, 0, side, side);
const pad = 52;
const cx = side / 2;
const cy = side / 2;
const maxR = side / 2 - pad;
const totalW = weights.reduce((a, b) => a + b, 0);
const sysFont = "system-ui, sans-serif";
// Cerchi
[2,4,6,8,10].forEach(lvl => {
const r = (lvl / 10) * maxR;
ctx.beginPath();
ctx.arc(cx, cy, r, 0, 2*Math.PI);
ctx.strokeStyle = lvl === 10 ? '#c4b8a8' : '#e0d8cc';
ctx.lineWidth = 1;
ctx.setLineDash(lvl === 10 ? [] : [2,3]);
ctx.stroke();
ctx.setLineDash([]);
});
// Spicchi
let angle = -Math.PI / 2;
areas.forEach((area, i) => {
const v = values[i], w = weights[i];
const sweep = (w / totalW) * 2 * Math.PI;
const r = (v / 10) * maxR;
const mid = angle + sweep / 2;
ctx.beginPath();
ctx.moveTo(cx, cy);
if (r > 0) ctx.arc(cx, cy, r, angle, angle + sweep);
ctx.closePath();
ctx.fillStyle = sliceColor(v, 0.82);
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(cx + Math.cos(angle)*maxR, cy + Math.sin(angle)*maxR);
ctx.strokeStyle = '#e8e0d4';
ctx.lineWidth = 0.8;
ctx.stroke();
// Etichette
const lr = maxR + 16;
const lx = cx + Math.cos(mid) * lr;
const ly = cy + Math.sin(mid) * lr;
ctx.font = `11px ${sysFont}`;
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
ctx.fillStyle = '#5c4a3d';
ctx.fillText(area.emoji, lx, ly);
ctx.font = `bold 9px ${sysFont}`;
ctx.fillStyle = sliceColor(v, 1);
ctx.textBaseline = 'top';
ctx.fillText(v + '/10', lx, ly + 1);
angle += sweep;
});
// Centro media pesata
const wAvg = calcWeightedAvg(values, weights);
ctx.beginPath();
ctx.arc(cx, cy, 22, 0, 2*Math.PI);
ctx.fillStyle = '#fffdf8';
ctx.fill();
ctx.strokeStyle = '#d4c8b8';
ctx.lineWidth = 1;
ctx.stroke();
ctx.fillStyle = '#5c4a3d';
ctx.font = `bold 11px ${sysFont}`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(wAvg.toFixed(1), cx, cy - 4);
ctx.font = `8px ${sysFont}`;
ctx.fillStyle = '#a09080';
ctx.fillText('media', cx, cy + 6);
});
window.addEventListener('afterprint', () => {
updateChart(); // ripristina dimensioni schermo
});
});
</script>