ダークモード
Browse files- effects/base.js +32 -10
- effects/gold.js +1 -1
- effects/multiline-neon.js +1 -1
- index.html +20 -3
- index.js +23 -0
- styles.css +0 -2
effects/base.js
CHANGED
|
@@ -315,22 +315,44 @@ export class BaseEffect {
|
|
| 315 |
if (!this.strokeOptions) return;
|
| 316 |
|
| 317 |
const { color, width } = this.strokeOptions;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
ctx.strokeStyle = color;
|
| 319 |
ctx.lineWidth = width;
|
|
|
|
|
|
|
| 320 |
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
}
|
| 331 |
-
ctx.restore();
|
| 332 |
}
|
| 333 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
}
|
| 335 |
|
| 336 |
/**
|
|
|
|
| 315 |
if (!this.strokeOptions) return;
|
| 316 |
|
| 317 |
const { color, width } = this.strokeOptions;
|
| 318 |
+
const originalLineWidth = ctx.lineWidth;
|
| 319 |
+
const originalStrokeStyle = ctx.strokeStyle;
|
| 320 |
+
|
| 321 |
+
// 縁取りの設定
|
| 322 |
ctx.strokeStyle = color;
|
| 323 |
ctx.lineWidth = width;
|
| 324 |
+
ctx.lineJoin = 'round'; // 角を丸くする
|
| 325 |
+
ctx.lineCap = 'round'; // 線の端を丸くする
|
| 326 |
|
| 327 |
+
// 外側に広げるため、複数回描画
|
| 328 |
+
const iterations = 8; // 描画回数
|
| 329 |
+
const angleStep = (Math.PI * 2) / iterations;
|
| 330 |
+
|
| 331 |
+
for (let i = 0; i < iterations; i++) {
|
| 332 |
+
const angle = i * angleStep;
|
| 333 |
+
const offsetX = Math.cos(angle) * (width * 0.5);
|
| 334 |
+
const offsetY = Math.sin(angle) * (width * 0.5);
|
| 335 |
+
|
| 336 |
+
for (const lineCoords of this.coordinates) {
|
| 337 |
+
for (const coord of lineCoords) {
|
| 338 |
+
ctx.save();
|
| 339 |
+
if (coord.rotate) {
|
| 340 |
+
ctx.translate(coord.x + offsetX, coord.y + offsetY);
|
| 341 |
+
ctx.rotate(Math.PI/2);
|
| 342 |
+
ctx.strokeText(coord.char, -coord.width/2, 0);
|
| 343 |
+
} else {
|
| 344 |
+
ctx.strokeText(coord.char, coord.x + offsetX, coord.y + offsetY);
|
| 345 |
+
}
|
| 346 |
+
ctx.restore();
|
| 347 |
}
|
|
|
|
| 348 |
}
|
| 349 |
}
|
| 350 |
+
|
| 351 |
+
// 元の設定を復元
|
| 352 |
+
ctx.lineWidth = originalLineWidth;
|
| 353 |
+
ctx.strokeStyle = originalStrokeStyle;
|
| 354 |
+
ctx.lineJoin = 'miter';
|
| 355 |
+
ctx.lineCap = 'butt';
|
| 356 |
}
|
| 357 |
|
| 358 |
/**
|
effects/gold.js
CHANGED
|
@@ -5,7 +5,7 @@ export class GoldEffect extends BaseEffect {
|
|
| 5 |
super();
|
| 6 |
this.strokeOptions = {
|
| 7 |
color: '#b8860b',
|
| 8 |
-
width:
|
| 9 |
};
|
| 10 |
}
|
| 11 |
|
|
|
|
| 5 |
super();
|
| 6 |
this.strokeOptions = {
|
| 7 |
color: '#b8860b',
|
| 8 |
+
width: 2
|
| 9 |
};
|
| 10 |
}
|
| 11 |
|
effects/multiline-neon.js
CHANGED
|
@@ -10,7 +10,7 @@ export class MultilineNeonEffect extends BaseEffect {
|
|
| 10 |
};
|
| 11 |
this.strokeOptions = {
|
| 12 |
color: '#ffffff',
|
| 13 |
-
width:
|
| 14 |
};
|
| 15 |
}
|
| 16 |
|
|
|
|
| 10 |
};
|
| 11 |
this.strokeOptions = {
|
| 12 |
color: '#ffffff',
|
| 13 |
+
width: 1
|
| 14 |
};
|
| 15 |
}
|
| 16 |
|
index.html
CHANGED
|
@@ -1,15 +1,25 @@
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
-
<html lang="ja">
|
| 3 |
|
| 4 |
<head>
|
| 5 |
<meta charset="UTF-8">
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 7 |
<title>ロゴジェネレーター</title>
|
| 8 |
-
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.
|
| 9 |
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+JP" rel="stylesheet">
|
| 10 |
<link href="styles.css" rel="stylesheet">
|
| 11 |
<script type="module" src="effects.js"></script>
|
| 12 |
<script type="module" src="index.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
</head>
|
| 14 |
|
| 15 |
<body>
|
|
@@ -17,8 +27,15 @@
|
|
| 17 |
<div class="row justify-content-center">
|
| 18 |
<div class="col-md-8">
|
| 19 |
<div class="card">
|
| 20 |
-
<div class="card-header">
|
| 21 |
<h2 class="h4 mb-0">ロゴジェネレーター</h2>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
</div>
|
| 23 |
<div class="card-body">
|
| 24 |
<div class="mb-3">
|
|
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
+
<html lang="ja" data-bs-theme="light">
|
| 3 |
|
| 4 |
<head>
|
| 5 |
<meta charset="UTF-8">
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 7 |
<title>ロゴジェネレーター</title>
|
| 8 |
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
| 9 |
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+JP" rel="stylesheet">
|
| 10 |
<link href="styles.css" rel="stylesheet">
|
| 11 |
<script type="module" src="effects.js"></script>
|
| 12 |
<script type="module" src="index.js"></script>
|
| 13 |
+
<style>
|
| 14 |
+
.theme-switch {
|
| 15 |
+
display: flex;
|
| 16 |
+
align-items: center;
|
| 17 |
+
gap: 0.5rem;
|
| 18 |
+
}
|
| 19 |
+
.theme-switch .icon {
|
| 20 |
+
font-size: 1.2rem;
|
| 21 |
+
}
|
| 22 |
+
</style>
|
| 23 |
</head>
|
| 24 |
|
| 25 |
<body>
|
|
|
|
| 27 |
<div class="row justify-content-center">
|
| 28 |
<div class="col-md-8">
|
| 29 |
<div class="card">
|
| 30 |
+
<div class="card-header d-flex justify-content-between align-items-center">
|
| 31 |
<h2 class="h4 mb-0">ロゴジェネレーター</h2>
|
| 32 |
+
<div class="theme-switch">
|
| 33 |
+
<span class="icon">☀️</span>
|
| 34 |
+
<div class="form-check form-switch">
|
| 35 |
+
<input class="form-check-input" type="checkbox" id="themeSwitch">
|
| 36 |
+
</div>
|
| 37 |
+
<span class="icon">🌙</span>
|
| 38 |
+
</div>
|
| 39 |
</div>
|
| 40 |
<div class="card-body">
|
| 41 |
<div class="mb-3">
|
index.js
CHANGED
|
@@ -636,3 +636,26 @@ document.getElementById('verticalSpacing').addEventListener('input', function (e
|
|
| 636 |
document.getElementById('verticalSpacingValue').textContent = e.target.value;
|
| 637 |
// ロゴの再生成処理を呼び出す
|
| 638 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 636 |
document.getElementById('verticalSpacingValue').textContent = e.target.value;
|
| 637 |
// ロゴの再生成処理を呼び出す
|
| 638 |
});
|
| 639 |
+
|
| 640 |
+
// ダークモード切り替え機能
|
| 641 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 642 |
+
const themeSwitch = document.getElementById('themeSwitch');
|
| 643 |
+
|
| 644 |
+
// 保存された設定を読み込む
|
| 645 |
+
const savedTheme = localStorage.getItem('theme');
|
| 646 |
+
if (savedTheme === 'dark') {
|
| 647 |
+
document.documentElement.setAttribute('data-bs-theme', 'dark');
|
| 648 |
+
themeSwitch.checked = true;
|
| 649 |
+
}
|
| 650 |
+
|
| 651 |
+
// テーマ切り替えの処理
|
| 652 |
+
themeSwitch.addEventListener('change', () => {
|
| 653 |
+
if (themeSwitch.checked) {
|
| 654 |
+
document.documentElement.setAttribute('data-bs-theme', 'dark');
|
| 655 |
+
localStorage.setItem('theme', 'dark');
|
| 656 |
+
} else {
|
| 657 |
+
document.documentElement.setAttribute('data-bs-theme', 'light');
|
| 658 |
+
localStorage.setItem('theme', 'light');
|
| 659 |
+
}
|
| 660 |
+
});
|
| 661 |
+
});
|
styles.css
CHANGED
|
@@ -32,7 +32,6 @@
|
|
| 32 |
}
|
| 33 |
|
| 34 |
.effect-item {
|
| 35 |
-
background: #f8f9fa;
|
| 36 |
border: 1px solid #dee2e6;
|
| 37 |
border-radius: 0.5rem;
|
| 38 |
padding: 1rem;
|
|
@@ -48,7 +47,6 @@
|
|
| 48 |
}
|
| 49 |
|
| 50 |
.effect-item.error {
|
| 51 |
-
background: #fff3f3;
|
| 52 |
border-color: #ffcdd2;
|
| 53 |
}
|
| 54 |
|
|
|
|
| 32 |
}
|
| 33 |
|
| 34 |
.effect-item {
|
|
|
|
| 35 |
border: 1px solid #dee2e6;
|
| 36 |
border-radius: 0.5rem;
|
| 37 |
padding: 1rem;
|
|
|
|
| 47 |
}
|
| 48 |
|
| 49 |
.effect-item.error {
|
|
|
|
| 50 |
border-color: #ffcdd2;
|
| 51 |
}
|
| 52 |
|