@pixerate/editor 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canvas/index.cjs +282 -7
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +90 -9
- package/dist/canvas/index.d.ts +90 -9
- package/dist/canvas/index.js +281 -6
- package/dist/canvas/index.js.map +1 -1
- package/package.json +1 -1
package/dist/canvas/index.js
CHANGED
|
@@ -2452,6 +2452,233 @@ function fitFreehandPath(points, options = {}) {
|
|
|
2452
2452
|
return fitPathData(points, { tolerance: 1, ...options, closed: false });
|
|
2453
2453
|
}
|
|
2454
2454
|
|
|
2455
|
+
// src/canvas/gradient.ts
|
|
2456
|
+
var CANVAS_GRADIENT_PRESETS = {
|
|
2457
|
+
sunset: {
|
|
2458
|
+
type: "linear",
|
|
2459
|
+
angle: 90,
|
|
2460
|
+
stops: [
|
|
2461
|
+
{ offset: 0, color: "#f97316" },
|
|
2462
|
+
{ offset: 1, color: "#ec4899" }
|
|
2463
|
+
]
|
|
2464
|
+
},
|
|
2465
|
+
ocean: {
|
|
2466
|
+
type: "linear",
|
|
2467
|
+
angle: 90,
|
|
2468
|
+
stops: [
|
|
2469
|
+
{ offset: 0, color: "#06b6d4" },
|
|
2470
|
+
{ offset: 1, color: "#3b82f6" }
|
|
2471
|
+
]
|
|
2472
|
+
},
|
|
2473
|
+
aurora: {
|
|
2474
|
+
type: "linear",
|
|
2475
|
+
angle: 90,
|
|
2476
|
+
stops: [
|
|
2477
|
+
{ offset: 0, color: "#10b981" },
|
|
2478
|
+
{ offset: 1, color: "#6366f1" }
|
|
2479
|
+
]
|
|
2480
|
+
},
|
|
2481
|
+
neon: {
|
|
2482
|
+
type: "linear",
|
|
2483
|
+
angle: 90,
|
|
2484
|
+
stops: [
|
|
2485
|
+
{ offset: 0, color: "#a855f7" },
|
|
2486
|
+
{ offset: 1, color: "#ec4899" }
|
|
2487
|
+
]
|
|
2488
|
+
},
|
|
2489
|
+
amber: {
|
|
2490
|
+
type: "linear",
|
|
2491
|
+
angle: 90,
|
|
2492
|
+
stops: [
|
|
2493
|
+
{ offset: 0, color: "#f59e0b" },
|
|
2494
|
+
{ offset: 1, color: "#ef4444" }
|
|
2495
|
+
]
|
|
2496
|
+
},
|
|
2497
|
+
cosmic: {
|
|
2498
|
+
type: "linear",
|
|
2499
|
+
angle: 90,
|
|
2500
|
+
stops: [
|
|
2501
|
+
{ offset: 0, color: "#3b82f6" },
|
|
2502
|
+
{ offset: 0.5, color: "#8b5cf6" },
|
|
2503
|
+
{ offset: 1, color: "#ec4899" }
|
|
2504
|
+
]
|
|
2505
|
+
}
|
|
2506
|
+
};
|
|
2507
|
+
function isCanvasGradient(value) {
|
|
2508
|
+
if (!value || typeof value !== "object") return false;
|
|
2509
|
+
const val = value;
|
|
2510
|
+
if (val.type !== "linear" && val.type !== "radial") return false;
|
|
2511
|
+
if (!Array.isArray(val.stops)) return false;
|
|
2512
|
+
return true;
|
|
2513
|
+
}
|
|
2514
|
+
function splitCssArgs(str) {
|
|
2515
|
+
const parts = [];
|
|
2516
|
+
let depth = 0;
|
|
2517
|
+
let current = "";
|
|
2518
|
+
for (let i = 0; i < str.length; i++) {
|
|
2519
|
+
const char = str[i];
|
|
2520
|
+
if (char === "(") depth++;
|
|
2521
|
+
else if (char === ")") depth = Math.max(0, depth - 1);
|
|
2522
|
+
if (char === "," && depth === 0) {
|
|
2523
|
+
if (current.trim()) parts.push(current.trim());
|
|
2524
|
+
current = "";
|
|
2525
|
+
} else {
|
|
2526
|
+
current += char;
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
if (current.trim()) parts.push(current.trim());
|
|
2530
|
+
return parts;
|
|
2531
|
+
}
|
|
2532
|
+
function parseCssAngle(val) {
|
|
2533
|
+
const trimmed = val.trim().toLowerCase();
|
|
2534
|
+
if (trimmed.endsWith("deg")) {
|
|
2535
|
+
return parseFloat(trimmed);
|
|
2536
|
+
}
|
|
2537
|
+
if (trimmed.endsWith("turn")) {
|
|
2538
|
+
return parseFloat(trimmed) * 360;
|
|
2539
|
+
}
|
|
2540
|
+
if (trimmed.endsWith("rad")) {
|
|
2541
|
+
return parseFloat(trimmed) * 180 / Math.PI;
|
|
2542
|
+
}
|
|
2543
|
+
if (trimmed.startsWith("to ")) {
|
|
2544
|
+
const dir = trimmed.slice(3).trim();
|
|
2545
|
+
if (dir === "bottom") return 180;
|
|
2546
|
+
if (dir === "top") return 0;
|
|
2547
|
+
if (dir === "right") return 90;
|
|
2548
|
+
if (dir === "left") return 270;
|
|
2549
|
+
if (dir === "bottom right" || dir === "right bottom") return 135;
|
|
2550
|
+
if (dir === "bottom left" || dir === "left bottom") return 225;
|
|
2551
|
+
if (dir === "top right" || dir === "right top") return 45;
|
|
2552
|
+
if (dir === "top left" || dir === "left top") return 315;
|
|
2553
|
+
}
|
|
2554
|
+
const n = parseFloat(trimmed);
|
|
2555
|
+
return Number.isFinite(n) ? n : 180;
|
|
2556
|
+
}
|
|
2557
|
+
function parseCssGradient(css) {
|
|
2558
|
+
const trimmed = css.trim();
|
|
2559
|
+
const linearMatch = trimmed.match(/^linear-gradient\s*\((.*)\)$/is);
|
|
2560
|
+
if (linearMatch) {
|
|
2561
|
+
const rawArgs = splitCssArgs(linearMatch[1]);
|
|
2562
|
+
if (rawArgs.length === 0) return null;
|
|
2563
|
+
let angle = 180;
|
|
2564
|
+
let stopStartIndex = 0;
|
|
2565
|
+
const first = rawArgs[0].trim().toLowerCase();
|
|
2566
|
+
if (first.endsWith("deg") || first.endsWith("turn") || first.endsWith("rad") || first.startsWith("to ")) {
|
|
2567
|
+
angle = parseCssAngle(first);
|
|
2568
|
+
stopStartIndex = 1;
|
|
2569
|
+
}
|
|
2570
|
+
const stopStrings = rawArgs.slice(stopStartIndex);
|
|
2571
|
+
if (stopStrings.length === 0) return null;
|
|
2572
|
+
const stops = parseStopStrings(stopStrings);
|
|
2573
|
+
if (stops.length === 0) return null;
|
|
2574
|
+
return {
|
|
2575
|
+
type: "linear",
|
|
2576
|
+
angle,
|
|
2577
|
+
stops
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
const radialMatch = trimmed.match(/^radial-gradient\s*\((.*)\)$/is);
|
|
2581
|
+
if (radialMatch) {
|
|
2582
|
+
const rawArgs = splitCssArgs(radialMatch[1]);
|
|
2583
|
+
if (rawArgs.length === 0) return null;
|
|
2584
|
+
let stopStartIndex = 0;
|
|
2585
|
+
const first = rawArgs[0].trim().toLowerCase();
|
|
2586
|
+
if (first.startsWith("circle") || first.startsWith("ellipse") || first.includes("at ")) {
|
|
2587
|
+
stopStartIndex = 1;
|
|
2588
|
+
}
|
|
2589
|
+
const stopStrings = rawArgs.slice(stopStartIndex);
|
|
2590
|
+
if (stopStrings.length === 0) return null;
|
|
2591
|
+
const stops = parseStopStrings(stopStrings);
|
|
2592
|
+
if (stops.length === 0) return null;
|
|
2593
|
+
return {
|
|
2594
|
+
type: "radial",
|
|
2595
|
+
cx: 0.5,
|
|
2596
|
+
cy: 0.5,
|
|
2597
|
+
r: 0.5,
|
|
2598
|
+
stops
|
|
2599
|
+
};
|
|
2600
|
+
}
|
|
2601
|
+
return null;
|
|
2602
|
+
}
|
|
2603
|
+
function parseStopStrings(stopStrings) {
|
|
2604
|
+
const count = stopStrings.length;
|
|
2605
|
+
return stopStrings.map((str, idx) => {
|
|
2606
|
+
const parts = str.trim().split(/\s+(?=[0-9%])/);
|
|
2607
|
+
const color = parts[0];
|
|
2608
|
+
let offset = count === 1 ? 0 : idx / (count - 1);
|
|
2609
|
+
if (parts.length > 1) {
|
|
2610
|
+
const posStr = parts[1].trim();
|
|
2611
|
+
if (posStr.endsWith("%")) {
|
|
2612
|
+
offset = parseFloat(posStr) / 100;
|
|
2613
|
+
} else {
|
|
2614
|
+
const val = parseFloat(posStr);
|
|
2615
|
+
if (Number.isFinite(val)) offset = val;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
return {
|
|
2619
|
+
offset: Math.min(1, Math.max(0, offset)),
|
|
2620
|
+
color
|
|
2621
|
+
};
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
function resolveCanvasGradient(value) {
|
|
2625
|
+
if (!value) return null;
|
|
2626
|
+
if (isCanvasGradient(value)) return value;
|
|
2627
|
+
if (typeof value === "string") {
|
|
2628
|
+
const lower = value.trim().toLowerCase();
|
|
2629
|
+
if (CANVAS_GRADIENT_PRESETS[lower]) {
|
|
2630
|
+
return CANVAS_GRADIENT_PRESETS[lower];
|
|
2631
|
+
}
|
|
2632
|
+
if (lower.startsWith("linear-gradient") || lower.startsWith("radial-gradient")) {
|
|
2633
|
+
return parseCssGradient(value);
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
return null;
|
|
2637
|
+
}
|
|
2638
|
+
function angleToSvgLinearCoords(angleDeg = 180) {
|
|
2639
|
+
const rad = (90 - angleDeg) * Math.PI / 180;
|
|
2640
|
+
const dx = Math.cos(rad);
|
|
2641
|
+
const dy = -Math.sin(rad);
|
|
2642
|
+
const half = 0.5;
|
|
2643
|
+
return {
|
|
2644
|
+
x1: Math.round((half - dx * half) * 1e3) / 1e3,
|
|
2645
|
+
y1: Math.round((half - dy * half) * 1e3) / 1e3,
|
|
2646
|
+
x2: Math.round((half + dx * half) * 1e3) / 1e3,
|
|
2647
|
+
y2: Math.round((half + dy * half) * 1e3) / 1e3
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
function gradientToSvgDef(gradient, id) {
|
|
2651
|
+
const stops = (gradient.stops ?? []).map((s) => ({
|
|
2652
|
+
offset: `${Math.round(s.offset * 1e3) / 10}%`,
|
|
2653
|
+
stopColor: s.color,
|
|
2654
|
+
stopOpacity: s.opacity
|
|
2655
|
+
}));
|
|
2656
|
+
if (gradient.type === "radial") {
|
|
2657
|
+
return {
|
|
2658
|
+
id,
|
|
2659
|
+
type: "radial",
|
|
2660
|
+
attrs: {
|
|
2661
|
+
cx: gradient.cx !== void 0 ? `${gradient.cx * 100}%` : "50%",
|
|
2662
|
+
cy: gradient.cy !== void 0 ? `${gradient.cy * 100}%` : "50%",
|
|
2663
|
+
r: gradient.r !== void 0 ? `${gradient.r * 100}%` : "50%"
|
|
2664
|
+
},
|
|
2665
|
+
stops
|
|
2666
|
+
};
|
|
2667
|
+
}
|
|
2668
|
+
const coords = angleToSvgLinearCoords(gradient.angle ?? 180);
|
|
2669
|
+
return {
|
|
2670
|
+
id,
|
|
2671
|
+
type: "linear",
|
|
2672
|
+
attrs: {
|
|
2673
|
+
x1: `${coords.x1 * 100}%`,
|
|
2674
|
+
y1: `${coords.y1 * 100}%`,
|
|
2675
|
+
x2: `${coords.x2 * 100}%`,
|
|
2676
|
+
y2: `${coords.y2 * 100}%`
|
|
2677
|
+
},
|
|
2678
|
+
stops
|
|
2679
|
+
};
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2455
2682
|
// src/canvas/shapeGeometry.ts
|
|
2456
2683
|
var SHAPE_KINDS = ["rect", "ellipse", "polygon", "star", "path"];
|
|
2457
2684
|
function isShapeDescription(value) {
|
|
@@ -2861,23 +3088,54 @@ function getShapeSvg(shape, size) {
|
|
|
2861
3088
|
}
|
|
2862
3089
|
return transforms.length > 0 ? { d, transform: transforms.join(" "), fillRule } : { d, fillRule };
|
|
2863
3090
|
}
|
|
2864
|
-
function getShapeRenderData(shape, size = {}) {
|
|
3091
|
+
function getShapeRenderData(shape, size = {}, options = {}) {
|
|
2865
3092
|
const vb = shape.kind === "path" ? validViewBox(shape.viewBox) : null;
|
|
2866
3093
|
const width = size.width && size.width > 0 ? size.width : vb ? vb[2] : 100;
|
|
2867
3094
|
const height = size.height && size.height > 0 ? size.height : vb ? vb[3] : 100;
|
|
2868
3095
|
const svg = getShapeSvg(shape, { width, height });
|
|
3096
|
+
const baseId = options.id || Math.random().toString(36).slice(2, 9);
|
|
3097
|
+
let fillStr = "currentColor";
|
|
3098
|
+
let fillGradientDef;
|
|
3099
|
+
const rawFill = shape.fillGradient ?? shape.fill;
|
|
3100
|
+
const resolvedFillGradient = resolveCanvasGradient(rawFill);
|
|
3101
|
+
if (resolvedFillGradient) {
|
|
3102
|
+
const gradId = `shape-fill-${baseId}`;
|
|
3103
|
+
fillGradientDef = gradientToSvgDef(resolvedFillGradient, gradId);
|
|
3104
|
+
fillStr = `url(#${gradId})`;
|
|
3105
|
+
} else if (typeof shape.fill === "string") {
|
|
3106
|
+
fillStr = shape.fill;
|
|
3107
|
+
} else if (shape.fill === void 0) {
|
|
3108
|
+
fillStr = "currentColor";
|
|
3109
|
+
}
|
|
3110
|
+
let strokeStr = "none";
|
|
3111
|
+
let strokeGradientDef;
|
|
3112
|
+
const rawStroke = shape.strokeGradient ?? shape.stroke;
|
|
3113
|
+
const resolvedStrokeGradient = resolveCanvasGradient(rawStroke);
|
|
3114
|
+
if (resolvedStrokeGradient) {
|
|
3115
|
+
const gradId = `shape-stroke-${baseId}`;
|
|
3116
|
+
strokeGradientDef = gradientToSvgDef(resolvedStrokeGradient, gradId);
|
|
3117
|
+
strokeStr = `url(#${gradId})`;
|
|
3118
|
+
} else if (typeof shape.stroke === "string") {
|
|
3119
|
+
strokeStr = shape.stroke;
|
|
3120
|
+
} else if (shape.stroke === void 0) {
|
|
3121
|
+
strokeStr = "none";
|
|
3122
|
+
}
|
|
2869
3123
|
const data = {
|
|
2870
3124
|
viewBox: `0 0 ${formatNumber2(width, 3)} ${formatNumber2(height, 3)}`,
|
|
2871
3125
|
width,
|
|
2872
3126
|
height,
|
|
2873
3127
|
d: svg.d,
|
|
2874
3128
|
fillRule: svg.fillRule,
|
|
2875
|
-
fill:
|
|
2876
|
-
stroke:
|
|
3129
|
+
fill: fillStr,
|
|
3130
|
+
stroke: strokeStr,
|
|
2877
3131
|
strokeWidth: shape.strokeWidth ?? 1
|
|
2878
3132
|
};
|
|
2879
3133
|
if (svg.transform) data.transform = svg.transform;
|
|
2880
3134
|
if (shape.opacity !== void 0) data.opacity = shape.opacity;
|
|
3135
|
+
if (shape.strokeLinecap) data.strokeLinecap = shape.strokeLinecap;
|
|
3136
|
+
if (shape.strokeLinejoin) data.strokeLinejoin = shape.strokeLinejoin;
|
|
3137
|
+
if (fillGradientDef) data.fillGradient = fillGradientDef;
|
|
3138
|
+
if (strokeGradientDef) data.strokeGradient = strokeGradientDef;
|
|
2881
3139
|
return data;
|
|
2882
3140
|
}
|
|
2883
3141
|
function clampRatio(value) {
|
|
@@ -5313,12 +5571,18 @@ function revertShapeCombination(nodes, change, previous) {
|
|
|
5313
5571
|
}
|
|
5314
5572
|
var INVISIBLE_PAINT = /* @__PURE__ */ new Set(["none", "transparent"]);
|
|
5315
5573
|
function hasVisibleStroke(shape) {
|
|
5316
|
-
if (!shape
|
|
5574
|
+
if (!shape) return false;
|
|
5575
|
+
const stroke = shape.strokeGradient ?? shape.stroke;
|
|
5576
|
+
if (stroke === void 0 || stroke === null) return false;
|
|
5577
|
+
if (typeof stroke === "string" && INVISIBLE_PAINT.has(stroke.trim().toLowerCase())) return false;
|
|
5317
5578
|
const width = shape.strokeWidth ?? 1;
|
|
5318
5579
|
return Number.isFinite(width) && width > 0;
|
|
5319
5580
|
}
|
|
5320
5581
|
function hasVisibleFill(shape) {
|
|
5321
|
-
|
|
5582
|
+
if (shape.fillGradient) return true;
|
|
5583
|
+
if (shape.fill === void 0) return true;
|
|
5584
|
+
if (typeof shape.fill === "object" && shape.fill !== null) return true;
|
|
5585
|
+
return !INVISIBLE_PAINT.has(String(shape.fill).trim().toLowerCase());
|
|
5322
5586
|
}
|
|
5323
5587
|
function shapeTargets(nodes, ids) {
|
|
5324
5588
|
const idSet = new Set(ids);
|
|
@@ -11483,6 +11747,8 @@ function strokeToShapeNode(stroke, options = {}) {
|
|
|
11483
11747
|
});
|
|
11484
11748
|
const width = Math.max(bounds.width, 1);
|
|
11485
11749
|
const height = Math.max(bounds.height, 1);
|
|
11750
|
+
const strokePaint = stroke.style.gradient ?? stroke.style.color;
|
|
11751
|
+
const strokeGradient = stroke.style.gradient ?? (typeof stroke.style.color === "object" && stroke.style.color ? stroke.style.color : void 0);
|
|
11486
11752
|
return {
|
|
11487
11753
|
id: options.id ?? stroke.id,
|
|
11488
11754
|
type: "shape",
|
|
@@ -11498,9 +11764,12 @@ function strokeToShapeNode(stroke, options = {}) {
|
|
|
11498
11764
|
kind: "path",
|
|
11499
11765
|
d,
|
|
11500
11766
|
viewBox: [0, 0, width, height],
|
|
11501
|
-
stroke:
|
|
11767
|
+
stroke: strokePaint,
|
|
11502
11768
|
strokeWidth,
|
|
11503
11769
|
opacity: stroke.style.opacity,
|
|
11770
|
+
strokeLinecap: stroke.style.cap ?? "round",
|
|
11771
|
+
strokeLinejoin: stroke.style.join ?? "round",
|
|
11772
|
+
...strokeGradient ? { strokeGradient } : {},
|
|
11504
11773
|
fill: "none"
|
|
11505
11774
|
},
|
|
11506
11775
|
tool: stroke.tool
|
|
@@ -11709,6 +11978,7 @@ export {
|
|
|
11709
11978
|
AnimationEditError,
|
|
11710
11979
|
AnimationPlayer,
|
|
11711
11980
|
CANVAS_COMPONENT_DRAG_TYPE,
|
|
11981
|
+
CANVAS_GRADIENT_PRESETS,
|
|
11712
11982
|
CANVAS_TOKEN_PROPERTIES,
|
|
11713
11983
|
CHILD_LAYOUT_DATA_KEY,
|
|
11714
11984
|
COMPONENT_DATA_KEY,
|
|
@@ -11741,6 +12011,7 @@ export {
|
|
|
11741
12011
|
addCanvasVariant,
|
|
11742
12012
|
addKeyframesAtTime,
|
|
11743
12013
|
alignNodes,
|
|
12014
|
+
angleToSvgLinearCoords,
|
|
11744
12015
|
animateNode,
|
|
11745
12016
|
applyAnimation,
|
|
11746
12017
|
applyAnimationOverrides,
|
|
@@ -11888,6 +12159,7 @@ export {
|
|
|
11888
12159
|
getVariantOptions,
|
|
11889
12160
|
getVariantSet,
|
|
11890
12161
|
getVariantValues,
|
|
12162
|
+
gradientToSvgDef,
|
|
11891
12163
|
handlePoint,
|
|
11892
12164
|
hasVisibleStroke,
|
|
11893
12165
|
inferResizeHandle,
|
|
@@ -11902,6 +12174,7 @@ export {
|
|
|
11902
12174
|
isAutoLayoutFrame,
|
|
11903
12175
|
isCanvasComponent,
|
|
11904
12176
|
isCanvasComponentSet,
|
|
12177
|
+
isCanvasGradient,
|
|
11905
12178
|
isCanvasInstance,
|
|
11906
12179
|
isCssExportExact,
|
|
11907
12180
|
isEventFromTextInput,
|
|
@@ -11953,6 +12226,7 @@ export {
|
|
|
11953
12226
|
parseCanvasClipboardData,
|
|
11954
12227
|
parseCanvasScenes,
|
|
11955
12228
|
parseCssEasing,
|
|
12229
|
+
parseCssGradient,
|
|
11956
12230
|
parseKeyframeClipboard,
|
|
11957
12231
|
parseNodeStyleDeclarations,
|
|
11958
12232
|
parseOklab,
|
|
@@ -11988,6 +12262,7 @@ export {
|
|
|
11988
12262
|
repositionNodesTo,
|
|
11989
12263
|
resetCanvasInstanceOverrides,
|
|
11990
12264
|
resizeHandleAxes,
|
|
12265
|
+
resolveCanvasGradient,
|
|
11991
12266
|
resolveCanvasTokens,
|
|
11992
12267
|
resolveEasing,
|
|
11993
12268
|
resolvePadding,
|