diff --git a/index.php b/index.php
index c9d772c..5dbba4b 100644
--- a/index.php
+++ b/index.php
@@ -639,12 +639,12 @@ https://live.staticflickr.com/65535/12345678901_abcdef1234_b.jpg">
-
-
+
+
-
-
+
+
@@ -685,17 +685,24 @@ https://live.staticflickr.com/65535/12345678901_abcdef1234_b.jpg">
По умолчанию — из настроек.
+
+
+
+
+
+
+
+
diff --git a/js/app.js b/js/app.js
index fba9eaa..64249a2 100644
--- a/js/app.js
+++ b/js/app.js
@@ -4260,6 +4260,11 @@ document.addEventListener('DOMContentLoaded', function() {
nickEnabled: document.getElementById('badge-nickname-enabled'),
nickOptions: document.getElementById('badge-nickname-options'),
nickValue: document.getElementById('badge-nickname-value'),
+ nickColor: document.getElementById('badge-nick-color'),
+ nickSize: document.getElementById('badge-nick-size'),
+ nickSizeValue: document.getElementById('badge-nick-size-value'),
+ nickEdge: document.getElementById('badge-nick-edge'),
+ nickEdgeValue: document.getElementById('badge-nick-edge-value'),
btnDownload: document.getElementById('btn-badge-download'),
btnRemove: document.getElementById('btn-badge-remove'),
batchActions: document.getElementById('badge-batch-actions'),
@@ -4372,12 +4377,32 @@ document.addEventListener('DOMContentLoaded', function() {
c.restore();
}
- function drawNicknameArc(c, size, text, color, position) {
+ function hexBrightness(hex) {
+ const c = (hex || '').replace('#', '');
+ if (c.length !== 6) return 255;
+ const r = parseInt(c.substr(0, 2), 16);
+ const g = parseInt(c.substr(2, 2), 16);
+ const b = parseInt(c.substr(4, 2), 16);
+ return (r * 299 + g * 587 + b * 114) / 1000;
+ }
+
+ // Map legacy "white"/"black" values to hex so old items still render.
+ function normalizeNickColor(v) {
+ if (!v) return '#FFFFFF';
+ if (v === 'white') return '#FFFFFF';
+ if (v === 'black') return '#000000';
+ return v;
+ }
+
+ function drawNicknameArc(c, size, text, item, position) {
+ const color = normalizeNickColor(item.nickColor);
+ const sizePct = item.nickSize || 100;
+ const edgePct = (item.nickEdge != null) ? item.nickEdge : 14;
const cx = size / 2;
const cy = size / 2;
const R = size / 2;
- const textRadius = R * 0.86;
- const fontSize = Math.max(11, size * 0.075);
+ const textRadius = R * Math.max(0.55, Math.min(0.99, 1 - edgePct / 100));
+ const fontSize = Math.max(8, size * 0.075 * (sizePct / 100));
c.save();
c.font = `700 ${fontSize}px ${FONT_STACK}`;
c.textAlign = 'center';
@@ -4400,6 +4425,11 @@ document.addEventListener('DOMContentLoaded', function() {
rotationOffset = -Math.PI / 2;
}
+ // Pick a contrasting shadow so the text is readable against any background.
+ const isDarkText = hexBrightness(color) < 130;
+ const shadowColor = isDarkText ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.75)';
+ const shadowBlur = size * (isDarkText ? 0.012 : 0.016);
+
let cumulative = 0;
for (let i = 0; i < chars.length; i++) {
const w = widths[i];
@@ -4410,16 +4440,10 @@ document.addEventListener('DOMContentLoaded', function() {
c.save();
c.translate(x, y);
c.rotate(a + rotationOffset);
- if (color === 'black') {
- c.shadowColor = 'rgba(255,255,255,0.7)';
- c.shadowBlur = size * 0.012;
- c.fillStyle = '#000';
- } else {
- c.shadowColor = 'rgba(0,0,0,0.75)';
- c.shadowBlur = size * 0.016;
- c.shadowOffsetY = size * 0.003;
- c.fillStyle = '#fff';
- }
+ c.shadowColor = shadowColor;
+ c.shadowBlur = shadowBlur;
+ if (!isDarkText) c.shadowOffsetY = size * 0.003;
+ c.fillStyle = color;
c.fillText(chars[i], 0, 0);
c.restore();
}
@@ -4473,7 +4497,7 @@ document.addEventListener('DOMContentLoaded', function() {
const nick = (item.nickname || '').trim();
if (nick) {
// Auto-position: if price occupies the bottom, draw nickname as a top arc.
- drawNicknameArc(c, size, nick, item.nickColor || 'white', hasPrice ? 'top' : 'bottom');
+ drawNicknameArc(c, size, nick, item, hasPrice ? 'top' : 'bottom');
}
}
@@ -4530,13 +4554,15 @@ document.addEventListener('DOMContentLoaded', function() {
showPrice: false,
priceValue: '',
priceStyle: 'arc',
- priceY: 0, // -50..+20: vertical offset from default position
- priceSize: 100, // 60..160 (% scale of band height)
+ priceY: 9, // -50..+20: vertical offset from default position
+ priceSize: 60, // 60..160 (% scale of band height)
priceBg: '#FFCC00', // ribbon color
priceFg: '#000000', // digits color
showNickname: false,
nickname: '',
- nickColor: 'white',
+ nickColor: '#FFFFFF', // text color (hex)
+ nickSize: 100, // 60..160 (% scale of font)
+ nickEdge: 14, // 0..30: percent inset from circle edge (0 = at edge)
};
}
@@ -4593,9 +4619,14 @@ document.addEventListener('DOMContentLoaded', function() {
el.nickEnabled.checked = item.showNickname;
el.nickOptions.classList.toggle('hidden', !item.showNickname);
el.nickValue.value = item.nickname || badgeState.defaultNickname || '';
- document.querySelectorAll('input[name="badge-nick-color"]').forEach(r => {
- r.checked = r.value === item.nickColor;
- });
+ const nickHex = normalizeNickColor(item.nickColor);
+ item.nickColor = nickHex;
+ el.nickColor.value = nickHex;
+ syncSwatches('nick', nickHex);
+ el.nickSize.value = item.nickSize;
+ el.nickSizeValue.textContent = item.nickSize + '%';
+ el.nickEdge.value = item.nickEdge;
+ el.nickEdgeValue.textContent = item.nickEdge;
renderItemsGrid();
renderPreview();
}
@@ -4873,6 +4904,9 @@ document.addEventListener('DOMContentLoaded', function() {
} else if (target === 'fg') {
item.priceFg = color;
el.priceFg.value = color;
+ } else if (target === 'nick') {
+ item.nickColor = color;
+ el.nickColor.value = color;
}
syncSwatches(target, color);
renderPreview();
@@ -4899,15 +4933,33 @@ document.addEventListener('DOMContentLoaded', function() {
renderPreview();
});
- document.querySelectorAll('input[name="badge-nick-color"]').forEach(r => {
- r.addEventListener('change', () => {
- const item = currentItem();
- if (!item) return;
- if (r.checked) {
- item.nickColor = r.value;
- renderPreview();
- }
- });
+ // Nickname color (hex input + swatches handled together with price swatches)
+ el.nickColor.addEventListener('input', () => {
+ const item = currentItem();
+ if (!item) return;
+ item.nickColor = el.nickColor.value;
+ syncSwatches('nick', item.nickColor);
+ renderPreview();
+ });
+
+ // Nickname size
+ el.nickSize.addEventListener('input', () => {
+ const item = currentItem();
+ if (!item) return;
+ const v = parseInt(el.nickSize.value, 10) || 100;
+ item.nickSize = v;
+ el.nickSizeValue.textContent = v + '%';
+ renderPreview();
+ });
+
+ // Nickname edge offset
+ el.nickEdge.addEventListener('input', () => {
+ const item = currentItem();
+ if (!item) return;
+ const v = parseInt(el.nickEdge.value, 10) || 0;
+ item.nickEdge = v;
+ el.nickEdgeValue.textContent = v;
+ renderPreview();
});
el.btnRemove.addEventListener('click', () => {