From 931e39e0115c7e226b50cb8bf1888e0fedcdeb0c Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Mon, 18 May 2026 12:26:05 +0300 Subject: [PATCH] fix(canvas): render imported component set strokes --- packages/core/src/canvas/scene.ts | 45 ++++++++++------------------- packages/core/src/canvas/strokes.ts | 44 +++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/packages/core/src/canvas/scene.ts b/packages/core/src/canvas/scene.ts index c7c46a384..4e1e3dafc 100644 --- a/packages/core/src/canvas/scene.ts +++ b/packages/core/src/canvas/scene.ts @@ -1,4 +1,4 @@ -import type { Canvas, EmbindEnumEntity, Path } from 'canvaskit-wasm' +import type { Canvas, Path } from 'canvaskit-wasm' import { DROP_HIGHLIGHT_ALPHA, DROP_HIGHLIGHT_STROKE, SECTION_CORNER_RADIUS } from '#core/constants' import type { SceneNode, SceneGraph, Fill } from '#core/scene-graph' @@ -8,6 +8,7 @@ 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 { drawFigmaDerivedText } from './text-derived' function drawVisibleFills( @@ -240,6 +241,14 @@ export function renderComponentSet( drawVisibleFills(r, node, graph, () => canvas.drawRRect(rrect, r.fillPaint)) + 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) + ) + return + } + r.auxStroke.setStrokeWidth(r.COMPONENT_SET_BORDER_WIDTH / r.zoom) r.auxStroke.setColor(r.compColor()) r.auxStroke.setPathEffect( @@ -291,28 +300,6 @@ function getShadowShapeChild(node: SceneNode, graph: SceneGraph): SceneNode | nu return child } -function getCapEntity(r: SkiaRenderer, cap: string | undefined): EmbindEnumEntity { - switch (cap) { - case 'ROUND': - return r.ck.StrokeCap.Round - case 'SQUARE': - return r.ck.StrokeCap.Square - default: - return r.ck.StrokeCap.Butt - } -} - -function getJoinEntity(r: SkiaRenderer, join: string | undefined): EmbindEnumEntity { - switch (join) { - case 'ROUND': - return r.ck.StrokeJoin.Round - case 'BEVEL': - return r.ck.StrokeJoin.Bevel - default: - return r.ck.StrokeJoin.Miter - } -} - function drawVectorStrokeGeometry( r: SkiaRenderer, canvas: Canvas, @@ -376,8 +363,8 @@ function drawVectorPathStrokes( r.strokePaint.setColor(r.ck.Color4f(sc.r, sc.g, sc.b, sc.a)) r.strokePaint.setAlphaf(stroke.opacity) r.strokePaint.setStrokeWidth(stroke.weight) - r.strokePaint.setStrokeCap(getCapEntity(r, stroke.cap ?? 'NONE')) - r.strokePaint.setStrokeJoin(getJoinEntity(r, stroke.join ?? 'MITER')) + r.strokePaint.setStrokeCap(getStrokeCapEntity(r, stroke.cap ?? 'NONE')) + r.strokePaint.setStrokeJoin(getStrokeJoinEntity(r, stroke.join ?? 'MITER')) r.strokePaint.setShader(null) const effect = r.ck.PathEffect.MakeDash(dash, 0) r.strokePaint.setPathEffect(effect) @@ -389,8 +376,8 @@ function drawVectorPathStrokes( const strokeOpts = { width: stroke.weight, miter_limit: 4, - cap: getCapEntity(r, stroke.cap ?? 'NONE'), - join: getJoinEntity(r, stroke.join ?? 'MITER') + cap: getStrokeCapEntity(r, stroke.cap ?? 'NONE'), + join: getStrokeJoinEntity(r, stroke.join ?? 'MITER') } r.fillPaint.setColor(r.ck.Color4f(sc.r, sc.g, sc.b, sc.a)) r.fillPaint.setAlphaf(stroke.opacity) @@ -422,10 +409,10 @@ function drawRegularStroke( r.strokePaint.setAlphaf(stroke.opacity) if (stroke.cap) { - r.strokePaint.setStrokeCap(getCapEntity(r, stroke.cap)) + r.strokePaint.setStrokeCap(getStrokeCapEntity(r, stroke.cap)) } if (stroke.join) { - r.strokePaint.setStrokeJoin(getJoinEntity(r, stroke.join)) + r.strokePaint.setStrokeJoin(getStrokeJoinEntity(r, stroke.join)) } if (stroke.dashPattern && stroke.dashPattern.length > 0) { r.strokePaint.setPathEffect(r.ck.PathEffect.MakeDash(stroke.dashPattern, 0)) diff --git a/packages/core/src/canvas/strokes.ts b/packages/core/src/canvas/strokes.ts index 5f65216ec..52dcfdfbc 100644 --- a/packages/core/src/canvas/strokes.ts +++ b/packages/core/src/canvas/strokes.ts @@ -1,9 +1,51 @@ -import type { Canvas, Paint } from 'canvaskit-wasm' +import type { Canvas, EmbindEnumEntity, Paint } from 'canvaskit-wasm' import type { SceneNode, Stroke } from '#core/scene-graph' +import type { Color } from '#core/types' import type { SkiaRenderer } from './renderer' +export function getStrokeCapEntity(r: SkiaRenderer, cap: string | undefined): EmbindEnumEntity { + switch (cap) { + case 'ROUND': + return r.ck.StrokeCap.Round + case 'SQUARE': + return r.ck.StrokeCap.Square + default: + return r.ck.StrokeCap.Butt + } +} + +export function getStrokeJoinEntity(r: SkiaRenderer, join: string | undefined): EmbindEnumEntity { + switch (join) { + case 'ROUND': + return r.ck.StrokeJoin.Round + case 'BEVEL': + return r.ck.StrokeJoin.Bevel + default: + return r.ck.StrokeJoin.Miter + } +} + +export function drawStyledRRectStroke( + r: SkiaRenderer, + canvas: Canvas, + rrect: Float32Array, + node: SceneNode, + stroke: Stroke, + color: Color +): void { + const dash = stroke.dashPattern ?? [] + 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(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.drawRRectStrokeWithAlign(canvas, rrect, node, stroke) + r.strokePaint.setPathEffect(null) +} + export function drawNodeStroke( r: SkiaRenderer, canvas: Canvas,