Erweitert die Funktionalität in CourtDrawingRender.vue und CourtDrawingTool.vue zur Verbesserung der Zeichnungslogik. Fügt neue Offset-Parameter für Zielkreise hinzu und optimiert die Berechnung der Zielpositionen. Entfernt die Schaltflächen für das manuelle Speichern in CourtDrawingTool.vue zugunsten einer automatischen Speicherung. Aktualisiert die Benutzeroberfläche in PredefinedActivities.vue zur Unterstützung der neuen Zeichnungsdaten-Logik.
This commit is contained in:
@@ -66,14 +66,27 @@ export default {
|
||||
primaryColor: '#d32f2f', // rechts -> target (rot)
|
||||
secondaryColor: '#1565c0', // zurück (blau)
|
||||
width: 6,
|
||||
headLength: 24
|
||||
headLength: 24,
|
||||
vhOffsetX: 5,
|
||||
vhOffsetY: 8,
|
||||
rhOffsetX: 10,
|
||||
rhOffsetY: 0
|
||||
},
|
||||
targetCircles: {
|
||||
radius: 13,
|
||||
rightXOffset: 20,
|
||||
middleXOffset: 40,
|
||||
topYOffset: 45,
|
||||
bottomYOffset: 45,
|
||||
transparency: 0.25
|
||||
},
|
||||
leftTargetCircles: {
|
||||
radius: 10,
|
||||
leftXOffset: 20,
|
||||
rightXOffset: 40,
|
||||
topYOffset: 40,
|
||||
bottomYOffset: 40
|
||||
},
|
||||
hitMarker: {
|
||||
radius: 10.5,
|
||||
fill: '#ffffff',
|
||||
@@ -223,17 +236,17 @@ export default {
|
||||
ctx.stroke();
|
||||
},
|
||||
computeRightTargetPosition(number) {
|
||||
// replicate the same geometry as in the drawing tool
|
||||
// Geometrie wie im Tool: nutzt rightXOffset/middleXOffset
|
||||
const tableWidth = this.config.table.width;
|
||||
const tableHeight = this.config.table.height;
|
||||
const tableX = (this.config.canvas.width - tableWidth) / 2;
|
||||
const tableY = (this.config.canvas.height - tableHeight) / 2;
|
||||
const centerX = tableX + tableWidth / 2;
|
||||
const cfg = this.config.targetCircles;
|
||||
|
||||
const x1 = tableX + tableWidth - 30; // rightmost column (1,2,3)
|
||||
const x3 = centerX + (x1 - centerX) * 0.35; // near net (7,8,9)
|
||||
const x2 = (x1 + x3) / 2; // middle column (4,5,6)
|
||||
const x1 = tableX + tableWidth - cfg.rightXOffset; // (1,2,3)
|
||||
const x3 = tableX + tableWidth / 2 + cfg.middleXOffset; // (7,8,9)
|
||||
const xdiff = x3 - x1;
|
||||
const x2 = x3 - xdiff / 2; // (4,5,6)
|
||||
|
||||
const positions = {
|
||||
1: { x: x1, y: tableY + cfg.topYOffset },
|
||||
@@ -249,22 +262,23 @@ export default {
|
||||
return positions[number] || null;
|
||||
},
|
||||
getStartPoint() {
|
||||
const cfg = this.config.startCircles;
|
||||
const tableWidth = this.config.table.width;
|
||||
const tableHeight = this.config.table.height;
|
||||
const tableX = (this.config.canvas.width - tableWidth) / 2;
|
||||
const tableY = (this.config.canvas.height - tableHeight) / 2;
|
||||
const map = {
|
||||
AS1: 'top', AS2: 'middle', AS3: 'bottom', AS: 'middle'
|
||||
};
|
||||
const selected = map[this.drawingData?.selectedStartPosition] || 'middle';
|
||||
const circleX = tableX - cfg.x; // Links vom Tisch
|
||||
const topY = tableY + cfg.topYOffset;
|
||||
const midY = tableY + tableHeight / 2;
|
||||
const botY = tableY + tableHeight - cfg.bottomYOffset;
|
||||
const y = selected === 'top' ? topY : selected === 'bottom' ? botY : midY;
|
||||
// arrow should start slightly to the right of the start circle
|
||||
return { x: circleX + cfg.radius + 6, y };
|
||||
// Startpunkt wie im Tool abhängig von Schlagseite (VH/RH)
|
||||
const sc = this.config.startCircles;
|
||||
const ar = this.config.arrows;
|
||||
const tblW = this.config.table.width;
|
||||
const tblH = this.config.table.height;
|
||||
const tableX = (this.config.canvas.width - tblW) / 2;
|
||||
const tableY = (this.config.canvas.height - tblH) / 2;
|
||||
const circleX = tableX - sc.x; // Kreis links vor dem Tisch
|
||||
const map = { AS1: 'top', AS2: 'middle', AS3: 'bottom', AS: 'middle' };
|
||||
const pos = map[this.drawingData?.selectedStartPosition] || 'middle';
|
||||
const y = pos === 'top' ? tableY + sc.topYOffset : pos === 'bottom' ? tableY + tblH - sc.bottomYOffset : tableY + tblH / 2;
|
||||
const isVH = (this.drawingData?.strokeType || 'VH') === 'VH';
|
||||
const startX = isVH
|
||||
? circleX + sc.radius + ar.vhOffsetX
|
||||
: circleX + sc.radius + ar.rhOffsetX;
|
||||
const startYOffset = isVH ? ar.vhOffsetY : ar.rhOffsetY;
|
||||
return { x: startX, y: y + startYOffset };
|
||||
},
|
||||
drawArrow(ctx, from, to, color, label) {
|
||||
const { width, headLength } = this.config.arrows;
|
||||
@@ -318,7 +332,8 @@ export default {
|
||||
const spinAbbrev = this.abbrevSpin(this.drawingData.spinType);
|
||||
// Text gehört an die Quelle (ohne "target")
|
||||
const sourceLabel = `${strokeSide} ${spinAbbrev}`.trim();
|
||||
this.drawArrow(ctx, from, to, this.config.arrows.primaryColor);
|
||||
const toEnd = { x: to.x - this.config.targetCircles.radius, y: to.y };
|
||||
this.drawArrow(ctx, from, toEnd, this.config.arrows.primaryColor);
|
||||
// Unter dem Startkreis beschriften
|
||||
const startCenter = this.getStartCircleCenter();
|
||||
this.drawLabelBelow(ctx, sourceLabel, startCenter);
|
||||
@@ -328,18 +343,20 @@ export default {
|
||||
const leftTarget = this.drawingData.nextStrokeTargetPosition ? Number(this.drawingData.nextStrokeTargetPosition) : null;
|
||||
if (tp && leftTarget) {
|
||||
// source near previous right target
|
||||
const sourceRight = this.computeRightTargetPosition(tp);
|
||||
const sourceRightCenter = this.computeRightTargetPosition(tp);
|
||||
// left target mapping: mirror scheme to left half
|
||||
const toLeft = this.computeLeftTargetPosition(leftTarget);
|
||||
const toLeftCenter = this.computeLeftTargetPosition(leftTarget);
|
||||
// Zielmarkierung links
|
||||
this.drawHitMarker(ctx, toLeft);
|
||||
this.drawHitMarker(ctx, toLeftCenter);
|
||||
const side = this.drawingData.nextStrokeSide || '';
|
||||
const type = this.drawingData.nextStrokeType || '';
|
||||
// Text gehört ans Ziel (ohne "extra target")
|
||||
const targetLabel = `${side} ${type}`.trim();
|
||||
const sourceRight = { x: sourceRightCenter.x - this.config.targetCircles.radius, y: sourceRightCenter.y };
|
||||
const toLeft = { x: toLeftCenter.x + this.config.leftTargetCircles.radius, y: toLeftCenter.y };
|
||||
this.drawArrow(ctx, sourceRight, toLeft, this.config.arrows.secondaryColor);
|
||||
// Unter dem rechten Ziel (target der ersten Linie) beschriften
|
||||
this.drawLabelBelow(ctx, targetLabel, sourceRight);
|
||||
this.drawLabelBelow(ctx, targetLabel, sourceRightCenter);
|
||||
}
|
||||
},
|
||||
getStartCircleCenter() {
|
||||
@@ -370,28 +387,32 @@ export default {
|
||||
ctx.stroke();
|
||||
},
|
||||
computeLeftTargetPosition(number) {
|
||||
// mirror target grid to left side
|
||||
// Spiegelung wie im Tool: nutzt leftTargetCircles Offsets
|
||||
const tableWidth = this.config.table.width;
|
||||
const tableHeight = this.config.table.height;
|
||||
const tableX = (this.config.canvas.width - tableWidth) / 2;
|
||||
const tableY = (this.config.canvas.height - tableHeight) / 2;
|
||||
const centerX = tableX + tableWidth / 2;
|
||||
const cfg = this.config.targetCircles;
|
||||
const cfg = this.config.leftTargetCircles;
|
||||
|
||||
const x1 = tableX + 30; // leftmost column
|
||||
const x3 = centerX - (centerX - x1) * 0.35; // near net
|
||||
const x2 = (x1 + x3) / 2;
|
||||
const x1 = tableX + cfg.leftXOffset; // linke Spalte (Lang)
|
||||
const x3 = tableX + tableWidth / 2 - cfg.rightXOffset; // nah am Netz (Kurz)
|
||||
const xdiff = x3 - x1;
|
||||
const x2 = x3 - xdiff / 2;
|
||||
|
||||
// Gespiegelte Y-Zuordnung wie im Tool:
|
||||
// 1,4,7 = unten (VH gespiegelt)
|
||||
// 2,5,8 = mitte
|
||||
// 3,6,9 = oben (RH gespiegelt)
|
||||
const positions = {
|
||||
1: { x: x1, y: tableY + cfg.topYOffset },
|
||||
1: { x: x1, y: tableY + tableHeight - cfg.bottomYOffset },
|
||||
2: { x: x1, y: tableY + tableHeight / 2 },
|
||||
3: { x: x1, y: tableY + tableHeight - cfg.bottomYOffset },
|
||||
4: { x: x2, y: tableY + cfg.topYOffset },
|
||||
3: { x: x1, y: tableY + cfg.topYOffset },
|
||||
4: { x: x2, y: tableY + tableHeight - cfg.bottomYOffset },
|
||||
5: { x: x2, y: tableY + tableHeight / 2 },
|
||||
6: { x: x2, y: tableY + tableHeight - cfg.bottomYOffset },
|
||||
7: { x: x3, y: tableY + cfg.topYOffset },
|
||||
6: { x: x2, y: tableY + cfg.topYOffset },
|
||||
7: { x: x3, y: tableY + tableHeight - cfg.bottomYOffset },
|
||||
8: { x: x3, y: tableY + tableHeight / 2 },
|
||||
9: { x: x3, y: tableY + tableHeight - cfg.bottomYOffset }
|
||||
9: { x: x3, y: tableY + cfg.topYOffset }
|
||||
};
|
||||
return positions[number] || null;
|
||||
},
|
||||
|
||||
@@ -172,11 +172,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Buttons ganz nach unten -->
|
||||
<div class="tool-controls">
|
||||
<button @click="clearCanvas" class="btn-secondary btn-small">Löschen</button>
|
||||
<button @click="saveDrawing" class="btn-primary btn-small">Speichern</button>
|
||||
</div>
|
||||
<!-- Buttons entfernt: automatische Speicherung aktiv -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -323,13 +319,16 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
strokeType() {
|
||||
this.emitDrawingData();
|
||||
this.updateTextFields();
|
||||
},
|
||||
spinType() {
|
||||
this.drawCourt(); // Neu zeichnen wenn sich Schnittoption ändert
|
||||
this.emitDrawingData();
|
||||
this.updateTextFields();
|
||||
},
|
||||
targetPosition() {
|
||||
this.emitDrawingData();
|
||||
this.updateTextFields();
|
||||
},
|
||||
selectedStartPosition() {
|
||||
@@ -337,6 +336,7 @@ export default {
|
||||
if (this.canvas && this.ctx) {
|
||||
this.drawCourt();
|
||||
}
|
||||
this.emitDrawingData();
|
||||
},
|
||||
drawingData: {
|
||||
handler(newVal, oldVal) {
|
||||
@@ -889,6 +889,7 @@ export default {
|
||||
this.selectedCirclePosition = clickedCircle.circlePosition;
|
||||
this.selectedStartPosition = clickedCircle.position;
|
||||
this.drawCourt(); // Neu zeichnen für Kreis-Hervorhebung
|
||||
this.emitDrawingData();
|
||||
this.updateTextFields();
|
||||
return;
|
||||
}
|
||||
@@ -898,13 +899,14 @@ export default {
|
||||
if (clickedTarget) {
|
||||
this.targetPosition = clickedTarget;
|
||||
|
||||
// Wenn keine Startposition ausgewählt ist, setze eine Standard-Startposition
|
||||
// Wenn keine Startposition ausgewählt ist, setze standardmäßig die mittlere Startposition (AS2)
|
||||
if (!this.selectedStartPosition) {
|
||||
this.selectedStartPosition = 'AS';
|
||||
this.selectedStartPosition = 'AS2';
|
||||
this.selectedCirclePosition = 'middle';
|
||||
}
|
||||
|
||||
this.drawCourt(); // Neu zeichnen für Zielposition-Hervorhebung
|
||||
this.emitDrawingData();
|
||||
this.updateTextFields();
|
||||
return;
|
||||
}
|
||||
@@ -915,6 +917,7 @@ export default {
|
||||
if (clickedLeftTarget) {
|
||||
this.nextStrokeTargetPosition = clickedLeftTarget;
|
||||
this.drawCourt();
|
||||
this.emitDrawingData();
|
||||
this.updateTextFields();
|
||||
return;
|
||||
}
|
||||
@@ -943,9 +946,9 @@ export default {
|
||||
|
||||
// Prüfe Abstand zu den Kreisen
|
||||
const circles = [
|
||||
{ x: circleX, y: topCircleY, position: 'AS', circlePosition: 'top' },
|
||||
{ x: circleX, y: middleCircleY, position: 'AS', circlePosition: 'middle' },
|
||||
{ x: circleX, y: bottomCircleY, position: 'AS', circlePosition: 'bottom' }
|
||||
{ x: circleX, y: topCircleY, position: 'AS1', circlePosition: 'top' },
|
||||
{ x: circleX, y: middleCircleY, position: 'AS2', circlePosition: 'middle' },
|
||||
{ x: circleX, y: bottomCircleY, position: 'AS3', circlePosition: 'bottom' }
|
||||
];
|
||||
|
||||
for (const circle of circles) {
|
||||
@@ -1252,27 +1255,38 @@ export default {
|
||||
'Gegenläufer': 'GL'
|
||||
};
|
||||
|
||||
// Label-Mapping für Zielpositionen
|
||||
const targetLabelMap = {
|
||||
// Suffix-Mapping für Startposition (an AS direkt anhängen, ohne Leerzeichen)
|
||||
const startSuffixMap = {
|
||||
'AS1': 'vL',
|
||||
'AS2': 'vM',
|
||||
'AS3': 'vR'
|
||||
};
|
||||
const startSuffix = startSuffixMap[this.selectedStartPosition] || '';
|
||||
const startCode = `AS${startSuffix}`;
|
||||
|
||||
// Label-Mapping für erstes Ziel (Hauptpfeil): altes Schema
|
||||
const mainTargetLabelMap = {
|
||||
'1': 'VH L', '2': 'M L', '3': 'RH L',
|
||||
'4': 'VH H', '5': 'M H', '6': 'RH H',
|
||||
'7': 'VH K', '8': 'M K', '9': 'RH K'
|
||||
};
|
||||
// Label-Mapping für Zusatzschläge: wie Hauptziel (VH L, M L, RH L ...)
|
||||
const additionalTargetLabelMap = mainTargetLabelMap;
|
||||
|
||||
let code = `${this.selectedStartPosition} ${this.strokeType}`;
|
||||
let code = `${startCode} ${this.strokeType}`;
|
||||
if (this.spinType) {
|
||||
const spinCode = spinCodeMap[this.spinType] || this.spinType;
|
||||
code += ` ${spinCode}`;
|
||||
}
|
||||
if (this.targetPosition) {
|
||||
const targetLabel = targetLabelMap[this.targetPosition] || this.targetPosition;
|
||||
const targetLabel = mainTargetLabelMap[this.targetPosition] || this.targetPosition;
|
||||
code += ` → ${targetLabel}`;
|
||||
}
|
||||
|
||||
// Zusätzliche Schläge hinzufügen
|
||||
if (this.additionalStrokes.length > 0) {
|
||||
this.additionalStrokes.forEach(stroke => {
|
||||
const targetLabel = targetLabelMap[stroke.targetPosition] || stroke.targetPosition;
|
||||
const targetLabel = additionalTargetLabelMap[stroke.targetPosition] || stroke.targetPosition;
|
||||
code += ` / ${stroke.side} ${stroke.type} → ${targetLabel}`;
|
||||
});
|
||||
}
|
||||
@@ -1342,6 +1356,7 @@ export default {
|
||||
|
||||
// Canvas neu zeichnen
|
||||
this.drawCourt();
|
||||
this.emitDrawingData();
|
||||
},
|
||||
|
||||
updateTextFields() {
|
||||
@@ -1358,6 +1373,21 @@ export default {
|
||||
description: description
|
||||
});
|
||||
}
|
||||
,
|
||||
emitDrawingData() {
|
||||
const drawingData = {
|
||||
selectedStartPosition: this.selectedStartPosition,
|
||||
strokeType: this.strokeType,
|
||||
spinType: this.spinType,
|
||||
targetPosition: this.targetPosition,
|
||||
nextStrokeType: this.nextStrokeType,
|
||||
nextStrokeSide: this.nextStrokeSide,
|
||||
nextStrokeTargetPosition: this.nextStrokeTargetPosition,
|
||||
exerciseCounter: this.exerciseCounter,
|
||||
additionalStrokes: this.additionalStrokes
|
||||
};
|
||||
this.$emit('update-drawing-data', drawingData);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -78,15 +78,11 @@
|
||||
<div class="drawing-section">
|
||||
<h5>Übungszeichnung erstellen</h5>
|
||||
<CourtDrawingTool
|
||||
v-model="editModel.drawingData"
|
||||
:activity-id="editModel.id"
|
||||
:drawing-data="editModel.drawingData"
|
||||
:allow-image-upload="false"
|
||||
@save="onDrawingSave"
|
||||
@update-fields="onUpdateFields"
|
||||
@update-drawing-data="onUpdateDrawingData"
|
||||
@upload-image="onDrawingImageUpload"
|
||||
@image-uploaded="onImageUploaded"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user