duqing2026 commited on
Commit
c2abd76
·
1 Parent(s): 94b7cac
static/css/styles.css CHANGED
@@ -31,7 +31,7 @@ input[type="range"]{width:100%}
31
  .preview{overflow:auto}
32
  .stage-wrap{background:#0b1224;border:1px solid var(--border);border-radius:16px;padding:12px}
33
  .stage-toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;color:#94a3b8}
34
- .stage{position:relative;margin:0 auto;background:#111827;border-radius:14px;box-shadow:0 8px 30px rgba(0,0,0,.35);overflow:hidden;width:960px;height:504px;display:flex;align-items:center;justify-content:center}
35
  .logo-wrap{position:absolute;top:24px;left:24px;width:80px;height:80px;border-radius:12px;background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:center;overflow:hidden}
36
  .logo-wrap img{max-width:70px;max-height:70px}
37
  .content{padding:40px;text-align:center;max-width:85%}
 
31
  .preview{overflow:auto}
32
  .stage-wrap{background:#0b1224;border:1px solid var(--border);border-radius:16px;padding:12px}
33
  .stage-toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;color:#94a3b8}
34
+ .stage{position:relative;margin:0 auto;background:#111827;border-radius:14px;box-shadow:0 8px 30px rgba(0,0,0,.35);overflow:hidden;width:960px;height:504px;display:flex;align-items:center;justify-content:center;background-size:cover;background-position:center;background-repeat:no-repeat}
35
  .logo-wrap{position:absolute;top:24px;left:24px;width:80px;height:80px;border-radius:12px;background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:center;overflow:hidden}
36
  .logo-wrap img{max-width:70px;max-height:70px}
37
  .content{padding:40px;text-align:center;max-width:85%}
static/js/html2canvas.min.js ADDED
The diff for this file is too large to render. See raw diff
 
static/js/main.js CHANGED
@@ -16,7 +16,8 @@ const state = {
16
  gradient: "",
17
  shadow: true,
18
  padding: 64,
19
- logo: null
 
20
  };
21
 
22
  function applySize() {
@@ -36,7 +37,9 @@ function applyTheme() {
36
  const content = el("content");
37
 
38
  // 背景
39
- if (state.gradient) {
 
 
40
  stage.style.background = `linear-gradient(${state.gradient})`;
41
  } else {
42
  stage.style.background = state.bgColor;
@@ -150,6 +153,17 @@ function bindInputs() {
150
  fr.readAsDataURL(file);
151
  });
152
 
 
 
 
 
 
 
 
 
 
 
 
153
  document.querySelectorAll(".preset").forEach((btn) => {
154
  btn.addEventListener("click", () => {
155
  const p = btn.dataset.preset;
@@ -198,6 +212,33 @@ function applyPreset(name) {
198
  applyTheme();
199
  }
200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  function resetAll() {
202
  Object.assign(state, {
203
  w: 1200, h: 630,
@@ -211,11 +252,13 @@ function resetAll() {
211
  gradient: "",
212
  shadow: true,
213
  padding: 64,
214
- logo: null
 
215
  });
216
  const wrap = el("logo");
217
  wrap.style.display = "none";
218
  el("logoImg").src = "";
 
219
  el("titleInput").value = state.title;
220
  el("subtitleInput").value = state.subtitle;
221
  el("sizePreset").value = "1200x630";
@@ -259,5 +302,19 @@ function init() {
259
  applyTheme();
260
  }
261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  window.addEventListener("DOMContentLoaded", init);
263
 
 
16
  gradient: "",
17
  shadow: true,
18
  padding: 64,
19
+ logo: null,
20
+ bgImg: null
21
  };
22
 
23
  function applySize() {
 
37
  const content = el("content");
38
 
39
  // 背景
40
+ if (state.bgImg) {
41
+ stage.style.background = `url(${state.bgImg}) center/cover no-repeat`;
42
+ } else if (state.gradient) {
43
  stage.style.background = `linear-gradient(${state.gradient})`;
44
  } else {
45
  stage.style.background = state.bgColor;
 
153
  fr.readAsDataURL(file);
154
  });
155
 
156
+ el("bgImgInput").addEventListener("change", (e) => {
157
+ const file = e.target.files && e.target.files[0];
158
+ if (!file) return;
159
+ const fr = new FileReader();
160
+ fr.onload = () => {
161
+ state.bgImg = fr.result;
162
+ applyTheme();
163
+ };
164
+ fr.readAsDataURL(file);
165
+ });
166
+
167
  document.querySelectorAll(".preset").forEach((btn) => {
168
  btn.addEventListener("click", () => {
169
  const p = btn.dataset.preset;
 
212
  applyTheme();
213
  }
214
 
215
+ function randomTheme() {
216
+ const r = () => Math.floor(Math.random() * 256);
217
+ const rc = () => `#${r().toString(16).padStart(2,'0')}${r().toString(16).padStart(2,'0')}${r().toString(16).padStart(2,'0')}`;
218
+
219
+ state.bgColor = rc();
220
+ state.fgColor = rc();
221
+ state.gradient = Math.random() > 0.5 ? "" : `to right, ${state.bgColor}, ${rc()}`;
222
+ state.titleSize = 32 + Math.floor(Math.random() * 64);
223
+ state.subtitleSize = 18 + Math.floor(Math.random() * 30);
224
+ state.textAlign = ["left", "center", "right"][Math.floor(Math.random() * 3)];
225
+ state.shadow = Math.random() > 0.5;
226
+ state.padding = 24 + Math.floor(Math.random() * 96);
227
+ state.bgImg = null; // 随机时清除背景图
228
+
229
+ el("bgColor").value = state.bgColor;
230
+ el("fgColor").value = state.fgColor;
231
+ el("titleSize").value = state.titleSize;
232
+ el("subtitleSize").value = state.subtitleSize;
233
+ el("textAlign").value = state.textAlign;
234
+ el("gradient").value = state.gradient || ""; // 简单的处理
235
+ el("shadow").checked = state.shadow;
236
+ el("padding").value = state.padding;
237
+ el("bgImgInput").value = "";
238
+
239
+ applyTheme();
240
+ }
241
+
242
  function resetAll() {
243
  Object.assign(state, {
244
  w: 1200, h: 630,
 
252
  gradient: "",
253
  shadow: true,
254
  padding: 64,
255
+ logo: null,
256
+ bgImg: null
257
  });
258
  const wrap = el("logo");
259
  wrap.style.display = "none";
260
  el("logoImg").src = "";
261
+ el("bgImgInput").value = "";
262
  el("titleInput").value = state.title;
263
  el("subtitleInput").value = state.subtitle;
264
  el("sizePreset").value = "1200x630";
 
302
  applyTheme();
303
  }
304
 
305
+ window.addEventListener("DOMContentLoaded", init);
306
+
307
+ a.href = data;
308
+ a.click();
309
+ }
310
+
311
+ function init() {
312
+ el("title").textContent = state.title;
313
+ el("subtitle").textContent = state.subtitle;
314
+ bindInputs();
315
+ applySize();
316
+ applyTheme();
317
+ }
318
+
319
  window.addEventListener("DOMContentLoaded", init);
320
 
templates/index.html CHANGED
@@ -5,8 +5,6 @@
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <title>OG 社交预览图工坊 | OG Image Studio</title>
7
  <link rel="stylesheet" href="/static/css/styles.css" />
8
- <link rel="preconnect" href="https://fonts.googleapis.com">
9
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
  <script>
11
  window.__APP_CONFIG__ = {
12
  defaultTitle: "你的标题在这里",
@@ -25,7 +23,7 @@
25
  <p>一键生成 1200×630/675 的社交分享封面,适配公众号/微博/小红书/推特</p>
26
  </div>
27
  </div>
28
- <a class="link" href="https://huggingface.co/spaces/duqing2026" target="_blank" rel="noreferrer">Hugging Face Spaces</a>
29
  </header>
30
 
31
  <main class="container">
@@ -132,6 +130,7 @@
132
 
133
  <div class="actions">
134
  <button id="resetBtn" class="ghost">重置</button>
 
135
  <button id="downloadBtn" class="primary">导出 PNG</button>
136
  </div>
137
  </section>
@@ -161,7 +160,7 @@
161
  <a href="/healthz" target="_blank" rel="noreferrer">健康检查</a>
162
  </footer>
163
 
164
- <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
165
  <script src="/static/js/main.js"></script>
166
  </body>
167
  </html>
 
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <title>OG 社交预览图工坊 | OG Image Studio</title>
7
  <link rel="stylesheet" href="/static/css/styles.css" />
 
 
8
  <script>
9
  window.__APP_CONFIG__ = {
10
  defaultTitle: "你的标题在这里",
 
23
  <p>一键生成 1200×630/675 的社交分享封面,适配公众号/微博/小红书/推特</p>
24
  </div>
25
  </div>
26
+ <!-- <a class="link" href="https://huggingface.co/spaces/duqing2026" target="_blank" rel="noreferrer">Hugging Face Spaces</a> -->
27
  </header>
28
 
29
  <main class="container">
 
130
 
131
  <div class="actions">
132
  <button id="resetBtn" class="ghost">重置</button>
133
+ <button id="randomBtn" class="ghost">🎲 随机</button>
134
  <button id="downloadBtn" class="primary">导出 PNG</button>
135
  </div>
136
  </section>
 
160
  <a href="/healthz" target="_blank" rel="noreferrer">健康检查</a>
161
  </footer>
162
 
163
+ <script src="/static/js/html2canvas.min.js"></script>
164
  <script src="/static/js/main.js"></script>
165
  </body>
166
  </html>