@effing/canvas 0.18.3 → 0.18.5
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/index.js +254 -186
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -650,7 +650,7 @@ async function drawImage(ctx, src, x, y, width, height, style, preloadedImage) {
|
|
|
650
650
|
|
|
651
651
|
// src/jsx/draw/rect.ts
|
|
652
652
|
function drawRect(ctx, x, y, width, height, style) {
|
|
653
|
-
const borderRadius = getBorderRadius(style);
|
|
653
|
+
const borderRadius = getBorderRadius(style, width, height);
|
|
654
654
|
const hasRoundedCorners = borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0;
|
|
655
655
|
if (style.boxShadow) {
|
|
656
656
|
drawBoxShadow(ctx, x, y, width, height, style.boxShadow, borderRadius);
|
|
@@ -677,16 +677,23 @@ function drawRect(ctx, x, y, width, height, style) {
|
|
|
677
677
|
}
|
|
678
678
|
drawBorders(ctx, x, y, width, height, style, borderRadius);
|
|
679
679
|
}
|
|
680
|
-
function
|
|
680
|
+
function resolveRadius(v, width, height) {
|
|
681
|
+
if (typeof v === "string" && v.endsWith("%")) {
|
|
682
|
+
const pct = parseFloat(v) / 100;
|
|
683
|
+
return pct * Math.min(width, height);
|
|
684
|
+
}
|
|
685
|
+
return toNumber(v);
|
|
686
|
+
}
|
|
687
|
+
function getBorderRadius(style, width, height) {
|
|
681
688
|
return {
|
|
682
|
-
topLeft:
|
|
683
|
-
topRight:
|
|
684
|
-
bottomRight:
|
|
685
|
-
bottomLeft:
|
|
689
|
+
topLeft: resolveRadius(style.borderTopLeftRadius, width, height),
|
|
690
|
+
topRight: resolveRadius(style.borderTopRightRadius, width, height),
|
|
691
|
+
bottomRight: resolveRadius(style.borderBottomRightRadius, width, height),
|
|
692
|
+
bottomLeft: resolveRadius(style.borderBottomLeftRadius, width, height)
|
|
686
693
|
};
|
|
687
694
|
}
|
|
688
|
-
function getBorderRadiusFromStyle(style) {
|
|
689
|
-
return getBorderRadius(style);
|
|
695
|
+
function getBorderRadiusFromStyle(style, width, height) {
|
|
696
|
+
return getBorderRadius(style, width, height);
|
|
690
697
|
}
|
|
691
698
|
function drawBorders(ctx, x, y, width, height, style, borderRadius) {
|
|
692
699
|
const hasRoundedCorners = borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0;
|
|
@@ -795,6 +802,10 @@ function toNumber(v) {
|
|
|
795
802
|
|
|
796
803
|
// src/jsx/draw/svg.ts
|
|
797
804
|
import { Path2D } from "@napi-rs/canvas";
|
|
805
|
+
function resolveCurrentColor(value, color) {
|
|
806
|
+
if (value?.toLowerCase() === "currentcolor") return color;
|
|
807
|
+
return value;
|
|
808
|
+
}
|
|
798
809
|
function mergeStyleIntoProps(props) {
|
|
799
810
|
const style = props.style;
|
|
800
811
|
if (!style) return props;
|
|
@@ -814,65 +825,66 @@ function drawSvgContainer(ctx, node, x, y, width, height) {
|
|
|
814
825
|
ctx.translate(-vbX, -vbY);
|
|
815
826
|
}
|
|
816
827
|
}
|
|
828
|
+
const color = node.style.color ?? "black";
|
|
817
829
|
const merged = mergeStyleIntoProps(node.props);
|
|
818
|
-
const inheritedFill = merged.fill ?? "black";
|
|
830
|
+
const inheritedFill = resolveCurrentColor(merged.fill, color) ?? "black";
|
|
819
831
|
const children = node.props.children;
|
|
820
832
|
if (children != null) {
|
|
821
833
|
const childArray = Array.isArray(children) ? children : [children];
|
|
822
834
|
for (const child of childArray) {
|
|
823
835
|
if (child != null && typeof child === "object") {
|
|
824
|
-
drawSvgChild(ctx, child, inheritedFill);
|
|
836
|
+
drawSvgChild(ctx, child, inheritedFill, color);
|
|
825
837
|
}
|
|
826
838
|
}
|
|
827
839
|
}
|
|
828
840
|
ctx.restore();
|
|
829
841
|
}
|
|
830
|
-
function drawSvgChild(ctx, child, inheritedFill) {
|
|
842
|
+
function drawSvgChild(ctx, child, inheritedFill, color) {
|
|
831
843
|
const { type } = child;
|
|
832
844
|
const props = mergeStyleIntoProps(child.props);
|
|
833
845
|
switch (type) {
|
|
834
846
|
case "path":
|
|
835
|
-
drawPath(ctx, props, inheritedFill);
|
|
847
|
+
drawPath(ctx, props, inheritedFill, color);
|
|
836
848
|
break;
|
|
837
849
|
case "circle":
|
|
838
|
-
drawCircle(ctx, props, inheritedFill);
|
|
850
|
+
drawCircle(ctx, props, inheritedFill, color);
|
|
839
851
|
break;
|
|
840
852
|
case "rect":
|
|
841
|
-
drawSvgRect(ctx, props, inheritedFill);
|
|
853
|
+
drawSvgRect(ctx, props, inheritedFill, color);
|
|
842
854
|
break;
|
|
843
855
|
case "line":
|
|
844
|
-
drawLine(ctx, props);
|
|
856
|
+
drawLine(ctx, props, color);
|
|
845
857
|
break;
|
|
846
858
|
case "ellipse":
|
|
847
|
-
drawEllipse(ctx, props, inheritedFill);
|
|
859
|
+
drawEllipse(ctx, props, inheritedFill, color);
|
|
848
860
|
break;
|
|
849
861
|
case "polygon":
|
|
850
|
-
drawPolygon(ctx, props, inheritedFill);
|
|
862
|
+
drawPolygon(ctx, props, inheritedFill, color);
|
|
851
863
|
break;
|
|
852
864
|
case "polyline":
|
|
853
|
-
drawPolyline(ctx, props, inheritedFill);
|
|
865
|
+
drawPolyline(ctx, props, inheritedFill, color);
|
|
854
866
|
break;
|
|
855
867
|
case "g":
|
|
856
|
-
drawGroup(ctx, child, inheritedFill);
|
|
868
|
+
drawGroup(ctx, child, inheritedFill, color);
|
|
857
869
|
break;
|
|
858
870
|
}
|
|
859
871
|
}
|
|
860
|
-
function drawPath(ctx, props, inheritedFill) {
|
|
872
|
+
function drawPath(ctx, props, inheritedFill, color) {
|
|
861
873
|
const d = props.d;
|
|
862
874
|
if (!d) return;
|
|
863
875
|
const path = new Path2D(d);
|
|
864
|
-
applyFillAndStroke(ctx, props, path, inheritedFill);
|
|
876
|
+
applyFillAndStroke(ctx, props, path, inheritedFill, color);
|
|
865
877
|
}
|
|
866
|
-
function drawCircle(ctx, props, inheritedFill) {
|
|
878
|
+
function drawCircle(ctx, props, inheritedFill, color) {
|
|
867
879
|
const cx = Number(props.cx ?? 0);
|
|
868
880
|
const cy = Number(props.cy ?? 0);
|
|
869
881
|
const r = Number(props.r ?? 0);
|
|
870
882
|
if (r <= 0) return;
|
|
871
883
|
const path = new Path2D();
|
|
872
884
|
path.arc(cx, cy, r, 0, Math.PI * 2);
|
|
873
|
-
applyFillAndStroke(ctx, props, path, inheritedFill);
|
|
885
|
+
applyFillAndStroke(ctx, props, path, inheritedFill, color);
|
|
874
886
|
}
|
|
875
|
-
function drawSvgRect(ctx, props, inheritedFill) {
|
|
887
|
+
function drawSvgRect(ctx, props, inheritedFill, color) {
|
|
876
888
|
const rx = Number(props.x ?? 0);
|
|
877
889
|
const ry = Number(props.y ?? 0);
|
|
878
890
|
const w = Number(props.width ?? 0);
|
|
@@ -880,9 +892,9 @@ function drawSvgRect(ctx, props, inheritedFill) {
|
|
|
880
892
|
if (w <= 0 || h <= 0) return;
|
|
881
893
|
const path = new Path2D();
|
|
882
894
|
path.rect(rx, ry, w, h);
|
|
883
|
-
applyFillAndStroke(ctx, props, path, inheritedFill);
|
|
895
|
+
applyFillAndStroke(ctx, props, path, inheritedFill, color);
|
|
884
896
|
}
|
|
885
|
-
function drawLine(ctx, props) {
|
|
897
|
+
function drawLine(ctx, props, color) {
|
|
886
898
|
const x1 = Number(props.x1 ?? 0);
|
|
887
899
|
const y1 = Number(props.y1 ?? 0);
|
|
888
900
|
const x2 = Number(props.x2 ?? 0);
|
|
@@ -890,9 +902,9 @@ function drawLine(ctx, props) {
|
|
|
890
902
|
const path = new Path2D();
|
|
891
903
|
path.moveTo(x1, y1);
|
|
892
904
|
path.lineTo(x2, y2);
|
|
893
|
-
applyStroke(ctx, props, path);
|
|
905
|
+
applyStroke(ctx, props, path, color);
|
|
894
906
|
}
|
|
895
|
-
function drawEllipse(ctx, props, inheritedFill) {
|
|
907
|
+
function drawEllipse(ctx, props, inheritedFill, color) {
|
|
896
908
|
const cx = Number(props.cx ?? 0);
|
|
897
909
|
const cy = Number(props.cy ?? 0);
|
|
898
910
|
const rx = Number(props.rx ?? 0);
|
|
@@ -900,9 +912,9 @@ function drawEllipse(ctx, props, inheritedFill) {
|
|
|
900
912
|
if (rx <= 0 || ry <= 0) return;
|
|
901
913
|
const path = new Path2D();
|
|
902
914
|
path.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
|
|
903
|
-
applyFillAndStroke(ctx, props, path, inheritedFill);
|
|
915
|
+
applyFillAndStroke(ctx, props, path, inheritedFill, color);
|
|
904
916
|
}
|
|
905
|
-
function drawPolygon(ctx, props, inheritedFill) {
|
|
917
|
+
function drawPolygon(ctx, props, inheritedFill, color) {
|
|
906
918
|
const points = parsePoints(props.points);
|
|
907
919
|
if (points.length < 2) return;
|
|
908
920
|
const path = new Path2D();
|
|
@@ -911,9 +923,9 @@ function drawPolygon(ctx, props, inheritedFill) {
|
|
|
911
923
|
path.lineTo(points[i][0], points[i][1]);
|
|
912
924
|
}
|
|
913
925
|
path.closePath();
|
|
914
|
-
applyFillAndStroke(ctx, props, path, inheritedFill);
|
|
926
|
+
applyFillAndStroke(ctx, props, path, inheritedFill, color);
|
|
915
927
|
}
|
|
916
|
-
function drawPolyline(ctx, props, inheritedFill) {
|
|
928
|
+
function drawPolyline(ctx, props, inheritedFill, color) {
|
|
917
929
|
const points = parsePoints(props.points);
|
|
918
930
|
if (points.length < 2) return;
|
|
919
931
|
const path = new Path2D();
|
|
@@ -921,17 +933,17 @@ function drawPolyline(ctx, props, inheritedFill) {
|
|
|
921
933
|
for (let i = 1; i < points.length; i++) {
|
|
922
934
|
path.lineTo(points[i][0], points[i][1]);
|
|
923
935
|
}
|
|
924
|
-
applyFillAndStroke(ctx, props, path, inheritedFill);
|
|
936
|
+
applyFillAndStroke(ctx, props, path, inheritedFill, color);
|
|
925
937
|
}
|
|
926
|
-
function drawGroup(ctx, node, inheritedFill) {
|
|
938
|
+
function drawGroup(ctx, node, inheritedFill, color) {
|
|
927
939
|
const children = node.children ?? node.props.children;
|
|
928
940
|
if (children == null) return;
|
|
929
941
|
const merged = mergeStyleIntoProps(node.props);
|
|
930
|
-
const groupFill = merged.fill ?? inheritedFill;
|
|
942
|
+
const groupFill = resolveCurrentColor(merged.fill, color) ?? inheritedFill;
|
|
931
943
|
const childArray = Array.isArray(children) ? children : [children];
|
|
932
944
|
for (const child of childArray) {
|
|
933
945
|
if (child != null && typeof child === "object") {
|
|
934
|
-
drawSvgChild(ctx, child, groupFill);
|
|
946
|
+
drawSvgChild(ctx, child, groupFill, color);
|
|
935
947
|
}
|
|
936
948
|
}
|
|
937
949
|
}
|
|
@@ -944,16 +956,21 @@ function parsePoints(value) {
|
|
|
944
956
|
}
|
|
945
957
|
return result;
|
|
946
958
|
}
|
|
947
|
-
function applyFillAndStroke(ctx, props, path, inheritedFill) {
|
|
948
|
-
const fill = props.fill ?? inheritedFill;
|
|
959
|
+
function applyFillAndStroke(ctx, props, path, inheritedFill, color) {
|
|
960
|
+
const fill = resolveCurrentColor(props.fill, color) ?? inheritedFill;
|
|
961
|
+
const fillRule = props.fillRule ?? props["fill-rule"];
|
|
962
|
+
const clipRule = props.clipRule ?? props["clip-rule"];
|
|
963
|
+
if (clipRule) {
|
|
964
|
+
ctx.clip(path, clipRule);
|
|
965
|
+
}
|
|
949
966
|
if (fill !== "none") {
|
|
950
967
|
ctx.fillStyle = fill;
|
|
951
|
-
ctx.fill(path);
|
|
968
|
+
ctx.fill(path, fillRule ?? "nonzero");
|
|
952
969
|
}
|
|
953
|
-
applyStroke(ctx, props, path);
|
|
970
|
+
applyStroke(ctx, props, path, color);
|
|
954
971
|
}
|
|
955
|
-
function applyStroke(ctx, props, path) {
|
|
956
|
-
const stroke = props.stroke;
|
|
972
|
+
function applyStroke(ctx, props, path, color) {
|
|
973
|
+
const stroke = resolveCurrentColor(props.stroke, color);
|
|
957
974
|
if (!stroke || stroke === "none") return;
|
|
958
975
|
ctx.strokeStyle = stroke;
|
|
959
976
|
ctx.lineWidth = Number(props.strokeWidth ?? props["stroke-width"] ?? 1);
|
|
@@ -1009,7 +1026,7 @@ async function loadEmoji(type, code) {
|
|
|
1009
1026
|
}
|
|
1010
1027
|
|
|
1011
1028
|
// src/jsx/text/emoji-split.ts
|
|
1012
|
-
function splitTextIntoRuns(text, measureText2, emojiSize) {
|
|
1029
|
+
function splitTextIntoRuns(text, measureText2, emojiSize, letterSpacing = 0) {
|
|
1013
1030
|
const runs = [];
|
|
1014
1031
|
const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
1015
1032
|
let currentText = "";
|
|
@@ -1019,13 +1036,18 @@ function splitTextIntoRuns(text, measureText2, emojiSize) {
|
|
|
1019
1036
|
if (isEmojiGrapheme(segment)) {
|
|
1020
1037
|
if (currentText) {
|
|
1021
1038
|
const textWidth = measureText2(currentText);
|
|
1039
|
+
const graphemeCount = [
|
|
1040
|
+
...new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(
|
|
1041
|
+
currentText
|
|
1042
|
+
)
|
|
1043
|
+
].length;
|
|
1022
1044
|
runs.push({
|
|
1023
1045
|
kind: "text",
|
|
1024
1046
|
text: currentText,
|
|
1025
1047
|
x: textStartX,
|
|
1026
|
-
width: textWidth
|
|
1048
|
+
width: textWidth + letterSpacing * graphemeCount
|
|
1027
1049
|
});
|
|
1028
|
-
currentX = textStartX + textWidth;
|
|
1050
|
+
currentX = textStartX + textWidth + letterSpacing * graphemeCount;
|
|
1029
1051
|
currentText = "";
|
|
1030
1052
|
}
|
|
1031
1053
|
runs.push({
|
|
@@ -1034,7 +1056,7 @@ function splitTextIntoRuns(text, measureText2, emojiSize) {
|
|
|
1034
1056
|
x: currentX,
|
|
1035
1057
|
width: emojiSize
|
|
1036
1058
|
});
|
|
1037
|
-
currentX += emojiSize;
|
|
1059
|
+
currentX += emojiSize + letterSpacing;
|
|
1038
1060
|
textStartX = currentX;
|
|
1039
1061
|
} else {
|
|
1040
1062
|
if (!currentText) textStartX = currentX;
|
|
@@ -1043,11 +1065,16 @@ function splitTextIntoRuns(text, measureText2, emojiSize) {
|
|
|
1043
1065
|
}
|
|
1044
1066
|
if (currentText) {
|
|
1045
1067
|
const textWidth = measureText2(currentText);
|
|
1068
|
+
const graphemeCount = [
|
|
1069
|
+
...new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(
|
|
1070
|
+
currentText
|
|
1071
|
+
)
|
|
1072
|
+
].length;
|
|
1046
1073
|
runs.push({
|
|
1047
1074
|
kind: "text",
|
|
1048
1075
|
text: currentText,
|
|
1049
1076
|
x: textStartX,
|
|
1050
|
-
width: textWidth
|
|
1077
|
+
width: textWidth + letterSpacing * graphemeCount
|
|
1051
1078
|
});
|
|
1052
1079
|
}
|
|
1053
1080
|
return runs;
|
|
@@ -1098,20 +1125,29 @@ async function drawText(ctx, segments, offsetX, offsetY, textShadow, emojiStyle)
|
|
|
1098
1125
|
}
|
|
1099
1126
|
}
|
|
1100
1127
|
async function drawSegmentWithEmoji(ctx, seg, x, y, textShadow, emojiStyle) {
|
|
1128
|
+
const letterSpacing = seg.letterSpacing ?? 0;
|
|
1101
1129
|
const runs = splitTextIntoRuns(
|
|
1102
1130
|
seg.text,
|
|
1103
1131
|
(text) => {
|
|
1104
1132
|
setFont(ctx, seg.fontSize, seg.fontFamily, seg.fontWeight, seg.fontStyle);
|
|
1105
1133
|
return ctx.measureText(text).width;
|
|
1106
1134
|
},
|
|
1107
|
-
seg.fontSize
|
|
1135
|
+
seg.fontSize,
|
|
1136
|
+
letterSpacing
|
|
1108
1137
|
);
|
|
1109
1138
|
for (const run of runs) {
|
|
1110
1139
|
if (run.kind === "text") {
|
|
1111
|
-
if (
|
|
1112
|
-
|
|
1140
|
+
if (letterSpacing !== 0) {
|
|
1141
|
+
if (textShadow) {
|
|
1142
|
+
drawTextShadow(ctx, run.text, x + run.x, y, textShadow);
|
|
1143
|
+
}
|
|
1144
|
+
drawTextWithLetterSpacing(ctx, run.text, x + run.x, y, letterSpacing);
|
|
1145
|
+
} else {
|
|
1146
|
+
if (textShadow) {
|
|
1147
|
+
drawTextShadow(ctx, run.text, x + run.x, y, textShadow);
|
|
1148
|
+
}
|
|
1149
|
+
ctx.fillText(run.text, x + run.x, y);
|
|
1113
1150
|
}
|
|
1114
|
-
ctx.fillText(run.text, x + run.x, y);
|
|
1115
1151
|
} else {
|
|
1116
1152
|
const img = await loadEmojiImage(emojiStyle, run.char);
|
|
1117
1153
|
if (img) {
|
|
@@ -1286,84 +1322,91 @@ async function drawNode(ctx, node, parentX, parentY, debug, emojiStyle) {
|
|
|
1286
1322
|
}
|
|
1287
1323
|
const isClipped = style.overflow === "hidden" || style.overflowX === "hidden" || style.overflowY === "hidden";
|
|
1288
1324
|
if (isClipped) {
|
|
1289
|
-
const borderRadius = getBorderRadiusFromStyle(style);
|
|
1325
|
+
const borderRadius = getBorderRadiusFromStyle(style, width, height);
|
|
1290
1326
|
applyClip(ctx, x, y, width, height, borderRadius);
|
|
1291
1327
|
}
|
|
1292
1328
|
if (style.backgroundColor || style.borderTopWidth || style.borderRightWidth || style.borderBottomWidth || style.borderLeftWidth || style.boxShadow) {
|
|
1293
1329
|
drawRect(ctx, x, y, width, height, style);
|
|
1294
1330
|
}
|
|
1295
1331
|
if (style.backgroundImage) {
|
|
1296
|
-
const
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
x,
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
if (borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0) {
|
|
1308
|
-
ctx.beginPath();
|
|
1309
|
-
roundedRect(
|
|
1310
|
-
ctx,
|
|
1311
|
-
x,
|
|
1312
|
-
y,
|
|
1313
|
-
width,
|
|
1314
|
-
height,
|
|
1315
|
-
borderRadius.topLeft,
|
|
1316
|
-
borderRadius.topRight,
|
|
1317
|
-
borderRadius.bottomRight,
|
|
1318
|
-
borderRadius.bottomLeft
|
|
1319
|
-
);
|
|
1320
|
-
ctx.fill();
|
|
1321
|
-
} else {
|
|
1322
|
-
ctx.fillRect(x, y, width, height);
|
|
1323
|
-
}
|
|
1324
|
-
} else {
|
|
1325
|
-
const urlMatch = style.backgroundImage.match(/url\(["']?(.*?)["']?\)/);
|
|
1326
|
-
if (urlMatch) {
|
|
1327
|
-
const borderRadius = getBorderRadiusFromStyle(style);
|
|
1328
|
-
const hasRadius2 = borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0;
|
|
1329
|
-
if (hasRadius2) {
|
|
1330
|
-
applyClip(ctx, x, y, width, height, borderRadius);
|
|
1331
|
-
}
|
|
1332
|
-
const image = await loadImage3(urlMatch[1]);
|
|
1333
|
-
const bgSize = style.backgroundSize;
|
|
1334
|
-
if (bgSize === "cover") {
|
|
1335
|
-
const r = computeCover(
|
|
1336
|
-
image.width,
|
|
1337
|
-
image.height,
|
|
1332
|
+
const layers = splitGradientArgs(style.backgroundImage);
|
|
1333
|
+
for (let i = layers.length - 1; i >= 0; i--) {
|
|
1334
|
+
const layer = layers[i].trim();
|
|
1335
|
+
const gradient = createGradientFromCSS(ctx, layer, x, y, width, height);
|
|
1336
|
+
if (gradient) {
|
|
1337
|
+
ctx.fillStyle = gradient;
|
|
1338
|
+
const borderRadius = getBorderRadiusFromStyle(style, width, height);
|
|
1339
|
+
if (borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0) {
|
|
1340
|
+
ctx.beginPath();
|
|
1341
|
+
roundedRect(
|
|
1342
|
+
ctx,
|
|
1338
1343
|
x,
|
|
1339
1344
|
y,
|
|
1340
1345
|
width,
|
|
1341
|
-
height
|
|
1346
|
+
height,
|
|
1347
|
+
borderRadius.topLeft,
|
|
1348
|
+
borderRadius.topRight,
|
|
1349
|
+
borderRadius.bottomRight,
|
|
1350
|
+
borderRadius.bottomLeft
|
|
1342
1351
|
);
|
|
1343
|
-
ctx.
|
|
1352
|
+
ctx.fill();
|
|
1344
1353
|
} else {
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1354
|
+
ctx.fillRect(x, y, width, height);
|
|
1355
|
+
}
|
|
1356
|
+
} else {
|
|
1357
|
+
const urlMatch = layer.match(/url\(["']?(.*?)["']?\)/);
|
|
1358
|
+
if (urlMatch) {
|
|
1359
|
+
const borderRadius = getBorderRadiusFromStyle(style, width, height);
|
|
1360
|
+
const hasRadius2 = borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0;
|
|
1361
|
+
if (hasRadius2) {
|
|
1362
|
+
applyClip(ctx, x, y, width, height, borderRadius);
|
|
1363
|
+
}
|
|
1364
|
+
const image = await loadImage3(urlMatch[1]);
|
|
1365
|
+
const bgSize = style.backgroundSize;
|
|
1366
|
+
if (bgSize === "cover") {
|
|
1367
|
+
const r = computeCover(
|
|
1348
1368
|
image.width,
|
|
1349
1369
|
image.height,
|
|
1350
|
-
|
|
1351
|
-
|
|
1370
|
+
x,
|
|
1371
|
+
y,
|
|
1352
1372
|
width,
|
|
1353
1373
|
height
|
|
1354
1374
|
);
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1375
|
+
ctx.drawImage(
|
|
1376
|
+
image,
|
|
1377
|
+
r.sx,
|
|
1378
|
+
r.sy,
|
|
1379
|
+
r.sw,
|
|
1380
|
+
r.sh,
|
|
1381
|
+
r.dx,
|
|
1382
|
+
r.dy,
|
|
1383
|
+
r.dw,
|
|
1384
|
+
r.dh
|
|
1385
|
+
);
|
|
1360
1386
|
} else {
|
|
1361
|
-
tileW
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1387
|
+
let tileW, tileH;
|
|
1388
|
+
if (bgSize === "contain") {
|
|
1389
|
+
const r = computeContain(
|
|
1390
|
+
image.width,
|
|
1391
|
+
image.height,
|
|
1392
|
+
0,
|
|
1393
|
+
0,
|
|
1394
|
+
width,
|
|
1395
|
+
height
|
|
1396
|
+
);
|
|
1397
|
+
tileW = r.dw;
|
|
1398
|
+
tileH = r.dh;
|
|
1399
|
+
} else if (bgSize === "100% 100%") {
|
|
1400
|
+
tileW = width;
|
|
1401
|
+
tileH = height;
|
|
1402
|
+
} else {
|
|
1403
|
+
tileW = image.width;
|
|
1404
|
+
tileH = image.height;
|
|
1405
|
+
}
|
|
1406
|
+
for (let ty = y; ty < y + height; ty += tileH) {
|
|
1407
|
+
for (let tx = x; tx < x + width; tx += tileW) {
|
|
1408
|
+
ctx.drawImage(image, tx, ty, tileW, tileH);
|
|
1409
|
+
}
|
|
1367
1410
|
}
|
|
1368
1411
|
}
|
|
1369
1412
|
}
|
|
@@ -1411,7 +1454,7 @@ async function drawNode(ctx, node, parentX, parentY, debug, emojiStyle) {
|
|
|
1411
1454
|
const imgW = width - paddingLeft - paddingRight;
|
|
1412
1455
|
const imgH = height - paddingTop - paddingBottom;
|
|
1413
1456
|
if (!isClipped) {
|
|
1414
|
-
const borderRadius = getBorderRadiusFromStyle(style);
|
|
1457
|
+
const borderRadius = getBorderRadiusFromStyle(style, width, height);
|
|
1415
1458
|
if (borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0) {
|
|
1416
1459
|
applyClip(ctx, imgX, imgY, imgW, imgH, borderRadius);
|
|
1417
1460
|
}
|
|
@@ -1474,84 +1517,91 @@ async function drawNodeInner(ctx, node, parentX, parentY, offsetX, offsetY, debu
|
|
|
1474
1517
|
}
|
|
1475
1518
|
const isClipped = style.overflow === "hidden" || style.overflowX === "hidden" || style.overflowY === "hidden";
|
|
1476
1519
|
if (isClipped) {
|
|
1477
|
-
const borderRadius = getBorderRadiusFromStyle(style);
|
|
1520
|
+
const borderRadius = getBorderRadiusFromStyle(style, width, height);
|
|
1478
1521
|
applyClip(ctx, x, y, width, height, borderRadius);
|
|
1479
1522
|
}
|
|
1480
1523
|
if (style.backgroundColor || style.borderTopWidth || style.borderRightWidth || style.borderBottomWidth || style.borderLeftWidth || style.boxShadow) {
|
|
1481
1524
|
drawRect(ctx, x, y, width, height, style);
|
|
1482
1525
|
}
|
|
1483
1526
|
if (style.backgroundImage) {
|
|
1484
|
-
const
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
x,
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
if (borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0) {
|
|
1496
|
-
ctx.beginPath();
|
|
1497
|
-
roundedRect(
|
|
1498
|
-
ctx,
|
|
1499
|
-
x,
|
|
1500
|
-
y,
|
|
1501
|
-
width,
|
|
1502
|
-
height,
|
|
1503
|
-
borderRadius.topLeft,
|
|
1504
|
-
borderRadius.topRight,
|
|
1505
|
-
borderRadius.bottomRight,
|
|
1506
|
-
borderRadius.bottomLeft
|
|
1507
|
-
);
|
|
1508
|
-
ctx.fill();
|
|
1509
|
-
} else {
|
|
1510
|
-
ctx.fillRect(x, y, width, height);
|
|
1511
|
-
}
|
|
1512
|
-
} else {
|
|
1513
|
-
const urlMatch = style.backgroundImage.match(/url\(["']?(.*?)["']?\)/);
|
|
1514
|
-
if (urlMatch) {
|
|
1515
|
-
const borderRadius = getBorderRadiusFromStyle(style);
|
|
1516
|
-
const hasRadius2 = borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0;
|
|
1517
|
-
if (hasRadius2) {
|
|
1518
|
-
applyClip(ctx, x, y, width, height, borderRadius);
|
|
1519
|
-
}
|
|
1520
|
-
const image = await loadImage3(urlMatch[1]);
|
|
1521
|
-
const bgSize = style.backgroundSize;
|
|
1522
|
-
if (bgSize === "cover") {
|
|
1523
|
-
const r = computeCover(
|
|
1524
|
-
image.width,
|
|
1525
|
-
image.height,
|
|
1527
|
+
const layers = splitGradientArgs(style.backgroundImage);
|
|
1528
|
+
for (let i = layers.length - 1; i >= 0; i--) {
|
|
1529
|
+
const layer = layers[i].trim();
|
|
1530
|
+
const gradient = createGradientFromCSS(ctx, layer, x, y, width, height);
|
|
1531
|
+
if (gradient) {
|
|
1532
|
+
ctx.fillStyle = gradient;
|
|
1533
|
+
const borderRadius = getBorderRadiusFromStyle(style, width, height);
|
|
1534
|
+
if (borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0) {
|
|
1535
|
+
ctx.beginPath();
|
|
1536
|
+
roundedRect(
|
|
1537
|
+
ctx,
|
|
1526
1538
|
x,
|
|
1527
1539
|
y,
|
|
1528
1540
|
width,
|
|
1529
|
-
height
|
|
1541
|
+
height,
|
|
1542
|
+
borderRadius.topLeft,
|
|
1543
|
+
borderRadius.topRight,
|
|
1544
|
+
borderRadius.bottomRight,
|
|
1545
|
+
borderRadius.bottomLeft
|
|
1530
1546
|
);
|
|
1531
|
-
ctx.
|
|
1547
|
+
ctx.fill();
|
|
1532
1548
|
} else {
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1549
|
+
ctx.fillRect(x, y, width, height);
|
|
1550
|
+
}
|
|
1551
|
+
} else {
|
|
1552
|
+
const urlMatch = layer.match(/url\(["']?(.*?)["']?\)/);
|
|
1553
|
+
if (urlMatch) {
|
|
1554
|
+
const borderRadius = getBorderRadiusFromStyle(style, width, height);
|
|
1555
|
+
const hasRadius2 = borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0;
|
|
1556
|
+
if (hasRadius2) {
|
|
1557
|
+
applyClip(ctx, x, y, width, height, borderRadius);
|
|
1558
|
+
}
|
|
1559
|
+
const image = await loadImage3(urlMatch[1]);
|
|
1560
|
+
const bgSize = style.backgroundSize;
|
|
1561
|
+
if (bgSize === "cover") {
|
|
1562
|
+
const r = computeCover(
|
|
1536
1563
|
image.width,
|
|
1537
1564
|
image.height,
|
|
1538
|
-
|
|
1539
|
-
|
|
1565
|
+
x,
|
|
1566
|
+
y,
|
|
1540
1567
|
width,
|
|
1541
1568
|
height
|
|
1542
1569
|
);
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1570
|
+
ctx.drawImage(
|
|
1571
|
+
image,
|
|
1572
|
+
r.sx,
|
|
1573
|
+
r.sy,
|
|
1574
|
+
r.sw,
|
|
1575
|
+
r.sh,
|
|
1576
|
+
r.dx,
|
|
1577
|
+
r.dy,
|
|
1578
|
+
r.dw,
|
|
1579
|
+
r.dh
|
|
1580
|
+
);
|
|
1548
1581
|
} else {
|
|
1549
|
-
tileW
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1582
|
+
let tileW, tileH;
|
|
1583
|
+
if (bgSize === "contain") {
|
|
1584
|
+
const r = computeContain(
|
|
1585
|
+
image.width,
|
|
1586
|
+
image.height,
|
|
1587
|
+
0,
|
|
1588
|
+
0,
|
|
1589
|
+
width,
|
|
1590
|
+
height
|
|
1591
|
+
);
|
|
1592
|
+
tileW = r.dw;
|
|
1593
|
+
tileH = r.dh;
|
|
1594
|
+
} else if (bgSize === "100% 100%") {
|
|
1595
|
+
tileW = width;
|
|
1596
|
+
tileH = height;
|
|
1597
|
+
} else {
|
|
1598
|
+
tileW = image.width;
|
|
1599
|
+
tileH = image.height;
|
|
1600
|
+
}
|
|
1601
|
+
for (let ty = y; ty < y + height; ty += tileH) {
|
|
1602
|
+
for (let tx = x; tx < x + width; tx += tileW) {
|
|
1603
|
+
ctx.drawImage(image, tx, ty, tileW, tileH);
|
|
1604
|
+
}
|
|
1555
1605
|
}
|
|
1556
1606
|
}
|
|
1557
1607
|
}
|
|
@@ -1599,7 +1649,7 @@ async function drawNodeInner(ctx, node, parentX, parentY, offsetX, offsetY, debu
|
|
|
1599
1649
|
const imgW = width - paddingLeft - paddingRight;
|
|
1600
1650
|
const imgH = height - paddingTop - paddingBottom;
|
|
1601
1651
|
if (!isClipped) {
|
|
1602
|
-
const borderRadius = getBorderRadiusFromStyle(style);
|
|
1652
|
+
const borderRadius = getBorderRadiusFromStyle(style, width, height);
|
|
1603
1653
|
if (borderRadius.topLeft > 0 || borderRadius.topRight > 0 || borderRadius.bottomRight > 0 || borderRadius.bottomLeft > 0) {
|
|
1604
1654
|
applyClip(ctx, imgX, imgY, imgW, imgH, borderRadius);
|
|
1605
1655
|
}
|
|
@@ -1737,8 +1787,8 @@ function parseValue(v) {
|
|
|
1737
1787
|
if (v === void 0 || v === null) return void 0;
|
|
1738
1788
|
const s = String(v);
|
|
1739
1789
|
if (s === "auto") return "auto";
|
|
1740
|
-
const n =
|
|
1741
|
-
if (!isNaN(n)) return n;
|
|
1790
|
+
const n = Number(s);
|
|
1791
|
+
if (s !== "" && !isNaN(n)) return n;
|
|
1742
1792
|
return s;
|
|
1743
1793
|
}
|
|
1744
1794
|
function expandStyle(raw) {
|
|
@@ -1947,7 +1997,15 @@ var DIMENSION_PROPS = [
|
|
|
1947
1997
|
"paddingLeft",
|
|
1948
1998
|
"rowGap",
|
|
1949
1999
|
"columnGap",
|
|
1950
|
-
"flexBasis"
|
|
2000
|
+
"flexBasis",
|
|
2001
|
+
"borderTopWidth",
|
|
2002
|
+
"borderRightWidth",
|
|
2003
|
+
"borderBottomWidth",
|
|
2004
|
+
"borderLeftWidth",
|
|
2005
|
+
"borderTopLeftRadius",
|
|
2006
|
+
"borderTopRightRadius",
|
|
2007
|
+
"borderBottomRightRadius",
|
|
2008
|
+
"borderBottomLeftRadius"
|
|
1951
2009
|
];
|
|
1952
2010
|
function resolveUnit(value, viewportWidth, viewportHeight, fontSize, rootFontSize) {
|
|
1953
2011
|
if (value.endsWith("%") || value === "auto") return value;
|
|
@@ -2301,10 +2359,14 @@ async function buildNode(element, parentStyle, yogaNode, viewportWidth, viewport
|
|
|
2301
2359
|
resolveUnits(style, viewportWidth, viewportHeight);
|
|
2302
2360
|
const tagName = String(type);
|
|
2303
2361
|
if (tagName === "svg") {
|
|
2304
|
-
if (props.width != null && style.width === void 0)
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2362
|
+
if (props.width != null && style.width === void 0) {
|
|
2363
|
+
const v = props.width;
|
|
2364
|
+
style.width = typeof v === "string" && v.endsWith("%") ? v : Number(v);
|
|
2365
|
+
}
|
|
2366
|
+
if (props.height != null && style.height === void 0) {
|
|
2367
|
+
const v = props.height;
|
|
2368
|
+
style.height = typeof v === "string" && v.endsWith("%") ? v : Number(v);
|
|
2369
|
+
}
|
|
2308
2370
|
const viewBox = props.viewBox;
|
|
2309
2371
|
if (viewBox) {
|
|
2310
2372
|
const parts = viewBox.split(/[\s,]+/).map(Number);
|
|
@@ -2326,10 +2388,14 @@ async function buildNode(element, parentStyle, yogaNode, viewportWidth, viewport
|
|
|
2326
2388
|
}
|
|
2327
2389
|
}
|
|
2328
2390
|
if (tagName === "img") {
|
|
2329
|
-
if (props.width != null && style.width === void 0)
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2391
|
+
if (props.width != null && style.width === void 0) {
|
|
2392
|
+
const v = props.width;
|
|
2393
|
+
style.width = typeof v === "string" && v.endsWith("%") ? v : Number(v);
|
|
2394
|
+
}
|
|
2395
|
+
if (props.height != null && style.height === void 0) {
|
|
2396
|
+
const v = props.height;
|
|
2397
|
+
style.height = typeof v === "string" && v.endsWith("%") ? v : Number(v);
|
|
2398
|
+
}
|
|
2333
2399
|
const src = props.src;
|
|
2334
2400
|
if (src) {
|
|
2335
2401
|
try {
|
|
@@ -2369,6 +2435,8 @@ async function buildNode(element, parentStyle, yogaNode, viewportWidth, viewport
|
|
|
2369
2435
|
emojiEnabled
|
|
2370
2436
|
);
|
|
2371
2437
|
childYogaNode.setMeasureFunc(measureFunc);
|
|
2438
|
+
childYogaNode.setFlexGrow(1);
|
|
2439
|
+
childYogaNode.setFlexShrink(1);
|
|
2372
2440
|
yogaNode.insertChild(childYogaNode, 0);
|
|
2373
2441
|
return {
|
|
2374
2442
|
type: tagName,
|