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', () => {