diff --git a/symbols/css/text-generator.css b/symbols/css/text-generator.css
index 94d1730..afa9844 100644
--- a/symbols/css/text-generator.css
+++ b/symbols/css/text-generator.css
@@ -245,6 +245,34 @@
color: var(--gray-600);
}
+/* ========== ALIGN OPTIONS ========== */
+.align-options {
+ display: flex;
+ gap: 0.35rem;
+}
+
+.align-btn {
+ padding: 0.4rem 0.65rem;
+ border: 1px solid var(--gray-300);
+ background: white;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 0.8rem;
+ transition: all 0.2s;
+ color: var(--gray-600);
+}
+
+.align-btn:hover {
+ border-color: var(--primary);
+ color: var(--primary);
+}
+
+.align-btn.active {
+ background: var(--primary);
+ color: white;
+ border-color: var(--primary);
+}
+
/* ========== ARROW OPTIONS ========== */
.arrow-options {
display: flex;
@@ -320,6 +348,34 @@ input[type="range"]::-moz-range-thumb {
border: none;
}
+/* ========== NUMBER INPUTS ========== */
+.num-input {
+ width: 55px;
+ padding: 0.25rem 0.4rem;
+ border: 1px solid var(--gray-300);
+ border-radius: 4px;
+ font-size: 0.8rem;
+ text-align: center;
+ transition: border-color 0.2s, box-shadow 0.2s;
+}
+
+.num-input:focus {
+ outline: none;
+ border-color: var(--primary);
+ box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);
+}
+
+.num-input::-webkit-inner-spin-button,
+.num-input::-webkit-outer-spin-button {
+ opacity: 1;
+}
+
+.num-unit {
+ font-size: 0.75rem;
+ color: var(--gray-500);
+ min-width: 20px;
+}
+
/* ========== RESET BUTTON ========== */
.btn-reset {
background: #ef4444;
diff --git a/symbols/index.html b/symbols/index.html
index 01dadc5..85fd692 100644
--- a/symbols/index.html
+++ b/symbols/index.html
@@ -48,7 +48,8 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/symbols/js/text-generator/state.js b/symbols/js/text-generator/state.js
index 2639c0b..7083cd0 100644
--- a/symbols/js/text-generator/state.js
+++ b/symbols/js/text-generator/state.js
@@ -16,6 +16,8 @@ const TextGenState = {
paddingRight: 10,
paddingBottom: 10,
paddingLeft: 10,
+ textAlignX: 'center',
+ textAlignY: 'center',
lineStyle: 'solid',
lineWeight: 2,
arrow: 'none',
@@ -38,6 +40,8 @@ const TextGenState = {
paddingRight: 10,
paddingBottom: 10,
paddingLeft: 10,
+ textAlignX: 'center',
+ textAlignY: 'center',
lineStyle: 'solid',
lineWeight: 2,
arrow: 'none',
diff --git a/symbols/js/text-generator/svg-generator.js b/symbols/js/text-generator/svg-generator.js
index 65938fc..cc1c734 100644
--- a/symbols/js/text-generator/svg-generator.js
+++ b/symbols/js/text-generator/svg-generator.js
@@ -183,25 +183,49 @@ var SvgGenerator = {
var color = options.color;
var lines = options.lines;
var lineHeight = options.lineHeight;
+ var alignX = options.alignX || 'center';
+ var alignY = options.alignY || 'center';
+ var frameWidth = options.frameWidth || 0;
+ var frameHeight = options.frameHeight || 0;
var group = draw.group();
var totalHeight = lines.length * lineHeight;
- var startY = y - (totalHeight / 2) + (fontSize * 0.8);
+ var startY;
+
+ // Vertikale Ausrichtung
+ switch(alignY) {
+ case 'top':
+ startY = y - frameHeight / 2 + fontSize * 0.8 + 5;
+ break;
+ case 'bottom':
+ startY = y + frameHeight / 2 - totalHeight + fontSize * 0.8 - 5;
+ break;
+ default: // center
+ startY = y - (totalHeight / 2) + (fontSize * 0.8);
+ }
+
+ // Text-Anchor fuer horizontale Ausrichtung
+ var anchor = alignX === 'left' ? 'start' : (alignX === 'right' ? 'end' : 'middle');
+ var textX = x;
+ if (alignX === 'left') {
+ textX = x - frameWidth / 2 + 5;
+ } else if (alignX === 'right') {
+ textX = x + frameWidth / 2 - 5;
+ }
for (var i = 0; i < lines.length; i++) {
var lineY = startY + (i * lineHeight);
var lineText = lines[i] || ' ';
- // Verwende plain SVG text element fuer bessere Kontrolle
var textEl = draw.plain(lineText)
.font({
family: 'Arial, sans-serif',
size: fontSize,
- anchor: 'middle'
+ anchor: anchor
})
.fill(color)
- .attr('x', x)
+ .attr('x', textX)
.attr('y', lineY)
- .attr('text-anchor', 'middle');
+ .attr('text-anchor', anchor);
group.add(textEl);
}
return group;
@@ -224,13 +248,13 @@ var SvgGenerator = {
if (s.arrow !== 'none') {
var angleRad = Math.abs(s.arrowAngle) * Math.PI / 180;
var bendFactor = s.arrowBend / 100;
- var effectiveLength = s.arrowLength * (1 - bendFactor);
+ var angledPart = s.arrowLength * (1 - bendFactor);
- // Hauptrichtung: volle Laenge + Spitze + Puffer
- arrowSpace = s.arrowLength + s.arrowTipLength + 20;
+ // Hauptrichtung: volle Laenge + Spitze + grosszuegiger Puffer
+ arrowSpace = s.arrowLength + s.arrowTipLength + s.arrowSize + 25;
- // Seitliche Abweichung durch Winkel
- arrowSideSpace = Math.abs(Math.sin(angleRad) * effectiveLength) + s.arrowSize + 10;
+ // Seitliche Abweichung durch Winkel (beruecksichtige Pfeilspitze)
+ arrowSideSpace = Math.abs(Math.sin(angleRad) * angledPart) + s.arrowSize + s.arrowTipLength + 15;
}
var svgWidth = minWidth;
@@ -262,7 +286,14 @@ var SvgGenerator = {
break;
}
- var draw = SVG().size(svgWidth, svgHeight);
+ // Puffer um alle Elemente
+ var padding = 5;
+ svgWidth += padding * 2;
+ svgHeight += padding * 2;
+ offsetX += padding;
+ offsetY += padding;
+
+ var draw = SVG().size(svgWidth, svgHeight).viewbox(0, 0, svgWidth, svgHeight);
var frameX = offsetX;
var frameY = offsetY;
@@ -295,7 +326,11 @@ var SvgGenerator = {
this.createText(draw, s.text, textCx, textCy, {
fontSize: s.fontSize, color: s.textColor,
- lines: textMetrics.lines, lineHeight: textMetrics.lineHeight
+ lines: textMetrics.lines, lineHeight: textMetrics.lineHeight,
+ alignX: s.textAlignX || 'center',
+ alignY: s.textAlignY || 'center',
+ frameWidth: minWidth,
+ frameHeight: minHeight
});
return draw.svg();
@@ -353,11 +388,11 @@ var SvgGenerator = {
break;
}
- // Stelle sicher, dass alle Werte positiv sind
- width = Math.max(width, 80);
- height = Math.max(height, 60);
+ // Stelle sicher, dass alle Werte positiv sind und genuegend Platz haben
+ width = Math.max(width, 100);
+ height = Math.max(height, 80);
- var draw = SVG().size(width, height);
+ var draw = SVG().size(width, height).viewbox(0, 0, width, height);
// Simuliere einen Rahmen-Punkt als Startpunkt
var frameRect = {
diff --git a/symbols/js/text-generator/ui-bindings.js b/symbols/js/text-generator/ui-bindings.js
index 0e6a16d..d4595a1 100644
--- a/symbols/js/text-generator/ui-bindings.js
+++ b/symbols/js/text-generator/ui-bindings.js
@@ -20,6 +20,7 @@ var UiBindings = {
// Text & Farben
e.textInput = document.getElementById('customText');
e.fontSizeInput = document.getElementById('fontSize');
+ e.fontSizeNum = document.getElementById('fontSizeNum');
e.fontSizeValue = document.getElementById('fontSizeValue');
e.textColorInput = document.getElementById('textColor');
e.textColorValue = document.getElementById('textColorValue');
@@ -28,11 +29,13 @@ var UiBindings = {
// Rahmen
e.frameScaleInput = document.getElementById('frameScale');
+ e.frameScaleNum = document.getElementById('frameScaleNum');
e.frameScaleValue = document.getElementById('frameScaleValue');
e.frameScaleRow = document.getElementById('frameScaleRow');
// Padding
e.paddingAllInput = document.getElementById('paddingAll');
+ e.paddingAllNum = document.getElementById('paddingAllNum');
e.paddingAllValue = document.getElementById('paddingAllValue');
e.paddingTopInput = document.getElementById('paddingTop');
e.paddingTopValue = document.getElementById('paddingTopValue');
@@ -47,14 +50,19 @@ var UiBindings = {
// Pfeil
e.arrowLengthInput = document.getElementById('arrowLength');
+ e.arrowLengthNum = document.getElementById('arrowLengthNum');
e.arrowLengthValue = document.getElementById('arrowLengthValue');
e.arrowAngleInput = document.getElementById('arrowAngle');
+ e.arrowAngleNum = document.getElementById('arrowAngleNum');
e.arrowAngleValue = document.getElementById('arrowAngleValue');
e.arrowBendInput = document.getElementById('arrowBend');
+ e.arrowBendNum = document.getElementById('arrowBendNum');
e.arrowBendValue = document.getElementById('arrowBendValue');
e.arrowSizeInput = document.getElementById('arrowSize');
+ e.arrowSizeNum = document.getElementById('arrowSizeNum');
e.arrowSizeValue = document.getElementById('arrowSizeValue');
e.arrowTipLengthInput = document.getElementById('arrowTipLength');
+ e.arrowTipLengthNum = document.getElementById('arrowTipLengthNum');
e.arrowTipLengthValue = document.getElementById('arrowTipLengthValue');
e.arrowDetailsRow = document.getElementById('arrowDetailsRow');
e.arrowDetailsRow2 = document.getElementById('arrowDetailsRow2');
@@ -63,6 +71,11 @@ var UiBindings = {
e.textPreview = document.getElementById('textPreview');
e.standaloneArrowPreview = document.getElementById('standaloneArrowPreview');
+ // Text-Ausrichtung
+ e.textAlignRow = document.getElementById('textAlignRow');
+ e.alignXButtons = document.querySelectorAll('[data-align-x]');
+ e.alignYButtons = document.querySelectorAll('[data-align-y]');
+
// Buttons
e.shapeButtons = document.querySelectorAll('.shape-btn');
e.arrowButtons = document.querySelectorAll('.arrow-btn');
@@ -83,12 +96,23 @@ var UiBindings = {
});
}
- // Schriftgroesse
+ // Schriftgroesse - Slider und Number-Input synchronisieren
if (e.fontSizeInput) {
e.fontSizeInput.addEventListener('input', function(ev) {
var val = parseInt(ev.target.value);
state.set('fontSize', val);
if (e.fontSizeValue) e.fontSizeValue.textContent = val + 'px';
+ if (e.fontSizeNum) e.fontSizeNum.value = val;
+ self.updatePreview();
+ });
+ }
+ if (e.fontSizeNum) {
+ e.fontSizeNum.addEventListener('input', function(ev) {
+ var val = parseInt(ev.target.value) || 8;
+ val = Math.max(8, Math.min(48, val));
+ state.set('fontSize', val);
+ if (e.fontSizeInput) e.fontSizeInput.value = val;
+ if (e.fontSizeValue) e.fontSizeValue.textContent = val + 'px';
self.updatePreview();
});
}
@@ -109,37 +133,55 @@ var UiBindings = {
});
}
- // Rahmen-Skalierung
+ // Rahmen-Skalierung - Slider und Number-Input synchronisieren
if (e.frameScaleInput) {
e.frameScaleInput.addEventListener('input', function(ev) {
var val = parseInt(ev.target.value);
state.set('frameScale', val);
if (e.frameScaleValue) e.frameScaleValue.textContent = val + '%';
+ if (e.frameScaleNum) e.frameScaleNum.value = val;
+ self.updatePreview();
+ });
+ }
+ if (e.frameScaleNum) {
+ e.frameScaleNum.addEventListener('input', function(ev) {
+ var val = parseInt(ev.target.value) || 50;
+ val = Math.max(50, Math.min(200, val));
+ state.set('frameScale', val);
+ if (e.frameScaleInput) e.frameScaleInput.value = val;
+ if (e.frameScaleValue) e.frameScaleValue.textContent = val + '%';
self.updatePreview();
});
}
- // Gesamt-Padding
+ // Gesamt-Padding - Slider und Number-Input synchronisieren
+ var syncAllPadding = function(val) {
+ state.setMultiple({
+ paddingTop: val, paddingRight: val, paddingBottom: val, paddingLeft: val
+ });
+ if (e.paddingAllValue) e.paddingAllValue.textContent = val + 'px';
+ if (e.paddingAllNum) e.paddingAllNum.value = val;
+ if (e.paddingAllInput) e.paddingAllInput.value = val;
+ if (e.paddingTopInput) e.paddingTopInput.value = val;
+ if (e.paddingTopValue) e.paddingTopValue.textContent = val + 'px';
+ if (e.paddingRightInput) e.paddingRightInput.value = val;
+ if (e.paddingRightValue) e.paddingRightValue.textContent = val + 'px';
+ if (e.paddingBottomInput) e.paddingBottomInput.value = val;
+ if (e.paddingBottomValue) e.paddingBottomValue.textContent = val + 'px';
+ if (e.paddingLeftInput) e.paddingLeftInput.value = val;
+ if (e.paddingLeftValue) e.paddingLeftValue.textContent = val + 'px';
+ self.updatePreview();
+ };
if (e.paddingAllInput) {
e.paddingAllInput.addEventListener('input', function(ev) {
- var val = parseInt(ev.target.value);
- state.setMultiple({
- paddingTop: val,
- paddingRight: val,
- paddingBottom: val,
- paddingLeft: val
- });
- if (e.paddingAllValue) e.paddingAllValue.textContent = val + 'px';
- // Einzelne Slider synchronisieren
- if (e.paddingTopInput) e.paddingTopInput.value = val;
- if (e.paddingTopValue) e.paddingTopValue.textContent = val + 'px';
- if (e.paddingRightInput) e.paddingRightInput.value = val;
- if (e.paddingRightValue) e.paddingRightValue.textContent = val + 'px';
- if (e.paddingBottomInput) e.paddingBottomInput.value = val;
- if (e.paddingBottomValue) e.paddingBottomValue.textContent = val + 'px';
- if (e.paddingLeftInput) e.paddingLeftInput.value = val;
- if (e.paddingLeftValue) e.paddingLeftValue.textContent = val + 'px';
- self.updatePreview();
+ syncAllPadding(parseInt(ev.target.value));
+ });
+ }
+ if (e.paddingAllNum) {
+ e.paddingAllNum.addEventListener('input', function(ev) {
+ var val = parseInt(ev.target.value) || 0;
+ val = Math.max(0, Math.min(50, val));
+ syncAllPadding(val);
});
}
@@ -198,12 +240,34 @@ var UiBindings = {
self.updatePreview();
});
});
+
+ // Align-X-Buttons (horizontal)
+ e.alignXButtons.forEach(function(btn) {
+ btn.addEventListener('click', function() {
+ e.alignXButtons.forEach(function(b) { b.classList.remove('active'); });
+ btn.classList.add('active');
+ state.set('textAlignX', btn.dataset.alignX);
+ self.updatePreview();
+ });
+ });
+
+ // Align-Y-Buttons (vertikal)
+ e.alignYButtons.forEach(function(btn) {
+ btn.addEventListener('click', function() {
+ e.alignYButtons.forEach(function(b) { b.classList.remove('active'); });
+ btn.classList.add('active');
+ state.set('textAlignY', btn.dataset.alignY);
+ self.updatePreview();
+ });
+ });
},
bindSlider: function(inputKey, valueKey, stateKey, unit) {
var self = this;
var input = this.elements[inputKey];
var valueEl = this.elements[valueKey];
+ var numKey = inputKey.replace('Input', 'Num');
+ var numInput = this.elements[numKey];
var state = window.TextGenState;
if (input) {
@@ -211,6 +275,22 @@ var UiBindings = {
var val = parseInt(ev.target.value);
state.set(stateKey, val);
if (valueEl) valueEl.textContent = val + unit;
+ if (numInput) numInput.value = val;
+ self.updatePreview();
+ if (stateKey.indexOf('arrow') === 0) {
+ self.updateStandaloneArrowPreview();
+ }
+ });
+ }
+ if (numInput) {
+ var min = input ? parseInt(input.min) : 0;
+ var max = input ? parseInt(input.max) : 100;
+ numInput.addEventListener('input', function(ev) {
+ var val = parseInt(ev.target.value) || min;
+ val = Math.max(min, Math.min(max, val));
+ state.set(stateKey, val);
+ if (input) input.value = val;
+ if (valueEl) valueEl.textContent = val + unit;
self.updatePreview();
if (stateKey.indexOf('arrow') === 0) {
self.updateStandaloneArrowPreview();
@@ -241,14 +321,17 @@ var UiBindings = {
if (e.textInput) e.textInput.value = s.text;
if (e.fontSizeInput) e.fontSizeInput.value = s.fontSize;
+ if (e.fontSizeNum) e.fontSizeNum.value = s.fontSize;
if (e.fontSizeValue) e.fontSizeValue.textContent = s.fontSize + 'px';
if (e.textColorInput) e.textColorInput.value = s.textColor;
if (e.frameColorInput) e.frameColorInput.value = s.frameColor;
if (e.frameScaleInput) e.frameScaleInput.value = s.frameScale;
+ if (e.frameScaleNum) e.frameScaleNum.value = s.frameScale;
if (e.frameScaleValue) e.frameScaleValue.textContent = s.frameScale + '%';
// Padding
if (e.paddingAllInput) e.paddingAllInput.value = s.paddingTop;
+ if (e.paddingAllNum) e.paddingAllNum.value = s.paddingTop;
if (e.paddingAllValue) e.paddingAllValue.textContent = s.paddingTop + 'px';
if (e.paddingTopInput) e.paddingTopInput.value = s.paddingTop;
if (e.paddingTopValue) e.paddingTopValue.textContent = s.paddingTop + 'px';
@@ -261,14 +344,19 @@ var UiBindings = {
// Pfeil
if (e.arrowLengthInput) e.arrowLengthInput.value = s.arrowLength;
+ if (e.arrowLengthNum) e.arrowLengthNum.value = s.arrowLength;
if (e.arrowLengthValue) e.arrowLengthValue.textContent = s.arrowLength + 'px';
if (e.arrowAngleInput) e.arrowAngleInput.value = s.arrowAngle;
+ if (e.arrowAngleNum) e.arrowAngleNum.value = s.arrowAngle;
if (e.arrowAngleValue) e.arrowAngleValue.textContent = s.arrowAngle + '\u00B0';
if (e.arrowBendInput) e.arrowBendInput.value = s.arrowBend;
+ if (e.arrowBendNum) e.arrowBendNum.value = s.arrowBend;
if (e.arrowBendValue) e.arrowBendValue.textContent = s.arrowBend + '%';
if (e.arrowSizeInput) e.arrowSizeInput.value = s.arrowSize;
+ if (e.arrowSizeNum) e.arrowSizeNum.value = s.arrowSize;
if (e.arrowSizeValue) e.arrowSizeValue.textContent = s.arrowSize + 'px';
if (e.arrowTipLengthInput) e.arrowTipLengthInput.value = s.arrowTipLength;
+ if (e.arrowTipLengthNum) e.arrowTipLengthNum.value = s.arrowTipLength;
if (e.arrowTipLengthValue) e.arrowTipLengthValue.textContent = s.arrowTipLength + 'px';
// Buttons aktivieren
@@ -276,6 +364,8 @@ var UiBindings = {
this.activateButton(e.arrowButtons, 'arrow', s.arrow);
this.activateButton(e.lineStyleButtons, 'style', s.lineStyle);
this.activateButtonByValue(e.lineWeightButtons, 'weight', s.lineWeight);
+ this.activateButton(e.alignXButtons, 'alignX', s.textAlignX);
+ this.activateButton(e.alignYButtons, 'alignY', s.textAlignY);
},
activateButton: function(buttons, dataAttr, value) {
@@ -305,6 +395,7 @@ var UiBindings = {
if (e.frameScaleRow) e.frameScaleRow.style.display = hasShape ? 'flex' : 'none';
if (e.paddingAllRow) e.paddingAllRow.style.display = hasShape ? 'flex' : 'none';
if (e.paddingRow) e.paddingRow.style.display = hasShape ? 'flex' : 'none';
+ if (e.textAlignRow) e.textAlignRow.style.display = hasShape ? 'flex' : 'none';
if (e.arrowDetailsRow) e.arrowDetailsRow.style.display = hasArrow ? 'flex' : 'none';
if (e.arrowDetailsRow2) e.arrowDetailsRow2.style.display = hasArrow ? 'flex' : 'none';
},