modern-text 0.3.0 → 0.3.2

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.mjs CHANGED
@@ -79,21 +79,22 @@ function drawPath(options) {
79
79
  function setupView(ctx, pixelRatio, boundingBox) {
80
80
  const { left, top, width, height } = boundingBox;
81
81
  const view = ctx.canvas;
82
- view.dataset.viewbox = String(`${left} ${top} ${width} ${height}`);
82
+ view.dataset.viewBox = String(`${left} ${top} ${width} ${height}`);
83
83
  view.dataset.pixelRatio = String(pixelRatio);
84
- const canvasWidth = width + left;
85
- const canvasHeight = height + top;
84
+ const canvasWidth = width + Math.abs(left);
85
+ const canvasHeight = height + Math.abs(top);
86
86
  view.width = Math.max(1, Math.ceil(canvasWidth * pixelRatio));
87
87
  view.height = Math.max(1, Math.ceil(canvasHeight * pixelRatio));
88
88
  view.style.width = `${canvasWidth}px`;
89
89
  view.style.height = `${canvasHeight}px`;
90
- ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
90
+ ctx.clearRect(0, 0, view.width, view.height);
91
91
  ctx.scale(pixelRatio, pixelRatio);
92
+ ctx.translate(-Math.min(0, left), -Math.min(0, top));
92
93
  }
93
94
 
94
95
  function uploadColors(ctx, text) {
95
- const { paragraphs, computedStyle: style, renderBoundingBox } = text;
96
- uploadColor(style, renderBoundingBox, ctx);
96
+ const { paragraphs, computedStyle: style, glyphBox } = text;
97
+ uploadColor(style, glyphBox, ctx);
97
98
  paragraphs.forEach((paragraph) => {
98
99
  uploadColor(paragraph.computedStyle, paragraph.lineBox, ctx);
99
100
  paragraph.fragments.forEach((fragment) => {
@@ -150,7 +151,6 @@ class Character {
150
151
  __publicField$3(this, "lineBox", new BoundingBox());
151
152
  __publicField$3(this, "inlineBox", new BoundingBox());
152
153
  __publicField$3(this, "glyphBox");
153
- __publicField$3(this, "center");
154
154
  __publicField$3(this, "underlinePosition", 0);
155
155
  __publicField$3(this, "underlineThickness", 0);
156
156
  __publicField$3(this, "yStrikeoutPosition", 0);
@@ -159,6 +159,9 @@ class Character {
159
159
  __publicField$3(this, "centerDiviation", 0);
160
160
  __publicField$3(this, "path", new Path2D());
161
161
  }
162
+ get center() {
163
+ return this.glyphBox?.center;
164
+ }
162
165
  get computedStyle() {
163
166
  return this.parent.computedStyle;
164
167
  }
@@ -288,7 +291,6 @@ class Character {
288
291
  };
289
292
  this.path = path;
290
293
  this.glyphBox = this.getGlyphBoundingBox();
291
- this.center = this.glyphBox?.getCenterPoint();
292
294
  return this;
293
295
  }
294
296
  update() {
@@ -747,8 +749,8 @@ function getTransformMatrix(a, b, c, isVertical) {
747
749
  y: c.height / b.width
748
750
  };
749
751
  }
750
- const offset = c.getCenterPoint().add(
751
- a.getCenterPoint().sub(b.getCenterPoint()).scale(scale.x, scale.y)
752
+ const offset = c.center.add(
753
+ a.center.sub(b.center).scale(scale.x, scale.y)
752
754
  ).sub({
753
755
  x: a.width / 2 * scale.x,
754
756
  y: a.height / 2 * scale.y
@@ -979,7 +981,7 @@ function render() {
979
981
  return boxes.length ? BoundingBox.from(...boxes) : void 0;
980
982
  },
981
983
  render: (ctx, text) => {
982
- const { characters, paragraphs, renderBoundingBox, effects, style } = text;
984
+ const { characters, paragraphs, glyphBox, effects, style } = text;
983
985
  function fillBackground(color, box) {
984
986
  ctx.fillStyle = color;
985
987
  ctx.fillRect(box.left, box.top, box.width, box.height);
@@ -994,7 +996,7 @@ function render() {
994
996
  });
995
997
  if (effects) {
996
998
  effects.forEach((style2) => {
997
- uploadColor(style2, renderBoundingBox, ctx);
999
+ uploadColor(style2, glyphBox, ctx);
998
1000
  ctx.save();
999
1001
  const [a, c, e, b, d, f] = getTransform2D(text, style2).transpose().elements;
1000
1002
  ctx.transform(a, b, c, d, e, f);
@@ -1022,13 +1024,13 @@ function render() {
1022
1024
  });
1023
1025
  }
1024
1026
  function getTransform2D(text, style) {
1025
- const { fontSize, renderBoundingBox } = text;
1027
+ const { fontSize, glyphBox } = text;
1026
1028
  const translateX = (style.translateX ?? 0) * fontSize;
1027
1029
  const translateY = (style.translateY ?? 0) * fontSize;
1028
1030
  const PI_2 = Math.PI * 2;
1029
1031
  const skewX = (style.skewX ?? 0) / 360 * PI_2;
1030
1032
  const skewY = (style.skewY ?? 0) / 360 * PI_2;
1031
- const { left, top, width, height } = renderBoundingBox;
1033
+ const { left, top, width, height } = glyphBox;
1032
1034
  const centerX = left + width / 2;
1033
1035
  const centerY = top + height / 2;
1034
1036
  tempM1.identity();
@@ -1103,8 +1105,10 @@ class Text {
1103
1105
  __publicField(this, "needsUpdate", true);
1104
1106
  __publicField(this, "computedStyle", { ...defaultTextStyles });
1105
1107
  __publicField(this, "paragraphs", []);
1108
+ __publicField(this, "lineBox", new BoundingBox());
1109
+ __publicField(this, "glyphBox", new BoundingBox());
1110
+ __publicField(this, "pathBox", new BoundingBox());
1106
1111
  __publicField(this, "boundingBox", new BoundingBox());
1107
- __publicField(this, "renderBoundingBox", new BoundingBox());
1108
1112
  __publicField(this, "parser", new Parser(this));
1109
1113
  __publicField(this, "measurer", new Measurer(this));
1110
1114
  __publicField(this, "plugins", /* @__PURE__ */ new Map());
@@ -1130,19 +1134,35 @@ class Text {
1130
1134
  }
1131
1135
  measure(dom = this.measureDom) {
1132
1136
  this.computedStyle = { ...defaultTextStyles, ...this.style };
1133
- const oldParagraphs = this.paragraphs;
1134
- const oldRenderBoundingBox = this.renderBoundingBox;
1137
+ const old = {
1138
+ paragraphs: this.paragraphs,
1139
+ lineBox: this.lineBox,
1140
+ glyphBox: this.glyphBox,
1141
+ pathBox: this.pathBox,
1142
+ boundingBox: this.boundingBox
1143
+ };
1135
1144
  this.paragraphs = this.parser.parse();
1136
1145
  const result = this.measurer.measure(dom);
1137
- const characters = this.characters;
1138
- characters.forEach((c) => c.update());
1146
+ this.paragraphs = result.paragraphs;
1147
+ this.lineBox = result.boundingBox;
1148
+ this.characters.forEach((c) => {
1149
+ c.update();
1150
+ });
1139
1151
  const plugins = [...this.plugins.values()];
1140
1152
  plugins.sort((a, b) => (a.updateOrder ?? 0) - (b.updateOrder ?? 0)).forEach((plugin) => {
1141
1153
  plugin.update?.(this);
1142
1154
  });
1155
+ this.updateGlyphBox().updatePathBox().updateBoundingBox();
1156
+ for (const key in old) {
1157
+ result[key] = this[key];
1158
+ this[key] = old[key];
1159
+ }
1160
+ return result;
1161
+ }
1162
+ updateGlyphBox() {
1143
1163
  const min = Vector2.MAX;
1144
1164
  const max = Vector2.MIN;
1145
- characters.forEach((c) => {
1165
+ this.characters.forEach((c) => {
1146
1166
  if (!c.getGlyphMinMax(min, max)) {
1147
1167
  const { inlineBox } = c;
1148
1168
  const a = new Vector2(inlineBox.left, inlineBox.top);
@@ -1151,30 +1171,47 @@ class Text {
1151
1171
  max.max(a, b);
1152
1172
  }
1153
1173
  });
1154
- this.renderBoundingBox = new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
1155
- this.renderBoundingBox = BoundingBox.from(
1156
- this.renderBoundingBox,
1174
+ this.glyphBox = new BoundingBox(
1175
+ min.x,
1176
+ min.y,
1177
+ max.x - min.x,
1178
+ max.y - min.y
1179
+ );
1180
+ return this;
1181
+ }
1182
+ updatePathBox() {
1183
+ const plugins = [...this.plugins.values()];
1184
+ this.pathBox = BoundingBox.from(
1185
+ this.glyphBox,
1157
1186
  ...plugins.map((plugin) => {
1158
- if (plugin.getBoundingBox) {
1159
- return plugin.getBoundingBox(this);
1160
- }
1161
- return getPathsBoundingBox(plugin.paths ?? []);
1187
+ return plugin.getBoundingBox ? plugin.getBoundingBox(this) : getPathsBoundingBox(plugin.paths ?? []);
1162
1188
  }).filter(Boolean)
1163
1189
  );
1164
- result.renderBoundingBox = this.renderBoundingBox;
1165
- this.paragraphs = oldParagraphs;
1166
- this.renderBoundingBox = oldRenderBoundingBox;
1167
- return result;
1190
+ return this;
1191
+ }
1192
+ updateBoundingBox() {
1193
+ const { lineBox, glyphBox, pathBox } = this;
1194
+ const left = pathBox.left + lineBox.left - glyphBox.left;
1195
+ const top = pathBox.top + lineBox.top - glyphBox.top;
1196
+ const right = pathBox.right + Math.max(0, lineBox.right - glyphBox.right);
1197
+ const bottom = pathBox.bottom + Math.max(0, lineBox.bottom - glyphBox.bottom);
1198
+ this.boundingBox = new BoundingBox(
1199
+ left,
1200
+ top,
1201
+ right - left,
1202
+ bottom - top
1203
+ );
1204
+ return this;
1168
1205
  }
1169
1206
  requestUpdate() {
1170
1207
  this.needsUpdate = true;
1171
1208
  return this;
1172
1209
  }
1173
1210
  update() {
1174
- const { paragraphs, boundingBox, renderBoundingBox } = this.measure();
1175
- this.paragraphs = paragraphs;
1176
- this.boundingBox = boundingBox;
1177
- this.renderBoundingBox = renderBoundingBox;
1211
+ const result = this.measure();
1212
+ for (const key in result) {
1213
+ this[key] = result[key];
1214
+ }
1178
1215
  return this;
1179
1216
  }
1180
1217
  render(options) {
@@ -1186,7 +1223,7 @@ class Text {
1186
1223
  if (this.needsUpdate) {
1187
1224
  this.update();
1188
1225
  }
1189
- setupView(ctx, pixelRatio, this.renderBoundingBox);
1226
+ setupView(ctx, pixelRatio, this.boundingBox);
1190
1227
  uploadColors(ctx, this);
1191
1228
  const plugins = [...this.plugins.values()];
1192
1229
  plugins.sort((a, b) => (a.renderOrder ?? 0) - (b.renderOrder ?? 0)).forEach((plugin) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "modern-text",
3
3
  "type": "module",
4
- "version": "0.3.0",
4
+ "version": "0.3.2",
5
5
  "packageManager": "pnpm@9.9.0",
6
6
  "description": "Measure and render text in a way that describes the DOM.",
7
7
  "author": "wxm",
@@ -58,7 +58,7 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "modern-font": "^0.2.1",
61
- "modern-path2d": "^0.2.4"
61
+ "modern-path2d": "^0.2.5"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@antfu/eslint-config": "^3.7.3",