1101 lines
38 KiB
HTML
1101 lines
38 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>💌 Secret Message – Versteckte Nachrichten</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@400;600;700&family=Sacramento&display=swap" rel="stylesheet">
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
min-height: 100vh;
|
||
font-family: 'Quicksand', sans-serif;
|
||
background: linear-gradient(135deg, #fce4ec 0%, #f3e5f5 30%, #e8eaf6 60%, #fce4ec 100%);
|
||
background-size: 400% 400%;
|
||
animation: gradientShift 12s ease infinite;
|
||
color: #5d4037;
|
||
padding: 20px;
|
||
}
|
||
|
||
@keyframes gradientShift {
|
||
0% { background-position: 0% 50%; }
|
||
50% { background-position: 100% 50%; }
|
||
100% { background-position: 0% 50%; }
|
||
}
|
||
|
||
/* Floating hearts background */
|
||
.hearts-bg {
|
||
position: fixed;
|
||
top: 0; left: 0; right: 0; bottom: 0;
|
||
pointer-events: none;
|
||
overflow: hidden;
|
||
z-index: 0;
|
||
}
|
||
.hearts-bg .heart {
|
||
position: absolute;
|
||
font-size: 20px;
|
||
opacity: 0.15;
|
||
animation: floatUp linear infinite;
|
||
}
|
||
@keyframes floatUp {
|
||
0% { transform: translateY(100vh) rotate(0deg) scale(1); opacity: 0; }
|
||
10% { opacity: 0.15; }
|
||
90% { opacity: 0.15; }
|
||
100% { transform: translateY(-10vh) rotate(360deg) scale(0.5); opacity: 0; }
|
||
}
|
||
|
||
.container {
|
||
max-width: 520px;
|
||
margin: 0 auto;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* Header */
|
||
.header {
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
}
|
||
.header .logo {
|
||
font-size: 48px;
|
||
margin-bottom: 6px;
|
||
}
|
||
.header h1 {
|
||
font-family: 'Sacramento', cursive;
|
||
font-size: 42px;
|
||
color: #e91e63;
|
||
text-shadow: 0 2px 8px rgba(233,30,99,0.15);
|
||
}
|
||
.header p {
|
||
font-size: 14px;
|
||
color: #ab8a8a;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
/* Tabs */
|
||
.tabs {
|
||
display: flex;
|
||
gap: 8px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.tab-btn {
|
||
flex: 1;
|
||
padding: 12px;
|
||
border: none;
|
||
border-radius: 16px;
|
||
font-family: 'Quicksand', sans-serif;
|
||
font-weight: 700;
|
||
font-size: 15px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
background: rgba(255,255,255,0.5);
|
||
color: #b0879b;
|
||
backdrop-filter: blur(8px);
|
||
}
|
||
.tab-btn.active {
|
||
background: linear-gradient(135deg, #f48fb1, #ce93d8);
|
||
color: #fff;
|
||
box-shadow: 0 4px 20px rgba(244,143,177,0.4);
|
||
transform: translateY(-2px);
|
||
}
|
||
.tab-btn:hover:not(.active) {
|
||
background: rgba(255,255,255,0.8);
|
||
}
|
||
|
||
/* Card */
|
||
.card {
|
||
background: rgba(255,255,255,0.7);
|
||
backdrop-filter: blur(16px);
|
||
border-radius: 24px;
|
||
padding: 28px 24px;
|
||
box-shadow: 0 8px 32px rgba(183,130,163,0.15);
|
||
border: 1px solid rgba(255,255,255,0.6);
|
||
margin-bottom: 20px;
|
||
transition: all 0.4s ease;
|
||
}
|
||
.card.hidden { display: none; }
|
||
|
||
.card-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
margin-bottom: 16px;
|
||
color: #7b5a6e;
|
||
}
|
||
|
||
.hint {
|
||
background: rgba(244,143,177,0.1);
|
||
border-radius: 12px;
|
||
padding: 12px 16px;
|
||
font-size: 13px;
|
||
color: #b0879b;
|
||
margin-bottom: 18px;
|
||
line-height: 1.5;
|
||
}
|
||
.hint strong { color: #e91e63; }
|
||
|
||
textarea {
|
||
width: 100%;
|
||
min-height: 110px;
|
||
border: 2px solid rgba(206,147,216,0.3);
|
||
border-radius: 16px;
|
||
padding: 16px;
|
||
font-family: 'Quicksand', sans-serif;
|
||
font-size: 15px;
|
||
color: #5d4037;
|
||
background: rgba(255,255,255,0.6);
|
||
resize: vertical;
|
||
transition: border-color 0.3s;
|
||
outline: none;
|
||
}
|
||
textarea:focus {
|
||
border-color: #f48fb1;
|
||
box-shadow: 0 0 0 3px rgba(244,143,177,0.15);
|
||
}
|
||
textarea::placeholder { color: #cbb3c0; }
|
||
|
||
.result-box {
|
||
width: 100%;
|
||
min-height: 80px;
|
||
background: linear-gradient(135deg, rgba(252,228,236,0.5), rgba(243,229,245,0.5));
|
||
border: 2px dashed rgba(206,147,216,0.4);
|
||
border-radius: 16px;
|
||
padding: 16px;
|
||
margin-top: 14px;
|
||
font-family: 'Quicksand', sans-serif;
|
||
font-size: 14px;
|
||
color: #7b5a6e;
|
||
word-break: break-all;
|
||
line-height: 1.6;
|
||
position: relative;
|
||
}
|
||
.result-box.empty {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #cbb3c0;
|
||
font-style: italic;
|
||
}
|
||
|
||
.label {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: #b0879b;
|
||
margin-bottom: 8px;
|
||
margin-top: 14px;
|
||
}
|
||
|
||
/* Buttons */
|
||
.btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 12px 22px;
|
||
border: none;
|
||
border-radius: 14px;
|
||
font-family: 'Quicksand', sans-serif;
|
||
font-weight: 700;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
.btn:active { transform: scale(0.96); }
|
||
|
||
.btn-primary {
|
||
background: linear-gradient(135deg, #f48fb1, #ce93d8);
|
||
color: #fff;
|
||
box-shadow: 0 4px 16px rgba(244,143,177,0.35);
|
||
}
|
||
.btn-primary:hover {
|
||
box-shadow: 0 6px 24px rgba(244,143,177,0.5);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: rgba(255,255,255,0.7);
|
||
color: #b0879b;
|
||
border: 2px solid rgba(206,147,216,0.3);
|
||
}
|
||
.btn-secondary:hover {
|
||
background: #fff;
|
||
border-color: #f48fb1;
|
||
color: #e91e63;
|
||
}
|
||
|
||
.btn-group {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
/* Share buttons */
|
||
.share-section {
|
||
margin-top: 20px;
|
||
padding-top: 20px;
|
||
border-top: 1px solid rgba(206,147,216,0.2);
|
||
}
|
||
.share-title {
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
color: #b0879b;
|
||
margin-bottom: 12px;
|
||
}
|
||
.share-btns {
|
||
display: flex;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.btn-whatsapp {
|
||
background: linear-gradient(135deg, #66bb6a, #43a047);
|
||
color: #fff;
|
||
box-shadow: 0 4px 16px rgba(102,187,106,0.3);
|
||
}
|
||
.btn-whatsapp:hover {
|
||
box-shadow: 0 6px 24px rgba(102,187,106,0.5);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.btn-insta {
|
||
background: linear-gradient(135deg, #f58529, #dd2a7b, #8134af);
|
||
color: #fff;
|
||
box-shadow: 0 4px 16px rgba(221,42,123,0.3);
|
||
}
|
||
.btn-insta:hover {
|
||
box-shadow: 0 6px 24px rgba(221,42,123,0.5);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.btn-download {
|
||
background: linear-gradient(135deg, #7986cb, #5c6bc0);
|
||
color: #fff;
|
||
box-shadow: 0 4px 16px rgba(121,134,203,0.3);
|
||
}
|
||
.btn-download:hover {
|
||
box-shadow: 0 6px 24px rgba(121,134,203,0.5);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
/* Image Preview */
|
||
.image-preview-section {
|
||
margin-top: 20px;
|
||
}
|
||
.image-preview-container {
|
||
border-radius: 16px;
|
||
overflow: hidden;
|
||
box-shadow: 0 4px 20px rgba(183,130,163,0.2);
|
||
position: relative;
|
||
}
|
||
.image-preview-container canvas {
|
||
width: 100%;
|
||
display: block;
|
||
border-radius: 16px;
|
||
}
|
||
|
||
/* Style selector */
|
||
.style-selector {
|
||
display: flex;
|
||
gap: 8px;
|
||
margin-top: 14px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.style-option {
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: 50%;
|
||
border: 3px solid transparent;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
.style-option:hover, .style-option.active {
|
||
border-color: #e91e63;
|
||
transform: scale(1.15);
|
||
box-shadow: 0 2px 12px rgba(233,30,99,0.3);
|
||
}
|
||
.style-option.s1 { background: linear-gradient(135deg, #fce4ec, #f8bbd0); }
|
||
.style-option.s2 { background: linear-gradient(135deg, #e8eaf6, #c5cae9); }
|
||
.style-option.s3 { background: linear-gradient(135deg, #e0f2f1, #b2dfdb); }
|
||
.style-option.s4 { background: linear-gradient(135deg, #fff8e1, #ffe082); }
|
||
.style-option.s5 { background: linear-gradient(135deg, #212121, #424242); }
|
||
|
||
/* Decode Reveal Animation */
|
||
.result-box.revealing {
|
||
background: linear-gradient(135deg, rgba(252,228,236,0.7), rgba(243,229,245,0.7));
|
||
border-color: #f48fb1;
|
||
border-style: solid;
|
||
animation: revealGlow 1.5s ease-in-out;
|
||
}
|
||
@keyframes revealGlow {
|
||
0% { box-shadow: 0 0 0 0 rgba(244,143,177,0); }
|
||
30% { box-shadow: 0 0 30px 8px rgba(244,143,177,0.4); }
|
||
60% { box-shadow: 0 0 20px 4px rgba(206,147,216,0.3); }
|
||
100% { box-shadow: 0 0 0 0 rgba(244,143,177,0); }
|
||
}
|
||
.result-box .reveal-char {
|
||
display: inline;
|
||
opacity: 0;
|
||
animation: charReveal 0.3s ease forwards;
|
||
}
|
||
@keyframes charReveal {
|
||
0% { opacity: 0; transform: translateY(8px) scale(0.7); filter: blur(4px); }
|
||
60% { opacity: 1; transform: translateY(-2px) scale(1.05); filter: blur(0); }
|
||
100% { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
|
||
}
|
||
.reveal-cursor {
|
||
display: inline-block;
|
||
width: 2px;
|
||
height: 1.1em;
|
||
background: #e91e63;
|
||
margin-left: 2px;
|
||
vertical-align: text-bottom;
|
||
animation: cursorBlink 0.6s step-end infinite;
|
||
}
|
||
@keyframes cursorBlink {
|
||
50% { opacity: 0; }
|
||
}
|
||
.reveal-emoji-burst {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
font-size: 28px;
|
||
pointer-events: none;
|
||
animation: emojiBurst 1s ease-out forwards;
|
||
z-index: 5;
|
||
}
|
||
@keyframes emojiBurst {
|
||
0% { transform: translate(-50%, -50%) scale(0) rotate(0deg); opacity: 1; }
|
||
50% { opacity: 1; }
|
||
100% { transform: translate(var(--tx), var(--ty)) scale(1.2) rotate(var(--rot)); opacity: 0; }
|
||
}
|
||
|
||
/* Confetti Canvas */
|
||
#confettiCanvas {
|
||
position: fixed;
|
||
top: 0; left: 0;
|
||
width: 100%; height: 100%;
|
||
pointer-events: none;
|
||
z-index: 999;
|
||
}
|
||
|
||
/* Toast */
|
||
.toast {
|
||
position: fixed;
|
||
bottom: 30px;
|
||
left: 50%;
|
||
transform: translateX(-50%) translateY(80px);
|
||
background: linear-gradient(135deg, #f48fb1, #ce93d8);
|
||
color: #fff;
|
||
padding: 14px 28px;
|
||
border-radius: 16px;
|
||
font-family: 'Quicksand', sans-serif;
|
||
font-weight: 700;
|
||
font-size: 14px;
|
||
box-shadow: 0 8px 32px rgba(244,143,177,0.4);
|
||
z-index: 1000;
|
||
transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
||
pointer-events: none;
|
||
}
|
||
.toast.show {
|
||
transform: translateX(-50%) translateY(0);
|
||
}
|
||
|
||
/* Language Toggle */
|
||
.lang-toggle {
|
||
position: fixed;
|
||
top: 16px;
|
||
right: 16px;
|
||
z-index: 100;
|
||
display: flex;
|
||
background: rgba(255,255,255,0.6);
|
||
backdrop-filter: blur(12px);
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
border: 2px solid rgba(206,147,216,0.25);
|
||
box-shadow: 0 4px 16px rgba(183,130,163,0.12);
|
||
}
|
||
.lang-btn {
|
||
padding: 8px 14px;
|
||
border: none;
|
||
background: transparent;
|
||
font-family: 'Quicksand', sans-serif;
|
||
font-weight: 700;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
color: #b0879b;
|
||
transition: all 0.3s;
|
||
}
|
||
.lang-btn.active {
|
||
background: linear-gradient(135deg, #f48fb1, #ce93d8);
|
||
color: #fff;
|
||
}
|
||
.lang-btn:hover:not(.active) {
|
||
background: rgba(244,143,177,0.12);
|
||
}
|
||
|
||
/* Footer */
|
||
.footer {
|
||
text-align: center;
|
||
margin-top: 30px;
|
||
font-size: 13px;
|
||
color: #cbb3c0;
|
||
}
|
||
.footer .made-by {
|
||
display: inline-block;
|
||
margin-top: 6px;
|
||
padding: 6px 18px;
|
||
background: linear-gradient(135deg, rgba(244,143,177,0.2), rgba(206,147,216,0.2));
|
||
border-radius: 20px;
|
||
font-weight: 700;
|
||
font-size: 14px;
|
||
color: #e91e63;
|
||
letter-spacing: 0.5px;
|
||
border: 1.5px solid rgba(233,30,99,0.15);
|
||
text-shadow: 0 1px 4px rgba(233,30,99,0.1);
|
||
}
|
||
|
||
/* Responsive */
|
||
@media (max-width: 480px) {
|
||
.header h1 { font-size: 34px; }
|
||
.card { padding: 20px 16px; }
|
||
.btn { padding: 10px 16px; font-size: 13px; }
|
||
.share-btns { flex-direction: column; }
|
||
.share-btns .btn { justify-content: center; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- Floating hearts -->
|
||
<div class="hearts-bg" id="heartsBg"></div>
|
||
|
||
<!-- Language Toggle -->
|
||
<div class="lang-toggle">
|
||
<button class="lang-btn active" onclick="setLang('de')">🇩🇪 DE</button>
|
||
<button class="lang-btn" onclick="setLang('en')">🇬🇧 EN</button>
|
||
</div>
|
||
|
||
<div class="container">
|
||
<!-- Header -->
|
||
<div class="header">
|
||
<div class="logo">💌</div>
|
||
<h1>Secret Message</h1>
|
||
<p data-i18n="subtitle">Verstecke deine Nachrichten – nur Eingeweihte können sie lesen 🤫</p>
|
||
</div>
|
||
|
||
<!-- Tabs -->
|
||
<div class="tabs">
|
||
<button class="tab-btn active" onclick="switchTab('encode')" data-i18n="tabEncode">🔒 Verschlüsseln</button>
|
||
<button class="tab-btn" onclick="switchTab('decode')" data-i18n="tabDecode">🔓 Entschlüsseln</button>
|
||
</div>
|
||
|
||
<!-- ENCODE -->
|
||
<div class="card" id="encodeCard">
|
||
<div class="card-title" data-i18n="encodeTitle">✨ Geheime Nachricht erstellen</div>
|
||
<div class="hint" data-i18n-html="encodeHint">
|
||
Schreibe deine geheime Nachricht – z.B. <strong>"Ich liebe dich 💕"</strong> – und verschlüssele sie!
|
||
Teile den Code in deinem Insta-Status und nur wer weiß, wie man entschlüsselt, kann sie lesen 🤭
|
||
</div>
|
||
|
||
<div class="label" data-i18n="encodeLabel">Deine geheime Nachricht:</div>
|
||
<textarea id="encodeInput" data-i18n-placeholder="encodePlaceholder" placeholder="Schreibe hier deine geheime Nachricht… 💭"></textarea>
|
||
|
||
<div class="btn-group">
|
||
<button class="btn btn-primary" onclick="encodeMessage()" data-i18n="btnEncode">🔒 Verschlüsseln</button>
|
||
<button class="btn btn-secondary" onclick="clearEncode()" data-i18n="btnDelete">🗑️ Löschen</button>
|
||
</div>
|
||
|
||
<div class="label" data-i18n="encodeResultLabel">Verschlüsselter Code:</div>
|
||
<div class="result-box empty" id="encodeResult" data-i18n="encodeResultEmpty">Hier erscheint dein geheimer Code ✨</div>
|
||
|
||
<div class="btn-group" id="encodeBtns" style="display:none;">
|
||
<button class="btn btn-secondary" onclick="copyResult()" data-i18n="btnCopy">📋 Kopieren</button>
|
||
</div>
|
||
|
||
<!-- Image Generator -->
|
||
<div class="image-preview-section" id="imageSection" style="display:none;">
|
||
<div class="label" data-i18n="styleLabel">🎨 Bild-Stil wählen:</div>
|
||
<div class="style-selector">
|
||
<div class="style-option s1 active" onclick="selectStyle(0)" title="Rosa Traum"></div>
|
||
<div class="style-option s2" onclick="selectStyle(1)" title="Lavendel"></div>
|
||
<div class="style-option s3" onclick="selectStyle(2)" title="Minze"></div>
|
||
<div class="style-option s4" onclick="selectStyle(3)" title="Sonnenschein"></div>
|
||
<div class="style-option s5" onclick="selectStyle(4)" title="Dunkel"></div>
|
||
</div>
|
||
|
||
<div class="label" data-i18n="previewLabel">Vorschau:</div>
|
||
<div class="image-preview-container">
|
||
<canvas id="previewCanvas" width="1080" height="1080"></canvas>
|
||
</div>
|
||
|
||
<div class="share-section">
|
||
<div class="share-title" data-i18n="shareTitle">📤 Teilen & Herunterladen:</div>
|
||
<div class="share-btns">
|
||
<button class="btn btn-download" onclick="downloadImage()" data-i18n="btnDownload">📥 Bild speichern</button>
|
||
<button class="btn btn-whatsapp" onclick="shareWhatsApp()" data-i18n="btnWhatsApp">💬 WhatsApp</button>
|
||
<button class="btn btn-insta" onclick="shareInstagram()" data-i18n="btnInsta">📸 Instagram</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DECODE -->
|
||
<div class="card hidden" id="decodeCard">
|
||
<div class="card-title" data-i18n="decodeTitle">🔍 Geheime Nachricht entschlüsseln</div>
|
||
<div class="hint" data-i18n-html="decodeHint">
|
||
Hat dir jemand einen geheimen Code geschickt? 🤔<br>
|
||
Füge ihn hier ein und finde heraus, was die Nachricht verrät! 💕
|
||
</div>
|
||
|
||
<div class="label" data-i18n="decodeLabel">Verschlüsselter Code:</div>
|
||
<textarea id="decodeInput" data-i18n-placeholder="decodePlaceholder" placeholder="Füge hier den geheimen Code ein… 🔑"></textarea>
|
||
|
||
<div class="btn-group">
|
||
<button class="btn btn-primary" onclick="decodeMessage()" data-i18n="btnDecode">🔓 Entschlüsseln</button>
|
||
<button class="btn btn-secondary" onclick="clearDecode()" data-i18n="btnDelete">🗑️ Löschen</button>
|
||
</div>
|
||
|
||
<div class="label" data-i18n="decodeResultLabel">Die geheime Nachricht:</div>
|
||
<div class="result-box empty" id="decodeResult" data-i18n="decodeResultEmpty">Hier erscheint die entschlüsselte Nachricht 💌</div>
|
||
</div>
|
||
|
||
<!-- Footer -->
|
||
<div class="footer">
|
||
<span data-i18n="footerText">Sende geheime Nachrichten an deine Liebsten 💕</span><br>
|
||
<span class="made-by">Made by Mandy 🏳️⚧️</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Confetti -->
|
||
<canvas id="confettiCanvas"></canvas>
|
||
|
||
<!-- Toast -->
|
||
<div class="toast" id="toast"></div>
|
||
|
||
<script>
|
||
// ─── State ──────────────────────────────────────────────
|
||
let currentStyle = 0;
|
||
let encodedText = '';
|
||
let lang = 'de';
|
||
|
||
// ─── i18n ───────────────────────────────────────────────
|
||
const i18n = {
|
||
de: {
|
||
subtitle: 'Verstecke deine Nachrichten – nur Eingeweihte können sie lesen 🤫',
|
||
tabEncode: '🔒 Verschlüsseln',
|
||
tabDecode: '🔓 Entschlüsseln',
|
||
encodeTitle: '✨ Geheime Nachricht erstellen',
|
||
encodeHint: 'Schreibe deine geheime Nachricht – z.B. <strong>"Ich liebe dich 💕"</strong> – und verschlüssele sie! Teile den Code in deinem Insta-Status und nur wer weiß, wie man entschlüsselt, kann sie lesen 🤭',
|
||
encodeLabel: 'Deine geheime Nachricht:',
|
||
encodePlaceholder: 'Schreibe hier deine geheime Nachricht… 💭',
|
||
btnEncode: '🔒 Verschlüsseln',
|
||
btnDelete: '🗑️ Löschen',
|
||
encodeResultLabel: 'Verschlüsselter Code:',
|
||
encodeResultEmpty: 'Hier erscheint dein geheimer Code ✨',
|
||
btnCopy: '📋 Kopieren',
|
||
styleLabel: '🎨 Bild-Stil wählen:',
|
||
previewLabel: 'Vorschau:',
|
||
shareTitle: '📤 Teilen & Herunterladen:',
|
||
btnDownload: '📥 Bild speichern',
|
||
btnWhatsApp: '💬 WhatsApp',
|
||
btnInsta: '📸 Instagram',
|
||
decodeTitle: '🔍 Geheime Nachricht entschlüsseln',
|
||
decodeHint: 'Hat dir jemand einen geheimen Code geschickt? 🤔<br>Füge ihn hier ein und finde heraus, was die Nachricht verrät! 💕',
|
||
decodeLabel: 'Verschlüsselter Code:',
|
||
decodePlaceholder: 'Füge hier den geheimen Code ein… 🔑',
|
||
btnDecode: '🔓 Entschlüsseln',
|
||
decodeResultLabel: 'Die geheime Nachricht:',
|
||
decodeResultEmpty: 'Hier erscheint die entschlüsselte Nachricht 💌',
|
||
footerText: 'Sende geheime Nachrichten an deine Liebsten 💕',
|
||
toastWriteFirst: '✏️ Schreibe zuerst eine Nachricht!',
|
||
toastEncoded: '🔒 Nachricht verschlüsselt!',
|
||
toastPasteFirst: '📋 Füge zuerst einen Code ein!',
|
||
toastDecoded: '💌 Geheime Nachricht entdeckt!',
|
||
toastInvalid: '❌ Ungültiger Code – prüfe die Eingabe!',
|
||
toastCopied: '📋 Code kopiert!',
|
||
toastSaved: '📥 Bild gespeichert!',
|
||
toastInstaFallback: '📸 Bild gespeichert – teile es in deiner Insta-Story!',
|
||
canvasTitle: 'Geheime Nachricht',
|
||
canvasSubtitle: 'Kannst du sie entschlüsseln?',
|
||
canvasDecodeHint: '🔓 Entschlüssele mit Base64-Decoder',
|
||
whatsAppText: '🤫 Ich habe eine geheime Nachricht für dich!\n\n🔒 Code: {code}\n\n🔓 Entschlüssele sie mit einem Base64-Decoder! 💌',
|
||
},
|
||
en: {
|
||
subtitle: 'Hide your messages – only insiders can read them 🤫',
|
||
tabEncode: '🔒 Encrypt',
|
||
tabDecode: '🔓 Decrypt',
|
||
encodeTitle: '✨ Create Secret Message',
|
||
encodeHint: 'Write your secret message – e.g. <strong>"I love you 💕"</strong> – and encrypt it! Share the code in your Insta story and only those who know how to decrypt can read it 🤭',
|
||
encodeLabel: 'Your secret message:',
|
||
encodePlaceholder: 'Write your secret message here… 💭',
|
||
btnEncode: '🔒 Encrypt',
|
||
btnDelete: '🗑️ Clear',
|
||
encodeResultLabel: 'Encrypted code:',
|
||
encodeResultEmpty: 'Your secret code will appear here ✨',
|
||
btnCopy: '📋 Copy',
|
||
styleLabel: '🎨 Choose image style:',
|
||
previewLabel: 'Preview:',
|
||
shareTitle: '📤 Share & Download:',
|
||
btnDownload: '📥 Save image',
|
||
btnWhatsApp: '💬 WhatsApp',
|
||
btnInsta: '📸 Instagram',
|
||
decodeTitle: '🔍 Decrypt Secret Message',
|
||
decodeHint: 'Did someone send you a secret code? 🤔<br>Paste it here and find out what the message says! 💕',
|
||
decodeLabel: 'Encrypted code:',
|
||
decodePlaceholder: 'Paste the secret code here… 🔑',
|
||
btnDecode: '🔓 Decrypt',
|
||
decodeResultLabel: 'The secret message:',
|
||
decodeResultEmpty: 'The decrypted message will appear here 💌',
|
||
footerText: 'Send secret messages to your loved ones 💕',
|
||
toastWriteFirst: '✏️ Write a message first!',
|
||
toastEncoded: '🔒 Message encrypted!',
|
||
toastPasteFirst: '📋 Paste a code first!',
|
||
toastDecoded: '💌 Secret message revealed!',
|
||
toastInvalid: '❌ Invalid code – check your input!',
|
||
toastCopied: '📋 Code copied!',
|
||
toastSaved: '📥 Image saved!',
|
||
toastInstaFallback: '📸 Image saved – share it in your Insta story!',
|
||
canvasTitle: 'Secret Message',
|
||
canvasSubtitle: 'Can you decrypt it?',
|
||
canvasDecodeHint: '🔓 Decrypt with a Base64 decoder',
|
||
whatsAppText: '🤫 I have a secret message for you!\n\n🔒 Code: {code}\n\n🔓 Decrypt it with a Base64 decoder! 💌',
|
||
}
|
||
};
|
||
|
||
function t(key) { return i18n[lang][key] || key; }
|
||
|
||
function setLang(l) {
|
||
lang = l;
|
||
document.querySelectorAll('.lang-btn').forEach(b => b.classList.remove('active'));
|
||
document.querySelector(`.lang-btn:${l === 'de' ? 'first' : 'last'}-child`).classList.add('active');
|
||
// Update text content
|
||
document.querySelectorAll('[data-i18n]').forEach(el => {
|
||
el.textContent = t(el.dataset.i18n);
|
||
});
|
||
// Update innerHTML (for hints with <strong>)
|
||
document.querySelectorAll('[data-i18n-html]').forEach(el => {
|
||
el.innerHTML = t(el.dataset.i18nHtml);
|
||
});
|
||
// Update placeholders
|
||
document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
|
||
el.placeholder = t(el.dataset.i18nPlaceholder);
|
||
});
|
||
// Re-render canvas if visible
|
||
if (encodedText) renderPreview();
|
||
}
|
||
|
||
const styles = [
|
||
{ bg1: '#fce4ec', bg2: '#f8bbd0', accent: '#e91e63', text: '#880e4f', label: '#c2185d', emoji: '💕' },
|
||
{ bg1: '#e8eaf6', bg2: '#c5cae9', accent: '#5c6bc0', text: '#283593', label: '#3949ab', emoji: '💜' },
|
||
{ bg1: '#e0f2f1', bg2: '#b2dfdb', accent: '#26a69a', text: '#00695c', label: '#00897b', emoji: '🌿' },
|
||
{ bg1: '#fff8e1', bg2: '#ffe082', accent: '#ffa000', text: '#e65100', label: '#f57c00', emoji: '☀️' },
|
||
{ bg1: '#212121', bg2: '#424242', accent: '#f48fb1', text: '#fce4ec', label: '#f8bbd0', emoji: '🖤' },
|
||
];
|
||
|
||
// ─── Tabs ───────────────────────────────────────────────
|
||
function switchTab(tab) {
|
||
const btns = document.querySelectorAll('.tab-btn');
|
||
btns.forEach(b => b.classList.remove('active'));
|
||
|
||
if (tab === 'encode') {
|
||
btns[0].classList.add('active');
|
||
document.getElementById('encodeCard').classList.remove('hidden');
|
||
document.getElementById('decodeCard').classList.add('hidden');
|
||
} else {
|
||
btns[1].classList.add('active');
|
||
document.getElementById('encodeCard').classList.add('hidden');
|
||
document.getElementById('decodeCard').classList.remove('hidden');
|
||
}
|
||
}
|
||
|
||
// ─── Encode ─────────────────────────────────────────────
|
||
function encodeMessage() {
|
||
const input = document.getElementById('encodeInput').value.trim();
|
||
if (!input) {
|
||
showToast(t('toastWriteFirst'));
|
||
return;
|
||
}
|
||
|
||
// UTF-8 safe base64 encode
|
||
encodedText = btoa(unescape(encodeURIComponent(input)));
|
||
|
||
const resultEl = document.getElementById('encodeResult');
|
||
resultEl.textContent = encodedText;
|
||
resultEl.classList.remove('empty');
|
||
|
||
document.getElementById('encodeBtns').style.display = 'flex';
|
||
document.getElementById('imageSection').style.display = 'block';
|
||
|
||
renderPreview();
|
||
showToast(t('toastEncoded'));
|
||
}
|
||
|
||
function clearEncode() {
|
||
document.getElementById('encodeInput').value = '';
|
||
const resultEl = document.getElementById('encodeResult');
|
||
resultEl.textContent = t('encodeResultEmpty');
|
||
resultEl.classList.add('empty');
|
||
document.getElementById('encodeBtns').style.display = 'none';
|
||
document.getElementById('imageSection').style.display = 'none';
|
||
encodedText = '';
|
||
}
|
||
|
||
// ─── Decode with Reveal Animation ────────────────────────
|
||
function decodeMessage() {
|
||
const input = document.getElementById('decodeInput').value.trim();
|
||
if (!input) {
|
||
showToast(t('toastPasteFirst'));
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const decoded = decodeURIComponent(escape(atob(input)));
|
||
const resultEl = document.getElementById('decodeResult');
|
||
resultEl.classList.remove('empty');
|
||
resultEl.classList.add('revealing');
|
||
resultEl.innerHTML = '<span class="reveal-cursor"></span>';
|
||
|
||
// Typewriter letter-by-letter reveal
|
||
const chars = [...decoded];
|
||
const baseDelay = Math.max(20, Math.min(60, 1500 / chars.length)); // adaptive speed
|
||
let i = 0;
|
||
|
||
function typeNext() {
|
||
if (i >= chars.length) {
|
||
// Remove cursor when done
|
||
const cursor = resultEl.querySelector('.reveal-cursor');
|
||
if (cursor) cursor.remove();
|
||
// Remove glow class after animation
|
||
setTimeout(() => resultEl.classList.remove('revealing'), 500);
|
||
// Launch confetti & emoji burst!
|
||
launchConfetti();
|
||
spawnEmojiBurst(resultEl);
|
||
showToast(t('toastDecoded'));
|
||
return;
|
||
}
|
||
|
||
const span = document.createElement('span');
|
||
span.className = 'reveal-char';
|
||
span.style.animationDelay = '0ms';
|
||
const ch = chars[i];
|
||
if (ch === ' ') {
|
||
span.innerHTML = ' ';
|
||
} else if (ch === '\n') {
|
||
resultEl.insertBefore(document.createElement('br'), resultEl.querySelector('.reveal-cursor'));
|
||
i++;
|
||
typeNext();
|
||
return;
|
||
} else {
|
||
span.textContent = ch;
|
||
}
|
||
|
||
resultEl.insertBefore(span, resultEl.querySelector('.reveal-cursor'));
|
||
i++;
|
||
setTimeout(typeNext, baseDelay);
|
||
}
|
||
|
||
// Small initial pause for dramatic effect
|
||
setTimeout(typeNext, 300);
|
||
} catch (e) {
|
||
showToast(t('toastInvalid'));
|
||
}
|
||
}
|
||
|
||
// ─── Emoji Burst around result box ───────────────────────
|
||
function spawnEmojiBurst(container) {
|
||
const emojis = ['💕', '💖', '✨', '💌', '🦋', '🌸', '💗', '🥰', '💜'];
|
||
const rect = container.getBoundingClientRect();
|
||
for (let i = 0; i < 10; i++) {
|
||
const el = document.createElement('span');
|
||
el.className = 'reveal-emoji-burst';
|
||
el.textContent = emojis[Math.floor(Math.random() * emojis.length)];
|
||
const angle = (i / 10) * Math.PI * 2;
|
||
const dist = 60 + Math.random() * 80;
|
||
el.style.setProperty('--tx', `${Math.cos(angle) * dist}px`);
|
||
el.style.setProperty('--ty', `${Math.sin(angle) * dist - 40}px`);
|
||
el.style.setProperty('--rot', `${Math.random() * 360}deg`);
|
||
el.style.animationDelay = `${Math.random() * 0.3}s`;
|
||
el.style.position = 'absolute';
|
||
container.style.position = 'relative';
|
||
container.appendChild(el);
|
||
setTimeout(() => el.remove(), 1500);
|
||
}
|
||
}
|
||
|
||
// ─── Confetti ────────────────────────────────────────────
|
||
function launchConfetti() {
|
||
const canvas = document.getElementById('confettiCanvas');
|
||
const ctx = canvas.getContext('2d');
|
||
canvas.width = window.innerWidth;
|
||
canvas.height = window.innerHeight;
|
||
|
||
const colors = ['#f48fb1','#ce93d8','#e91e63','#ff80ab','#ea80fc','#b388ff','#82b1ff','#fce4ec','#f8bbd0'];
|
||
const particles = [];
|
||
|
||
for (let i = 0; i < 120; i++) {
|
||
particles.push({
|
||
x: canvas.width / 2 + (Math.random() - 0.5) * 200,
|
||
y: canvas.height / 2,
|
||
vx: (Math.random() - 0.5) * 16,
|
||
vy: -Math.random() * 18 - 4,
|
||
w: 6 + Math.random() * 6,
|
||
h: 4 + Math.random() * 8,
|
||
color: colors[Math.floor(Math.random() * colors.length)],
|
||
rotation: Math.random() * 360,
|
||
rotSpeed: (Math.random() - 0.5) * 12,
|
||
gravity: 0.25 + Math.random() * 0.15,
|
||
opacity: 1,
|
||
shape: Math.random() > 0.5 ? 'rect' : 'circle',
|
||
});
|
||
}
|
||
|
||
let frame = 0;
|
||
function animate() {
|
||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
let alive = false;
|
||
|
||
particles.forEach(p => {
|
||
p.vy += p.gravity;
|
||
p.x += p.vx;
|
||
p.y += p.vy;
|
||
p.vx *= 0.99;
|
||
p.rotation += p.rotSpeed;
|
||
if (frame > 40) p.opacity -= 0.015;
|
||
|
||
if (p.opacity > 0 && p.y < canvas.height + 20) {
|
||
alive = true;
|
||
ctx.save();
|
||
ctx.globalAlpha = Math.max(0, p.opacity);
|
||
ctx.translate(p.x, p.y);
|
||
ctx.rotate((p.rotation * Math.PI) / 180);
|
||
ctx.fillStyle = p.color;
|
||
if (p.shape === 'rect') {
|
||
ctx.fillRect(-p.w / 2, -p.h / 2, p.w, p.h);
|
||
} else {
|
||
ctx.beginPath();
|
||
ctx.arc(0, 0, p.w / 2, 0, Math.PI * 2);
|
||
ctx.fill();
|
||
}
|
||
ctx.restore();
|
||
}
|
||
});
|
||
|
||
frame++;
|
||
if (alive) {
|
||
requestAnimationFrame(animate);
|
||
} else {
|
||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
}
|
||
}
|
||
animate();
|
||
}
|
||
|
||
function clearDecode() {
|
||
document.getElementById('decodeInput').value = '';
|
||
const resultEl = document.getElementById('decodeResult');
|
||
resultEl.textContent = t('decodeResultEmpty');
|
||
resultEl.classList.add('empty');
|
||
}
|
||
|
||
// ─── Copy ───────────────────────────────────────────────
|
||
function copyResult() {
|
||
if (!encodedText) return;
|
||
navigator.clipboard.writeText(encodedText).then(() => {
|
||
showToast(t('toastCopied'));
|
||
});
|
||
}
|
||
|
||
// ─── Image Generation (Canvas) ─────────────────────────
|
||
function selectStyle(idx) {
|
||
currentStyle = idx;
|
||
document.querySelectorAll('.style-option').forEach((el, i) => {
|
||
el.classList.toggle('active', i === idx);
|
||
});
|
||
renderPreview();
|
||
}
|
||
|
||
function renderPreview() {
|
||
const canvas = document.getElementById('previewCanvas');
|
||
const ctx = canvas.getContext('2d');
|
||
const s = styles[currentStyle];
|
||
const W = 1080, H = 1080;
|
||
|
||
// Background gradient
|
||
const grad = ctx.createLinearGradient(0, 0, W, H);
|
||
grad.addColorStop(0, s.bg1);
|
||
grad.addColorStop(1, s.bg2);
|
||
ctx.fillStyle = grad;
|
||
ctx.fillRect(0, 0, W, H);
|
||
|
||
// Decorative circles
|
||
ctx.globalAlpha = 0.07;
|
||
ctx.fillStyle = s.accent;
|
||
ctx.beginPath(); ctx.arc(150, 150, 200, 0, Math.PI * 2); ctx.fill();
|
||
ctx.beginPath(); ctx.arc(930, 900, 250, 0, Math.PI * 2); ctx.fill();
|
||
ctx.beginPath(); ctx.arc(850, 200, 120, 0, Math.PI * 2); ctx.fill();
|
||
ctx.globalAlpha = 1;
|
||
|
||
// Top emoji
|
||
ctx.font = '72px serif';
|
||
ctx.textAlign = 'center';
|
||
ctx.fillText('🤫', W / 2, 160);
|
||
|
||
// Title
|
||
ctx.font = 'bold 48px Quicksand, sans-serif';
|
||
ctx.fillStyle = s.accent;
|
||
ctx.fillText(t('canvasTitle'), W / 2, 250);
|
||
|
||
// Subtitle
|
||
ctx.font = '28px Quicksand, sans-serif';
|
||
ctx.fillStyle = s.label;
|
||
ctx.fillText(t('canvasSubtitle') + ' ' + s.emoji, W / 2, 310);
|
||
|
||
// Code box background
|
||
const boxX = 80, boxY = 380, boxW = W - 160, boxH = 340;
|
||
ctx.fillStyle = currentStyle === 4 ? 'rgba(255,255,255,0.08)' : 'rgba(255,255,255,0.5)';
|
||
roundRect(ctx, boxX, boxY, boxW, boxH, 24);
|
||
ctx.fill();
|
||
|
||
// Border
|
||
ctx.strokeStyle = s.accent + '40';
|
||
ctx.lineWidth = 3;
|
||
roundRect(ctx, boxX, boxY, boxW, boxH, 24);
|
||
ctx.stroke();
|
||
|
||
// Code text (word-wrap)
|
||
ctx.font = '26px Quicksand, sans-serif';
|
||
ctx.fillStyle = s.text;
|
||
ctx.textAlign = 'center';
|
||
wrapText(ctx, encodedText, W / 2, boxY + 60, boxW - 60, 38);
|
||
|
||
// Decode hint
|
||
ctx.font = '24px Quicksand, sans-serif';
|
||
ctx.fillStyle = s.label;
|
||
ctx.textAlign = 'center';
|
||
ctx.fillText(t('canvasDecodeHint'), W / 2, 800);
|
||
|
||
// Website / branding
|
||
ctx.font = 'bold 22px Quicksand, sans-serif';
|
||
ctx.fillStyle = s.accent;
|
||
ctx.fillText('💌 Secret Message', W / 2, 880);
|
||
|
||
// Bottom decorative dots
|
||
ctx.globalAlpha = 0.15;
|
||
ctx.fillStyle = s.accent;
|
||
for (let i = 0; i < 5; i++) {
|
||
ctx.beginPath();
|
||
ctx.arc(W / 2 - 80 + i * 40, 940, 6, 0, Math.PI * 2);
|
||
ctx.fill();
|
||
}
|
||
ctx.globalAlpha = 1;
|
||
}
|
||
|
||
function roundRect(ctx, x, y, w, h, r) {
|
||
ctx.beginPath();
|
||
ctx.moveTo(x + r, y);
|
||
ctx.lineTo(x + w - r, y);
|
||
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
|
||
ctx.lineTo(x + w, y + h - r);
|
||
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
|
||
ctx.lineTo(x + r, y + h);
|
||
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
|
||
ctx.lineTo(x, y + r);
|
||
ctx.quadraticCurveTo(x, y, x + r, y);
|
||
ctx.closePath();
|
||
}
|
||
|
||
function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
|
||
if (!text) return;
|
||
const chars = text.split('');
|
||
let line = '';
|
||
let lineY = y;
|
||
for (let i = 0; i < chars.length; i++) {
|
||
const testLine = line + chars[i];
|
||
const metrics = ctx.measureText(testLine);
|
||
if (metrics.width > maxWidth && line.length > 0) {
|
||
ctx.fillText(line, x, lineY);
|
||
line = chars[i];
|
||
lineY += lineHeight;
|
||
} else {
|
||
line = testLine;
|
||
}
|
||
}
|
||
ctx.fillText(line, x, lineY);
|
||
}
|
||
|
||
// ─── Download Image ─────────────────────────────────────
|
||
function downloadImage() {
|
||
const canvas = document.getElementById('previewCanvas');
|
||
const link = document.createElement('a');
|
||
link.download = 'secret-message.png';
|
||
link.href = canvas.toDataURL('image/png');
|
||
link.click();
|
||
showToast(t('toastSaved'));
|
||
}
|
||
|
||
// ─── Share WhatsApp ─────────────────────────────────────
|
||
function shareWhatsApp() {
|
||
const text = t('whatsAppText').replace('{code}', encodedText);
|
||
const url = `https://wa.me/?text=${encodeURIComponent(text)}`;
|
||
window.open(url, '_blank');
|
||
}
|
||
|
||
// ─── Share Instagram ────────────────────────────────────
|
||
async function shareInstagram() {
|
||
const canvas = document.getElementById('previewCanvas');
|
||
|
||
// Try Web Share API first (works on mobile)
|
||
if (navigator.share && navigator.canShare) {
|
||
try {
|
||
const blob = await new Promise(res => canvas.toBlob(res, 'image/png'));
|
||
const file = new File([blob], 'secret-message.png', { type: 'image/png' });
|
||
if (navigator.canShare({ files: [file] })) {
|
||
await navigator.share({
|
||
title: '💌 Secret Message',
|
||
text: '🤫 Kannst du meine geheime Nachricht entschlüsseln?',
|
||
files: [file],
|
||
});
|
||
return;
|
||
}
|
||
} catch (e) {
|
||
// User cancelled or error – fall through
|
||
}
|
||
}
|
||
|
||
// Fallback: download the image and show instructions
|
||
downloadImage();
|
||
showToast(t('toastInstaFallback'));
|
||
}
|
||
|
||
// ─── Toast ──────────────────────────────────────────────
|
||
function showToast(msg) {
|
||
const toast = document.getElementById('toast');
|
||
toast.textContent = msg;
|
||
toast.classList.add('show');
|
||
setTimeout(() => toast.classList.remove('show'), 2500);
|
||
}
|
||
|
||
// ─── Floating Hearts ────────────────────────────────────
|
||
function createHearts() {
|
||
const container = document.getElementById('heartsBg');
|
||
const emojis = ['💕', '💗', '💖', '✨', '🌸', '💌', '🦋'];
|
||
for (let i = 0; i < 18; i++) {
|
||
const heart = document.createElement('span');
|
||
heart.className = 'heart';
|
||
heart.textContent = emojis[Math.floor(Math.random() * emojis.length)];
|
||
heart.style.left = Math.random() * 100 + '%';
|
||
heart.style.fontSize = (14 + Math.random() * 18) + 'px';
|
||
heart.style.animationDuration = (8 + Math.random() * 14) + 's';
|
||
heart.style.animationDelay = (Math.random() * 10) + 's';
|
||
container.appendChild(heart);
|
||
}
|
||
}
|
||
createHearts();
|
||
</script>
|
||
</body>
|
||
</html>
|