by3axap commited on
Commit
e3067e8
·
verified ·
1 Parent(s): 00ff2ed

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +90 -26
templates/index.html CHANGED
@@ -1,22 +1,41 @@
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
- <title>OpArt Dither</title>
5
  <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&display=swap" rel="stylesheet">
6
  <style>
7
  :root { --border: #999; --pixel-font: 'Press Start 2P', cursive; --mono-font: 'VT323', monospace; --bg: #f0f0f0; }
8
  body { margin: 0; padding: 0; font-family: var(--mono-font); display: flex; height: 100vh; background: var(--bg); overflow: hidden; }
9
- .sidebar { width: 320px; border-right: 1px solid var(--border); padding: 20px; display: flex; flex-direction: column; gap: 20px; background: #fff; overflow-y: auto; }
10
- .group-title { font-family: var(--pixel-font); font-size: 8px; margin-bottom: 10px; color: #555; text-transform: uppercase; }
11
- .btn-upload { border: 2px dashed var(--border); padding: 15px; text-align: center; cursor: pointer; position: relative; background: #f9f9f9; }
 
 
 
 
12
  .btn-upload input { opacity: 0; position: absolute; inset: 0; cursor: pointer; }
13
- .label-val { display: flex; justify-content: space-between; font-size: 16px; margin-bottom: 2px; margin-top: 10px; }
 
 
14
  input[type="range"] { width: 100%; cursor: pointer; accent-color: #000; }
15
- .main { flex: 1; display: flex; align-items: center; justify-content: center; background: #ddd; position: relative; }
16
- #canvas-view { max-width: 95%; max-height: 95%; background: #fff; box-shadow: 0 0 20px rgba(0,0,0,0.1); display: flex; align-items: center; justify-content: center; }
17
- #canvas-view svg { max-width: 100%; max-height: 100%; }
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  .export-btn { background: #000; color: #fff; border: none; padding: 15px; font-family: var(--pixel-font); font-size: 10px; cursor: pointer; margin-top: auto; }
19
- #status { position: absolute; top: 10px; right: 10px; background: #CFFF22; padding: 5px 10px; font-size: 10px; font-family: var(--pixel-font); display: none; z-index: 10; border: 1px solid #000; }
20
  hr { border: 0; border-top: 1px dotted #999; width: 100%; margin: 5px 0; }
21
  </style>
22
  </head>
@@ -24,27 +43,36 @@
24
  <div id="status">PROCESSING...</div>
25
  <div class="sidebar">
26
  <div>
27
- <div class="group-title">1. Source</div>
28
- <div class="btn-upload">Image <input type="file" id="imgInp" accept="image/*"></div>
29
- <div style="margin-top:5px" class="btn-upload">Pattern (SVG) <input type="file" id="patInp" accept=".svg"></div>
 
 
 
 
 
 
 
 
 
 
30
  </div>
31
 
32
  <hr>
33
 
34
  <div>
35
- <div class="group-title">2. Dither</div>
36
  <div class="label-val"><span>Contrast</span><span id="v_black_point">0</span></div>
37
  <input type="range" id="black_point" min="0" max="200" value="0">
38
 
39
- <div class="label-val"><span>Density</span><span id="v_density">60</span></div>
40
  <input type="range" id="density" min="10" max="200" value="60">
41
  </div>
42
 
43
  <hr>
44
 
45
  <div>
46
- <div class="group-title">3. Deformation (Op Art)</div>
47
-
48
  <div class="label-val"><span>Warp Amount</span><span id="v_warp_amp">0.0</span></div>
49
  <input type="range" id="warp_amp" min="0" max="0.8" step="0.05" value="0.0">
50
 
@@ -54,51 +82,87 @@
54
 
55
  <button class="export-btn" onclick="saveSvg()">DOWNLOAD SVG</button>
56
  </div>
 
57
  <div class="main">
58
- <div id="canvas-view"></div>
 
 
59
  </div>
60
 
61
  <script>
62
  let lastSvg = "";
63
  const inputs = ['black_point', 'density', 'warp_amp', 'warp_freq'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
  function process() {
66
  const img = document.getElementById('imgInp').files[0];
67
  const pat = document.getElementById('patInp').files[0];
68
 
 
69
  inputs.forEach(id => {
70
  document.getElementById('v_'+id).innerText = document.getElementById(id).value;
71
  });
72
 
 
73
  if(!img || !pat) return;
74
 
75
  document.getElementById('status').style.display = 'block';
 
76
  const fd = new FormData();
77
  fd.append('image', img);
78
  fd.append('patterns', pat);
79
- inputs.forEach(id => {
80
- fd.append(id, document.getElementById(id).value);
81
- });
82
 
83
  fetch('/generate', { method: 'POST', body: fd })
84
  .then(r => r.text())
85
  .then(svg => {
86
- lastSvg = svg;
87
- document.getElementById('canvas-view').innerHTML = svg;
 
 
 
 
 
 
 
 
88
  document.getElementById('status').style.display = 'none';
89
  });
90
  }
91
 
92
- document.getElementById('imgInp').onchange = process;
93
- document.getElementById('patInp').onchange = process;
94
- inputs.forEach(id => document.getElementById(id).oninput = process);
 
95
 
96
  function saveSvg() {
97
  if(!lastSvg) return;
98
  const blob = new Blob([lastSvg], {type: 'image/svg+xml'});
99
  const link = document.createElement('a');
100
  link.href = URL.createObjectURL(blob);
101
- link.download = 'op_art_dither.svg';
102
  link.click();
103
  }
104
  </script>
 
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
+ <title>OpArt Dither Engine</title>
5
  <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&display=swap" rel="stylesheet">
6
  <style>
7
  :root { --border: #999; --pixel-font: 'Press Start 2P', cursive; --mono-font: 'VT323', monospace; --bg: #f0f0f0; }
8
  body { margin: 0; padding: 0; font-family: var(--mono-font); display: flex; height: 100vh; background: var(--bg); overflow: hidden; }
9
+
10
+ /* Sidebar */
11
+ .sidebar { width: 320px; border-right: 1px solid var(--border); padding: 20px; display: flex; flex-direction: column; gap: 15px; background: #fff; overflow-y: auto; z-index: 10; }
12
+ .group-title { font-family: var(--pixel-font); font-size: 8px; margin-bottom: 5px; color: #555; text-transform: uppercase; }
13
+
14
+ .btn-upload { border: 2px dashed var(--border); padding: 12px; text-align: center; cursor: pointer; position: relative; background: #f9f9f9; transition: 0.2s; }
15
+ .btn-upload:hover { background: #eee; border-color: #000; }
16
  .btn-upload input { opacity: 0; position: absolute; inset: 0; cursor: pointer; }
17
+ .file-status { font-size: 12px; color: #28a745; display: none; margin-top: 5px; }
18
+
19
+ .label-val { display: flex; justify-content: space-between; font-size: 16px; margin-bottom: 2px; }
20
  input[type="range"] { width: 100%; cursor: pointer; accent-color: #000; }
21
+
22
+ /* Main Viewport */
23
+ .main { flex: 1; display: flex; align-items: center; justify-content: center; background: #ddd; position: relative; padding: 20px; }
24
+ #canvas-view {
25
+ max-width: 100%;
26
+ max-height: 100%;
27
+ background: #fff;
28
+ box-shadow: 0 0 30px rgba(0,0,0,0.1);
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ overflow: hidden;
33
+ }
34
+ #canvas-view img, #canvas-view svg { max-width: 100%; max-height: 100%; object-fit: contain; }
35
+
36
+ /* UI Elements */
37
  .export-btn { background: #000; color: #fff; border: none; padding: 15px; font-family: var(--pixel-font); font-size: 10px; cursor: pointer; margin-top: auto; }
38
+ #status { position: absolute; top: 10px; right: 10px; background: #CFFF22; padding: 5px 10px; font-size: 10px; font-family: var(--pixel-font); display: none; z-index: 100; border: 1px solid #000; }
39
  hr { border: 0; border-top: 1px dotted #999; width: 100%; margin: 5px 0; }
40
  </style>
41
  </head>
 
43
  <div id="status">PROCESSING...</div>
44
  <div class="sidebar">
45
  <div>
46
+ <div class="group-title">1. Source Image</div>
47
+ <div class="btn-upload">
48
+ <span id="img-label">SELECT IMAGE</span>
49
+ <input type="file" id="imgInp" accept="image/*">
50
+ </div>
51
+ </div>
52
+
53
+ <div>
54
+ <div class="group-title">2. Pattern (SVG)</div>
55
+ <div class="btn-upload">
56
+ <span id="pat-label">SELECT SVG PATTERN</span>
57
+ <input type="file" id="patInp" accept=".svg">
58
+ </div>
59
  </div>
60
 
61
  <hr>
62
 
63
  <div>
64
+ <div class="group-title">3. Dither Settings</div>
65
  <div class="label-val"><span>Contrast</span><span id="v_black_point">0</span></div>
66
  <input type="range" id="black_point" min="0" max="200" value="0">
67
 
68
+ <div class="label-val"><span>Density (Resolution)</span><span id="v_density">60</span></div>
69
  <input type="range" id="density" min="10" max="200" value="60">
70
  </div>
71
 
72
  <hr>
73
 
74
  <div>
75
+ <div class="group-title">4. Warp Deformation</div>
 
76
  <div class="label-val"><span>Warp Amount</span><span id="v_warp_amp">0.0</span></div>
77
  <input type="range" id="warp_amp" min="0" max="0.8" step="0.05" value="0.0">
78
 
 
82
 
83
  <button class="export-btn" onclick="saveSvg()">DOWNLOAD SVG</button>
84
  </div>
85
+
86
  <div class="main">
87
+ <div id="canvas-view">
88
+ <div style="color: #888; font-family: var(--pixel-font); font-size: 12px;">AWAITING MEDIA...</div>
89
+ </div>
90
  </div>
91
 
92
  <script>
93
  let lastSvg = "";
94
  const inputs = ['black_point', 'density', 'warp_amp', 'warp_freq'];
95
+ const canvas = document.getElementById('canvas-view');
96
+
97
+ // Превью изображения сразу после выбора
98
+ document.getElementById('imgInp').onchange = function(e) {
99
+ const file = e.target.files[0];
100
+ if (file) {
101
+ document.getElementById('img-label').innerText = "✓ IMAGE LOADED";
102
+ const reader = new FileReader();
103
+ reader.onload = function(event) {
104
+ canvas.innerHTML = `<img src="${event.target.result}" id="preview-img">`;
105
+ process(); // Пытаемся запустить генерацию
106
+ };
107
+ reader.readAsDataURL(file);
108
+ }
109
+ };
110
+
111
+ // Подтверждение выбора паттерна
112
+ document.getElementById('patInp').onchange = function(e) {
113
+ if (e.target.files[0]) {
114
+ document.getElementById('pat-label').innerText = "✓ PATTERN LOADED";
115
+ process();
116
+ }
117
+ };
118
 
119
  function process() {
120
  const img = document.getElementById('imgInp').files[0];
121
  const pat = document.getElementById('patInp').files[0];
122
 
123
+ // Обновляем цифры в интерфейсе
124
  inputs.forEach(id => {
125
  document.getElementById('v_'+id).innerText = document.getElementById(id).value;
126
  });
127
 
128
+ // Если паттерна еще нет, просто выходим (превью картинки уже на экране)
129
  if(!img || !pat) return;
130
 
131
  document.getElementById('status').style.display = 'block';
132
+
133
  const fd = new FormData();
134
  fd.append('image', img);
135
  fd.append('patterns', pat);
136
+ inputs.forEach(id => fd.append(id, document.getElementById(id).value));
 
 
137
 
138
  fetch('/generate', { method: 'POST', body: fd })
139
  .then(r => r.text())
140
  .then(svg => {
141
+ if (svg.startsWith('<svg')) {
142
+ lastSvg = svg;
143
+ canvas.innerHTML = svg;
144
+ } else {
145
+ console.error("Server error:", svg);
146
+ }
147
+ document.getElementById('status').style.display = 'none';
148
+ })
149
+ .catch(err => {
150
+ console.error("Fetch error:", err);
151
  document.getElementById('status').style.display = 'none';
152
  });
153
  }
154
 
155
+ // Слушатели для ползунков
156
+ inputs.forEach(id => {
157
+ document.getElementById(id).oninput = process;
158
+ });
159
 
160
  function saveSvg() {
161
  if(!lastSvg) return;
162
  const blob = new Blob([lastSvg], {type: 'image/svg+xml'});
163
  const link = document.createElement('a');
164
  link.href = URL.createObjectURL(blob);
165
+ link.download = 'opart_result.svg';
166
  link.click();
167
  }
168
  </script>