diff --git a/components/agents/agent-psd-template-export-preview.tsx b/components/agents/agent-psd-template-export-preview.tsx index 6151556bf..42820d951 100644 --- a/components/agents/agent-psd-template-export-preview.tsx +++ b/components/agents/agent-psd-template-export-preview.tsx @@ -83,6 +83,9 @@ const PREVIEW_MIN_HEIGHT = 340; const LABEL_PLACEHOLDER = "例如:120cm / 50cm"; const IMAGE_EXTENSION_REGEX = /\.(png|jpg|jpeg|webp)$/i; const DIMENSION_SUFFIX_REGEX = /-尺寸标注(?=\.)/; +const DIMENSION_LINE_COLOR = "#666b73"; +const DIMENSION_TEXT_COLOR = "#2a2f35"; +const DIMENSION_TEXT_HALO_COLOR = "rgba(255,255,255,0.92)"; function clamp(value: number, min: number, max: number) { return Math.max(min, Math.min(max, value)); @@ -219,17 +222,21 @@ function getPreviewAnnotationGeometry( ); const vector = getAnnotationVector(start.x, start.y, end.x, end.y); const label = getDimensionLabel(annotation); - const labelWidth = clamp(label.length * 11 + 26, 76, 240); - const labelHeight = 30; - const labelOffset = 22; + const labelOffset = 16; + const rawAngleRadians = Math.atan2(vector.dy, vector.dx); + const angleRadians = + rawAngleRadians > Math.PI / 2 + ? rawAngleRadians - Math.PI + : rawAngleRadians < -Math.PI / 2 + ? rawAngleRadians + Math.PI + : rawAngleRadians; return { start, end, vector, label, - labelWidth, - labelHeight, + angleDegrees: (angleRadians * 180) / Math.PI, labelCenterX: (start.x + end.x) / 2 + vector.nx * labelOffset, labelCenterY: (start.y + end.y) / 2 + vector.ny * labelOffset, }; @@ -303,62 +310,19 @@ function downloadBlob(blob: Blob, fileName: string) { URL.revokeObjectURL(objectUrl); } -function drawArrowHead(params: { +function drawDimensionCap(params: { context: CanvasRenderingContext2D; point: Point; - direction: Point; + vector: ReturnType; size: number; - color: string; }) { - const { context, point, direction, size, color } = params; - const directionLength = Math.max(1, Math.hypot(direction.x, direction.y)); - const ux = direction.x / directionLength; - const uy = direction.y / directionLength; - const angle = Math.PI / 7; - const leftAngleX = ux * Math.cos(angle) - uy * Math.sin(angle); - const leftAngleY = ux * Math.sin(angle) + uy * Math.cos(angle); - const rightAngleX = ux * Math.cos(-angle) - uy * Math.sin(-angle); - const rightAngleY = ux * Math.sin(-angle) + uy * Math.cos(-angle); - + const { context, point, vector, size } = params; context.beginPath(); - context.moveTo(point.x, point.y); - context.lineTo(point.x - leftAngleX * size, point.y - leftAngleY * size); - context.moveTo(point.x, point.y); - context.lineTo(point.x - rightAngleX * size, point.y - rightAngleY * size); - context.strokeStyle = color; - context.lineCap = "round"; + context.moveTo(point.x + vector.nx * size, point.y + vector.ny * size); + context.lineTo(point.x - vector.nx * size, point.y - vector.ny * size); context.stroke(); } -function fillRoundedRect(params: { - context: CanvasRenderingContext2D; - x: number; - y: number; - width: number; - height: number; - radius: number; -}) { - const { context, x, y, width, height, radius } = params; - const clampedRadius = Math.min(radius, width / 2, height / 2); - context.beginPath(); - context.moveTo(x + clampedRadius, y); - context.lineTo(x + width - clampedRadius, y); - context.quadraticCurveTo(x + width, y, x + width, y + clampedRadius); - context.lineTo(x + width, y + height - clampedRadius); - context.quadraticCurveTo( - x + width, - y + height, - x + width - clampedRadius, - y + height - ); - context.lineTo(x + clampedRadius, y + height); - context.quadraticCurveTo(x, y + height, x, y + height - clampedRadius); - context.lineTo(x, y + clampedRadius); - context.quadraticCurveTo(x, y, x + clampedRadius, y); - context.closePath(); - context.fill(); -} - function drawAnnotationToCanvas( context: CanvasRenderingContext2D, annotation: DimensionAnnotation, @@ -371,99 +335,62 @@ function drawAnnotationToCanvas( annotation.x2, annotation.y2 ); + const rawAngleRadians = Math.atan2(vector.dy, vector.dx); + const angleRadians = + rawAngleRadians > Math.PI / 2 + ? rawAngleRadians - Math.PI + : rawAngleRadians < -Math.PI / 2 + ? rawAngleRadians + Math.PI + : rawAngleRadians; const strokeWidth = clamp( - Math.round(Math.min(imageSize.width, imageSize.height) * 0.004), - 2, - 8 + Math.min(imageSize.width, imageSize.height) * 0.0018, + 1.25, + 3 ); - const tickLength = strokeWidth * 2.5; - const arrowSize = strokeWidth * 3.5; + const capLength = strokeWidth * 6; const fontSize = clamp( - Math.round(Math.min(imageSize.width, imageSize.height) * 0.028), - 16, - 42 + Math.round(Math.min(imageSize.width, imageSize.height) * 0.022), + 14, + 34 ); - const labelPaddingX = fontSize * 0.7; - const labelPaddingY = fontSize * 0.45; - const labelOffset = fontSize * 1.4; + const labelOffset = fontSize * 0.95; const centerX = (annotation.x1 + annotation.x2) / 2 + vector.nx * labelOffset; const centerY = (annotation.y1 + annotation.y2) / 2 + vector.ny * labelOffset; context.save(); - context.strokeStyle = "#fb923c"; + context.strokeStyle = DIMENSION_LINE_COLOR; context.lineWidth = strokeWidth; context.lineCap = "round"; context.lineJoin = "round"; - context.shadowColor = "rgba(249, 115, 22, 0.22)"; - context.shadowBlur = strokeWidth * 2; context.beginPath(); context.moveTo(annotation.x1, annotation.y1); context.lineTo(annotation.x2, annotation.y2); context.stroke(); - context.beginPath(); - context.moveTo( - annotation.x1 + vector.nx * tickLength, - annotation.y1 + vector.ny * tickLength - ); - context.lineTo( - annotation.x1 - vector.nx * tickLength, - annotation.y1 - vector.ny * tickLength - ); - context.moveTo( - annotation.x2 + vector.nx * tickLength, - annotation.y2 + vector.ny * tickLength - ); - context.lineTo( - annotation.x2 - vector.nx * tickLength, - annotation.y2 - vector.ny * tickLength - ); - context.stroke(); - - drawArrowHead({ + drawDimensionCap({ context, point: { x: annotation.x1, y: annotation.y1 }, - direction: { - x: annotation.x1 - annotation.x2, - y: annotation.y1 - annotation.y2, - }, - size: arrowSize, - color: "#fb923c", + size: capLength, + vector, }); - drawArrowHead({ + drawDimensionCap({ context, point: { x: annotation.x2, y: annotation.y2 }, - direction: { - x: annotation.x2 - annotation.x1, - y: annotation.y2 - annotation.y1, - }, - size: arrowSize, - color: "#fb923c", + size: capLength, + vector, }); - context.shadowBlur = 0; - context.font = `600 ${fontSize}px "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif`; - const textMetrics = context.measureText(label); - const labelWidth = textMetrics.width + labelPaddingX * 2; - const labelHeight = fontSize + labelPaddingY * 2; - const labelX = centerX - labelWidth / 2; - const labelY = centerY - labelHeight / 2; - - context.fillStyle = "rgba(9, 9, 11, 0.88)"; - fillRoundedRect({ - context, - x: labelX, - y: labelY, - width: labelWidth, - height: labelHeight, - radius: fontSize * 0.55, - }); - - context.fillStyle = "#ffffff"; + context.translate(centerX, centerY); + context.rotate(angleRadians); + context.font = `500 ${fontSize}px "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif`; context.textAlign = "center"; context.textBaseline = "middle"; - context.fillText(label, centerX, centerY + 1); + context.lineWidth = Math.max(2, fontSize * 0.22); + context.strokeStyle = DIMENSION_TEXT_HALO_COLOR; + context.strokeText(label, 0, 0); + context.fillStyle = DIMENSION_TEXT_COLOR; + context.fillText(label, 0, 0); context.restore(); } @@ -986,19 +913,6 @@ export function AgentPsdTemplateExportPreview({ className="absolute inset-0 h-full w-full" onPointerDown={handleOverlayPointerDown} > - - - - - - {annotations.map((annotation) => { const isSelected = annotation.id === selectedAnnotationId; const geometry = getPreviewAnnotationGeometry( @@ -1006,7 +920,8 @@ export function AgentPsdTemplateExportPreview({ previewRect, imageSize ); - const tickLength = isSelected ? 10 : 8; + const strokeWidth = isSelected ? 2 : 1.5; + const tickLength = isSelected ? 8 : 6.5; return ( @@ -1022,13 +937,11 @@ export function AgentPsdTemplateExportPreview({ y2={geometry.end.y} /> - @@ -1099,9 +1003,9 @@ export function AgentPsdTemplateExportPreview({ event ) } - r={6} - stroke="#fb923c" - strokeWidth={2} + r={4} + stroke={DIMENSION_LINE_COLOR} + strokeWidth={1.5} /> ) : null}