diff --git a/packages/core/src/canvas/scene.ts b/packages/core/src/canvas/scene.ts index 4e1e3dafc..af3217fdd 100644 --- a/packages/core/src/canvas/scene.ts +++ b/packages/core/src/canvas/scene.ts @@ -8,7 +8,12 @@ import { vectorNetworkToCenterlinePath } from '#core/vector' import { renderBooleanOperation } from './boolean' import { nodeHasRadius } from './shapes' import type { SkiaRenderer, RenderOverlays } from './renderer' -import { drawStyledRRectStroke, getStrokeCapEntity, getStrokeJoinEntity } from './strokes' +import { + drawDashedRRectWithSolidCorners, + drawStyledRRectStroke, + getStrokeCapEntity, + getStrokeJoinEntity +} from './strokes' import { drawFigmaDerivedText } from './text-derived' function drawVisibleFills( @@ -243,9 +248,14 @@ export function renderComponentSet( const visibleStrokes = node.strokes.filter((stroke) => stroke.visible) if (visibleStrokes.length > 0) { - forVisibleStrokes(r, node, graph, (stroke, color) => - drawStyledRRectStroke(r, canvas, rrect, node, stroke, color) - ) + forVisibleStrokes(r, node, graph, (stroke, color) => { + const dashPhase = stroke.dashPattern?.[1] ?? 0 + if (stroke.dashPattern && stroke.dashPattern.length > 0) { + drawDashedRRectWithSolidCorners(r, canvas, node, stroke, color, 5, dashPhase) + } else { + drawStyledRRectStroke(r, canvas, rrect, node, stroke, color, dashPhase) + } + }) return } diff --git a/packages/core/src/canvas/strokes.ts b/packages/core/src/canvas/strokes.ts index 52dcfdfbc..128bc843d 100644 --- a/packages/core/src/canvas/strokes.ts +++ b/packages/core/src/canvas/strokes.ts @@ -27,13 +27,81 @@ export function getStrokeJoinEntity(r: SkiaRenderer, join: string | undefined): } } +function strokeInset(stroke: Stroke): number { + if (stroke.align === 'INSIDE') return stroke.weight / 2 + if (stroke.align === 'OUTSIDE') return -stroke.weight / 2 + return 0 +} + +export function drawDashedRRectWithSolidCorners( + r: SkiaRenderer, + canvas: Canvas, + node: SceneNode, + stroke: Stroke, + color: Color, + cornerRadius: number, + dashPhase = 0 +): void { + const dash = stroke.dashPattern ?? [] + const inset = strokeInset(stroke) + const left = inset + const top = inset + const right = node.width - inset + const bottom = node.height - inset + const radius = Math.max(0, cornerRadius - inset) + + r.strokePaint.setColor(r.ck.Color4f(color.r, color.g, color.b, color.a)) + r.strokePaint.setStrokeWidth(stroke.weight) + r.strokePaint.setAlphaf(stroke.opacity) + r.strokePaint.setStrokeCap(r.ck.StrokeCap.Butt) + r.strokePaint.setStrokeJoin(getStrokeJoinEntity(r, stroke.join)) + r.strokePaint.setPathEffect(null) + + canvas.drawArc( + r.ck.LTRBRect(left, top, left + radius * 2, top + radius * 2), + 180, + 90, + false, + r.strokePaint + ) + canvas.drawArc( + r.ck.LTRBRect(right - radius * 2, top, right, top + radius * 2), + 270, + 90, + false, + r.strokePaint + ) + canvas.drawArc( + r.ck.LTRBRect(right - radius * 2, bottom - radius * 2, right, bottom), + 0, + 90, + false, + r.strokePaint + ) + canvas.drawArc( + r.ck.LTRBRect(left, bottom - radius * 2, left + radius * 2, bottom), + 90, + 90, + false, + r.strokePaint + ) + + r.strokePaint.setPathEffect(dash.length > 0 ? r.ck.PathEffect.MakeDash(dash, dashPhase) : null) + canvas.drawLine(left + radius, top, right - radius, top, r.strokePaint) + canvas.drawLine(right, top + radius, right, bottom - radius, r.strokePaint) + canvas.drawLine(right - radius, bottom, left + radius, bottom, r.strokePaint) + canvas.drawLine(left, bottom - radius, left, top + radius, r.strokePaint) + r.strokePaint.setPathEffect(null) +} + export function drawStyledRRectStroke( r: SkiaRenderer, canvas: Canvas, rrect: Float32Array, node: SceneNode, stroke: Stroke, - color: Color + color: Color, + dashPhase = 0 ): void { const dash = stroke.dashPattern ?? [] r.strokePaint.setColor(r.ck.Color4f(color.r, color.g, color.b, color.a)) @@ -41,7 +109,7 @@ export function drawStyledRRectStroke( r.strokePaint.setAlphaf(stroke.opacity) r.strokePaint.setStrokeCap(getStrokeCapEntity(r, stroke.cap)) r.strokePaint.setStrokeJoin(getStrokeJoinEntity(r, stroke.join)) - r.strokePaint.setPathEffect(dash.length > 0 ? r.ck.PathEffect.MakeDash(dash, 0) : null) + r.strokePaint.setPathEffect(dash.length > 0 ? r.ck.PathEffect.MakeDash(dash, dashPhase) : null) r.drawRRectStrokeWithAlign(canvas, rrect, node, stroke) r.strokePaint.setPathEffect(null) }