QR Oluşturucu Nasıl Yapılır ?
QR kodlar, günümüzde hızlı ve kolay bir şekilde bilgi paylaşımını sağlayan bir teknolojidir. Önceden tanımlanmış bir alana tıklayarak veya QR kodunu tarayarak, kullanıcılar bilgiye hızlıca erişebilirler. Bu makalede, QR kod oluşturma süreci hakkında detaylı bir bilgi verilecek ve farklı içerik türleri ile profesyonel tasarım seçenekleri konusunda bilgi sahibi olunacak.
1. İçerik Türü Seçimi (Dinamik ve Çeşitli)
QR kodunun temelini oluşturan verinin türünü belirlediğiniz alandır. Kullanıcılara geniş bir yelpazede seçenekler sunulmuştur:
- Metin / URL (Varsayılan): Herhangi bir metni veya web sitesi bağlantısını paylaşmak için en yaygın kullanılan seçenektir.
- Wi-Fi: Ağ adı (SSID), şifre ve güvenlik türünü (WPA/WEP) içeren bir kod oluşturarak, misafirlerinizin ağınıza anında bağlanmasını sağlar.
- vCard: Ad, telefon, e-posta gibi iletişim bilgilerini dijital kartvizit olarak paylaşmanıza olanak tanır. Tarandığında doğrudan telefon rehberine kaydedilebilir.
- E-posta: Önceden tanımlanmış alıcı, konu ve gövde metniyle otomatik olarak bir e-posta taslağı açılmasını sağlar.
- SMS: Belirli bir telefon numarasına hazır bir mesaj göndermek için kullanılır.
- Konum: Google Maps veya Apple Maps üzerinde belirli bir koordinatı açarak konum paylaşımını kolaylaştırır.
2. Özelleştirme Panelleri (Tasarım Sizin Elinizde)
QR kodunuzun görünümünü tamamen değiştirebileceğiniz, uygulamanın en güçlü olduğu bölümdür.
- Renk ve Tema:
- Ön Ayarlar: Hızlıca seçim yapabileceğiniz popüler renk paletleri (siyah-beyaz, renkli gradyanlar vb.).
- QR ve Arka Plan Rengi: Kodun kendisinin ve arka planının renklerini HEX kodlarıyla (#1a1a2e, #ffffff) belirleyebilirsiniz.
- Degrade (Gradyan) Desteği: QR koduna iki renk arasında yumuşak bir geçiş ekleyerek modern bir görünüm verebilirsiniz.
- Piksel Şekli: QR kodunu oluşturan küçük karelerin şeklini değiştirebilirsiniz. Seçenekler:
- Kare (Klasik)
- Yuvarlak (Daha yumuşak)
- Nokta (Retro)
- Ve diğer özelleştirilmiş stiller (Klasik-Y, Süper-Y).
- Köşe Kareleri (Corner Squares): Kodun üç köşesindeki büyük bulucu kareleri bağımsız olarak özelleştirebilirsiniz.
- Dış Şekil: Kare, Yuvarlak, Nokta gibi şekiller.
- İç Şekil: İçerideki küçük kare/nokta şekli.
- Renkler: Köşe karelerinin rengini kodun geri kalanından farklı bir renk (örneğin, logonuzun bir rengi) yapabilirsiniz.
3. Logo / Görsel Ekleme
Kodun tam ortasına marka logonuzu veya istediğiniz herhangi bir görseli (ikon) ekleyebilirsiniz. Bu, marka bilinirliğini artırmak için harikadır.
- "Tıkla ya da sürükle - Logo Ekle" alanı ile kullanımı oldukça basittir.
4. Boyut ve Kalite Kontrolleri (Profesyonel Çıktı)
Oluşturulan kodun nerede kullanılacağına bağlı olarak teknik ayarlarını yapmanızı sağlar:
- Çıktı Boyutu (px): Kodun piksel cinsinden genişliğini ve yüksekliğini (örneğin: 400px - 2000px arası) ayarlayın. Baskı için yüksek, dijital kullanım için düşük boyutlar seçilebilir.
- Kenar Boşluğu: Kodun etrafındaki beyaz (veya arka plan renginde) boşluğun kalınlığını belirler. Tarama başarısı için önemlidir.
- Hata Toleransı: Kodun bir kısmının zarar görmesi (örneğin, üzerine bir logo konulması veya kirlenmesi) durumunda dahi taranabilmesini sağlayan "Yedekleme/Düzeltme" seviyesidir.
- JPEG Kalitesi: Çıktı formatı olarak JPEG seçildiğinde, dosya boyutu ve görüntü netliği arasındaki dengeyi ayarlamanızı sağlar.
5. Canlı Önizleme ve İndirme
Önizleme Alanı: Tasarımda yaptığınız her değişiklik (renk, logo, şekil), sağdaki bu alanda anında güncellenir. "QR oluşturuluyor..." ibaresi ile dinamik bir yükleme gösterilir.
- İndirme Seçenekleri:
- PNG İndir (Ana Buton): En popüler, şeffaf arka plan desteği sunan kayıpsız format. Dijital kullanım ve birçok baskı işi için idealdir.
- JPEG İndir: Sıkıştırılmış görüntü formatı.
- Kopyala: Kodu doğrudan panoya kopyalayarak başka bir uygulamaya (Word, Photoshop vb.) hızlıca yapıştırmanızı sağlar.
"Bu uygulama, hem hızlı ve basit bir çözüm arayan son kullanıcılara hem de profesyonel ve markalı tasarımlar oluşturmak isteyen ajanslara/şirketlere hitap eder. Logonuzu ekleyebilmeniz, gradyan renkler kullanabilmeniz ve hata toleransını ayarlayabilmeniz, bu aracı piyasadaki standart oluşturuculardan ayırarak size tam kontrol ve güvenilirlik sunar."
QR kod oluşturma süreci, dinamik ve çeşitli içerik türleri ile profesyonel tasarım seçenekleri sunmaktadır. Herhangi bir veriyi veya web sitesi bağlantısını paylaşmak için en yaygın kullanılan seçenektir. Bu makalede, QR kod oluşturma süreci hakkında detaylı bir bilgi verildi ve farklı içerik türleri ile profesyonel tasarım seçenekleri konusunda bilgi sahibi olunacak.
Örnek Kod İçinden Fotoraflar
Örnek kod
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QR Üretici</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500;600&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<style>
:root {
--bg: #0a0a0f;
--surface: #12121a;
--surface2: #1a1a26;
--border: rgba(255,255,255,0.07);
--border-hover: rgba(255,255,255,0.15);
--accent: #7c6fff;
--accent2: #ff6b9d;
--text: #f0eff8;
--muted: #6e6d82;
--muted2: #9896b0;
--radius: 16px;
--radius-sm: 10px;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
font-family: 'DM Sans', sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
overflow-x: hidden;
}
/* Animated bg */
body::before {
content: '';
position: fixed;
top: -40%;
left: -20%;
width: 70%;
height: 70%;
background: radial-gradient(ellipse, rgba(124,111,255,0.12) 0%, transparent 70%);
pointer-events: none;
z-index: 0;
animation: floatBg 12s ease-in-out infinite alternate;
}
body::after {
content: '';
position: fixed;
bottom: -40%;
right: -20%;
width: 60%;
height: 60%;
background: radial-gradient(ellipse, rgba(255,107,157,0.09) 0%, transparent 70%);
pointer-events: none;
z-index: 0;
animation: floatBg 15s ease-in-out infinite alternate-reverse;
}
@keyframes floatBg {
from { transform: translate(0,0) scale(1); }
to { transform: translate(3%,5%) scale(1.1); }
}
.page {
position: relative;
z-index: 1;
max-width: 1100px;
margin: 0 auto;
padding: 2rem 1.25rem 4rem;
}
header {
text-align: center;
padding: 2.5rem 0 2rem;
animation: fadeUp 0.6s ease both;
}
.logo-mark {
display: inline-flex;
align-items: center;
gap: 10px;
margin-bottom: 1.2rem;
}
.logo-icon {
width: 42px; height: 42px;
background: linear-gradient(135deg, var(--accent), var(--accent2));
border-radius: 12px;
display: flex; align-items: center; justify-content: center;
font-size: 22px;
}
.logo-text {
font-size: 1.1rem;
font-weight: 600;
letter-spacing: -0.01em;
background: linear-gradient(135deg, var(--accent), var(--accent2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
h1 {
font-size: clamp(2rem, 5vw, 3.2rem);
font-weight: 600;
letter-spacing: -0.03em;
line-height: 1.1;
margin-bottom: 0.6rem;
}
h1 span {
background: linear-gradient(135deg, var(--accent) 30%, var(--accent2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle {
font-size: 1rem;
color: var(--muted2);
font-weight: 400;
}
/* Layout */
.layout {
display: grid;
grid-template-columns: 1fr 380px;
gap: 1.25rem;
align-items: start;
animation: fadeUp 0.7s 0.1s ease both;
}
@media (max-width: 820px) {
.layout {
display: flex;
flex-direction: column;
}
.preview-col {
order: -1;
position: static !important;
top: auto !important;
width: 100%;
}
.controls-col {
width: 100%;
}
}
/* Card */
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 1.5rem;
transition: border-color 0.2s;
}
.card:hover { border-color: var(--border-hover); }
.card-title {
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 8px;
}
.card-title::after {
content: '';
flex: 1;
height: 1px;
background: var(--border);
}
/* Type Tabs */
.tabs {
display: flex;
gap: 6px;
flex-wrap: wrap;
margin-bottom: 1.25rem;
}
.tab {
padding: 7px 14px;
border-radius: 8px;
border: 1px solid var(--border);
background: transparent;
color: var(--muted2);
font-size: 0.82rem;
font-family: 'DM Sans', sans-serif;
font-weight: 500;
cursor: pointer;
transition: all 0.18s;
}
.tab:hover { border-color: var(--border-hover); color: var(--text); }
.tab.active {
background: linear-gradient(135deg, var(--accent), var(--accent2));
border-color: transparent;
color: #fff;
}
/* Fields */
.field { margin-bottom: 1rem; }
.field:last-child { margin-bottom: 0; }
label {
display: block;
font-size: 0.8rem;
color: var(--muted2);
margin-bottom: 5px;
font-weight: 500;
}
input[type="text"], input[type="number"], input[type="email"], select, textarea {
width: 100%;
background: var(--surface2);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 10px 13px;
font-size: 0.9rem;
font-family: 'DM Mono', monospace;
color: var(--text);
outline: none;
transition: border-color 0.18s, box-shadow 0.18s;
}
input:focus, select:focus, textarea:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(124,111,255,0.12);
}
textarea { resize: vertical; min-height: 80px; line-height: 1.5; }
select option { background: var(--surface2); }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.grid3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }
/* Color input */
.color-wrap {
display: flex;
align-items: center;
gap: 10px;
background: var(--surface2);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 8px 12px;
cursor: pointer;
transition: border-color 0.18s;
}
.color-wrap:hover { border-color: var(--border-hover); }
.color-swatch {
width: 26px; height: 26px;
border-radius: 6px;
border: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
cursor: pointer;
position: relative;
overflow: hidden;
}
.color-swatch input[type="color"] {
position: absolute;
inset: -4px;
width: calc(100% + 8px);
height: calc(100% + 8px);
opacity: 0;
cursor: pointer;
border: none;
padding: 0;
}
.color-hex {
font-family: 'DM Mono', monospace;
font-size: 0.82rem;
color: var(--muted2);
flex: 1;
}
/* Pill toggles */
.pill-group {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.pill {
padding: 6px 13px;
border-radius: 999px;
border: 1px solid var(--border);
background: transparent;
color: var(--muted2);
font-size: 0.78rem;
font-family: 'DM Sans', sans-serif;
font-weight: 500;
cursor: pointer;
transition: all 0.16s;
}
.pill:hover { border-color: var(--border-hover); color: var(--text); }
.pill.active {
background: rgba(124,111,255,0.18);
border-color: rgba(124,111,255,0.5);
color: var(--accent);
}
/* Slider */
.slider-row {
display: flex;
align-items: center;
gap: 12px;
}
.slider-row input[type="range"] {
flex: 1;
-webkit-appearance: none;
height: 4px;
background: var(--surface2);
border-radius: 2px;
outline: none;
border: none;
box-shadow: none;
padding: 0;
}
.slider-row input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px; height: 16px;
border-radius: 50%;
background: linear-gradient(135deg, var(--accent), var(--accent2));
cursor: pointer;
box-shadow: 0 0 8px rgba(124,111,255,0.4);
}
.slider-val {
font-family: 'DM Mono', monospace;
font-size: 0.82rem;
color: var(--muted2);
min-width: 38px;
text-align: right;
}
/* Toggle switch */
.toggle-row {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
}
.toggle {
width: 40px; height: 22px;
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 11px;
position: relative;
cursor: pointer;
transition: background 0.2s, border-color 0.2s;
flex-shrink: 0;
}
.toggle.on {
background: linear-gradient(135deg, var(--accent), var(--accent2));
border-color: transparent;
}
.toggle::after {
content: '';
position: absolute;
width: 16px; height: 16px;
background: white;
border-radius: 50%;
top: 2px; left: 2px;
transition: transform 0.2s;
box-shadow: 0 1px 4px rgba(0,0,0,0.3);
}
.toggle.on::after { transform: translateX(18px); }
.toggle-label { font-size: 0.85rem; color: var(--muted2); }
/* Presets */
.preset-strip {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 1rem;
}
.preset {
width: 32px; height: 32px;
border-radius: 8px;
cursor: pointer;
border: 2px solid transparent;
transition: transform 0.15s, border-color 0.15s;
position: relative;
}
.preset:hover { transform: scale(1.15); }
.preset.active { border-color: white; }
/* Logo upload */
.upload-zone {
border: 1.5px dashed var(--border-hover);
border-radius: var(--radius-sm);
padding: 1.25rem;
text-align: center;
cursor: pointer;
transition: border-color 0.18s, background 0.18s;
position: relative;
}
.upload-zone:hover {
border-color: var(--accent);
background: rgba(124,111,255,0.04);
}
.upload-zone input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.upload-icon { font-size: 1.6rem; margin-bottom: 6px; }
.upload-text { font-size: 0.82rem; color: var(--muted); }
.upload-text span { color: var(--accent); }
.logo-prev {
display: none;
align-items: center;
gap: 10px;
margin-top: 10px;
background: var(--surface2);
border-radius: 8px;
padding: 8px 12px;
}
.logo-prev img { width: 36px; height: 36px; object-fit: contain; border-radius: 6px; }
.logo-prev-name { font-size: 0.82rem; color: var(--muted2); flex: 1; }
.remove-btn {
background: none;
border: 1px solid rgba(255,100,100,0.3);
color: #ff6b6b;
border-radius: 6px;
padding: 3px 9px;
font-size: 0.75rem;
cursor: pointer;
font-family: 'DM Sans', sans-serif;
transition: background 0.15s;
}
.remove-btn:hover { background: rgba(255,100,100,0.1); }
/* Preview panel */
.preview-col {
position: sticky;
top: 1.5rem;
}
@media (max-width: 820px) {
.preview-col {
position: static;
top: auto;
}
.qr-frame {
max-width: 100%;
}
#qr-canvas {
max-width: 100%;
}
}
.qr-display {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 2rem 1.5rem 1.5rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
width: 100%;
}
.qr-frame {
background: white;
border-radius: 16px;
padding: 20px;
width: 100%;
max-width: 280px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
box-shadow: 0 8px 40px rgba(0,0,0,0.4), 0 0 80px rgba(124,111,255,0.08);
}
#qr-canvas {
display: block;
border-radius: 8px;
width: 100%;
height: auto;
max-width: 240px;
}
.qr-meta {
font-size: 0.75rem;
color: var(--muted);
font-family: 'DM Mono', monospace;
text-align: center;
}
/* Download buttons */
.dl-buttons {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}
.dl-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 11px;
border-radius: 10px;
border: 1px solid var(--border);
background: var(--surface2);
color: var(--text);
font-size: 0.88rem;
font-family: 'DM Sans', sans-serif;
font-weight: 500;
cursor: pointer;
transition: all 0.18s;
}
.dl-btn:hover { border-color: var(--border-hover); background: rgba(255,255,255,0.05); }
.dl-btn.primary {
background: linear-gradient(135deg, var(--accent), var(--accent2));
border-color: transparent;
color: white;
}
.dl-btn.primary:hover { opacity: 0.88; transform: translateY(-1px); }
.dl-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.dl-icon { font-size: 1rem; }
/* Copy feedback */
.copy-toast {
position: fixed;
bottom: 2rem;
left: 50%;
transform: translateX(-50%) translateY(20px);
background: var(--accent);
color: white;
padding: 10px 20px;
border-radius: 999px;
font-size: 0.85rem;
font-weight: 500;
opacity: 0;
transition: all 0.3s;
pointer-events: none;
z-index: 999;
}
.copy-toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* Loading overlay on QR frame */
.qr-loading {
display: none;
position: absolute;
inset: 0;
border-radius: 16px;
background: rgba(10,10,15,0.80);
backdrop-filter: blur(3px);
flex-direction: column;
align-items: center;
justify-content: center;
gap: 14px;
z-index: 10;
}
.qr-loading.visible { display: flex; }
.spinner {
width: 36px; height: 36px;
border: 2.5px solid rgba(124,111,255,0.2);
border-top-color: var(--accent);
border-right-color: var(--accent2);
border-radius: 50%;
animation: spin 0.75s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-label {
font-size: 0.82rem;
font-weight: 500;
color: var(--muted2);
letter-spacing: 0.04em;
font-family: 'DM Mono', monospace;
}
.loading-dots::after {
content: '';
animation: ldots 1.2s steps(4,end) infinite;
}
@keyframes ldots {
0% { content: ''; }
25% { content: '.'; }
50% { content: '..'; }
75% { content: '...'; }
100% { content: ''; }
}
/* Pending badge above preview */
.pending-badge {
display: none;
align-items: center;
gap: 6px;
font-size: 0.75rem;
color: var(--muted2);
font-family: 'DM Mono', monospace;
padding: 5px 12px;
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 999px;
}
.pending-badge.visible { display: flex; }
.pending-dot {
width: 6px; height: 6px;
border-radius: 50%;
background: var(--accent2);
animation: pdot 1s ease-in-out infinite;
}
@keyframes pdot {
0%,100% { opacity:1; transform:scale(1); }
50% { opacity:0.3; transform:scale(0.6); }
}
.qr-frame { position: relative; }
@keyframes fadeUp {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
.controls-col {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Section divider inside card */
.divider {
height: 1px;
background: var(--border);
margin: 1rem 0;
}
</style>
</head>
<body>
<div class="page">
<header>
<div class="logo-mark">
<div class="logo-icon">⬛</div>
<span class="logo-text">QR Studio</span>
</div>
<h1>Gelişmiş <span>QR Üretici</span></h1>
<p class="subtitle">Her ayrıntısını özelleştir, anında indir.</p>
</header>
<div class="layout">
<!-- LEFT: Controls -->
<div class="controls-col">
<!-- Content Type -->
<div class="card">
<div class="card-title">İçerik Türü</div>
<div class="tabs" id="type-tabs">
<button class="tab active" data-val="text">Metin / URL</button>
<button class="tab" data-val="wifi">Wi-Fi</button>
<button class="tab" data-val="vcard">vCard</button>
<button class="tab" data-val="email">E-posta</button>
<button class="tab" data-val="sms">SMS</button>
<button class="tab" data-val="geo">Konum</button>
</div>
<div id="content-fields">
<div class="field">
<label>Metin veya URL</label>
<textarea id="main-text" placeholder="https://example.com veya herhangi bir metin..."></textarea>
</div>
</div>
</div>
<!-- Colors -->
<div class="card">
<div class="card-title">Renk ve Tema</div>
<div class="field">
<label>Ön ayarlar</label>
<div class="preset-strip" id="presets"></div>
</div>
<div class="grid2">
<div class="field">
<label>QR rengi</label>
<div class="color-wrap" onclick="this.querySelector('input').click()">
<div class="color-swatch" id="fg-swatch" style="background:#1a1a2e">
<input type="color" id="fg-color" value="#1a1a2e">
</div>
<span class="color-hex" id="fg-hex">#1a1a2e</span>
</div>
</div>
<div class="field">
<label>Arka plan</label>
<div class="color-wrap" onclick="this.querySelector('input').click()">
<div class="color-swatch" id="bg-swatch" style="background:#ffffff">
<input type="color" id="bg-color" value="#ffffff">
</div>
<span class="color-hex" id="bg-hex">#ffffff</span>
</div>
</div>
</div>
<div class="toggle-row">
<div class="toggle" id="grad-toggle"></div>
<span class="toggle-label">Degrade renk kullan</span>
</div>
<div id="grad-section" style="display:none;">
<div class="grid2">
<div class="field">
<label>Başlangıç rengi</label>
<div class="color-wrap" onclick="this.querySelector('input').click()">
<div class="color-swatch" id="g1-swatch" style="background:#7c6fff">
<input type="color" id="grad1" value="#7c6fff">
</div>
<span class="color-hex" id="g1-hex">#7c6fff</span>
</div>
</div>
<div class="field">
<label>Bitiş rengi</label>
<div class="color-wrap" onclick="this.querySelector('input').click()">
<div class="color-swatch" id="g2-swatch" style="background:#ff6b9d">
<input type="color" id="grad2" value="#ff6b9d">
</div>
<span class="color-hex" id="g2-hex">#ff6b9d</span>
</div>
</div>
</div>
<div class="field">
<label>Degrade yönü</label>
<div class="pill-group" id="grad-dir">
<button class="pill active" data-val="h">Yatay</button>
<button class="pill" data-val="v">Dikey</button>
<button class="pill" data-val="d">Çapraz</button>
<button class="pill" data-val="r">Radyal</button>
</div>
</div>
</div>
</div>
<!-- Shapes -->
<div class="card">
<div class="card-title">Piksel Şekli</div>
<div class="pill-group" id="dot-style">
<button class="pill active" data-val="square">Kare</button>
<button class="pill" data-val="rounded">Yuvarlak</button>
<button class="pill" data-val="dots">Nokta</button>
<button class="pill" data-val="classy">Klasik</button>
<button class="pill" data-val="classy-r">Klasik-Y</button>
<button class="pill" data-val="xround">Süper-Y</button>
</div>
<div class="divider"></div>
<div class="card-title" style="font-size:0.68rem;">Köşe Kareleri</div>
<div class="grid2">
<div class="field">
<label>Dış şekil</label>
<div class="pill-group" id="corner-outer">
<button class="pill active" data-val="square">Kare</button>
<button class="pill" data-val="round">Yuvarlak</button>
<button class="pill" data-val="dot">Nokta</button>
</div>
</div>
<div class="field">
<label>İç şekil</label>
<div class="pill-group" id="corner-inner">
<button class="pill active" data-val="square">Kare</button>
<button class="pill" data-val="dot">Nokta</button>
</div>
</div>
</div>
<div class="grid2">
<div class="field">
<label>Köşe rengi</label>
<div class="color-wrap" onclick="this.querySelector('input').click()">
<div class="color-swatch" id="co-swatch" style="background:#1a1a2e">
<input type="color" id="corner-color" value="#1a1a2e">
</div>
<span class="color-hex" id="co-hex">#1a1a2e</span>
</div>
</div>
<div class="field">
<label>İç rengi</label>
<div class="color-wrap" onclick="this.querySelector('input').click()">
<div class="color-swatch" id="ci-swatch" style="background:#1a1a2e">
<input type="color" id="corner-inner-color" value="#1a1a2e">
</div>
<span class="color-hex" id="ci-hex">#1a1a2e</span>
</div>
</div>
</div>
</div>
<!-- Logo -->
<div class="card">
<div class="card-title">Logo / Görsel</div>
<div class="upload-zone" id="upload-zone">
<input type="file" accept="image/*" id="logo-file">
<div class="upload-icon">🖼️</div>
<div class="upload-text">Tıkla ya da sürükle — <span>Logo Ekle</span></div>
</div>
<div class="logo-prev" id="logo-prev">
<img id="logo-thumb" src="" alt="">
<span class="logo-prev-name" id="logo-name">logo.png</span>
<button class="remove-btn" id="remove-logo">Kaldır</button>
</div>
<div id="logo-controls" style="display:none; margin-top:10px;">
<div class="field">
<label>Logo boyutu</label>
<div class="slider-row">
<input type="range" min="5" max="38" value="20" id="logo-size">
<span class="slider-val" id="logo-size-v">20%</span>
</div>
</div>
<div class="field">
<label>Kenar boşluğu</label>
<div class="slider-row">
<input type="range" min="0" max="20" value="5" id="logo-pad">
<span class="slider-val" id="logo-pad-v">5px</span>
</div>
</div>
</div>
</div>
<!-- Quality -->
<div class="card">
<div class="card-title">Boyut ve Kalite</div>
<div class="field">
<label>Çıktı boyutu (px)</label>
<div class="slider-row">
<input type="range" min="128" max="1024" step="32" value="400" id="qr-size">
<span class="slider-val" id="qr-size-v">400</span>
</div>
</div>
<div class="field">
<label>Kenar boşluğu</label>
<div class="slider-row">
<input type="range" min="0" max="6" step="1" value="1" id="qr-margin">
<span class="slider-val" id="qr-margin-v">1</span>
</div>
</div>
<div class="field">
<label>Hata toleransı</label>
<div class="pill-group" id="ecc-group">
<button class="pill" data-val="L">L (7%)</button>
<button class="pill active" data-val="M">M (15%)</button>
<button class="pill" data-val="Q">Q (25%)</button>
<button class="pill" data-val="H">H (30%)</button>
</div>
</div>
<div class="field">
<label>JPEG kalitesi (yalnızca JPEG için)</label>
<div class="slider-row">
<input type="range" min="50" max="100" step="5" value="92" id="jpeg-q">
<span class="slider-val" id="jpeg-q-v">92%</span>
</div>
</div>
</div>
</div><!-- /controls-col -->
<!-- RIGHT: Preview -->
<div class="preview-col">
<div class="qr-display">
<div class="card-title" style="align-self:flex-start; width:100%;">Önizleme</div>
<div class="pending-badge" id="pending-badge">
<div class="pending-dot"></div>
<span>Değişiklikler bekleniyor</span>
</div>
<div class="qr-frame" id="qr-frame">
<canvas id="qr-canvas" width="400" height="400"></canvas>
<div class="qr-loading" id="qr-loading">
<div class="spinner"></div>
<span class="loading-label">QR oluşturuluyor<span class="loading-dots"></span></span>
</div>
</div>
<div class="qr-meta" id="qr-meta">400 × 400 px</div>
<div class="dl-buttons">
<button class="dl-btn primary" id="dl-png">
<span class="dl-icon">⬇</span> PNG İndir
</button>
<div class="dl-row">
<button class="dl-btn" id="dl-jpg">
<span class="dl-icon">📷</span> JPEG İndir
</button>
<button class="dl-btn" id="dl-copy">
<span class="dl-icon">📋</span> Kopyala
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="copy-toast" id="toast">Panoya kopyalandı!</div>
<script>
const PRESETS = [
{fg:'#1a1a2e',bg:'#ffffff',g:false},
{fg:'#ffffff',bg:'#1e1b4b',g:false},
{fg:'#0f172a',bg:'#f8fafc',g:false},
{fg:'#16a34a',bg:'#f0fdf4',g:false},
{fg:'#0891b2',bg:'#ecfeff',g:false},
{fg:'#dc2626',bg:'#fff1f2',g:false},
{fg:'#7c6fff',bg:'#ffffff',g:true,g1:'#7c6fff',g2:'#ff6b9d'},
{fg:'#f59e0b',bg:'#fffbeb',g:true,g1:'#f59e0b',g2:'#ef4444'},
{fg:'#06b6d4',bg:'#f0f9ff',g:true,g1:'#06b6d4',g2:'#6366f1'},
];
const TYPES = {
text: () => document.getElementById('main-text').value || ' ',
wifi: () => `WIFI:T:${gv('wifi-enc')};S:${gv('wifi-ssid')};P:${gv('wifi-pass')};;`,
vcard: () => `BEGIN:VCARD\nVERSION:3.0\nFN:${gv('vc-name')}\nTEL:${gv('vc-tel')}\nEMAIL:${gv('vc-email')}\nORG:${gv('vc-org')}\nURL:${gv('vc-url')}\nEND:VCARD`,
email: () => `mailto:${gv('em-to')}?subject=${encodeURIComponent(gv('em-sub'))}&body=${encodeURIComponent(gv('em-body'))}`,
sms: () => `smsto:${gv('sms-num')}:${gv('sms-msg')}`,
geo: () => `geo:${gv('geo-lat')},${gv('geo-lng')}`,
};
const FIELD_TEMPLATES = {
wifi: `
<div class="field"><label>SSID (Ağ adı)</label><input type="text" id="wifi-ssid" placeholder="Ağ adı"></div>
<div class="field"><label>Şifre</label><input type="text" id="wifi-pass" placeholder="Wi-Fi şifresi"></div>
<div class="field"><label>Şifreleme</label>
<div class="pill-group"><button class="pill active" data-val="WPA" id="wifi-enc">WPA/WPA2</button><button class="pill" data-val="WEP">WEP</button><button class="pill" data-val="nopass">Açık</button></div>
</div>`,
vcard: `
<div class="grid2">
<div class="field"><label>Ad Soyad</label><input type="text" id="vc-name" placeholder="Ad Soyad"></div>
<div class="field"><label>Şirket</label><input type="text" id="vc-org" placeholder="Şirket"></div>
</div>
<div class="grid2">
<div class="field"><label>Telefon</label><input type="text" id="vc-tel" placeholder="+90..."></div>
<div class="field"><label>E-posta</label><input type="text" id="vc-email" placeholder="e@..."></div>
</div>
<div class="field"><label>Web sitesi</label><input type="text" id="vc-url" placeholder="https://..."></div>`,
email: `
<div class="field"><label>Alıcı</label><input type="text" id="em-to" placeholder="alici@eposta.com"></div>
<div class="field"><label>Konu</label><input type="text" id="em-sub" placeholder="Konu..."></div>
<div class="field"><label>Mesaj</label><textarea id="em-body" placeholder="Mesaj..."></textarea></div>`,
sms: `
<div class="field"><label>Numara</label><input type="text" id="sms-num" placeholder="+905..."></div>
<div class="field"><label>Mesaj</label><textarea id="sms-msg" placeholder="SMS içeriği..."></textarea></div>`,
geo: `
<div class="grid2">
<div class="field"><label>Enlem</label><input type="text" id="geo-lat" placeholder="41.0082"></div>
<div class="field"><label>Boylam</label><input type="text" id="geo-lng" placeholder="28.9784"></div>
</div>`,
text: `<div class="field"><label>Metin veya URL</label><textarea id="main-text" placeholder="https://example.com veya herhangi bir metin..."></textarea></div>`,
};
const S = {
type:'text', fg:'#1a1a2e', bg:'#ffffff',
gradOn:false, g1:'#7c6fff', g2:'#ff6b9d', gdir:'h',
dot:'square', co:'square', ci:'square',
cornerColor:'#1a1a2e', cornerInnerColor:'#1a1a2e',
logo:null, logoSize:20, logoPad:5,
size:400, margin:1, ecc:'M', jpegQ:92,
};
let logoUrl = null;
function gv(id){ const e=document.getElementById(id); return e?e.value:''; }
function el(id){ return document.getElementById(id); }
function syncColor(inputId, swatchId, hexId){
const inp = el(inputId), sw = el(swatchId), hx = el(hexId);
if(!inp||!sw||!hx) return;
sw.style.background = inp.value;
hx.textContent = inp.value;
}
// Init presets
const presetStrip = el('presets');
PRESETS.forEach((p,i) => {
const d = document.createElement('div');
d.className = 'preset' + (i===0?' active':'');
d.style.background = p.g ? `linear-gradient(135deg,${p.g1},${p.g2})` : p.fg;
if(p.bg==='#ffffff') d.style.outline='1px solid rgba(255,255,255,0.15)';
d.addEventListener('click', () => {
presetStrip.querySelectorAll('.preset').forEach(x=>x.classList.remove('active'));
d.classList.add('active');
S.fg=p.fg; S.bg=p.bg;
el('fg-color').value=p.fg; el('bg-color').value=p.bg;
syncColor('fg-color','fg-swatch','fg-hex');
syncColor('bg-color','bg-swatch','bg-hex');
S.gradOn=!!p.g;
el('grad-toggle').classList.toggle('on',S.gradOn);
el('grad-section').style.display=S.gradOn?'block':'none';
if(p.g){ S.g1=p.g1; S.g2=p.g2; el('grad1').value=p.g1; el('grad2').value=p.g2; syncColor('grad1','g1-swatch','g1-hex'); syncColor('grad2','g2-swatch','g2-hex'); }
scheduleRender();
});
presetStrip.appendChild(d);
});
// Pill groups
function setupPills(groupId, key){
const g = el(groupId);
if(!g) return;
g.querySelectorAll('.pill').forEach(b => {
b.addEventListener('click', () => {
g.querySelectorAll('.pill').forEach(x=>x.classList.remove('active'));
b.classList.add('active');
S[key] = b.dataset.val;
scheduleRender();
});
});
}
setupPills('dot-style','dot');
setupPills('corner-outer','co');
setupPills('corner-inner','ci');
setupPills('ecc-group','ecc');
setupPills('grad-dir','gdir');
// Type tabs
el('type-tabs').querySelectorAll('.tab').forEach(btn => {
btn.addEventListener('click', () => {
el('type-tabs').querySelectorAll('.tab').forEach(x=>x.classList.remove('active'));
btn.classList.add('active');
S.type = btn.dataset.val;
el('content-fields').innerHTML = FIELD_TEMPLATES[S.type]||FIELD_TEMPLATES.text;
el('content-fields').querySelectorAll('input,textarea').forEach(i=>i.addEventListener('input',scheduleRender));
if(S.type==='wifi') setupPills('wifi-enc-group','_dummy');
scheduleRender();
});
});
// Color inputs
['fg','bg','g1','g2','co','ci'].forEach(k => {
const map = {fg:['fg-color','fg-swatch','fg-hex','fg'], bg:['bg-color','bg-swatch','bg-hex','bg'], g1:['grad1','g1-swatch','g1-hex','g1'], g2:['grad2','g2-swatch','g2-hex','g2'], co:['corner-color','co-swatch','co-hex','cornerColor'], ci:['corner-inner-color','ci-swatch','ci-hex','cornerInnerColor']};
const [inpId, swId, hxId, stateKey] = map[k];
const inp = el(inpId);
if(!inp) return;
inp.addEventListener('input', () => {
S[stateKey] = inp.value;
syncColor(inpId, swId, hxId);
scheduleRender();
});
});
// Gradient toggle
el('grad-toggle').addEventListener('click', () => {
S.gradOn = !S.gradOn;
el('grad-toggle').classList.toggle('on', S.gradOn);
el('grad-section').style.display = S.gradOn ? 'block' : 'none';
scheduleRender();
});
// Sliders
function slider(id, valId, key, suffix, cb){
const s=el(id), v=el(valId);
if(!s) return;
s.addEventListener('input', () => {
S[key]=parseInt(s.value); v.textContent=s.value+suffix;
if(cb) cb(); else scheduleRender();
});
}
slider('qr-size','qr-size-v','size','');
slider('qr-margin','qr-margin-v','margin','');
slider('logo-size','logo-size-v','logoSize','%');
slider('logo-pad','logo-pad-v','logoPad','px');
slider('jpeg-q','jpeg-q-v','jpegQ','%');
// Logo
el('logo-file').addEventListener('change', e => {
const f=e.target.files[0]; if(!f) return;
const r=new FileReader();
r.onload=ev=>{
logoUrl=ev.target.result; S.logo=true;
el('logo-prev').style.display='flex';
el('logo-thumb').src=logoUrl;
el('logo-name').textContent=f.name;
el('logo-controls').style.display='block';
scheduleRender();
};
r.readAsDataURL(f);
});
el('remove-logo').addEventListener('click', () => {
logoUrl=null; S.logo=false;
el('logo-prev').style.display='none';
el('logo-controls').style.display='none';
el('logo-file').value='';
scheduleRender();
});
// Main text
el('main-text').addEventListener('input', scheduleRender);
// --- DEBOUNCED RENDER SYSTEM ---
let renderTimer = null;
let isFirstRender = true;
function scheduleRender(delay) {
const ms = (delay !== undefined) ? delay : 600;
// Show pending badge immediately
el('pending-badge').classList.add('visible');
clearTimeout(renderTimer);
renderTimer = setTimeout(() => {
el('pending-badge').classList.remove('visible');
el('qr-loading').classList.add('visible');
// Small timeout so browser paints loading screen before heavy work
setTimeout(() => { render(); }, 30);
}, ms);
}
function render(){
const text = TYPES[S.type] ? TYPES[S.type]() : ' ';
if(!text.trim()) return;
const size = S.size;
const eccMap = {L:QRCode.CorrectLevel.L,M:QRCode.CorrectLevel.M,Q:QRCode.CorrectLevel.Q,H:QRCode.CorrectLevel.H};
const tmp = document.createElement('div');
tmp.style.cssText='position:absolute;opacity:0;pointer-events:none;';
document.body.appendChild(tmp);
try {
new QRCode(tmp, {text, width:size, height:size, correctLevel:eccMap[S.ecc]||QRCode.CorrectLevel.M});
} catch(e){ document.body.removeChild(tmp); el('qr-loading').classList.remove('visible'); return; }
const tmpC = tmp.querySelector('canvas');
if(!tmpC){ document.body.removeChild(tmp); el('qr-loading').classList.remove('visible'); return; }
const ctx2 = tmpC.getContext('2d');
const imgD = ctx2.getImageData(0,0,tmpC.width,tmpC.height);
const data = imgD.data;
const qrW = tmpC.width;
document.body.removeChild(tmp);
function isBlack(px,py){
const i=(Math.floor(py)*qrW+Math.floor(px))*4;
return data[i]<128;
}
const canvas = el('qr-canvas');
canvas.width=size; canvas.height=size;
const ctx = canvas.getContext('2d');
ctx.fillStyle = S.bg;
ctx.fillRect(0,0,size,size);
const cells = qrW;
const ms = size/cells;
const mp = S.margin * ms * 0.2;
let fillColor;
if(S.gradOn){
const d=S.gdir;
let g;
if(d==='r') g=ctx.createRadialGradient(size/2,size/2,0,size/2,size/2,size*0.6);
else if(d==='v') g=ctx.createLinearGradient(0,0,0,size);
else if(d==='d') g=ctx.createLinearGradient(0,0,size,size);
else g=ctx.createLinearGradient(0,0,size,0);
g.addColorStop(0,S.g1); g.addColorStop(1,S.g2);
fillColor=g;
} else {
fillColor=S.fg;
}
const finderZones=[[0,0],[cells-7,0],[0,cells-7]];
function inFinder(c,r){ return finderZones.some(([fc,fr])=>c>=fc&&c<=fc+6&&r>=fr&&r<=fr+6); }
for(let r=0;r<cells;r++){
for(let c=0;c<cells;c++){
if(!isBlack(c*ms+ms/2,r*ms+ms/2)) continue;
if(inFinder(c,r)) continue;
drawDot(ctx, c*ms+mp, r*ms+mp, ms-mp*2, S.dot, fillColor);
}
}
finderZones.forEach(([fc,fr])=>drawFinder(ctx,fc,fr,ms,cells));
if(logoUrl&&S.logo){
const img=new Image();
img.onload=()=>{
const ls=size*(S.logoSize/100);
const lx=(size-ls)/2, ly=(size-ls)/2;
const pad=S.logoPad;
ctx.fillStyle=S.bg;
ctx.beginPath();
roundRect(ctx,lx-pad,ly-pad,ls+pad*2,ls+pad*2,8);
ctx.fill();
ctx.drawImage(img,lx,ly,ls,ls);
};
img.src=logoUrl;
}
el('qr-meta').textContent=`${size} × ${size} px · ${text.length} karakter`;
el('qr-loading').classList.remove('visible');
}
function drawDot(ctx,x,y,s,style,color){
ctx.fillStyle=color;
const r=s/2, cx=x+r, cy=y+r;
ctx.beginPath();
if(style==='square'){ ctx.rect(x,y,s,s); }
else if(style==='rounded'){ roundRect(ctx,x,y,s,s,s*0.25); }
else if(style==='dots'){ ctx.arc(cx,cy,r*0.85,0,2*Math.PI); }
else if(style==='classy'){
ctx.moveTo(x+s*0.3,y); ctx.lineTo(x+s,y); ctx.lineTo(x+s,y+s*0.7);
ctx.lineTo(x+s*0.7,y+s); ctx.lineTo(x,y+s); ctx.lineTo(x,y+s*0.3); ctx.closePath();
}
else if(style==='classy-r'){
const cr=s*0.28;
ctx.moveTo(x+s*0.35,y); ctx.lineTo(x+s-cr,y); ctx.quadraticCurveTo(x+s,y,x+s,y+cr);
ctx.lineTo(x+s,y+s*0.65); ctx.lineTo(x+s*0.65,y+s); ctx.lineTo(x+cr,y+s);
ctx.quadraticCurveTo(x,y+s,x,y+s-cr); ctx.lineTo(x,y+s*0.35); ctx.closePath();
}
else if(style==='xround'){ roundRect(ctx,x,y,s,s,s*0.42); }
ctx.fill();
}
function drawFinder(ctx,fc,fr,ms,cells){
const x=fc*ms, y=fr*ms;
const s7=ms*7,s5=ms*5,s3=ms*3;
const oc=S.co, ic=S.ci;
ctx.fillStyle=S.cornerColor;
ctx.beginPath();
if(oc==='round') roundRect(ctx,x,y,s7,s7,ms*1.4);
else if(oc==='dot'){ ctx.arc(x+s7/2,y+s7/2,s7/2,0,2*Math.PI); }
else ctx.rect(x,y,s7,s7);
ctx.fill();
ctx.fillStyle=S.bg;
if(oc==='round') roundRect(ctx,x+ms,y+ms,s5,s5,ms);
else ctx.fillRect(x+ms,y+ms,s5,s5);
ctx.fill();
ctx.fillStyle=S.cornerInnerColor;
ctx.beginPath();
if(ic==='dot'){ ctx.arc(x+s7/2,y+s7/2,s3/2,0,2*Math.PI); }
else if(oc==='round') roundRect(ctx,x+ms*2,y+ms*2,s3,s3,ms*0.5);
else ctx.rect(x+ms*2,y+ms*2,s3,s3);
ctx.fill();
}
function roundRect(ctx,x,y,w,h,r){
if(ctx.roundRect){ ctx.roundRect(x,y,w,h,r); return; }
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);
}
// Downloads
el('dl-png').addEventListener('click',()=>{
const a=document.createElement('a');
a.download='qr-code.png';
a.href=el('qr-canvas').toDataURL('image/png');
a.click();
});
el('dl-jpg').addEventListener('click',()=>{
const c=el('qr-canvas');
const tmp=document.createElement('canvas');
tmp.width=c.width; tmp.height=c.height;
const tc=tmp.getContext('2d');
tc.fillStyle=S.bg;
tc.fillRect(0,0,tmp.width,tmp.height);
tc.drawImage(c,0,0);
const a=document.createElement('a');
a.download='qr-code.jpg';
a.href=tmp.toDataURL('image/jpeg',S.jpegQ/100);
a.click();
});
el('dl-copy').addEventListener('click',()=>{
el('qr-canvas').toBlob(async blob=>{
try{
await navigator.clipboard.write([new ClipboardItem({'image/png':blob})]);
showToast('Panoya kopyalandı!');
} catch(e){ showToast('Kopyalanamadı — PNG\'yi indir'); }
});
});
function showToast(msg){
const t=el('toast');
t.textContent=msg;
t.classList.add('show');
setTimeout(()=>t.classList.remove('show'),2200);
}
scheduleRender(0);
</script>
</body>
</html>```
Konuyu Yanıtla
Markdown destekler · Alıntı, kod, liste kullanabilirsinizKonuyu yanıtlamak için giriş yapmalısınız.