@applemusic-like-lyrics/core 0.4.1 → 0.4.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.
@@ -3151,6 +3151,13 @@ const NORMAL_BREAK_PENALTY_RATIO = .5;
3151
3151
  */
3152
3152
  const SPACE_BREAK_REWARD_RATIO = .4;
3153
3153
  /**
3154
+ * 在标点符号处断开的奖励比例
3155
+ *
3156
+ * 比空格更高以便优先一点在标点处换行
3157
+ */
3158
+ const PUNCTUATION_BREAK_REWARD_RATIO = .6;
3159
+ const PUNCTUATION_REGEX = /[,.;:!?,。;:!?、)】》」』’”)[\]}>~…]$/;
3160
+ /**
3154
3161
  * 计算平均行长度的断点位置
3155
3162
  * @param children 子节点信息
3156
3163
  * @param containerWidth 容器可用内容宽度
@@ -3191,9 +3198,13 @@ function calcBalancedBreaks(children, containerWidth, fullText, segmenter) {
3191
3198
  else continue;
3192
3199
  else lineCost = (containerWidth - w) ** 2;
3193
3200
  let breakPenalty = 0;
3194
- if (j < n) if (children[j - 1].isSpace) breakPenalty = -((containerWidth * SPACE_BREAK_REWARD_RATIO) ** 2);
3195
- else if (cjkBoundaries.has(charOffsets[j])) breakPenalty = PENALTY_CJK;
3196
- else breakPenalty = PENALTY_NORMAL;
3201
+ if (j < n) {
3202
+ const prevChild = children[j - 1];
3203
+ if (PUNCTUATION_REGEX.test(prevChild.text)) breakPenalty = -((containerWidth * PUNCTUATION_BREAK_REWARD_RATIO) ** 2);
3204
+ else if (prevChild.isSpace) breakPenalty = -((containerWidth * SPACE_BREAK_REWARD_RATIO) ** 2);
3205
+ else if (cjkBoundaries.has(charOffsets[j])) breakPenalty = PENALTY_CJK;
3206
+ else breakPenalty = PENALTY_NORMAL;
3207
+ }
3197
3208
  const totalCost = lineCost + breakPenalty + dp[j];
3198
3209
  if (totalCost < dp[i]) {
3199
3210
  dp[i] = totalCost;
@@ -3218,13 +3229,9 @@ function getMeasurementContext() {
3218
3229
  /**
3219
3230
  * 用于平衡歌词行在换行后的各行长度
3220
3231
  */
3221
- var LineBalancer = class LineBalancer {
3232
+ var LineBalancer = class {
3222
3233
  isBalancing = false;
3223
3234
  lastBalancedContainerWidth = -1;
3224
- /**
3225
- * 防止误差导致的意外换行
3226
- */
3227
- static SAFE_WIDTH_PADDING = 25;
3228
3235
  constructor(mainElement) {
3229
3236
  this.mainElement = mainElement;
3230
3237
  }
@@ -3251,32 +3258,49 @@ var LineBalancer = class LineBalancer {
3251
3258
  adapter.resetDOM();
3252
3259
  const prevWhiteSpace = this.mainElement.style.whiteSpace;
3253
3260
  this.mainElement.style.whiteSpace = "nowrap";
3261
+ const parentElement = this.mainElement.parentElement;
3262
+ let prevTransform = "";
3263
+ let transformChanged = false;
3264
+ if (parentElement) {
3265
+ prevTransform = parentElement.style.transform;
3266
+ if (prevTransform && prevTransform !== "none") {
3267
+ parentElement.style.transform = "none";
3268
+ transformChanged = true;
3269
+ }
3270
+ }
3271
+ let lockAcquired = false;
3254
3272
  try {
3255
- const range = document.createRange();
3256
- range.selectNodeContents(this.mainElement);
3257
- const lineWidth = range.getBoundingClientRect().width;
3258
- const safeContainerWidth = Math.max(1, containerWidth - LineBalancer.SAFE_WIDTH_PADDING);
3259
- if (lineWidth <= safeContainerWidth) {
3273
+ const { childInfos, fullText } = adapter.buildChildInfos();
3274
+ let layoutWidth = childInfos.reduce((sum, c) => sum + c.width, 0);
3275
+ if (adapter.needsCalibration) {
3276
+ const range = document.createRange();
3277
+ range.selectNodeContents(this.mainElement);
3278
+ const visualWidth = range.getBoundingClientRect().width;
3279
+ if (layoutWidth > 0 && visualWidth > 0) {
3280
+ const scale = visualWidth / layoutWidth;
3281
+ for (const info of childInfos) info.width *= scale;
3282
+ }
3283
+ layoutWidth = visualWidth;
3284
+ }
3285
+ const safeContainerWidth = Math.max(1, containerWidth);
3286
+ if (layoutWidth <= safeContainerWidth) {
3260
3287
  this.lastBalancedContainerWidth = containerWidth;
3261
3288
  return;
3262
3289
  }
3263
- const { childInfos, fullText } = adapter.buildChildInfos();
3264
- const measuredTotal = childInfos.reduce((sum, c) => sum + c.width, 0);
3265
- if (measuredTotal > 0 && lineWidth > 0) {
3266
- const scale = lineWidth / measuredTotal;
3267
- for (const info of childInfos) info.width *= scale;
3268
- }
3269
3290
  const breaks = calcBalancedBreaks(childInfos, safeContainerWidth, fullText, wordSegmenter);
3270
3291
  if (breaks.length === 0) {
3271
3292
  this.lastBalancedContainerWidth = containerWidth;
3272
3293
  return;
3273
3294
  }
3274
3295
  this.isBalancing = true;
3296
+ lockAcquired = true;
3275
3297
  adapter.applyBreaks(breaks, childInfos);
3276
3298
  this.lastBalancedContainerWidth = containerWidth;
3277
3299
  this.isBalancing = false;
3278
3300
  } finally {
3279
3301
  this.mainElement.style.whiteSpace = prevWhiteSpace;
3302
+ if (transformChanged && parentElement) parentElement.style.transform = prevTransform;
3303
+ if (lockAcquired) this.isBalancing = false;
3280
3304
  }
3281
3305
  }
3282
3306
  balanceDynamicLineBreaks(containerWidth, wordSegmenter) {
@@ -3325,7 +3349,8 @@ var LineBalancer = class LineBalancer {
3325
3349
  const breakIndex = breaks[i];
3326
3350
  if (breakIndex >= 0 && breakIndex < infoToNode.length) this.mainElement.insertBefore(document.createElement("br"), infoToNode[breakIndex]);
3327
3351
  }
3328
- }
3352
+ },
3353
+ needsCalibration: false
3329
3354
  }, wordSegmenter);
3330
3355
  }
3331
3356
  balanceNonDynamicLineBreaks(containerWidth, computedStyle, wordSegmenter) {
@@ -3368,7 +3393,8 @@ var LineBalancer = class LineBalancer {
3368
3393
  fragment.appendChild(document.createTextNode(childInfos[i].text));
3369
3394
  }
3370
3395
  this.mainElement.appendChild(fragment);
3371
- }
3396
+ },
3397
+ needsCalibration: true
3372
3398
  }, wordSegmenter);
3373
3399
  }
3374
3400
  };