Merge pull request #619 from open-pencil/fix/581-intervals-array-import

fix(canvas): normalize imported dash patterns
This commit is contained in:
Danila Poyarkov 2026-08-31 22:54:56 +03:00 committed by GitHub
commit ebe75ea631
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 33 additions and 5 deletions

View file

@ -44,7 +44,8 @@ import {
drawDashedRRectWithSolidCorners,
drawStyledRRectStroke,
getStrokeCapEntity,
getStrokeJoinEntity
getStrokeJoinEntity,
normalizeDashPattern
} from './strokes'
import {
drawDerivedText,
@ -672,8 +673,8 @@ function drawVectorPathStrokes(
miterLimit: number,
outlineCacheKey?: string
): void {
const dash = stroke.dashPattern
if (dash && dash.length > 0) {
const dash = normalizeDashPattern(stroke.dashPattern)
if (dash.length > 0) {
r.strokePaint.setColor(r.ck.Color4f(sc.r, sc.g, sc.b, sc.a))
r.strokePaint.setAlphaf(stroke.opacity)
r.strokePaint.setStrokeWidth(stroke.weight)

View file

@ -30,6 +30,13 @@ export function getStrokeJoinEntity(r: SkiaRenderer, join: string | undefined):
}
}
export function normalizeDashPattern(dash: readonly number[] | undefined): number[] {
if (!dash || dash.length === 0) return []
// Figma permits odd-length alternating patterns; CanvasKit requires the
// on/off interval list to contain a pair for every cycle.
return dash.length % 2 === 0 ? [...dash] : [...dash, ...dash]
}
function strokeInset(stroke: Stroke): number {
if (stroke.align === 'INSIDE') return stroke.weight / 2
if (stroke.align === 'OUTSIDE') return -stroke.weight / 2
@ -45,7 +52,7 @@ export function drawDashedRRectWithSolidCorners(
cornerRadius: number,
dashPhase = 0
): void {
const dash = stroke.dashPattern ?? []
const dash = normalizeDashPattern(stroke.dashPattern)
const inset = strokeInset(stroke)
const left = inset
const top = inset
@ -166,7 +173,7 @@ export function drawStyledRRectStroke(
color: Color,
dashPhase = 0
): void {
const dash = stroke.dashPattern ?? []
const dash = normalizeDashPattern(stroke.dashPattern)
configureStrokePaint(r, node, stroke, color)
r.strokePaint.setPathEffect(dash.length > 0 ? r.ck.PathEffect.MakeDash(dash, dashPhase) : null)
r.drawRRectStrokeWithAlign(canvas, rrect, node, stroke)

View file

@ -0,0 +1,20 @@
import { describe, expect, test } from 'bun:test'
import { normalizeDashPattern } from '#core/canvas/strokes'
describe('normalizeDashPattern', () => {
test('preserves valid values and mirrors odd-length patterns', () => {
expect(normalizeDashPattern([])).toEqual([])
expect(normalizeDashPattern([8, 4])).toEqual([8, 4])
expect(normalizeDashPattern([8])).toEqual([8, 8])
expect(normalizeDashPattern([8, 4, 2])).toEqual([8, 4, 2, 8, 4, 2])
expect(normalizeDashPattern([0, -1, Number.NaN])).toEqual([
0,
-1,
Number.NaN,
0,
-1,
Number.NaN
])
})
})