gradiente 2.0.1 → 2.1.1
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/LICENSE +21 -0
- package/README.md +110 -93
- package/dist/gradiente.global.iife.js +60 -0
- package/dist/{index.d.mts → index.d.ts} +53 -26
- package/dist/{index.mjs → index.js} +630 -107
- package/package.json +16 -18
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { converter, formatRgb, parse as parse$1 } from "culori";
|
|
1
|
+
import { converter, fixupHueDecreasing, fixupHueIncreasing, fixupHueLonger, fixupHueShorter, formatRgb, interpolate, parse as parse$1 } from "culori";
|
|
2
2
|
//#region src/utils/math/base.ts
|
|
3
3
|
function roundTo(value, digits) {
|
|
4
4
|
const factor = 10 ** digits;
|
|
@@ -30,6 +30,26 @@ function fromPercent(value) {
|
|
|
30
30
|
function isAngleUnit(unit) {
|
|
31
31
|
return unit === "deg" || unit === "rad" || unit === "turn" || unit === "grad";
|
|
32
32
|
}
|
|
33
|
+
function isAngle(value) {
|
|
34
|
+
try {
|
|
35
|
+
return typeof angleValueFromString(value) === "number";
|
|
36
|
+
} catch (e) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function angleValueFromString(value) {
|
|
41
|
+
const match = value.match(/^([+-]?(?:\d+\.?\d*|\.\d+))(deg|rad|turn|grad)$/);
|
|
42
|
+
if (match === null) throw new Error(`Invalid angle value: "${value}"`);
|
|
43
|
+
if (!isAngleUnit(match[2])) throw new Error(`Unsupported angle unit: "${match[2]}"`);
|
|
44
|
+
if (!Number.isFinite(+match[1])) throw new SyntaxError(`Invalid angle value: "${match[1]}"`);
|
|
45
|
+
const angleValue = Number(match[1]);
|
|
46
|
+
switch (match[2]) {
|
|
47
|
+
case "deg": return degToRad(angleValue);
|
|
48
|
+
case "rad": return angleValue;
|
|
49
|
+
case "turn": return turnToRad(angleValue);
|
|
50
|
+
case "grad": return gradToRad(angleValue);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
33
53
|
function degToRad(value) {
|
|
34
54
|
return value * Math.PI / 180;
|
|
35
55
|
}
|
|
@@ -45,21 +65,9 @@ function gradToRad(value) {
|
|
|
45
65
|
function normalizeAngleDeg(value, digits = 3) {
|
|
46
66
|
return roundTo((value % 360 + 360) % 360, digits);
|
|
47
67
|
}
|
|
48
|
-
function normalizeAngleRad(value) {
|
|
68
|
+
function normalizeAngleRad(value, digits = 6) {
|
|
49
69
|
const tau = Math.PI * 2;
|
|
50
|
-
return (value % tau + tau) % tau;
|
|
51
|
-
}
|
|
52
|
-
function toAngleRad(value, unit) {
|
|
53
|
-
switch (unit) {
|
|
54
|
-
case "deg": return degToRad(value);
|
|
55
|
-
case "rad": return value;
|
|
56
|
-
case "turn": return turnToRad(value);
|
|
57
|
-
case "grad": return gradToRad(value);
|
|
58
|
-
default: throw new Error(`Unsupported angle unit: ${String(unit)}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function normalizeAngle(value, unit, digits = 6) {
|
|
62
|
-
return roundTo(normalizeAngleRad(toAngleRad(value, unit)), digits);
|
|
70
|
+
return roundTo((value % tau + tau) % tau, digits);
|
|
63
71
|
}
|
|
64
72
|
//#endregion
|
|
65
73
|
//#region src/dsl/types.ts
|
|
@@ -700,6 +708,44 @@ function splitTopLevelByWhitespace(value) {
|
|
|
700
708
|
return result;
|
|
701
709
|
}
|
|
702
710
|
//#endregion
|
|
711
|
+
//#region src/gradients/helpers.ts
|
|
712
|
+
const GRADIENT_COLOR_SPACE = [
|
|
713
|
+
"oklab",
|
|
714
|
+
"lch",
|
|
715
|
+
"oklch",
|
|
716
|
+
"hsl",
|
|
717
|
+
"hwb",
|
|
718
|
+
"lab",
|
|
719
|
+
"srgb",
|
|
720
|
+
"srgb-linear",
|
|
721
|
+
"xyz",
|
|
722
|
+
"display-p3",
|
|
723
|
+
"a98-rgb",
|
|
724
|
+
"prophoto-rgb",
|
|
725
|
+
"rec2020"
|
|
726
|
+
];
|
|
727
|
+
const GRADIENT_HUE_INTERPOLATIONS = [
|
|
728
|
+
"shorter",
|
|
729
|
+
"longer",
|
|
730
|
+
"increasing",
|
|
731
|
+
"decreasing"
|
|
732
|
+
];
|
|
733
|
+
const GRADIENT_POLAR_COLOR_SPACES = [
|
|
734
|
+
"hsl",
|
|
735
|
+
"hwb",
|
|
736
|
+
"lch",
|
|
737
|
+
"oklch"
|
|
738
|
+
];
|
|
739
|
+
function isGradientHueInterpolation(value) {
|
|
740
|
+
return GRADIENT_HUE_INTERPOLATIONS.includes(value);
|
|
741
|
+
}
|
|
742
|
+
function isGradientColorSpace(value) {
|
|
743
|
+
return GRADIENT_COLOR_SPACE.includes(value);
|
|
744
|
+
}
|
|
745
|
+
function isGradientPolarColorSpace(value) {
|
|
746
|
+
return GRADIENT_POLAR_COLOR_SPACES.includes(value);
|
|
747
|
+
}
|
|
748
|
+
//#endregion
|
|
703
749
|
//#region src/gradients/GradientBase.ts
|
|
704
750
|
var GradientBase = class {
|
|
705
751
|
_isRepeating;
|
|
@@ -725,8 +771,8 @@ var GradientBase = class {
|
|
|
725
771
|
return {
|
|
726
772
|
type: this.type,
|
|
727
773
|
isRepeating: this.isRepeating,
|
|
728
|
-
config: this.config,
|
|
729
|
-
stops: this.stops
|
|
774
|
+
config: this._cloneConfig(this.config),
|
|
775
|
+
stops: this._cloneStops(this.stops)
|
|
730
776
|
};
|
|
731
777
|
}
|
|
732
778
|
addStop(stop) {
|
|
@@ -799,12 +845,10 @@ var GradientBase = class {
|
|
|
799
845
|
}
|
|
800
846
|
}
|
|
801
847
|
_cloneStops(stops) {
|
|
802
|
-
return stops
|
|
848
|
+
return structuredClone(stops);
|
|
803
849
|
}
|
|
804
850
|
_cloneConfig(value) {
|
|
805
|
-
|
|
806
|
-
if (Array.isArray(value)) return [...value];
|
|
807
|
-
return { ...value };
|
|
851
|
+
return structuredClone(value);
|
|
808
852
|
}
|
|
809
853
|
_buildSerializedStopTokens() {
|
|
810
854
|
const result = [];
|
|
@@ -962,66 +1006,72 @@ var GradientBase = class {
|
|
|
962
1006
|
var LinearGradient = class LinearGradient extends GradientBase {
|
|
963
1007
|
type = "linear-gradient";
|
|
964
1008
|
constructor(config) {
|
|
1009
|
+
config.config.angle = normalizeAngleRad(config.config.angle);
|
|
1010
|
+
if (config.config.interpolation) config.config.interpolation = LinearGradient._normalizeConfigInterpolation(config.config.interpolation);
|
|
965
1011
|
super(config);
|
|
966
1012
|
}
|
|
967
1013
|
static normalizeConfig(value) {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
if (
|
|
972
|
-
|
|
973
|
-
if (directions.length === 0 || directions.length > 2) throw new SyntaxError("Linear gradient keyword direction must contain one or two direction tokens");
|
|
974
|
-
const allowed = new Set([
|
|
975
|
-
"top",
|
|
976
|
-
"right",
|
|
977
|
-
"bottom",
|
|
978
|
-
"left"
|
|
979
|
-
]);
|
|
980
|
-
for (const direction of directions) if (!allowed.has(direction)) throw new SyntaxError(`Invalid linear gradient direction token: "${direction}"`);
|
|
981
|
-
if (new Set(directions).size !== directions.length) throw new SyntaxError("Linear gradient keyword direction cannot contain duplicate tokens");
|
|
982
|
-
const hasTop = directions.includes("top");
|
|
983
|
-
const hasRight = directions.includes("right");
|
|
984
|
-
const hasBottom = directions.includes("bottom");
|
|
985
|
-
const hasLeft = directions.includes("left");
|
|
986
|
-
if (hasTop && hasBottom || hasLeft && hasRight) throw new SyntaxError("Linear gradient keyword direction contains conflicting tokens");
|
|
987
|
-
if (hasTop && hasLeft) return { angle: degToRad(315) };
|
|
988
|
-
else if (hasTop && hasRight) return { angle: degToRad(45) };
|
|
989
|
-
else if (hasBottom && hasLeft) return { angle: degToRad(225) };
|
|
990
|
-
else if (hasBottom && hasRight) return { angle: degToRad(135) };
|
|
991
|
-
else if (hasTop) return { angle: degToRad(0) };
|
|
992
|
-
else if (hasRight) return { angle: degToRad(90) };
|
|
993
|
-
else if (hasBottom) return { angle: degToRad(180) };
|
|
994
|
-
else if (hasLeft) return { angle: degToRad(270) };
|
|
995
|
-
throw new SyntaxError(`Unsupported linear gradient keyword direction: "${value}"`);
|
|
1014
|
+
const tokenizedValue = LinearGradient._tokenizeConfigInput(value);
|
|
1015
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1016
|
+
for (const token of tokenizedValue) {
|
|
1017
|
+
if (seen.has(token.type)) throw new SyntaxError(`Duplicate linear gradient config token: "${token.type}"`);
|
|
1018
|
+
seen.add(token.type);
|
|
996
1019
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1020
|
+
const angleToken = tokenizedValue.find((token) => token.type === "angle");
|
|
1021
|
+
const colorSpaceToken = tokenizedValue.find((token) => token.type === "colorSpace");
|
|
1022
|
+
const hueToken = tokenizedValue.find((token) => token.type === "hue");
|
|
1023
|
+
const assembledConfig = {
|
|
1024
|
+
angle: 3.141593,
|
|
1025
|
+
interpolation: {
|
|
1026
|
+
hue: "shorter",
|
|
1027
|
+
colorSpace: "oklab"
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
if (angleToken) {
|
|
1031
|
+
const value = angleToken.value;
|
|
1032
|
+
if (value.startsWith("to ")) {
|
|
1033
|
+
const tokens = value.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
1034
|
+
if (tokens.length === 0) throw new SyntaxError("Linear gradient angle keyword cannot be empty");
|
|
1035
|
+
if (tokens[0] !== "to") throw new SyntaxError("Linear gradient keyword direction must start with \"to\"");
|
|
1036
|
+
const directions = tokens.slice(1);
|
|
1037
|
+
if (directions.length === 0 || directions.length > 2) throw new SyntaxError("Linear gradient keyword direction must contain one or two direction tokens");
|
|
1038
|
+
const allowed = new Set([
|
|
1039
|
+
"top",
|
|
1040
|
+
"right",
|
|
1041
|
+
"bottom",
|
|
1042
|
+
"left"
|
|
1043
|
+
]);
|
|
1044
|
+
for (const direction of directions) if (!allowed.has(direction)) throw new SyntaxError(`Invalid linear gradient direction token: "${direction}"`);
|
|
1045
|
+
if (new Set(directions).size !== directions.length) throw new SyntaxError("Linear gradient keyword direction cannot contain duplicate tokens");
|
|
1046
|
+
const hasTop = directions.includes("top");
|
|
1047
|
+
const hasRight = directions.includes("right");
|
|
1048
|
+
const hasBottom = directions.includes("bottom");
|
|
1049
|
+
const hasLeft = directions.includes("left");
|
|
1050
|
+
if (hasTop && hasBottom || hasLeft && hasRight) throw new SyntaxError("Linear gradient keyword direction contains conflicting tokens");
|
|
1051
|
+
if (hasTop && hasLeft) assembledConfig.angle = degToRad(315);
|
|
1052
|
+
else if (hasTop && hasRight) assembledConfig.angle = degToRad(45);
|
|
1053
|
+
else if (hasBottom && hasLeft) assembledConfig.angle = degToRad(225);
|
|
1054
|
+
else if (hasBottom && hasRight) assembledConfig.angle = degToRad(135);
|
|
1055
|
+
else if (hasTop) assembledConfig.angle = degToRad(0);
|
|
1056
|
+
else if (hasRight) assembledConfig.angle = degToRad(90);
|
|
1057
|
+
else if (hasBottom) assembledConfig.angle = degToRad(180);
|
|
1058
|
+
else if (hasLeft) assembledConfig.angle = degToRad(270);
|
|
1059
|
+
else throw new SyntaxError(`Unsupported linear gradient keyword direction: "${value}"`);
|
|
1060
|
+
} else assembledConfig.angle = normalizeAngleRad(angleValueFromString(value));
|
|
1003
1061
|
}
|
|
1062
|
+
if (colorSpaceToken) assembledConfig.interpolation.colorSpace = colorSpaceToken.value;
|
|
1063
|
+
if (hueToken) assembledConfig.interpolation.hue = hueToken.value;
|
|
1064
|
+
return assembledConfig;
|
|
1004
1065
|
}
|
|
1005
1066
|
static fromString(input) {
|
|
1006
1067
|
return LinearGradient.fromAbi(parseStringToAbi(input));
|
|
1007
1068
|
}
|
|
1008
1069
|
static fromAbi(abi) {
|
|
1009
|
-
let config = { angle:
|
|
1070
|
+
let config = { angle: 3.141592 };
|
|
1010
1071
|
if (abi.inputs[0].type === "config") {
|
|
1011
1072
|
const inputValue = abi.inputs[0].value.trim().toLowerCase();
|
|
1012
1073
|
if (inputValue.length === 0) throw new SyntaxError("Linear gradient config cannot be empty");
|
|
1013
|
-
|
|
1014
|
-
else {
|
|
1015
|
-
const match = inputValue.match(/^([+-]?(?:\d+\.?\d*|\.\d+))(deg|rad|turn|grad)$/i);
|
|
1016
|
-
if (match === null) throw new SyntaxError(`Invalid linear gradient angle: "${inputValue}"`);
|
|
1017
|
-
const rawValue = Number(match[1]);
|
|
1018
|
-
const unit = match[2].toLowerCase();
|
|
1019
|
-
if (!Number.isFinite(rawValue)) throw new SyntaxError(`Invalid linear gradient angle value: "${inputValue}"`);
|
|
1020
|
-
config = LinearGradient.normalizeConfig({
|
|
1021
|
-
value: rawValue,
|
|
1022
|
-
unit
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1074
|
+
config = LinearGradient.normalizeConfig(inputValue);
|
|
1025
1075
|
}
|
|
1026
1076
|
const inputsWithoutConfig = abi.inputs[0]?.type === "config" ? abi.inputs.slice(1) : abi.inputs;
|
|
1027
1077
|
const stops = LinearGradient._normalizeAbiInputsToStops(inputsWithoutConfig);
|
|
@@ -1035,9 +1085,96 @@ var LinearGradient = class LinearGradient extends GradientBase {
|
|
|
1035
1085
|
return new LinearGradient(this.toJSON());
|
|
1036
1086
|
}
|
|
1037
1087
|
toString() {
|
|
1038
|
-
return `${this.isRepeating ? `repeating-${this.type}` : this.type}(${[this.
|
|
1088
|
+
return `${this.isRepeating ? `repeating-${this.type}` : this.type}(${[this._parseConfigToString(this.config), ...this._serializeStopsCompact()].filter(Boolean).join(", ")})`;
|
|
1039
1089
|
}
|
|
1040
1090
|
_validateConfig(_) {}
|
|
1091
|
+
static _normalizeConfigInterpolation(value) {
|
|
1092
|
+
const { colorSpace, hue } = value;
|
|
1093
|
+
if (hue === void 0) return { colorSpace };
|
|
1094
|
+
if (!isGradientPolarColorSpace(colorSpace)) return { colorSpace };
|
|
1095
|
+
return {
|
|
1096
|
+
colorSpace,
|
|
1097
|
+
hue
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
_parseConfigToString(config) {
|
|
1101
|
+
const { angle, interpolation } = config;
|
|
1102
|
+
const configParts = [];
|
|
1103
|
+
const angleString = this._parseAngleToString(angle);
|
|
1104
|
+
if (angleString.length > 0) configParts.push(angleString);
|
|
1105
|
+
if (interpolation !== void 0) configParts.push(this._parseInterpolationToString(interpolation));
|
|
1106
|
+
return configParts.join(" ");
|
|
1107
|
+
}
|
|
1108
|
+
_parseAngleToString(angle) {
|
|
1109
|
+
const angleDeg = normalizeAngleDeg(radToDeg(angle), 3);
|
|
1110
|
+
switch (angleDeg) {
|
|
1111
|
+
case 0: return "to top";
|
|
1112
|
+
case 45: return "to top right";
|
|
1113
|
+
case 90: return "to right";
|
|
1114
|
+
case 135: return "to bottom right";
|
|
1115
|
+
case 180: return "";
|
|
1116
|
+
case 225: return "to bottom left";
|
|
1117
|
+
case 270: return "to left";
|
|
1118
|
+
case 315: return "to top left";
|
|
1119
|
+
default: return `${angleDeg}deg`;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
_parseInterpolationToString(interpolation) {
|
|
1123
|
+
const { colorSpace, hue } = interpolation;
|
|
1124
|
+
if (hue === void 0) return `in ${colorSpace}`;
|
|
1125
|
+
return `in ${colorSpace} ${hue} hue`;
|
|
1126
|
+
}
|
|
1127
|
+
static _tokenizeConfigInput(value) {
|
|
1128
|
+
const inputValue = value.trim().toLowerCase();
|
|
1129
|
+
if (inputValue.length === 0) throw new SyntaxError("Linear gradient config cannot be empty");
|
|
1130
|
+
const parts = inputValue.split(/\s+/);
|
|
1131
|
+
const tokens = [];
|
|
1132
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
1133
|
+
const part = parts[index];
|
|
1134
|
+
if (part === "in") {
|
|
1135
|
+
const colorSpace = parts[index + 1];
|
|
1136
|
+
if (colorSpace === void 0 || !isGradientColorSpace(colorSpace)) throw new SyntaxError(`Expected color space after "in"`);
|
|
1137
|
+
tokens.push({
|
|
1138
|
+
type: "colorSpace",
|
|
1139
|
+
value: colorSpace
|
|
1140
|
+
});
|
|
1141
|
+
index += 1;
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1144
|
+
if (isAngle(part)) {
|
|
1145
|
+
tokens.push({
|
|
1146
|
+
type: "angle",
|
|
1147
|
+
value: part
|
|
1148
|
+
});
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
if (part === "to") {
|
|
1152
|
+
const directionParts = [];
|
|
1153
|
+
const firstDirection = parts[index + 1];
|
|
1154
|
+
const secondDirection = parts[index + 2];
|
|
1155
|
+
if (firstDirection !== void 0) directionParts.push(firstDirection);
|
|
1156
|
+
if (secondDirection === "left" || secondDirection === "right") directionParts.push(secondDirection);
|
|
1157
|
+
const direction = `to ${directionParts.join(" ")}`;
|
|
1158
|
+
tokens.push({
|
|
1159
|
+
type: "angle",
|
|
1160
|
+
value: direction
|
|
1161
|
+
});
|
|
1162
|
+
index += directionParts.length;
|
|
1163
|
+
continue;
|
|
1164
|
+
}
|
|
1165
|
+
if (isGradientHueInterpolation(part)) {
|
|
1166
|
+
if (parts[index + 1] !== "hue") throw new SyntaxError(`Expected "hue" after "${part}"`);
|
|
1167
|
+
tokens.push({
|
|
1168
|
+
type: "hue",
|
|
1169
|
+
value: part
|
|
1170
|
+
});
|
|
1171
|
+
index += 1;
|
|
1172
|
+
continue;
|
|
1173
|
+
}
|
|
1174
|
+
throw new SyntaxError(`Unknown linear gradient config token: "${part}"`);
|
|
1175
|
+
}
|
|
1176
|
+
return tokens;
|
|
1177
|
+
}
|
|
1041
1178
|
};
|
|
1042
1179
|
//#endregion
|
|
1043
1180
|
//#region src/gradients/RadialGradient.ts
|
|
@@ -1332,14 +1469,163 @@ var ModuleTransformerConicGradientToCss = class {
|
|
|
1332
1469
|
}
|
|
1333
1470
|
};
|
|
1334
1471
|
//#endregion
|
|
1472
|
+
//#region src/gradient-transformer/modules/helpers/expand-repeating-stops.ts
|
|
1473
|
+
function positiveModulo(value, modulo) {
|
|
1474
|
+
return (value % modulo + modulo) % modulo;
|
|
1475
|
+
}
|
|
1476
|
+
function sampleColorAtPosition(stops, position) {
|
|
1477
|
+
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
1478
|
+
if (colorStops.length === 0) throw new Error("Cannot sample color from empty stops.");
|
|
1479
|
+
if (position <= colorStops[0].position) return colorStops[0].value;
|
|
1480
|
+
const lastStop = colorStops[colorStops.length - 1];
|
|
1481
|
+
if (position >= lastStop.position) return lastStop.value;
|
|
1482
|
+
for (let index = 0; index < colorStops.length - 1; index += 1) {
|
|
1483
|
+
const current = colorStops[index];
|
|
1484
|
+
const next = colorStops[index + 1];
|
|
1485
|
+
if (position >= current.position && position <= next.position) {
|
|
1486
|
+
const range = next.position - current.position || 1;
|
|
1487
|
+
const localT = (position - current.position) / range;
|
|
1488
|
+
return formatRgb(interpolate([current.value, next.value], "rgb")(localT));
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
return lastStop.value;
|
|
1492
|
+
}
|
|
1493
|
+
function sampleRepeatingColorAtPosition(stops, position, firstPosition, period) {
|
|
1494
|
+
return sampleColorAtPosition(stops, firstPosition + positiveModulo(position - firstPosition, period));
|
|
1495
|
+
}
|
|
1496
|
+
function expandRepeatingStops(stops) {
|
|
1497
|
+
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
1498
|
+
if (colorStops.length < 2) return colorStops;
|
|
1499
|
+
const firstPosition = colorStops[0].position;
|
|
1500
|
+
const period = colorStops[colorStops.length - 1].position - firstPosition;
|
|
1501
|
+
if (period <= 0) return colorStops;
|
|
1502
|
+
const result = [];
|
|
1503
|
+
let order = 0;
|
|
1504
|
+
result.push({
|
|
1505
|
+
type: "color-stop",
|
|
1506
|
+
value: sampleRepeatingColorAtPosition(colorStops, 0, firstPosition, period),
|
|
1507
|
+
position: 0,
|
|
1508
|
+
_order: order
|
|
1509
|
+
});
|
|
1510
|
+
order += 1;
|
|
1511
|
+
const startRepeat = Math.floor((0 - firstPosition) / period) - 1;
|
|
1512
|
+
const endRepeat = Math.ceil((1 - firstPosition) / period) + 1;
|
|
1513
|
+
for (let repeatIndex = startRepeat; repeatIndex <= endRepeat; repeatIndex += 1) {
|
|
1514
|
+
const offset = repeatIndex * period;
|
|
1515
|
+
for (const stop of colorStops) {
|
|
1516
|
+
const position = stop.position + offset;
|
|
1517
|
+
if (position <= 0 || position >= 1) continue;
|
|
1518
|
+
result.push({
|
|
1519
|
+
...stop,
|
|
1520
|
+
position,
|
|
1521
|
+
_order: order
|
|
1522
|
+
});
|
|
1523
|
+
order += 1;
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
result.push({
|
|
1527
|
+
type: "color-stop",
|
|
1528
|
+
value: sampleRepeatingColorAtPosition(colorStops, 1, firstPosition, period),
|
|
1529
|
+
position: 1,
|
|
1530
|
+
_order: order
|
|
1531
|
+
});
|
|
1532
|
+
return result.sort((a, b) => {
|
|
1533
|
+
if (a.position === b.position) return a._order - b._order;
|
|
1534
|
+
return a.position - b.position;
|
|
1535
|
+
}).map(({ _order, ...stop }) => stop);
|
|
1536
|
+
}
|
|
1537
|
+
//#endregion
|
|
1538
|
+
//#region src/gradient-transformer/modules/helpers/resolve-renderable-linear-gradient-stops.ts
|
|
1539
|
+
function getHueFixup(hue) {
|
|
1540
|
+
switch (hue) {
|
|
1541
|
+
case "longer": return fixupHueLonger;
|
|
1542
|
+
case "increasing": return fixupHueIncreasing;
|
|
1543
|
+
case "decreasing": return fixupHueDecreasing;
|
|
1544
|
+
default: return fixupHueShorter;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
function colorSpaceToCuloriMode(colorSpace) {
|
|
1548
|
+
switch (colorSpace) {
|
|
1549
|
+
case "a98-rgb": return "a98";
|
|
1550
|
+
case "display-p3": return "p3";
|
|
1551
|
+
case "prophoto-rgb": return "prophoto";
|
|
1552
|
+
case "xyz": return "xyz65";
|
|
1553
|
+
case "srgb":
|
|
1554
|
+
case "srgb-linear": return "rgb";
|
|
1555
|
+
default: return colorSpace;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
function createCuloriInterpolationOverrides(interpolation) {
|
|
1559
|
+
if (interpolation.hue === void 0) return;
|
|
1560
|
+
return { h: { fixup: getHueFixup(interpolation.hue) } };
|
|
1561
|
+
}
|
|
1562
|
+
function getColorStopsWithPositions(stops) {
|
|
1563
|
+
const colorStops = stops.filter((stop) => stop.type === "color-stop");
|
|
1564
|
+
if (colorStops.length === 0) return [];
|
|
1565
|
+
if (colorStops.length === 1) return [{
|
|
1566
|
+
...colorStops[0],
|
|
1567
|
+
position: colorStops[0].position ?? 0
|
|
1568
|
+
}];
|
|
1569
|
+
return colorStops.map((stop, index) => {
|
|
1570
|
+
if (stop.position != null) return stop;
|
|
1571
|
+
if (index === 0) return {
|
|
1572
|
+
...stop,
|
|
1573
|
+
position: 0
|
|
1574
|
+
};
|
|
1575
|
+
if (index === colorStops.length - 1) return {
|
|
1576
|
+
...stop,
|
|
1577
|
+
position: 1
|
|
1578
|
+
};
|
|
1579
|
+
return {
|
|
1580
|
+
...stop,
|
|
1581
|
+
position: index / (colorStops.length - 1)
|
|
1582
|
+
};
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
function formatColorForCanvas(input) {
|
|
1586
|
+
const color = toRgb$4(input);
|
|
1587
|
+
if (!color) throw new Error("Failed to convert interpolated color to rgb.");
|
|
1588
|
+
return formatRgb(color);
|
|
1589
|
+
}
|
|
1590
|
+
const DEFAULT_SAMPLE_COUNT = 64;
|
|
1591
|
+
const toRgb$4 = converter("rgb");
|
|
1592
|
+
function resolveRenderableLinearGradientStops(gradient, sampleCount = DEFAULT_SAMPLE_COUNT) {
|
|
1593
|
+
const colorStops = getColorStopsWithPositions(gradient.stops);
|
|
1594
|
+
const interpolation = gradient.config.interpolation;
|
|
1595
|
+
if (colorStops.length < 2) return colorStops;
|
|
1596
|
+
if (interpolation === void 0) return gradient.isRepeating ? expandRepeatingStops(colorStops) : colorStops;
|
|
1597
|
+
const sampledStops = [];
|
|
1598
|
+
const mode = colorSpaceToCuloriMode(interpolation.colorSpace);
|
|
1599
|
+
const overrides = createCuloriInterpolationOverrides(interpolation);
|
|
1600
|
+
for (let index = 0; index < colorStops.length - 1; index += 1) {
|
|
1601
|
+
const current = colorStops[index];
|
|
1602
|
+
const next = colorStops[index + 1];
|
|
1603
|
+
const startPosition = current.position;
|
|
1604
|
+
const endPosition = next.position;
|
|
1605
|
+
const colorInterpolator = interpolate([current.value, next.value], mode, overrides);
|
|
1606
|
+
for (let sampleIndex = 0; sampleIndex <= sampleCount; sampleIndex += 1) {
|
|
1607
|
+
if (index > 0 && sampleIndex === 0) continue;
|
|
1608
|
+
const localT = sampleIndex / sampleCount;
|
|
1609
|
+
const position = startPosition + (endPosition - startPosition) * localT;
|
|
1610
|
+
const color = colorInterpolator(localT);
|
|
1611
|
+
sampledStops.push({
|
|
1612
|
+
type: "color-stop",
|
|
1613
|
+
value: formatColorForCanvas(color),
|
|
1614
|
+
position
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
return gradient.isRepeating ? expandRepeatingStops(sampledStops) : sampledStops;
|
|
1619
|
+
}
|
|
1620
|
+
//#endregion
|
|
1335
1621
|
//#region src/gradient-transformer/modules/canvas/ModuleTransformerLinearGradientToCanvas.ts
|
|
1336
|
-
const toRgb$
|
|
1622
|
+
const toRgb$3 = converter("rgb");
|
|
1337
1623
|
function toCanvasColor$1(input) {
|
|
1338
|
-
const color = toRgb$
|
|
1624
|
+
const color = toRgb$3(input);
|
|
1339
1625
|
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
1340
1626
|
return formatRgb(color);
|
|
1341
1627
|
}
|
|
1342
|
-
function getStopRange$
|
|
1628
|
+
function getStopRange$2(stops) {
|
|
1343
1629
|
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null);
|
|
1344
1630
|
if (!colorStops.length) return {
|
|
1345
1631
|
min: 0,
|
|
@@ -1352,7 +1638,7 @@ function getStopRange$1(stops) {
|
|
|
1352
1638
|
stops: colorStops
|
|
1353
1639
|
};
|
|
1354
1640
|
}
|
|
1355
|
-
function normalizeStops$
|
|
1641
|
+
function normalizeStops$2(stops, min, max) {
|
|
1356
1642
|
const range = max - min || 1;
|
|
1357
1643
|
return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
|
|
1358
1644
|
...stop,
|
|
@@ -1360,38 +1646,37 @@ function normalizeStops$1(stops, min, max) {
|
|
|
1360
1646
|
}));
|
|
1361
1647
|
}
|
|
1362
1648
|
var ModuleTransformerLinearGradientToCanvas = class {
|
|
1363
|
-
target = "canvas";
|
|
1649
|
+
target = "canvas-2d";
|
|
1364
1650
|
gradientType = "linear-gradient";
|
|
1365
1651
|
to(input) {
|
|
1366
1652
|
const gradient = input;
|
|
1367
1653
|
return { draw: (ctx, width, height) => {
|
|
1368
1654
|
const angle = gradient.config.angle;
|
|
1369
|
-
const
|
|
1370
|
-
const
|
|
1371
|
-
const
|
|
1372
|
-
const
|
|
1373
|
-
const
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
const { min, max, stops } = getStopRange$
|
|
1379
|
-
let startX = x0;
|
|
1380
|
-
let startY = y0;
|
|
1381
|
-
let endX = x1;
|
|
1382
|
-
let endY = y1;
|
|
1655
|
+
const dirX = Math.sin(angle);
|
|
1656
|
+
const dirY = -Math.cos(angle);
|
|
1657
|
+
const centerX = width / 2;
|
|
1658
|
+
const centerY = height / 2;
|
|
1659
|
+
const lineLength = Math.abs(width * dirX) + Math.abs(height * dirY);
|
|
1660
|
+
let startX = centerX - dirX * lineLength / 2;
|
|
1661
|
+
let startY = centerY - dirY * lineLength / 2;
|
|
1662
|
+
let endX = centerX + dirX * lineLength / 2;
|
|
1663
|
+
let endY = centerY + dirY * lineLength / 2;
|
|
1664
|
+
const { min, max, stops } = getStopRange$2(resolveRenderableLinearGradientStops(gradient));
|
|
1383
1665
|
let normalizedStops = stops;
|
|
1384
1666
|
if (min < 0 || max > 1) {
|
|
1385
|
-
const vx =
|
|
1386
|
-
const vy =
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1667
|
+
const vx = endX - startX;
|
|
1668
|
+
const vy = endY - startY;
|
|
1669
|
+
const baseStartX = startX;
|
|
1670
|
+
const baseStartY = startY;
|
|
1671
|
+
startX = baseStartX + vx * min;
|
|
1672
|
+
startY = baseStartY + vy * min;
|
|
1673
|
+
endX = baseStartX + vx * max;
|
|
1674
|
+
endY = baseStartY + vy * max;
|
|
1675
|
+
normalizedStops = normalizeStops$2(stops, min, max);
|
|
1392
1676
|
}
|
|
1393
1677
|
const canvasGradient = ctx.createLinearGradient(startX, startY, endX, endY);
|
|
1394
1678
|
for (const stop of normalizedStops) canvasGradient.addColorStop(stop.position, toCanvasColor$1(stop.value));
|
|
1679
|
+
ctx.clearRect(0, 0, width, height);
|
|
1395
1680
|
ctx.fillStyle = canvasGradient;
|
|
1396
1681
|
ctx.fillRect(0, 0, width, height);
|
|
1397
1682
|
} };
|
|
@@ -1399,13 +1684,13 @@ var ModuleTransformerLinearGradientToCanvas = class {
|
|
|
1399
1684
|
};
|
|
1400
1685
|
//#endregion
|
|
1401
1686
|
//#region src/gradient-transformer/modules/canvas/ModuleTransformerRadialGradientToCanvas.ts
|
|
1402
|
-
const toRgb$
|
|
1687
|
+
const toRgb$2 = converter("rgb");
|
|
1403
1688
|
function toCanvasColor(input) {
|
|
1404
|
-
const color = toRgb$
|
|
1689
|
+
const color = toRgb$2(input);
|
|
1405
1690
|
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
1406
1691
|
return formatRgb(color);
|
|
1407
1692
|
}
|
|
1408
|
-
function getStopRange(stops) {
|
|
1693
|
+
function getStopRange$1(stops) {
|
|
1409
1694
|
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null);
|
|
1410
1695
|
if (!colorStops.length) return {
|
|
1411
1696
|
min: 0,
|
|
@@ -1418,7 +1703,7 @@ function getStopRange(stops) {
|
|
|
1418
1703
|
stops: colorStops
|
|
1419
1704
|
};
|
|
1420
1705
|
}
|
|
1421
|
-
function normalizeStops(stops, min, max) {
|
|
1706
|
+
function normalizeStops$1(stops, min, max) {
|
|
1422
1707
|
const range = max - min || 1;
|
|
1423
1708
|
return stops.map((stop) => ({
|
|
1424
1709
|
...stop,
|
|
@@ -1426,7 +1711,7 @@ function normalizeStops(stops, min, max) {
|
|
|
1426
1711
|
}));
|
|
1427
1712
|
}
|
|
1428
1713
|
var ModuleTransformerRadialGradientToCanvas = class {
|
|
1429
|
-
target = "canvas";
|
|
1714
|
+
target = "canvas-2d";
|
|
1430
1715
|
gradientType = "radial-gradient";
|
|
1431
1716
|
to(input) {
|
|
1432
1717
|
const gradient = input;
|
|
@@ -1441,17 +1726,17 @@ var ModuleTransformerRadialGradientToCanvas = class {
|
|
|
1441
1726
|
const dx = Math.max(x, width - x);
|
|
1442
1727
|
const dy = Math.max(y, height - y);
|
|
1443
1728
|
const radius = Math.sqrt(dx * dx + dy * dy);
|
|
1444
|
-
const { min, max, stops } = getStopRange(gradient.stops);
|
|
1729
|
+
const { min, max, stops } = getStopRange$1(gradient.stops);
|
|
1445
1730
|
let innerRadius = 0;
|
|
1446
1731
|
let outerRadius = radius;
|
|
1447
1732
|
let normalizedStops = stops;
|
|
1448
1733
|
if (min >= 0 && (min < 0 || max > 1)) {
|
|
1449
1734
|
innerRadius = radius * min;
|
|
1450
1735
|
outerRadius = radius * max;
|
|
1451
|
-
normalizedStops = normalizeStops(stops, min, max);
|
|
1736
|
+
normalizedStops = normalizeStops$1(stops, min, max);
|
|
1452
1737
|
} else if (max > 1) {
|
|
1453
1738
|
outerRadius = radius * max;
|
|
1454
|
-
normalizedStops = normalizeStops(stops, min, max);
|
|
1739
|
+
normalizedStops = normalizeStops$1(stops, min, max);
|
|
1455
1740
|
}
|
|
1456
1741
|
const g = ctx.createRadialGradient(x, y, innerRadius, x, y, outerRadius);
|
|
1457
1742
|
for (const stop of normalizedStops) g.addColorStop(stop.position, toCanvasColor(stop.value));
|
|
@@ -1467,9 +1752,9 @@ var ModuleTransformerRadialGradientToCanvas = class {
|
|
|
1467
1752
|
};
|
|
1468
1753
|
//#endregion
|
|
1469
1754
|
//#region src/gradient-transformer/modules/canvas/ModuleTransformerConicGradientToCanvas.ts
|
|
1470
|
-
const toRgb = converter("rgb");
|
|
1755
|
+
const toRgb$1 = converter("rgb");
|
|
1471
1756
|
var ModuleTransformerConicGradientToCanvas = class {
|
|
1472
|
-
target = "canvas";
|
|
1757
|
+
target = "canvas-2d";
|
|
1473
1758
|
gradientType = "conic-gradient";
|
|
1474
1759
|
to(input) {
|
|
1475
1760
|
const gradient = input;
|
|
@@ -1566,7 +1851,7 @@ var ModuleTransformerConicGradientToCanvas = class {
|
|
|
1566
1851
|
};
|
|
1567
1852
|
}
|
|
1568
1853
|
_parseColor(input) {
|
|
1569
|
-
const color = toRgb(input);
|
|
1854
|
+
const color = toRgb$1(input);
|
|
1570
1855
|
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
1571
1856
|
return {
|
|
1572
1857
|
r: Math.round((color.r ?? 0) * 255),
|
|
@@ -1580,6 +1865,243 @@ var ModuleTransformerConicGradientToCanvas = class {
|
|
|
1580
1865
|
}
|
|
1581
1866
|
};
|
|
1582
1867
|
//#endregion
|
|
1868
|
+
//#region src/gradient-transformer/modules/webgl/ModuleTransformerLinearGradientToWebgl.ts
|
|
1869
|
+
const toRgb = converter("rgb");
|
|
1870
|
+
const MAX_STOPS = 128;
|
|
1871
|
+
function toWebGLColor(input) {
|
|
1872
|
+
const color = toRgb(input);
|
|
1873
|
+
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
1874
|
+
return [
|
|
1875
|
+
color.r ?? 0,
|
|
1876
|
+
color.g ?? 0,
|
|
1877
|
+
color.b ?? 0,
|
|
1878
|
+
color.alpha ?? 1
|
|
1879
|
+
];
|
|
1880
|
+
}
|
|
1881
|
+
function getStopRange(stops) {
|
|
1882
|
+
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null);
|
|
1883
|
+
if (!colorStops.length) return {
|
|
1884
|
+
min: 0,
|
|
1885
|
+
max: 1,
|
|
1886
|
+
stops: []
|
|
1887
|
+
};
|
|
1888
|
+
return {
|
|
1889
|
+
min: Math.min(...colorStops.map((stop) => stop.position)),
|
|
1890
|
+
max: Math.max(...colorStops.map((stop) => stop.position)),
|
|
1891
|
+
stops: colorStops
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
function normalizeStops(stops, min, max) {
|
|
1895
|
+
const range = max - min || 1;
|
|
1896
|
+
return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
|
|
1897
|
+
...stop,
|
|
1898
|
+
position: (stop.position - min) / range
|
|
1899
|
+
}));
|
|
1900
|
+
}
|
|
1901
|
+
function createShader(gl, type, source) {
|
|
1902
|
+
const shader = gl.createShader(type);
|
|
1903
|
+
if (!shader) throw new Error("Failed to create WebGL shader.");
|
|
1904
|
+
gl.shaderSource(shader, source);
|
|
1905
|
+
gl.compileShader(shader);
|
|
1906
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
1907
|
+
const error = gl.getShaderInfoLog(shader);
|
|
1908
|
+
gl.deleteShader(shader);
|
|
1909
|
+
throw new Error(`WebGL shader compile error: ${error}`);
|
|
1910
|
+
}
|
|
1911
|
+
return shader;
|
|
1912
|
+
}
|
|
1913
|
+
function createProgram(gl, vertexSource, fragmentSource) {
|
|
1914
|
+
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
|
|
1915
|
+
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
1916
|
+
const program = gl.createProgram();
|
|
1917
|
+
if (!program) throw new Error("Failed to create WebGL program.");
|
|
1918
|
+
gl.attachShader(program, vertexShader);
|
|
1919
|
+
gl.attachShader(program, fragmentShader);
|
|
1920
|
+
gl.linkProgram(program);
|
|
1921
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
1922
|
+
const error = gl.getProgramInfoLog(program);
|
|
1923
|
+
gl.deleteProgram(program);
|
|
1924
|
+
throw new Error(`WebGL program link error: ${error}`);
|
|
1925
|
+
}
|
|
1926
|
+
return program;
|
|
1927
|
+
}
|
|
1928
|
+
function getColorStopCount(stops) {
|
|
1929
|
+
return stops.filter((stop) => stop.type === "color-stop").length;
|
|
1930
|
+
}
|
|
1931
|
+
function getWebGLSampleCount(gradient, maxStops) {
|
|
1932
|
+
const colorStopCount = getColorStopCount(gradient.stops);
|
|
1933
|
+
const segmentCount = Math.max(1, colorStopCount - 1);
|
|
1934
|
+
return Math.max(2, Math.floor((maxStops - 1) / segmentCount));
|
|
1935
|
+
}
|
|
1936
|
+
function getColorAtPosition(stops, position) {
|
|
1937
|
+
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
1938
|
+
if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
|
|
1939
|
+
if (position <= colorStops[0].position) return colorStops[0].value;
|
|
1940
|
+
const lastStop = colorStops[colorStops.length - 1];
|
|
1941
|
+
if (position >= lastStop.position) return lastStop.value;
|
|
1942
|
+
for (let index = 0; index < colorStops.length - 1; index += 1) {
|
|
1943
|
+
const current = colorStops[index];
|
|
1944
|
+
const next = colorStops[index + 1];
|
|
1945
|
+
if (position >= current.position && position <= next.position) {
|
|
1946
|
+
const range = next.position - current.position || 1;
|
|
1947
|
+
const localT = (position - current.position) / range;
|
|
1948
|
+
return formatRgb(interpolate([current.value, next.value], "rgb")(localT));
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
return lastStop.value;
|
|
1952
|
+
}
|
|
1953
|
+
function fitStopsToWebGLLimit(stops, maxStops) {
|
|
1954
|
+
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
1955
|
+
if (colorStops.length <= maxStops) return colorStops;
|
|
1956
|
+
const sampledStops = [];
|
|
1957
|
+
for (let index = 0; index < maxStops; index += 1) {
|
|
1958
|
+
const position = index / (maxStops - 1);
|
|
1959
|
+
sampledStops.push({
|
|
1960
|
+
type: "color-stop",
|
|
1961
|
+
value: getColorAtPosition(colorStops, position),
|
|
1962
|
+
position
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
return sampledStops;
|
|
1966
|
+
}
|
|
1967
|
+
var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
1968
|
+
target = "canvas-webgl";
|
|
1969
|
+
gradientType = "linear-gradient";
|
|
1970
|
+
to(input) {
|
|
1971
|
+
const gradient = input;
|
|
1972
|
+
return { draw: (canvas, width, height) => {
|
|
1973
|
+
const gl = canvas.getContext("webgl");
|
|
1974
|
+
if (!gl) throw new Error("WebGL is not supported.");
|
|
1975
|
+
canvas.width = width;
|
|
1976
|
+
canvas.height = height;
|
|
1977
|
+
gl.viewport(0, 0, width, height);
|
|
1978
|
+
const program = createProgram(gl, `
|
|
1979
|
+
attribute vec2 a_position;
|
|
1980
|
+
varying vec2 v_uv;
|
|
1981
|
+
|
|
1982
|
+
void main() {
|
|
1983
|
+
v_uv = a_position * 0.5 + 0.5;
|
|
1984
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
1985
|
+
}
|
|
1986
|
+
`, `
|
|
1987
|
+
precision mediump float;
|
|
1988
|
+
|
|
1989
|
+
varying vec2 v_uv;
|
|
1990
|
+
|
|
1991
|
+
uniform vec2 u_start;
|
|
1992
|
+
uniform vec2 u_end;
|
|
1993
|
+
uniform int u_stopCount;
|
|
1994
|
+
uniform float u_positions[${MAX_STOPS}];
|
|
1995
|
+
uniform vec4 u_colors[${MAX_STOPS}];
|
|
1996
|
+
|
|
1997
|
+
vec4 getGradientColor(float t) {
|
|
1998
|
+
vec4 result = u_colors[0];
|
|
1999
|
+
|
|
2000
|
+
for (int i = 0; i < ${MAX_STOPS - 1}; i++) {
|
|
2001
|
+
if (i >= u_stopCount - 1) {
|
|
2002
|
+
break;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
float currentPosition = u_positions[i];
|
|
2006
|
+
float nextPosition = u_positions[i + 1];
|
|
2007
|
+
|
|
2008
|
+
if (t <= currentPosition) {
|
|
2009
|
+
return u_colors[i];
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
if (t >= currentPosition && t <= nextPosition) {
|
|
2013
|
+
float localT = (t - currentPosition) / max(nextPosition - currentPosition, 0.00001);
|
|
2014
|
+
return mix(u_colors[i], u_colors[i + 1], localT);
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
result = u_colors[i + 1];
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
return result;
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
void main() {
|
|
2024
|
+
vec2 axis = u_end - u_start;
|
|
2025
|
+
vec2 point = v_uv;
|
|
2026
|
+
|
|
2027
|
+
float axisLengthSquared = dot(axis, axis);
|
|
2028
|
+
float t = dot(point - u_start, axis) / axisLengthSquared;
|
|
2029
|
+
|
|
2030
|
+
t = clamp(t, 0.0, 1.0);
|
|
2031
|
+
|
|
2032
|
+
gl_FragColor = getGradientColor(t);
|
|
2033
|
+
}
|
|
2034
|
+
`);
|
|
2035
|
+
gl.useProgram(program);
|
|
2036
|
+
const positionBuffer = gl.createBuffer();
|
|
2037
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
2038
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
|
2039
|
+
-1,
|
|
2040
|
+
-1,
|
|
2041
|
+
1,
|
|
2042
|
+
-1,
|
|
2043
|
+
-1,
|
|
2044
|
+
1,
|
|
2045
|
+
-1,
|
|
2046
|
+
1,
|
|
2047
|
+
1,
|
|
2048
|
+
-1,
|
|
2049
|
+
1,
|
|
2050
|
+
1
|
|
2051
|
+
]), gl.STATIC_DRAW);
|
|
2052
|
+
const positionLocation = gl.getAttribLocation(program, "a_position");
|
|
2053
|
+
gl.enableVertexAttribArray(positionLocation);
|
|
2054
|
+
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
2055
|
+
const angle = gradient.config.angle;
|
|
2056
|
+
const dirX = Math.sin(angle);
|
|
2057
|
+
const dirY = -Math.cos(angle);
|
|
2058
|
+
const centerX = width / 2;
|
|
2059
|
+
const centerY = height / 2;
|
|
2060
|
+
const lineLength = Math.abs(width * dirX) + Math.abs(height * dirY);
|
|
2061
|
+
let startX = centerX - dirX * lineLength / 2;
|
|
2062
|
+
let startY = centerY - dirY * lineLength / 2;
|
|
2063
|
+
let endX = centerX + dirX * lineLength / 2;
|
|
2064
|
+
let endY = centerY + dirY * lineLength / 2;
|
|
2065
|
+
const { min, max, stops } = getStopRange(resolveRenderableLinearGradientStops(gradient, getWebGLSampleCount(gradient, MAX_STOPS)));
|
|
2066
|
+
let normalizedStops = stops;
|
|
2067
|
+
if (min < 0 || max > 1) {
|
|
2068
|
+
const vx = endX - startX;
|
|
2069
|
+
const vy = endY - startY;
|
|
2070
|
+
const baseStartX = startX;
|
|
2071
|
+
const baseStartY = startY;
|
|
2072
|
+
startX = baseStartX + vx * min;
|
|
2073
|
+
startY = baseStartY + vy * min;
|
|
2074
|
+
endX = baseStartX + vx * max;
|
|
2075
|
+
endY = baseStartY + vy * max;
|
|
2076
|
+
normalizedStops = normalizeStops(stops, min, max);
|
|
2077
|
+
}
|
|
2078
|
+
const startU = startX / width;
|
|
2079
|
+
const startV = 1 - startY / height;
|
|
2080
|
+
const endU = endX / width;
|
|
2081
|
+
const endV = 1 - endY / height;
|
|
2082
|
+
const limitedStops = fitStopsToWebGLLimit(normalizedStops, MAX_STOPS);
|
|
2083
|
+
const positions = new Float32Array(MAX_STOPS);
|
|
2084
|
+
const colors = new Float32Array(MAX_STOPS * 4);
|
|
2085
|
+
limitedStops.forEach((stop, index) => {
|
|
2086
|
+
const color = toWebGLColor(stop.value);
|
|
2087
|
+
positions[index] = stop.position;
|
|
2088
|
+
colors[index * 4 + 0] = color[0];
|
|
2089
|
+
colors[index * 4 + 1] = color[1];
|
|
2090
|
+
colors[index * 4 + 2] = color[2];
|
|
2091
|
+
colors[index * 4 + 3] = color[3];
|
|
2092
|
+
});
|
|
2093
|
+
gl.uniform2f(gl.getUniformLocation(program, "u_start"), startU, startV);
|
|
2094
|
+
gl.uniform2f(gl.getUniformLocation(program, "u_end"), endU, endV);
|
|
2095
|
+
gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
|
|
2096
|
+
gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
|
|
2097
|
+
gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
|
|
2098
|
+
gl.clearColor(0, 0, 0, 0);
|
|
2099
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
2100
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
2101
|
+
} };
|
|
2102
|
+
}
|
|
2103
|
+
};
|
|
2104
|
+
//#endregion
|
|
1583
2105
|
//#region src/gradient-transformer/GradientTransformer.ts
|
|
1584
2106
|
var GradientTransformer = class {
|
|
1585
2107
|
static _modules = /* @__PURE__ */ new Map();
|
|
@@ -1616,6 +2138,7 @@ var GradientTransformer = class {
|
|
|
1616
2138
|
this.add(new ModuleTransformerLinearGradientToCanvas());
|
|
1617
2139
|
this.add(new ModuleTransformerRadialGradientToCanvas());
|
|
1618
2140
|
this.add(new ModuleTransformerConicGradientToCanvas());
|
|
2141
|
+
this.add(new ModuleTransformerLinearGradientToCanvasWebGL());
|
|
1619
2142
|
}
|
|
1620
2143
|
static _getKey(target, gradientType) {
|
|
1621
2144
|
return `${target}:${gradientType}`;
|
|
@@ -1677,4 +2200,4 @@ function transformFrom(target, gradientType, input) {
|
|
|
1677
2200
|
return GradientTransformer.from(target, gradientType, input);
|
|
1678
2201
|
}
|
|
1679
2202
|
//#endregion
|
|
1680
|
-
export { ConicGradient, GradientBase, GradientFactory, GradientTransformer, LinearGradient, ModuleTransformerConicGradientToCanvas, ModuleTransformerConicGradientToCss, ModuleTransformerLinearGradientToCanvas, ModuleTransformerLinearGradientToCss, ModuleTransformerRadialGradientToCanvas, ModuleTransformerRadialGradientToCss, PatternTokenKind, RadialGradient, ceilTo, clamp, degToRad, floorTo, format, fromPercent, gradToRad, isAngleUnit, isColorHint, isColorStop, isConfig, isGradient, isPatternSyntaxValid, isPatternValid, isValid, matchExpression,
|
|
2203
|
+
export { ConicGradient, GRADIENT_COLOR_SPACE, GRADIENT_HUE_INTERPOLATIONS, GRADIENT_POLAR_COLOR_SPACES, GradientBase, GradientFactory, GradientTransformer, LinearGradient, ModuleTransformerConicGradientToCanvas, ModuleTransformerConicGradientToCss, ModuleTransformerLinearGradientToCanvas, ModuleTransformerLinearGradientToCanvasWebGL, ModuleTransformerLinearGradientToCss, ModuleTransformerRadialGradientToCanvas, ModuleTransformerRadialGradientToCss, PatternTokenKind, RadialGradient, angleValueFromString, ceilTo, clamp, degToRad, floorTo, format, fromPercent, gradToRad, isAngle, isAngleUnit, isColorHint, isColorStop, isConfig, isGradient, isGradientColorSpace, isGradientHueInterpolation, isGradientPolarColorSpace, isPatternSyntaxValid, isPatternValid, isValid, matchExpression, normalizeAngleDeg, normalizeAngleRad, parse, parseStringToAbi, radToDeg, roundTo, splitTopLevelByWhitespace, toPercent, tokenizePattern, transformFrom, transformTo, truncTo, turnToRad, validate, validatePattern, validatePatternSemantic, validatePatternStructure, validatePatternSyntax };
|