by3axap commited on
Commit
4376b67
·
verified ·
1 Parent(s): e26d41c

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +107 -117
templates/index.html CHANGED
@@ -3,134 +3,136 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>CELL CLIPPER / V1.1</title>
7
  <style>
8
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=JetBrains+Mono&display=swap');
9
 
10
  :root {
11
- --bg-page: #ffffff;
 
12
  --text-main: #000000;
13
  --text-muted: #888888;
14
- --border: #000000;
15
- --font-sans: 'Inter', -apple-system, sans-serif;
16
- --font-mono: 'JetBrains Mono', monospace;
17
  }
18
 
19
  body {
20
  margin: 0;
 
21
  display: flex;
 
22
  height: 100vh;
23
- font-family: var(--font-sans);
24
- background-color: var(--bg-page);
25
  color: var(--text-main);
 
26
  overflow: hidden;
27
  }
28
 
29
  .sidebar {
30
- width: 360px;
31
- background: var(--bg-page);
32
- border-right: 0.5px solid var(--border);
33
  display: flex;
34
  flex-direction: column;
35
- padding: 20px;
36
  box-sizing: border-box;
37
- z-index: 10;
38
  overflow-y: auto;
 
39
  }
40
 
41
- .section { margin-bottom: 30px; }
42
 
43
  .section-title {
44
- font-family: var(--font-mono);
45
- font-size: 9px;
46
  color: var(--text-muted);
47
  text-transform: uppercase;
48
- letter-spacing: 0.15em;
49
- margin-bottom: 15px;
50
  display: block;
51
  }
52
 
53
  #file-input { display: none; }
54
  .file-upload-label {
55
  display: block;
56
- border: 0.5px solid var(--border);
57
- padding: 15px;
58
  text-align: center;
59
  cursor: pointer;
60
- font-size: 11px;
61
- font-family: var(--font-mono);
62
- text-transform: uppercase;
63
- width: 100%;
64
- box-sizing: border-box;
65
  }
 
66
 
67
- .control-group { margin-bottom: 15px; width: 100%; }
68
 
69
  .label-row {
70
  display: flex;
71
  justify-content: space-between;
72
- font-size: 11px;
73
- margin-bottom: 8px;
 
74
  }
75
- .label-row span:last-child { font-family: var(--font-mono); }
76
 
77
  select {
78
  width: 100%;
79
- padding: 8px;
80
- font-family: var(--font-mono);
81
- font-size: 11px;
82
- border: 0.5px solid var(--border);
83
- background: white;
84
  outline: none;
85
- margin-bottom: 10px;
86
  cursor: pointer;
87
- text-transform: uppercase;
88
  }
89
 
90
  input[type="range"] {
91
  width: 100%;
92
  -webkit-appearance: none;
93
- background: transparent;
94
- outline: none;
95
- display: block;
96
- padding: 5px 0;
97
- }
98
- input[type="range"]::-webkit-slider-runnable-track {
99
- width: 100%;
100
- height: 1px;
101
  background: var(--border);
 
 
102
  }
103
  input[type="range"]::-webkit-slider-thumb {
104
  -webkit-appearance: none;
105
- width: 2px;
106
- height: 14px;
107
- background: var(--border);
 
108
  cursor: pointer;
109
- margin-top: -6px;
110
  }
111
 
112
- .buttons-gap { margin-top: auto; width: 100%; }
113
-
114
  .btn-download {
115
- background: var(--text-main);
116
  color: white;
117
  border: none;
118
- padding: 18px;
119
- font-family: var(--font-mono);
120
  font-size: 11px;
121
- letter-spacing: 0.2em;
122
  text-transform: uppercase;
123
  cursor: pointer;
124
  width: 100%;
 
 
125
  }
 
126
 
127
  .preview-area {
128
  flex: 1;
 
 
129
  display: flex;
130
  align-items: center;
131
  justify-content: center;
132
- background-color: var(--bg-page);
133
  padding: 40px;
 
 
 
134
  }
135
 
136
  #svg-container {
@@ -142,9 +144,10 @@
142
  }
143
 
144
  svg#main-svg {
145
- background: transparent;
146
  max-width: 100%;
147
  max-height: 100%;
 
 
148
  }
149
  </style>
150
  </head>
@@ -154,22 +157,21 @@
154
  <div class="section">
155
  <span class="section-title">01 / Source</span>
156
  <input type="file" id="file-input" accept=".svg">
157
- <label for="file-input" class="file-upload-label" id="drop-zone">Select SVG</label>
158
  </div>
159
 
160
  <div class="section">
161
- <span class="section-title">02 / Grid Layout</span>
162
  <div class="control-group">
163
  <div class="label-row"><span>Density</span> <span id="val-density">10</span></div>
164
- <input type="range" id="density" min="2" max="60" value="10">
165
  </div>
166
  <div class="control-group">
167
- <div class="label-row"><span>Gap X</span> <span id="val-gap_x">0.00</span></div>
168
- <input type="range" id="gap_x" min="0" max="0.9" step="0.01" value="0">
169
- </div>
170
- <div class="control-group">
171
- <div class="label-row"><span>Gap Y</span> <span id="val-gap_y">0.00</span></div>
172
- <input type="range" id="gap_y" min="0" max="0.9" step="0.01" value="0">
173
  </div>
174
  <div class="control-group">
175
  <div class="label-row"><span>Stagger</span> <span id="val-shift">0.00</span></div>
@@ -178,41 +180,31 @@
178
  </div>
179
 
180
  <div class="section">
181
- <span class="section-title">03 / Shape Transformation</span>
182
  <div class="control-group">
183
- <div class="label-row"><span>Base Scale</span> <span id="val-base">1.0</span></div>
184
- <input type="range" id="base" min="0.1" max="2.0" step="0.05" value="1.0">
185
  </div>
186
-
187
  <div class="control-group">
188
  <div class="label-row"><span>Rotation</span> <span id="val-rotation">0</span></div>
189
- <input type="range" id="rotation" min="-90" max="90" step="1" value="0">
190
- </div>
191
-
192
- <div class="control-group">
193
- <div class="label-row"><span>Growth Algorithm</span></div>
194
- <select id="growth_type">
195
- <option value="sine">Diagonal Sine</option>
196
- <option value="linear">Linear Left-Right</option>
197
- <option value="steps">Steps (5 levels)</option>
198
- </select>
199
-
200
- <div class="label-row"><span>Growth Strength</span> <span id="val-growth">0.0</span></div>
201
- <input type="range" id="growth" min="0" max="1.5" step="0.05" value="0">
202
  </div>
203
  </div>
204
 
205
  <div class="section">
206
- <span class="section-title">04 / Dynamics</span>
207
- <div class="control-group">
208
- <div class="label-row"><span>Wave Shift</span> <span id="val-wave">0.00</span></div>
209
- <input type="range" id="wave" min="0" max="2" step="0.01" value="0">
 
 
 
 
 
210
  </div>
211
  </div>
212
 
213
- <div class="buttons-gap">
214
- <button class="btn-download" id="download-svg">Export SVG</button>
215
- </div>
216
  </div>
217
 
218
  <div class="preview-area">
@@ -232,7 +224,6 @@
232
  gap_x: document.getElementById('gap_x'),
233
  gap_y: document.getElementById('gap_y'),
234
  shift: document.getElementById('shift'),
235
- wave: document.getElementById('wave'),
236
  base: document.getElementById('base'),
237
  rotation: document.getElementById('rotation'),
238
  growth: document.getElementById('growth'),
@@ -259,10 +250,11 @@
259
 
260
  function getParam(id) {
261
  const el = controls[id];
 
262
  if (el.tagName === 'SELECT') return el.value;
263
  const val = parseFloat(el.value);
264
  const display = document.getElementById(`val-${id}`);
265
- if (display) display.innerText = Number.isInteger(val) ? val : val.toFixed(2);
266
  return val;
267
  }
268
 
@@ -271,7 +263,6 @@
271
  const gapX = getParam('gap_x');
272
  const gapY = getParam('gap_y');
273
  const shiftStagger = getParam('shift');
274
- const shiftWave = getParam('wave');
275
  const baseScale = getParam('base');
276
  const rotation = getParam('rotation');
277
  const growth = getParam('growth');
@@ -280,50 +271,49 @@
280
  const vb = viewBox.split(' ').map(Number);
281
  const vW = vb[2], vH = vb[3];
282
 
283
- const canvasSize = 1000;
284
- const step = canvasSize / density;
 
 
 
 
285
  const cellWMax = step * (1.0 - gapX);
286
  const cellHMax = step * (1.0 - gapY);
287
  const offsetXBase = (step - cellWMax) / 2;
288
  const offsetYBase = (step - cellHMax) / 2;
289
 
290
  let body = '';
291
- for (let r = 0; r < density; r++) {
292
- let rowWave = Math.sin(r * 0.5) * shiftWave * step;
293
- let rowShift = (r * shiftStagger * step) + rowWave;
294
- for (let c = 0; c < density; c++) {
295
- let cx_grid = (c * step + rowShift) % canvasSize;
296
- let cy_grid = r * step;
297
 
298
  let waveNorm = 0;
299
- if (growthType === 'sine') {
300
- waveNorm = (Math.sin(c * 0.3 + r * 0.3) + 1) / 2;
301
- } else if (growthType === 'linear') {
302
- waveNorm = c / (density - 1);
303
- } else if (growthType === 'steps') {
304
- const numSteps = 5;
305
- waveNorm = Math.floor(c / (density / numSteps)) / (numSteps - 1);
306
- }
307
 
308
  let modW = Math.max(0, 1.0 - (waveNorm * growth));
309
  let contentW = cellWMax * baseScale * modW;
310
  let contentH = cellHMax * baseScale;
311
 
312
- // Центр ячейки
313
- let centerX = cx_grid + offsetXBase + cellWMax / 2;
314
- let centerY = cy_grid + offsetYBase + cellHMax / 2;
315
 
316
  let sX = (contentW / vW).toFixed(4);
317
  let sY = (contentH / vH).toFixed(4);
318
 
319
- // Оптимизированная трансформа для Figma: Translate -> Rotate -> Scale -> Translate back to object origin
320
- body += `<g transform="translate(${centerX.toFixed(2)} ${centerY.toFixed(2)}) rotate(${rotation}) scale(${sX} ${sY}) translate(${-vW/2} ${-vH/2})">
321
- ${symbolContent}
322
- </g>`;
323
  }
324
  }
325
 
326
- return `<svg id="main-svg" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 1000 1000">${body}</svg>`;
 
 
 
327
  }
328
 
329
  function update() { svgContainer.innerHTML = generateSVG(); }
@@ -338,7 +328,7 @@
338
  const blob = new Blob([svgData], {type: 'image/svg+xml;charset=utf-8'});
339
  const url = URL.createObjectURL(blob);
340
  const link = document.createElement('a');
341
- link.href = url; link.download = 'cell-clipper-clean.svg'; link.click();
342
  };
343
  update();
344
  </script>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>CELL CLIPPER / PRO</title>
7
  <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono&display=swap');
9
 
10
  :root {
11
+ --bg-body: #F0F0F0;
12
+ --bg-card: #ffffff;
13
  --text-main: #000000;
14
  --text-muted: #888888;
15
+ --accent: #000000;
16
+ --border: #E5E5E5;
17
+ --radius: 12px;
18
  }
19
 
20
  body {
21
  margin: 0;
22
+ padding: 20px;
23
  display: flex;
24
+ gap: 20px;
25
  height: 100vh;
26
+ font-family: 'Inter', sans-serif;
27
+ background-color: var(--bg-body);
28
  color: var(--text-main);
29
+ box-sizing: border-box;
30
  overflow: hidden;
31
  }
32
 
33
  .sidebar {
34
+ width: 340px;
35
+ background: var(--bg-card);
36
+ border-radius: var(--radius);
37
  display: flex;
38
  flex-direction: column;
39
+ padding: 24px;
40
  box-sizing: border-box;
 
41
  overflow-y: auto;
42
+ box-shadow: 0 4px 24px rgba(0,0,0,0.04);
43
  }
44
 
45
+ .section { margin-bottom: 32px; }
46
 
47
  .section-title {
48
+ font-family: 'JetBrains Mono';
49
+ font-size: 10px;
50
  color: var(--text-muted);
51
  text-transform: uppercase;
52
+ letter-spacing: 0.1em;
53
+ margin-bottom: 16px;
54
  display: block;
55
  }
56
 
57
  #file-input { display: none; }
58
  .file-upload-label {
59
  display: block;
60
+ border: 1px dashed #CCC;
61
+ padding: 16px;
62
  text-align: center;
63
  cursor: pointer;
64
+ font-size: 12px;
65
+ border-radius: 8px;
66
+ transition: 0.2s;
 
 
67
  }
68
+ .file-upload-label:hover { border-color: var(--accent); background: #F9F9F9; }
69
 
70
+ .control-group { margin-bottom: 20px; }
71
 
72
  .label-row {
73
  display: flex;
74
  justify-content: space-between;
75
+ font-size: 12px;
76
+ font-weight: 500;
77
+ margin-bottom: 10px;
78
  }
79
+ .label-row span:last-child { font-family: 'JetBrains Mono'; color: var(--text-muted); }
80
 
81
  select {
82
  width: 100%;
83
+ padding: 10px;
84
+ font-family: inherit;
85
+ font-size: 13px;
86
+ border: 1px solid var(--border);
87
+ border-radius: 6px;
88
  outline: none;
 
89
  cursor: pointer;
 
90
  }
91
 
92
  input[type="range"] {
93
  width: 100%;
94
  -webkit-appearance: none;
95
+ height: 4px;
 
 
 
 
 
 
 
96
  background: var(--border);
97
+ border-radius: 2px;
98
+ outline: none;
99
  }
100
  input[type="range"]::-webkit-slider-thumb {
101
  -webkit-appearance: none;
102
+ width: 16px;
103
+ height: 16px;
104
+ background: var(--accent);
105
+ border-radius: 50%;
106
  cursor: pointer;
 
107
  }
108
 
 
 
109
  .btn-download {
110
+ background: var(--accent);
111
  color: white;
112
  border: none;
113
+ padding: 16px;
114
+ font-family: 'JetBrains Mono';
115
  font-size: 11px;
116
+ border-radius: 8px;
117
  text-transform: uppercase;
118
  cursor: pointer;
119
  width: 100%;
120
+ margin-top: auto;
121
+ transition: 0.3s;
122
  }
123
+ .btn-download:hover { opacity: 0.8; transform: translateY(-1px); }
124
 
125
  .preview-area {
126
  flex: 1;
127
+ background: var(--bg-card);
128
+ border-radius: var(--radius);
129
  display: flex;
130
  align-items: center;
131
  justify-content: center;
 
132
  padding: 40px;
133
+ box-shadow: 0 4px 24px rgba(0,0,0,0.04);
134
+ position: relative;
135
+ overflow: hidden;
136
  }
137
 
138
  #svg-container {
 
144
  }
145
 
146
  svg#main-svg {
 
147
  max-width: 100%;
148
  max-height: 100%;
149
+ box-shadow: 0 0 40px rgba(0,0,0,0.05);
150
+ background: white;
151
  }
152
  </style>
153
  </head>
 
157
  <div class="section">
158
  <span class="section-title">01 / Source</span>
159
  <input type="file" id="file-input" accept=".svg">
160
+ <label for="file-input" class="file-upload-label" id="drop-zone">Drop SVG here</label>
161
  </div>
162
 
163
  <div class="section">
164
+ <span class="section-title">02 / Grid</span>
165
  <div class="control-group">
166
  <div class="label-row"><span>Density</span> <span id="val-density">10</span></div>
167
+ <input type="range" id="density" min="4" max="80" value="12">
168
  </div>
169
  <div class="control-group">
170
+ <div class="label-row"><span>Gap X / Y</span></div>
171
+ <div style="display: flex; gap: 10px;">
172
+ <input type="range" id="gap_x" min="0" max="0.9" step="0.01" value="0.1">
173
+ <input type="range" id="gap_y" min="0" max="0.9" step="0.01" value="0.1">
174
+ </div>
 
175
  </div>
176
  <div class="control-group">
177
  <div class="label-row"><span>Stagger</span> <span id="val-shift">0.00</span></div>
 
180
  </div>
181
 
182
  <div class="section">
183
+ <span class="section-title">03 / Transform</span>
184
  <div class="control-group">
185
+ <div class="label-row"><span>Scale</span> <span id="val-base">1.0</span></div>
186
+ <input type="range" id="base" min="0.1" max="3.0" step="0.05" value="1.0">
187
  </div>
 
188
  <div class="control-group">
189
  <div class="label-row"><span>Rotation</span> <span id="val-rotation">0</span></div>
190
+ <input type="range" id="rotation" min="-180" max="180" step="1" value="0">
 
 
 
 
 
 
 
 
 
 
 
 
191
  </div>
192
  </div>
193
 
194
  <div class="section">
195
+ <span class="section-title">04 / Growth</span>
196
+ <select id="growth_type">
197
+ <option value="sine">Diagonal Sine</option>
198
+ <option value="linear">Horizontal Linear</option>
199
+ <option value="steps">Quantized Steps</option>
200
+ </select>
201
+ <div class="control-group" style="margin-top: 15px;">
202
+ <div class="label-row"><span>Strength</span> <span id="val-growth">0.0</span></div>
203
+ <input type="range" id="growth" min="0" max="1.5" step="0.05" value="0">
204
  </div>
205
  </div>
206
 
207
+ <button class="btn-download" id="download-svg">Export Optimized SVG</button>
 
 
208
  </div>
209
 
210
  <div class="preview-area">
 
224
  gap_x: document.getElementById('gap_x'),
225
  gap_y: document.getElementById('gap_y'),
226
  shift: document.getElementById('shift'),
 
227
  base: document.getElementById('base'),
228
  rotation: document.getElementById('rotation'),
229
  growth: document.getElementById('growth'),
 
250
 
251
  function getParam(id) {
252
  const el = controls[id];
253
+ if (!el) return 0;
254
  if (el.tagName === 'SELECT') return el.value;
255
  const val = parseFloat(el.value);
256
  const display = document.getElementById(`val-${id}`);
257
+ if (display) display.innerText = val.toFixed(2);
258
  return val;
259
  }
260
 
 
263
  const gapX = getParam('gap_x');
264
  const gapY = getParam('gap_y');
265
  const shiftStagger = getParam('shift');
 
266
  const baseScale = getParam('base');
267
  const rotation = getParam('rotation');
268
  const growth = getParam('growth');
 
271
  const vb = viewBox.split(' ').map(Number);
272
  const vW = vb[2], vH = vb[3];
273
 
274
+ // Прямоугольник 1400x800
275
+ const canvasW = 1400;
276
+ const canvasH = 800;
277
+ const step = canvasW / density;
278
+ const rows = Math.ceil(canvasH / step);
279
+
280
  const cellWMax = step * (1.0 - gapX);
281
  const cellHMax = step * (1.0 - gapY);
282
  const offsetXBase = (step - cellWMax) / 2;
283
  const offsetYBase = (step - cellHMax) / 2;
284
 
285
  let body = '';
286
+ for (let r = 0; r < rows; r++) {
287
+ let rowShift = (r * shiftStagger * step);
288
+ for (let c = -1; c < density + 1; c++) {
289
+ let gx = (c * step + rowShift) % (canvasW + step);
290
+ let gy = r * step;
 
291
 
292
  let waveNorm = 0;
293
+ if (growthType === 'sine') waveNorm = (Math.sin(c * 0.3 + r * 0.3) + 1) / 2;
294
+ else if (growthType === 'linear') waveNorm = c / density;
 
 
 
 
 
 
295
 
296
  let modW = Math.max(0, 1.0 - (waveNorm * growth));
297
  let contentW = cellWMax * baseScale * modW;
298
  let contentH = cellHMax * baseScale;
299
 
300
+ let cx = gx + offsetXBase + cellWMax / 2;
301
+ let cy = gy + offsetYBase + cellHMax / 2;
 
302
 
303
  let sX = (contentW / vW).toFixed(4);
304
  let sY = (contentH / vH).toFixed(4);
305
 
306
+ // Оптимизированная трансформа для чистого импорта
307
+ let transform = `translate(${cx.toFixed(2)} ${cy.toFixed(2)}) rotate(${rotation}) scale(${sX} ${sY}) translate(${-vW/2} ${-vH/2})`;
308
+
309
+ body += `<g transform="${transform}">${symbolContent}</g>`;
310
  }
311
  }
312
 
313
+ return `<svg id="main-svg" xmlns="http://www.w3.org/2000/svg" width="${canvasW}" height="${canvasH}" viewBox="0 0 ${canvasW} {canvasH}" style="background:white">
314
+ <rect width="100%" height="100%" fill="white"/>
315
+ ${body}
316
+ </svg>`;
317
  }
318
 
319
  function update() { svgContainer.innerHTML = generateSVG(); }
 
328
  const blob = new Blob([svgData], {type: 'image/svg+xml;charset=utf-8'});
329
  const url = URL.createObjectURL(blob);
330
  const link = document.createElement('a');
331
+ link.href = url; link.download = 'clean-pattern.svg'; link.click();
332
  };
333
  update();
334
  </script>