|
|
| Baris 13: |
Baris 13: |
|
| |
|
| <!--2. TEMPAT KODE SUMBER (Hanya paste HTML Anda di sini)--> | | <!--2. TEMPAT KODE SUMBER (Hanya paste HTML Anda di sini)--> |
| <textarea style="display: none;"><!-- ↓↓↓ PASTE KODE HTML APLIKASI ANDA DI BAWAH BARIS INI ↓↓↓ --> | | <textarea style="display: none;"> |
| | <!-- ↓↓↓ PASTE KODE HTML APLIKASI ANDA DI BAWAH BARIS INI ↓↓↓ --> |
|
| |
|
| <!DOCTYPE html>
| |
| <html lang="id">
| |
| <head>
| |
| <meta charset="UTF-8">
| |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
| |
| <title>Visualisasi Pecahan, Desimal & Persen</title>
| |
| <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&family=Space+Mono:wght@700&display=swap" rel="stylesheet">
| |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
| |
| <style>
| |
| :root {
| |
| --primary: #0284C7; /* Light Blue 600 */
| |
| --primary-light: #38BDF8; /* Light Blue 400 */
| |
| --secondary: #059669; /* Emerald 600 */
| |
| --secondary-light: #34D399; /* Emerald 400 */
| |
| --accent: #D97706; /* Amber 600 */
| |
| --accent-light: #FBBF24; /* Amber 400 */
| |
| --danger: #EF4444;
| |
| --dark-bg: #0F172A; /* Slate 900 */
| |
| --panel-bg: rgba(30, 41, 59, 0.7); /* Slate 800 */
| |
| --glass-border: rgba(255, 255, 255, 0.1);
| |
| --text-main: #F8FAFC;
| |
| --text-muted: #94A3B8;
| |
| }
| |
|
| |
| * {
| |
| margin: 0;
| |
| padding: 0;
| |
| box-sizing: border-box;
| |
| font-family: 'Outfit', sans-serif;
| |
| touch-action: manipulation;
| |
| }
| |
|
| |
| body {
| |
| background: linear-gradient(135deg, var(--dark-bg), #082F49);
| |
| color: var(--text-main);
| |
| height: 100vh;
| |
| display: flex;
| |
| flex-direction: column;
| |
| overflow: hidden;
| |
| }
| |
|
| |
| /* Header */
| |
| header {
| |
| background: rgba(15, 23, 42, 0.8);
| |
| backdrop-filter: blur(10px);
| |
| border-bottom: 1px solid var(--glass-border);
| |
| padding: 10px 20px;
| |
| display: flex;
| |
| align-items: center;
| |
| justify-content: center;
| |
| gap: 12px;
| |
| z-index: 10;
| |
| height: 50px;
| |
| }
| |
|
| |
| header h1 {
| |
| font-size: 1.1rem;
| |
| font-weight: 800;
| |
| background: linear-gradient(to right, #7DD3FC, #6EE7B7);
| |
| -webkit-background-clip: text;
| |
| -webkit-text-fill-color: transparent;
| |
| text-transform: uppercase;
| |
| letter-spacing: 1px;
| |
| }
| |
|
| |
| header i {
| |
| color: #7DD3FC;
| |
| font-size: 1.3rem;
| |
| }
| |
|
| |
| /* Main Container */
| |
| .main-container {
| |
| flex: 1;
| |
| display: flex;
| |
| flex-direction: column;
| |
| padding: 12px;
| |
| gap: 12px;
| |
| height: calc(100vh - 50px);
| |
| }
| |
|
| |
| /* Visual Panel */
| |
| .visual-panel {
| |
| flex: 1.3;
| |
| background: var(--panel-bg);
| |
| border: 1px solid var(--glass-border);
| |
| border-radius: 20px;
| |
| position: relative;
| |
| overflow: hidden;
| |
| display: flex;
| |
| box-shadow: 0 10px 30px rgba(0,0,0,0.3);
| |
| background-image: radial-gradient(circle at 50% 50%, rgba(2, 132, 199, 0.1) 0%, transparent 70%),
| |
| repeating-linear-gradient(45deg, rgba(255,255,255,0.02) 0px, rgba(255,255,255,0.02) 20px, transparent 20px, transparent 40px);
| |
| }
| |
|
| |
| .visual-content {
| |
| display: flex;
| |
| flex-direction: column;
| |
| align-items: center;
| |
| justify-content: space-evenly;
| |
| width: 100%;
| |
| height: 100%;
| |
| padding: 10px;
| |
| }
| |
|
| |
| .grid-container {
| |
| position: relative;
| |
| width: 200px;
| |
| height: 200px;
| |
| }
| |
|
| |
| .grid-bg-glow {
| |
| position: absolute;
| |
| top: 50%;
| |
| left: 50%;
| |
| transform: translate(-50%, -50%);
| |
| width: 150%;
| |
| height: 150%;
| |
| background: radial-gradient(circle at center, rgba(52, 211, 153, 0.25) 0%, transparent 65%);
| |
| pointer-events: none;
| |
| transition: opacity 0.2s;
| |
| opacity: 0;
| |
| }
| |
|
| |
| .grid-100 {
| |
| position: absolute;
| |
| top: 0; left: 0; right: 0; bottom: 0;
| |
| display: grid;
| |
| grid-template-columns: repeat(10, 1fr);
| |
| grid-template-rows: repeat(10, 1fr);
| |
| gap: 2px;
| |
| }
| |
|
| |
| .grid-cell {
| |
| background: rgba(255,255,255,0.05);
| |
| border-radius: 3px;
| |
| }
| |
|
| |
| .value-cards {
| |
| display: flex;
| |
| gap: 10px;
| |
| width: 100%;
| |
| justify-content: center;
| |
| padding: 0 5px;
| |
| }
| |
|
| |
| .value-card {
| |
| background: rgba(15, 23, 42, 0.6);
| |
| backdrop-filter: blur(10px);
| |
| border: 1px solid var(--glass-border);
| |
| border-radius: 12px;
| |
| padding: 10px 5px;
| |
| flex: 1;
| |
| display: flex;
| |
| flex-direction: column;
| |
| align-items: center;
| |
| justify-content: center;
| |
| box-shadow: 0 4px 15px rgba(0,0,0,0.2);
| |
| transition: transform 0.2s, border-color 0.2s;
| |
| }
| |
|
| |
| .card-label {
| |
| font-size: 0.65rem;
| |
| text-transform: uppercase;
| |
| letter-spacing: 1px;
| |
| color: var(--text-muted);
| |
| margin-bottom: 5px;
| |
| }
| |
|
| |
| .card-value {
| |
| font-family: 'Space Mono', monospace;
| |
| font-size: 1.3rem;
| |
| font-weight: 700;
| |
| }
| |
|
| |
| .frac-display {
| |
| display: flex;
| |
| flex-direction: column;
| |
| align-items: center;
| |
| font-family: 'Space Mono', monospace;
| |
| font-weight: 700;
| |
| font-size: 1.2rem;
| |
| line-height: 1;
| |
| color: var(--primary-light);
| |
| }
| |
|
| |
| .frac-line {
| |
| width: 100%;
| |
| min-width: 20px;
| |
| height: 2px;
| |
| background: currentColor;
| |
| margin: 3px 0;
| |
| border-radius: 2px;
| |
| }
| |
|
| |
| /* Control Panel */
| |
| .control-panel {
| |
| flex: 0.7;
| |
| display: flex;
| |
| flex-direction: column;
| |
| background: var(--panel-bg);
| |
| border: 1px solid var(--glass-border);
| |
| border-radius: 20px;
| |
| padding: 12px;
| |
| }
| |
|
| |
| .tabs {
| |
| display: flex;
| |
| background: rgba(0,0,0,0.2);
| |
| border-radius: 10px;
| |
| padding: 4px;
| |
| margin-bottom: 12px;
| |
| }
| |
|
| |
| .tab-btn {
| |
| flex: 1;
| |
| padding: 8px;
| |
| background: transparent;
| |
| border: none;
| |
| color: var(--text-muted);
| |
| font-family: 'Outfit', sans-serif;
| |
| font-weight: 600;
| |
| font-size: 0.9rem;
| |
| cursor: pointer;
| |
| border-radius: 8px;
| |
| transition: all 0.3s;
| |
| display: flex;
| |
| justify-content: center;
| |
| align-items: center;
| |
| gap: 6px;
| |
| }
| |
|
| |
| .tab-btn.active {
| |
| background: var(--primary);
| |
| color: white;
| |
| box-shadow: 0 2px 10px rgba(2, 132, 199, 0.4);
| |
| }
| |
|
| |
| .mode-container {
| |
| flex: 1;
| |
| display: flex;
| |
| flex-direction: column;
| |
| gap: 10px;
| |
| }
| |
|
| |
| .slider-group {
| |
| background: rgba(255,255,255,0.05);
| |
| border: 1px solid var(--glass-border);
| |
| border-radius: 10px;
| |
| padding: 10px 12px;
| |
| }
| |
|
| |
| .slider-header {
| |
| display: flex;
| |
| justify-content: space-between;
| |
| align-items: center;
| |
| margin-bottom: 10px;
| |
| }
| |
|
| |
| .slider-label {
| |
| font-size: 0.85rem;
| |
| font-weight: 600;
| |
| color: var(--text-muted);
| |
| display: flex;
| |
| align-items: center;
| |
| gap: 6px;
| |
| }
| |
|
| |
| .slider-val-display {
| |
| font-family: 'Space Mono', monospace;
| |
| font-weight: 700;
| |
| font-size: 1rem;
| |
| color: white;
| |
| background: rgba(0,0,0,0.3);
| |
| padding: 2px 8px;
| |
| border-radius: 6px;
| |
| border: 1px solid var(--glass-border);
| |
| }
| |
|
| |
| input[type="range"] {
| |
| width: 100%;
| |
| -webkit-appearance: none;
| |
| height: 6px;
| |
| background: rgba(255,255,255,0.1);
| |
| border-radius: 3px;
| |
| outline: none;
| |
| }
| |
|
| |
| input[type="range"]::-webkit-slider-thumb {
| |
| -webkit-appearance: none;
| |
| width: 22px;
| |
| height: 22px;
| |
| border-radius: 50%;
| |
| background: white;
| |
| cursor: pointer;
| |
| box-shadow: 0 0 10px rgba(0,0,0,0.5);
| |
| transition: transform 0.1s;
| |
| }
| |
|
| |
| input[type="range"]::-webkit-slider-thumb:active {
| |
| transform: scale(1.2);
| |
| background: var(--secondary-light);
| |
| }
| |
|
| |
| .quick-presets {
| |
| margin-top: 2px;
| |
| }
| |
|
| |
| .preset-grid {
| |
| display: grid;
| |
| grid-template-columns: repeat(3, 1fr);
| |
| gap: 8px;
| |
| }
| |
|
| |
| .preset-btn {
| |
| background: rgba(255,255,255,0.05);
| |
| border: 1px solid rgba(255,255,255,0.1);
| |
| color: white;
| |
| font-family: 'Space Mono', monospace;
| |
| font-size: 0.95rem;
| |
| font-weight: 700;
| |
| padding: 8px 0;
| |
| border-radius: 8px;
| |
| cursor: pointer;
| |
| transition: all 0.2s;
| |
| }
| |
|
| |
| .preset-btn:active {
| |
| background: var(--primary);
| |
| transform: scale(0.95);
| |
| }
| |
|
| |
| .btn {
| |
| padding: 10px;
| |
| border: none;
| |
| border-radius: 10px;
| |
| font-size: 0.9rem;
| |
| font-weight: 700;
| |
| cursor: pointer;
| |
| transition: all 0.2s;
| |
| text-transform: uppercase;
| |
| letter-spacing: 0.5px;
| |
| display: flex;
| |
| align-items: center;
| |
| justify-content: center;
| |
| gap: 8px;
| |
| width: 100%;
| |
| }
| |
|
| |
| .btn-action {
| |
| background: linear-gradient(135deg, var(--primary), #0369A1);
| |
| color: white;
| |
| margin-top: auto;
| |
| box-shadow: 0 4px 15px rgba(2, 132, 199, 0.4);
| |
| }
| |
|
| |
| .btn-action:active { transform: translateY(2px); }
| |
|
| |
| /* Kuis Mode Styles */
| |
| .kuis-info {
| |
| text-align: center;
| |
| font-size: 0.9rem;
| |
| line-height: 1.4;
| |
| padding: 0 5px;
| |
| }
| |
|
| |
| .kuis-display-box {
| |
| background: rgba(0,0,0,0.3);
| |
| border: 1px dashed var(--glass-border);
| |
| border-radius: 12px;
| |
| padding: 10px;
| |
| display: flex;
| |
| justify-content: center;
| |
| align-items: center;
| |
| min-height: 70px;
| |
| margin: 5px 0;
| |
| }
| |
|
| |
| .kuis-text-val {
| |
| font-family: 'Space Mono', monospace;
| |
| font-size: 1.8rem;
| |
| font-weight: 700;
| |
| color: var(--accent-light);
| |
| }
| |
|
| |
| .input-group {
| |
| display: flex;
| |
| align-items: center;
| |
| background: rgba(0,0,0,0.4);
| |
| border: 1px solid var(--glass-border);
| |
| border-radius: 10px;
| |
| padding: 5px 15px;
| |
| }
| |
|
| |
| .input-group input {
| |
| flex: 1;
| |
| background: transparent;
| |
| border: none;
| |
| color: white;
| |
| font-family: 'Space Mono', monospace;
| |
| font-size: 1.4rem;
| |
| font-weight: bold;
| |
| text-align: center;
| |
| padding: 6px;
| |
| outline: none;
| |
| width: 100%;
| |
| }
| |
|
| |
| .input-group .unit {
| |
| font-size: 1.3rem;
| |
| font-weight: bold;
| |
| color: var(--text-muted);
| |
| }
| |
|
| |
| .action-area {
| |
| display: flex;
| |
| gap: 10px;
| |
| }
| |
|
| |
| .btn-check {
| |
| background: linear-gradient(135deg, var(--secondary), #047857);
| |
| color: white;
| |
| flex: 1.5;
| |
| box-shadow: 0 4px 15px rgba(5, 150, 105, 0.4);
| |
| }
| |
|
| |
| .btn-new {
| |
| background: rgba(255,255,255,0.1);
| |
| color: white;
| |
| border: 1px solid var(--glass-border);
| |
| flex: 1;
| |
| }
| |
|
| |
| /* Modals & Overlays */
| |
| .overlay {
| |
| position: fixed;
| |
| top: 0; left: 0; right: 0; bottom: 0;
| |
| background: rgba(0,0,0,0.8);
| |
| backdrop-filter: blur(5px);
| |
| display: flex;
| |
| justify-content: center;
| |
| align-items: center;
| |
| z-index: 1000;
| |
| opacity: 0;
| |
| pointer-events: none;
| |
| transition: opacity 0.3s;
| |
| }
| |
|
| |
| .overlay.show {
| |
| opacity: 1;
| |
| pointer-events: all;
| |
| }
| |
|
| |
| .modal-card {
| |
| background: var(--dark-bg);
| |
| border: 1px solid var(--glass-border);
| |
| border-radius: 20px;
| |
| width: 90%;
| |
| max-width: 450px;
| |
| max-height: 85vh;
| |
| display: flex;
| |
| flex-direction: column;
| |
| overflow: hidden;
| |
| transform: translateY(20px);
| |
| transition: transform 0.3s;
| |
| box-shadow: 0 15px 40px rgba(0,0,0,0.5);
| |
| }
| |
|
| |
| .overlay.show .modal-card {
| |
| transform: translateY(0);
| |
| }
| |
|
| |
| .modal-header {
| |
| background: rgba(255,255,255,0.05);
| |
| padding: 15px 20px;
| |
| display: flex;
| |
| justify-content: space-between;
| |
| align-items: center;
| |
| border-bottom: 1px solid var(--glass-border);
| |
| }
| |
|
| |
| .modal-header h2 {
| |
| font-size: 1.1rem;
| |
| color: var(--accent-light);
| |
| display: flex;
| |
| align-items: center;
| |
| gap: 10px;
| |
| }
| |
|
| |
| .btn-close-icon {
| |
| background: none;
| |
| border: none;
| |
| color: var(--text-muted);
| |
| font-size: 1.4rem;
| |
| cursor: pointer;
| |
| transition: color 0.2s;
| |
| padding: 5px;
| |
| }
| |
|
| |
| .btn-close-icon:hover { color: white; }
| |
|
| |
| .modal-body {
| |
| padding: 15px;
| |
| overflow-y: auto;
| |
| display: flex;
| |
| flex-direction: column;
| |
| gap: 12px;
| |
| }
| |
|
| |
| .step-card {
| |
| background: rgba(255,255,255,0.03);
| |
| border: 1px solid rgba(255,255,255,0.05);
| |
| border-radius: 12px;
| |
| padding: 15px;
| |
| }
| |
|
| |
| .step-title {
| |
| font-weight: 700;
| |
| color: var(--primary-light);
| |
| margin-bottom: 5px;
| |
| font-size: 0.95rem;
| |
| }
| |
|
| |
| .step-desc {
| |
| font-size: 0.85rem;
| |
| color: var(--text-muted);
| |
| margin-bottom: 10px;
| |
| line-height: 1.4;
| |
| }
| |
|
| |
| .step-math {
| |
| font-family: 'Space Mono', monospace;
| |
| font-size: 0.9rem;
| |
| line-height: 1.6;
| |
| background: rgba(0,0,0,0.3);
| |
| padding: 12px;
| |
| border-radius: 8px;
| |
| color: white;
| |
| }
| |
|
| |
| .highlight {
| |
| color: var(--secondary-light);
| |
| font-weight: bold;
| |
| }
| |
|
| |
| /* Status Modal */
| |
| .status-card {
| |
| background: var(--panel-bg);
| |
| border: 1px solid var(--glass-border);
| |
| padding: 25px;
| |
| border-radius: 20px;
| |
| text-align: center;
| |
| transform: scale(0.8);
| |
| transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
| |
| max-width: 80%;
| |
| box-shadow: 0 20px 50px rgba(0,0,0,0.5);
| |
| }
| |
|
| |
| .overlay.show .status-card { transform: scale(1); }
| |
|
| |
| .status-icon {
| |
| font-size: 3rem;
| |
| margin-bottom: 10px;
| |
| }
| |
|
| |
| .status-title {
| |
| font-size: 1.4rem;
| |
| font-weight: 800;
| |
| margin-bottom: 5px;
| |
| }
| |
|
| |
| .status-desc {
| |
| font-size: 0.9rem;
| |
| color: var(--text-muted);
| |
| margin-bottom: 15px;
| |
| line-height: 1.4;
| |
| }
| |
|
| |
| .correct-theme .status-icon, .correct-theme .status-title { color: var(--secondary-light); }
| |
| .wrong-theme .status-icon, .wrong-theme .status-title { color: var(--danger); }
| |
|
| |
| @media (min-width: 768px) {
| |
| .main-container {
| |
| flex-direction: row;
| |
| padding: 20px;
| |
| gap: 20px;
| |
| }
| |
| .visual-panel {
| |
| flex: 1.5;
| |
| }
| |
| .control-panel {
| |
| flex: 1;
| |
| padding: 20px;
| |
| gap: 15px;
| |
| }
| |
| .grid-container {
| |
| width: 280px;
| |
| height: 280px;
| |
| }
| |
| .value-card {
| |
| padding: 15px;
| |
| max-width: 140px;
| |
| }
| |
| .card-value {
| |
| font-size: 1.8rem;
| |
| }
| |
| .frac-display {
| |
| font-size: 1.6rem;
| |
| }
| |
| .btn {
| |
| padding: 12px;
| |
| font-size: 1rem;
| |
| }
| |
| }
| |
| </style>
| |
| </head>
| |
| <body>
| |
|
| |
| <header>
| |
| <i class="fas fa-chart-pie"></i>
| |
| <h1>Pecahan, Desimal & Persen</h1>
| |
| </header>
| |
|
| |
| <div class="main-container">
| |
| <!-- Visual Panel -->
| |
| <div class="visual-panel">
| |
| <div class="visual-content">
| |
| <div class="grid-container">
| |
| <div class="grid-bg-glow" id="gridGlow"></div>
| |
| <div class="grid-100" id="grid100">
| |
| <!-- Generated via JS -->
| |
| </div>
| |
| </div>
| |
|
| |
| <div class="value-cards">
| |
| <div class="value-card" id="cardPecahan">
| |
| <div class="card-label">Pecahan</div>
| |
| <div class="frac-display">
| |
| <div class="frac-num">1</div>
| |
| <div class="frac-line"></div>
| |
| <div class="frac-den">4</div>
| |
| </div>
| |
| </div>
| |
| <div class="value-card" id="cardDesimal">
| |
| <div class="card-label">Desimal</div>
| |
| <div class="card-value" style="color: var(--primary-light);" id="dispDes">0.25</div>
| |
| </div>
| |
| <div class="value-card" id="cardPersen">
| |
| <div class="card-label">Persen</div>
| |
| <div class="card-value" style="color: var(--accent-light);" id="dispPct">25%</div>
| |
| </div>
| |
| </div>
| |
| </div>
| |
| </div>
| |
|
| |
| <!-- Control Panel -->
| |
| <div class="control-panel">
| |
| <div class="tabs">
| |
| <button class="tab-btn active" id="tabEksplorasi" onclick="setMode('eksplorasi')"><i class="fas fa-search"></i> Eksplorasi</button>
| |
| <button class="tab-btn" id="tabKuis" onclick="setMode('kuis')"><i class="fas fa-gamepad"></i> Tantangan</button>
| |
| </div>
| |
|
| |
| <!-- Mode Eksplorasi -->
| |
| <div id="modeEksplorasi" class="mode-container">
| |
| <div class="slider-group">
| |
| <div class="slider-header">
| |
| <span class="slider-label"><i class="fas fa-sliders-h"></i> Atur Persentase</span>
| |
| <span class="slider-val-display" id="sliderValDisplay">25%</span>
| |
| </div>
| |
| <input type="range" id="mainSlider" min="0" max="100" step="0.5" value="25">
| |
| </div>
| |
|
| |
| <div class="quick-presets">
| |
| <div class="slider-label" style="margin-bottom:8px;"><i class="fas fa-bolt"></i> Tombol Cepat</div>
| |
| <div class="preset-grid">
| |
| <button class="preset-btn" onclick="setPreset(50)">1/2</button>
| |
| <button class="preset-btn" onclick="setPreset(25)">1/4</button>
| |
| <button class="preset-btn" onclick="setPreset(75)">3/4</button>
| |
| <button class="preset-btn" onclick="setPreset(20)">1/5</button>
| |
| <button class="preset-btn" onclick="setPreset(10)">1/10</button>
| |
| <button class="preset-btn" onclick="setPreset(12.5)">1/8</button>
| |
| </div>
| |
| </div>
| |
|
| |
| <button class="btn btn-action" onclick="showExplanation()">
| |
| <i class="fas fa-lightbulb"></i> Cara Mengubah
| |
| </button>
| |
| </div>
| |
|
| |
| <!-- Mode Kuis -->
| |
| <div id="modeKuis" class="mode-container" style="display:none;">
| |
| <div class="kuis-info" id="kuisQuestion">
| |
| Ubah bentuk di bawah menjadi <b>Persen</b>:
| |
| </div>
| |
|
| |
| <div class="kuis-display-box" id="kuisDisplayBox">
| |
| <!-- Placeholder -->
| |
| </div>
| |
|
| |
| <div class="input-group">
| |
| <input type="number" id="inputKuis" placeholder="Ketikan jawaban..." step="any">
| |
| <span class="unit" id="kuisUnit">%</span>
| |
| </div>
| |
| <div class="action-area" style="margin-top: auto;">
| |
| <button class="btn btn-new" onclick="randomKuis()">
| |
| <i class="fas fa-random"></i> Lewati
| |
| </button>
| |
| <button class="btn btn-check" onclick="cekKuis()">
| |
| <i class="fas fa-check"></i> Cek Jawaban
| |
| </button>
| |
| </div>
| |
| </div>
| |
| </div>
| |
| </div>
| |
|
| |
| <!-- Explanation Overlay -->
| |
| <div id="explanationOverlay" class="overlay">
| |
| <div class="modal-card">
| |
| <div class="modal-header">
| |
| <h2><i class="fas fa-lightbulb"></i> Cara Mengubah Bentuk</h2>
| |
| <button class="btn-close-icon" onclick="closeExplanation()"><i class="fas fa-times"></i></button>
| |
| </div>
| |
| <div class="modal-body">
| |
| <div class="step-card">
| |
| <div class="step-title">1. Pecahan ke Desimal</div>
| |
| <div class="step-desc">Ubah penyebut menjadi 10, 100, atau 1000. Atau bagikan langsung pembilang dengan penyebut.</div>
| |
| <div class="step-math">
| |
| <span class="highlight">1/4</span> = 1 ÷ 4 = <span class="highlight">0.25</span><br>
| |
| <span class="highlight">2/5</span> = (2×2) / (5×2) = 4/10 = <span class="highlight">0.4</span>
| |
| </div>
| |
| </div>
| |
| <div class="step-card">
| |
| <div class="step-title">2. Desimal ke Persen</div>
| |
| <div class="step-desc">Kalikan nilai desimal dengan 100 (geser koma ke kanan 2 kali) dan beri simbol %.</div>
| |
| <div class="step-math">
| |
| <span class="highlight">0.25</span> × 100 = <span class="highlight">25%</span><br>
| |
| <span class="highlight">0.4</span> × 100 = <span class="highlight">40%</span>
| |
| </div>
| |
| </div>
| |
| <div class="step-card">
| |
| <div class="step-title">3. Persen ke Pecahan</div>
| |
| <div class="step-desc">Tulis angka persen sebagai pembilang, dan 100 sebagai penyebut. Lalu sederhanakan.</div>
| |
| <div class="step-math">
| |
| <span class="highlight">25%</span> = 25/100 = <span class="highlight">1/4</span>
| |
| </div>
| |
| </div>
| |
| </div>
| |
| </div>
| |
| </div>
| |
|
| |
| <!-- Status Overlay -->
| |
| <div id="statusOverlay" class="overlay">
| |
| <div class="status-card">
| |
| <div class="status-icon" id="statusIcon"><i class="fas fa-check-circle"></i></div>
| |
| <div class="status-title" id="statusTitle">Tepat Sekali!</div>
| |
| <div class="status-desc" id="statusDesc">Jawabanmu sudah benar.</div>
| |
| <button class="btn btn-action" style="width: 100%; justify-content: center; margin-top: 10px;" onclick="closeStatus()">Lanjut</button>
| |
| </div>
| |
| </div>
| |
|
| |
| <script>
| |
| // Init Grid
| |
| const grid = document.getElementById('grid100');
| |
| for (let i = 0; i < 100; i++) {
| |
| const cell = document.createElement('div');
| |
| cell.className = 'grid-cell';
| |
| grid.appendChild(cell);
| |
| }
| |
|
| |
| // Logic Helpers
| |
| function gcd(a, b) {
| |
| return b ? gcd(b, a % b) : a;
| |
| }
| |
|
| |
| function getSimplifiedFraction(percent) {
| |
| let n = Math.round(percent * 10);
| |
| let d = 1000;
| |
| let divisor = gcd(n, d);
| |
| return { n: n / divisor, d: d / divisor };
| |
| }
| |
|
| |
| // Update Visuals Core Function
| |
| function updateVisuals(percent) {
| |
| // 1. Update Grid
| |
| const cells = grid.children;
| |
| for (let i = 0; i < 100; i++) {
| |
| if (percent >= i + 1) {
| |
| cells[i].style.background = 'var(--secondary-light)';
| |
| cells[i].style.boxShadow = '0 0 4px rgba(52, 211, 153, 0.4)';
| |
| } else if (percent > i) {
| |
| let fill = (percent - i) * 100;
| |
| cells[i].style.background = `linear-gradient(to right, var(--secondary-light) ${fill}%, rgba(255,255,255,0.05) ${fill}%)`;
| |
| cells[i].style.boxShadow = 'none';
| |
| } else {
| |
| cells[i].style.background = 'rgba(255,255,255,0.05)';
| |
| cells[i].style.boxShadow = 'none';
| |
| }
| |
| }
| |
| document.getElementById('gridGlow').style.opacity = (percent / 100) * 0.8;
| |
|
| |
| // 2. Format displays
| |
| let displayPercent = percent % 1 === 0 ? percent : percent.toFixed(1);
| |
| let decimalVal = percent / 100;
| |
|
| |
| document.getElementById('dispDes').innerText = decimalVal.toString();
| |
| document.getElementById('dispPct').innerText = displayPercent + '%';
| |
|
| |
| const cardPecahan = document.getElementById('cardPecahan');
| |
| if (percent === 0) {
| |
| cardPecahan.innerHTML = `<div class="card-label">Pecahan</div><div class="card-value" style="color: var(--primary-light);">0</div>`;
| |
| } else if (percent === 100) {
| |
| cardPecahan.innerHTML = `<div class="card-label">Pecahan</div><div class="card-value" style="color: var(--primary-light);">1</div>`;
| |
| } else {
| |
| let frac = getSimplifiedFraction(percent);
| |
| cardPecahan.innerHTML = `
| |
| <div class="card-label">Pecahan</div>
| |
| <div class="frac-display">
| |
| <div class="frac-num">${frac.n}</div>
| |
| <div class="frac-line"></div>
| |
| <div class="frac-den">${frac.d}</div>
| |
| </div>
| |
| `;
| |
| }
| |
| }
| |
|
| |
| // Event Listeners for Mode Eksplorasi
| |
| const mainSlider = document.getElementById('mainSlider');
| |
| const sliderValDisplay = document.getElementById('sliderValDisplay');
| |
|
| |
| mainSlider.addEventListener('input', (e) => {
| |
| let val = parseFloat(e.target.value);
| |
| let dispVal = val % 1 === 0 ? val : val.toFixed(1);
| |
| sliderValDisplay.innerText = dispVal + '%';
| |
| updateVisuals(val);
| |
| });
| |
|
| |
| function setPreset(val) {
| |
| mainSlider.value = val;
| |
| let dispVal = val % 1 === 0 ? val : val.toFixed(1);
| |
| sliderValDisplay.innerText = dispVal + '%';
| |
| updateVisuals(val);
| |
| }
| |
|
| |
| // Init initial visual
| |
| updateVisuals(25);
| |
|
| |
| // UI Tabs
| |
| const tabEksplorasi = document.getElementById('tabEksplorasi');
| |
| const tabKuis = document.getElementById('tabKuis');
| |
| const modeEksplorasi = document.getElementById('modeEksplorasi');
| |
| const modeKuis = document.getElementById('modeKuis');
| |
|
| |
| function setMode(mode) {
| |
| if (mode === 'eksplorasi') {
| |
| tabEksplorasi.classList.add('active');
| |
| tabKuis.classList.remove('active');
| |
| modeEksplorasi.style.display = 'flex';
| |
| modeKuis.style.display = 'none';
| |
|
| |
| // restore visual to slider
| |
| let val = parseFloat(mainSlider.value);
| |
| updateVisuals(val);
| |
| } else {
| |
| tabEksplorasi.classList.remove('active');
| |
| tabKuis.classList.add('active');
| |
| modeEksplorasi.style.display = 'none';
| |
| modeKuis.style.display = 'flex';
| |
| randomKuis();
| |
| }
| |
| }
| |
|
| |
| // Modals
| |
| function showExplanation() {
| |
| document.getElementById('explanationOverlay').classList.add('show');
| |
| }
| |
| function closeExplanation() {
| |
| document.getElementById('explanationOverlay').classList.remove('show');
| |
| }
| |
|
| |
| // Kuis Logic
| |
| const kuisTypes = [
| |
| { from: 'Pecahan', to: 'Persen', unit: '%' },
| |
| { from: 'Pecahan', to: 'Desimal', unit: '' },
| |
| { from: 'Desimal', to: 'Persen', unit: '%' },
| |
| { from: 'Persen', to: 'Desimal', unit: '' }
| |
| ];
| |
|
| |
| const kuisValues = [
| |
| { frac: {n:1, d:2}, dec: '0.5', pct: '50' },
| |
| { frac: {n:1, d:4}, dec: '0.25', pct: '25' },
| |
| { frac: {n:3, d:4}, dec: '0.75', pct: '75' },
| |
| { frac: {n:1, d:5}, dec: '0.2', pct: '20' },
| |
| { frac: {n:2, d:5}, dec: '0.4', pct: '40' },
| |
| { frac: {n:3, d:5}, dec: '0.6', pct: '60' },
| |
| { frac: {n:4, d:5}, dec: '0.8', pct: '80' },
| |
| { frac: {n:1, d:10}, dec: '0.1', pct: '10' },
| |
| { frac: {n:3, d:10}, dec: '0.3', pct: '30' },
| |
| { frac: {n:7, d:10}, dec: '0.7', pct: '70' },
| |
| { frac: {n:9, d:10}, dec: '0.9', pct: '90' },
| |
| { frac: {n:1, d:8}, dec: '0.125', pct: '12.5' },
| |
| { frac: {n:3, d:8}, dec: '0.375', pct: '37.5' },
| |
| { frac: {n:1, d:20}, dec: '0.05', pct: '5' },
| |
| { frac: {n:3, d:20}, dec: '0.15', pct: '15' }
| |
| ];
| |
|
| |
| let currentKuisAnswer = 0;
| |
| let currentKuisPctVal = 0;
| |
|
| |
| function randomKuis() {
| |
| // Update visual to empty or interrogative
| |
| updateVisuals(0);
| |
| document.getElementById('gridGlow').style.opacity = 0;
| |
|
| |
| const typeObj = kuisTypes[Math.floor(Math.random() * kuisTypes.length)];
| |
| const valObj = kuisValues[Math.floor(Math.random() * kuisValues.length)];
| |
|
| |
| let fromText = '';
| |
| let targetAnswer = 0;
| |
|
| |
| if (typeObj.from === 'Pecahan') {
| |
| fromText = `<div class="frac-display" style="font-size: 2.2rem; color: var(--text-main);">
| |
| <div class="frac-num">${valObj.frac.n}</div>
| |
| <div class="frac-line"></div>
| |
| <div class="frac-den">${valObj.frac.d}</div>
| |
| </div>`;
| |
| } else if (typeObj.from === 'Desimal') {
| |
| fromText = `<div class="kuis-text-val">${valObj.dec}</div>`;
| |
| } else if (typeObj.from === 'Persen') {
| |
| fromText = `<div class="kuis-text-val">${valObj.pct}%</div>`;
| |
| }
| |
|
| |
| if (typeObj.to === 'Persen') targetAnswer = parseFloat(valObj.pct);
| |
| else if (typeObj.to === 'Desimal') targetAnswer = parseFloat(valObj.dec);
| |
|
| |
| document.getElementById('kuisQuestion').innerHTML = `Ubah bentuk ${typeObj.from.toLowerCase()} di bawah menjadi <b>${typeObj.to}</b>:`;
| |
| document.getElementById('kuisDisplayBox').innerHTML = fromText;
| |
| document.getElementById('kuisUnit').innerText = typeObj.unit;
| |
| document.getElementById('inputKuis').value = '';
| |
| document.getElementById('inputKuis').focus();
| |
|
| |
| currentKuisAnswer = targetAnswer;
| |
| currentKuisPctVal = parseFloat(valObj.pct);
| |
| }
| |
|
| |
| function cekKuis() {
| |
| const userValStr = document.getElementById('inputKuis').value.replace(',', '.');
| |
| const userVal = parseFloat(userValStr);
| |
| if (isNaN(userVal)) return;
| |
|
| |
| if (Math.abs(userVal - currentKuisAnswer) < 0.001) {
| |
| let formattedAns = currentKuisAnswer + document.getElementById('kuisUnit').innerText;
| |
| showStatus(true, "Tepat Sekali!", `Jawabanmu benar: ${formattedAns}`);
| |
| updateVisuals(currentKuisPctVal); // Show visual feedback
| |
| } else {
| |
| showStatus(false, "Coba Lagi!", "Jawabanmu belum tepat. Ingat kembali caranya atau gunakan mode Eksplorasi.");
| |
| }
| |
| }
| |
|
| |
| function showStatus(isCorrect, title, desc) {
| |
| const overlay = document.getElementById('statusOverlay');
| |
| const icon = document.getElementById('statusIcon');
| |
| const titleEl = document.getElementById('statusTitle');
| |
| const descEl = document.getElementById('statusDesc');
| |
|
| |
| overlay.className = 'overlay show';
| |
| const card = overlay.querySelector('.status-card');
| |
|
| |
| if (isCorrect) {
| |
| card.className = 'status-card correct-theme';
| |
| icon.innerHTML = '<i class="fas fa-check-circle"></i>';
| |
| } else {
| |
| card.className = 'status-card wrong-theme';
| |
| icon.innerHTML = '<i class="fas fa-times-circle"></i>';
| |
| }
| |
|
| |
| titleEl.innerText = title;
| |
| descEl.innerText = desc;
| |
| }
| |
|
| |
| function closeStatus() {
| |
| document.getElementById('statusOverlay').classList.remove('show');
| |
| if (document.querySelector('.status-card').classList.contains('correct-theme')) {
| |
| randomKuis();
| |
| } else {
| |
| document.getElementById('inputKuis').focus();
| |
| }
| |
| }
| |
|
| |
| // Enter key to check
| |
| document.getElementById('inputKuis').addEventListener('keypress', function (e) {
| |
| if (e.key === 'Enter') {
| |
| cekKuis();
| |
| }
| |
| });
| |
| </script>
| |
| </body>
| |
| </html>
| |
|
| |
|
|
| |
|
| Baris 1.072: |
Baris 77: |
| <!--==========================================================--> | | <!--==========================================================--> |
| </syntaxhighlight> | | </syntaxhighlight> |
| | |
| | == Langkah Implementasi == |
| | |
| | # Copy kode HTML di atas |
| | # Buka blogger.com dan masuk ke mode editor kode HTML untuk penulisannya |
| | # Paste potongan kode di atas |
| | # Ambil kode HTML dari AI yang anda gunakan seperti Gemini, Canva.com/ai, ChatGPT, dsb. |
| | # Kembali ke blogger.com |
| | # Paste di baris ke 13 |
| | # Simpan / Terbitkan |