fix(canvas): offset component set dash phase

This commit is contained in:
Danila Poyarkov 2026-05-18 12:40:03 +03:00
parent 8f80396775
commit 2a4a0ce5a2
2 changed files with 84 additions and 6 deletions

View file

@ -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
}

View file

@ -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)
}