@shotstack/shotstack-canvas 2.1.5 → 2.1.6

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/entry.web.js CHANGED
@@ -17896,8 +17896,7 @@ import {
17896
17896
  svgShadowSchema,
17897
17897
  svgTransformSchema,
17898
17898
  svgGradientStopSchema,
17899
- richCaptionActiveSchema as baseCaptionActiveSchema,
17900
- richCaptionWordAnimationSchema as baseCaptionWordAnimationSchema
17899
+ richCaptionActiveSchema as baseCaptionActiveSchema
17901
17900
  } from "@shotstack/schemas/zod";
17902
17901
 
17903
17902
  // src/config/canvas-constants.ts
@@ -18076,23 +18075,28 @@ var richCaptionActiveSchema = baseCaptionActiveSchema.extend({
18076
18075
  opacity: external_exports.number().min(0).max(1).optional(),
18077
18076
  textDecoration: external_exports.enum(["none", "underline", "line-through"]).optional()
18078
18077
  }).optional(),
18079
- stroke: external_exports.object({
18080
- width: external_exports.number().min(0).optional(),
18081
- color: external_exports.string().regex(HEX6).optional(),
18082
- opacity: external_exports.number().min(0).max(1).optional()
18083
- }).optional(),
18084
- shadow: external_exports.object({
18085
- offsetX: external_exports.number().optional(),
18086
- offsetY: external_exports.number().optional(),
18087
- blur: external_exports.number().min(0).optional(),
18088
- color: external_exports.string().regex(HEX6).optional(),
18089
- opacity: external_exports.number().min(0).max(1).optional()
18090
- }).optional(),
18078
+ stroke: external_exports.union([
18079
+ external_exports.object({
18080
+ width: external_exports.number().min(0).optional(),
18081
+ color: external_exports.string().regex(HEX6).optional(),
18082
+ opacity: external_exports.number().min(0).max(1).optional()
18083
+ }),
18084
+ external_exports.literal("none")
18085
+ ]).optional(),
18086
+ shadow: external_exports.union([
18087
+ external_exports.object({
18088
+ offsetX: external_exports.number().optional(),
18089
+ offsetY: external_exports.number().optional(),
18090
+ blur: external_exports.number().min(0).optional(),
18091
+ color: external_exports.string().regex(HEX6).optional(),
18092
+ opacity: external_exports.number().min(0).max(1).optional()
18093
+ }),
18094
+ external_exports.literal("none")
18095
+ ]).optional(),
18091
18096
  scale: external_exports.number().min(0.5).max(2).default(1)
18092
18097
  });
18093
- var richCaptionWordAnimationSchema = baseCaptionWordAnimationSchema.extend({
18098
+ var richCaptionWordAnimationSchema = external_exports.object({
18094
18099
  style: external_exports.enum(["karaoke", "highlight", "pop", "fade", "slide", "bounce", "typewriter", "none"]).default("highlight"),
18095
- speed: external_exports.number().min(0.5).max(2).default(1),
18096
18100
  direction: external_exports.enum(["left", "right", "up", "down"]).default("up")
18097
18101
  });
18098
18102
  var richCaptionAssetSchema = external_exports.object({
@@ -35235,12 +35239,8 @@ function calculateWordProgress(ctx) {
35235
35239
  function isWordActive(ctx) {
35236
35240
  return ctx.currentTime >= ctx.wordStart && ctx.currentTime < ctx.wordEnd;
35237
35241
  }
35238
- function calculateKaraokeState(ctx, speed) {
35242
+ function calculateKaraokeState(ctx) {
35239
35243
  const isActive = isWordActive(ctx);
35240
- const wordDuration = ctx.wordEnd - ctx.wordStart;
35241
- const adjustedDuration = wordDuration / speed;
35242
- const adjustedEnd = ctx.wordStart + adjustedDuration;
35243
- const adjustedCtx = { ...ctx, wordEnd: adjustedEnd };
35244
35244
  if (ctx.currentTime < ctx.wordStart) {
35245
35245
  return {
35246
35246
  fillProgress: 0,
@@ -35248,7 +35248,7 @@ function calculateKaraokeState(ctx, speed) {
35248
35248
  opacity: 1
35249
35249
  };
35250
35250
  }
35251
- if (ctx.currentTime >= adjustedEnd) {
35251
+ if (ctx.currentTime >= ctx.wordEnd) {
35252
35252
  return {
35253
35253
  fillProgress: 1,
35254
35254
  isActive: false,
@@ -35256,7 +35256,7 @@ function calculateKaraokeState(ctx, speed) {
35256
35256
  };
35257
35257
  }
35258
35258
  return {
35259
- fillProgress: calculateWordProgress(adjustedCtx),
35259
+ fillProgress: calculateWordProgress(ctx),
35260
35260
  isActive,
35261
35261
  opacity: 1
35262
35262
  };
@@ -35269,7 +35269,7 @@ function calculateHighlightState(ctx) {
35269
35269
  opacity: 1
35270
35270
  };
35271
35271
  }
35272
- function calculatePopState(ctx, activeScale, speed) {
35272
+ function calculatePopState(ctx, activeScale) {
35273
35273
  if (ctx.currentTime < ctx.wordStart) {
35274
35274
  return {
35275
35275
  scale: 0.5,
@@ -35278,9 +35278,7 @@ function calculatePopState(ctx, activeScale, speed) {
35278
35278
  fillProgress: 0
35279
35279
  };
35280
35280
  }
35281
- const adjustedDuration = ctx.animationDuration / speed;
35282
- const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
35283
- const progress = calculateAnimationProgress(adjustedCtx);
35281
+ const progress = calculateAnimationProgress(ctx);
35284
35282
  const easedProgress = easeOutBack(progress);
35285
35283
  const startScale = 0.5;
35286
35284
  const isActive = isWordActive(ctx);
@@ -35293,7 +35291,7 @@ function calculatePopState(ctx, activeScale, speed) {
35293
35291
  fillProgress: isActive ? 1 : 0
35294
35292
  };
35295
35293
  }
35296
- function calculateFadeState(ctx, speed) {
35294
+ function calculateFadeState(ctx) {
35297
35295
  if (ctx.currentTime < ctx.wordStart) {
35298
35296
  return {
35299
35297
  opacity: 0,
@@ -35301,9 +35299,7 @@ function calculateFadeState(ctx, speed) {
35301
35299
  fillProgress: 0
35302
35300
  };
35303
35301
  }
35304
- const adjustedDuration = ctx.animationDuration / speed;
35305
- const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
35306
- const progress = calculateAnimationProgress(adjustedCtx);
35302
+ const progress = calculateAnimationProgress(ctx);
35307
35303
  const easedProgress = easeInOutQuad(progress);
35308
35304
  const isActive = isWordActive(ctx);
35309
35305
  return {
@@ -35312,7 +35308,7 @@ function calculateFadeState(ctx, speed) {
35312
35308
  fillProgress: isActive ? 1 : 0
35313
35309
  };
35314
35310
  }
35315
- function calculateSlideState(ctx, direction, speed, fontSize) {
35311
+ function calculateSlideState(ctx, direction, fontSize) {
35316
35312
  const slideDistance = fontSize * 1.5;
35317
35313
  if (ctx.currentTime < ctx.wordStart) {
35318
35314
  const offset2 = getDirectionOffset(direction, slideDistance);
@@ -35324,9 +35320,7 @@ function calculateSlideState(ctx, direction, speed, fontSize) {
35324
35320
  fillProgress: 0
35325
35321
  };
35326
35322
  }
35327
- const adjustedDuration = ctx.animationDuration / speed;
35328
- const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
35329
- const progress = calculateAnimationProgress(adjustedCtx);
35323
+ const progress = calculateAnimationProgress(ctx);
35330
35324
  const easedProgress = easeOutCirc(progress);
35331
35325
  const offset = getDirectionOffset(direction, slideDistance);
35332
35326
  const translateX = offset.x * (1 - easedProgress);
@@ -35352,7 +35346,7 @@ function getDirectionOffset(direction, distance) {
35352
35346
  return { x: 0, y: -distance };
35353
35347
  }
35354
35348
  }
35355
- function calculateBounceState(ctx, speed, fontSize) {
35349
+ function calculateBounceState(ctx, fontSize) {
35356
35350
  const bounceDistance = fontSize * 0.8;
35357
35351
  if (ctx.currentTime < ctx.wordStart) {
35358
35352
  return {
@@ -35362,9 +35356,7 @@ function calculateBounceState(ctx, speed, fontSize) {
35362
35356
  fillProgress: 0
35363
35357
  };
35364
35358
  }
35365
- const adjustedDuration = ctx.animationDuration / speed;
35366
- const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
35367
- const progress = calculateAnimationProgress(adjustedCtx);
35359
+ const progress = calculateAnimationProgress(ctx);
35368
35360
  const easedProgress = easeOutBounce(progress);
35369
35361
  const isActive = isWordActive(ctx);
35370
35362
  return {
@@ -35374,11 +35366,7 @@ function calculateBounceState(ctx, speed, fontSize) {
35374
35366
  fillProgress: isActive ? 1 : 0
35375
35367
  };
35376
35368
  }
35377
- function calculateTypewriterState(ctx, charCount, speed) {
35378
- const wordDuration = ctx.wordEnd - ctx.wordStart;
35379
- const adjustedDuration = wordDuration / speed;
35380
- const adjustedEnd = ctx.wordStart + adjustedDuration;
35381
- const adjustedCtx = { ...ctx, wordEnd: adjustedEnd };
35369
+ function calculateTypewriterState(ctx, charCount) {
35382
35370
  if (ctx.currentTime < ctx.wordStart) {
35383
35371
  return {
35384
35372
  visibleCharacters: 0,
@@ -35387,7 +35375,7 @@ function calculateTypewriterState(ctx, charCount, speed) {
35387
35375
  fillProgress: 0
35388
35376
  };
35389
35377
  }
35390
- if (ctx.currentTime >= adjustedEnd) {
35378
+ if (ctx.currentTime >= ctx.wordEnd) {
35391
35379
  return {
35392
35380
  visibleCharacters: charCount,
35393
35381
  opacity: 1,
@@ -35395,7 +35383,7 @@ function calculateTypewriterState(ctx, charCount, speed) {
35395
35383
  fillProgress: 0
35396
35384
  };
35397
35385
  }
35398
- const progress = calculateWordProgress(adjustedCtx);
35386
+ const progress = calculateWordProgress(ctx);
35399
35387
  const visibleCharacters = Math.ceil(progress * charCount);
35400
35388
  const isActive = isWordActive(ctx);
35401
35389
  return {
@@ -35413,7 +35401,6 @@ function calculateNoneState(_ctx) {
35413
35401
  };
35414
35402
  }
35415
35403
  function calculateWordAnimationState(wordStart, wordEnd, currentTime, config2, activeScale = 1, charCount = 0, fontSize = 48, isRTL = false) {
35416
- const safeSpeed = config2.speed > 0 ? config2.speed : 1;
35417
35404
  const ctx = {
35418
35405
  wordStart,
35419
35406
  wordEnd,
@@ -35424,27 +35411,27 @@ function calculateWordAnimationState(wordStart, wordEnd, currentTime, config2, a
35424
35411
  let partialState;
35425
35412
  switch (config2.style) {
35426
35413
  case "karaoke":
35427
- partialState = calculateKaraokeState(ctx, safeSpeed);
35414
+ partialState = calculateKaraokeState(ctx);
35428
35415
  break;
35429
35416
  case "highlight":
35430
35417
  partialState = calculateHighlightState(ctx);
35431
35418
  break;
35432
35419
  case "pop":
35433
- partialState = calculatePopState(ctx, activeScale, safeSpeed);
35420
+ partialState = calculatePopState(ctx, activeScale);
35434
35421
  break;
35435
35422
  case "fade":
35436
- partialState = calculateFadeState(ctx, safeSpeed);
35423
+ partialState = calculateFadeState(ctx);
35437
35424
  break;
35438
35425
  case "slide": {
35439
35426
  const slideDir = mirrorAnimationDirection(config2.direction, isRTL);
35440
- partialState = calculateSlideState(ctx, slideDir, config2.speed, fontSize);
35427
+ partialState = calculateSlideState(ctx, slideDir, fontSize);
35441
35428
  break;
35442
35429
  }
35443
35430
  case "bounce":
35444
- partialState = calculateBounceState(ctx, safeSpeed, fontSize);
35431
+ partialState = calculateBounceState(ctx, fontSize);
35445
35432
  break;
35446
35433
  case "typewriter":
35447
- partialState = calculateTypewriterState(ctx, charCount, safeSpeed);
35434
+ partialState = calculateTypewriterState(ctx, charCount);
35448
35435
  break;
35449
35436
  case "none":
35450
35437
  default:
@@ -35477,7 +35464,6 @@ function calculateAnimationStatesForGroup(words, currentTime, config2, activeSca
35477
35464
  function getDefaultAnimationConfig() {
35478
35465
  return {
35479
35466
  style: "highlight",
35480
- speed: 1,
35481
35467
  direction: "up"
35482
35468
  };
35483
35469
  }
@@ -35518,18 +35504,17 @@ function extractFontConfig(asset) {
35518
35504
  function extractStrokeConfig(asset, isActive) {
35519
35505
  const baseStroke = asset.stroke;
35520
35506
  const activeStroke = asset.active?.stroke;
35521
- if (!baseStroke && !activeStroke) {
35522
- return void 0;
35523
- }
35524
35507
  if (isActive) {
35525
- if (!activeStroke) {
35508
+ if (activeStroke === "none") {
35526
35509
  return void 0;
35527
35510
  }
35528
- return {
35529
- width: activeStroke.width ?? baseStroke?.width ?? 0,
35530
- color: activeStroke.color ?? baseStroke?.color ?? "#000000",
35531
- opacity: activeStroke.opacity ?? baseStroke?.opacity ?? 1
35532
- };
35511
+ if (activeStroke && typeof activeStroke === "object") {
35512
+ return {
35513
+ width: activeStroke.width ?? baseStroke?.width ?? 0,
35514
+ color: activeStroke.color ?? baseStroke?.color ?? "#000000",
35515
+ opacity: activeStroke.opacity ?? baseStroke?.opacity ?? 1
35516
+ };
35517
+ }
35533
35518
  }
35534
35519
  if (baseStroke) {
35535
35520
  return {
@@ -35543,20 +35528,19 @@ function extractStrokeConfig(asset, isActive) {
35543
35528
  function extractShadowConfig(asset, isActive) {
35544
35529
  const baseShadow = asset.shadow;
35545
35530
  const activeShadow = asset.active?.shadow;
35546
- if (!baseShadow && !activeShadow) {
35547
- return void 0;
35548
- }
35549
35531
  if (isActive) {
35550
- if (!activeShadow) {
35532
+ if (activeShadow === "none") {
35551
35533
  return void 0;
35552
35534
  }
35553
- return {
35554
- offsetX: activeShadow.offsetX ?? baseShadow?.offsetX ?? 0,
35555
- offsetY: activeShadow.offsetY ?? baseShadow?.offsetY ?? 0,
35556
- blur: activeShadow.blur ?? baseShadow?.blur ?? 0,
35557
- color: activeShadow.color ?? baseShadow?.color ?? "#000000",
35558
- opacity: activeShadow.opacity ?? baseShadow?.opacity ?? 0.5
35559
- };
35535
+ if (activeShadow && typeof activeShadow === "object") {
35536
+ return {
35537
+ offsetX: activeShadow.offsetX ?? baseShadow?.offsetX ?? 0,
35538
+ offsetY: activeShadow.offsetY ?? baseShadow?.offsetY ?? 0,
35539
+ blur: activeShadow.blur ?? baseShadow?.blur ?? 0,
35540
+ color: activeShadow.color ?? baseShadow?.color ?? "#000000",
35541
+ opacity: activeShadow.opacity ?? baseShadow?.opacity ?? 0.5
35542
+ };
35543
+ }
35560
35544
  }
35561
35545
  if (baseShadow) {
35562
35546
  return {
@@ -35572,7 +35556,12 @@ function extractShadowConfig(asset, isActive) {
35572
35556
  function extractBackgroundConfig(asset, isActive, fontSize) {
35573
35557
  const fontBackground = asset.font?.background;
35574
35558
  const activeBackground = asset.active?.font?.background;
35575
- const bgColor = isActive && activeBackground ? activeBackground : fontBackground;
35559
+ let bgColor;
35560
+ if (isActive) {
35561
+ bgColor = activeBackground ?? fontBackground;
35562
+ } else {
35563
+ bgColor = fontBackground;
35564
+ }
35576
35565
  if (!bgColor) {
35577
35566
  return void 0;
35578
35567
  }
@@ -35640,7 +35629,6 @@ function extractAnimationConfig(asset) {
35640
35629
  }
35641
35630
  return {
35642
35631
  style: wordAnim.style ?? "highlight",
35643
- speed: wordAnim.speed ?? 1,
35644
35632
  direction: wordAnim.direction ?? "up"
35645
35633
  };
35646
35634
  }
@@ -37849,7 +37837,7 @@ function findActiveWordIndex(store, groupWordIndices, timeMs) {
37849
37837
  }
37850
37838
  return -1;
37851
37839
  }
37852
- function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle, speed) {
37840
+ function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle) {
37853
37841
  if (groupWordIndices.length === 0) {
37854
37842
  return "idle";
37855
37843
  }
@@ -37866,7 +37854,7 @@ function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle, spee
37866
37854
  return "after";
37867
37855
  }
37868
37856
  if (TRANSITION_ANIMATION_STYLES.has(animationStyle)) {
37869
- const transitionDurationMs = (ANIMATION_DURATION_MS[animationStyle] ?? 200) / speed;
37857
+ const transitionDurationMs = ANIMATION_DURATION_MS[animationStyle] ?? 200;
37870
37858
  for (const idx of groupWordIndices) {
37871
37859
  const wordStart = store.startTimes[idx];
37872
37860
  if (timeMs >= wordStart && timeMs < wordStart + transitionDurationMs) {
@@ -37888,7 +37876,7 @@ function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle, spee
37888
37876
  }
37889
37877
  return "before";
37890
37878
  }
37891
- function computeStateSignature(layout, timeMs, animationStyle, speed) {
37879
+ function computeStateSignature(layout, timeMs, animationStyle) {
37892
37880
  const groupIndex = findGroupIndexAtTime(layout.groups, timeMs);
37893
37881
  if (groupIndex === -1) {
37894
37882
  return { groupIndex: -1, activeWordIndex: -1, animationPhase: "idle" };
@@ -37899,21 +37887,20 @@ function computeStateSignature(layout, timeMs, animationStyle, speed) {
37899
37887
  layout.store,
37900
37888
  group.wordIndices,
37901
37889
  timeMs,
37902
- animationStyle,
37903
- speed
37890
+ animationStyle
37904
37891
  );
37905
37892
  return { groupIndex, activeWordIndex, animationPhase };
37906
37893
  }
37907
37894
  function signaturesMatch(a, b) {
37908
37895
  return a.groupIndex === b.groupIndex && a.activeWordIndex === b.activeWordIndex && a.animationPhase === b.animationPhase;
37909
37896
  }
37910
- function createFrameSchedule(layout, durationMs, fps, animationStyle = "highlight", speed = 1) {
37897
+ function createFrameSchedule(layout, durationMs, fps, animationStyle = "highlight") {
37911
37898
  const totalFrames = Math.max(2, Math.round(durationMs / 1e3 * fps) + 1);
37912
37899
  const renderFrames = [];
37913
37900
  let previousSignature = null;
37914
37901
  for (let frame = 0; frame < totalFrames; frame++) {
37915
37902
  const timeMs = frame / (totalFrames - 1) * durationMs;
37916
- const signature = computeStateSignature(layout, timeMs, animationStyle, speed);
37903
+ const signature = computeStateSignature(layout, timeMs, animationStyle);
37917
37904
  const isAnimating = signature.animationPhase === "animating";
37918
37905
  if (isAnimating || previousSignature === null || !signaturesMatch(signature, previousSignature)) {
37919
37906
  renderFrames.push({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shotstack/shotstack-canvas",
3
- "version": "2.1.5",
3
+ "version": "2.1.6",
4
4
  "description": "Text layout & animation engine (HarfBuzz) for Node & Web - fully self-contained.",
5
5
  "type": "module",
6
6
  "main": "./dist/entry.node.cjs",
@@ -51,7 +51,7 @@
51
51
  "dependencies": {
52
52
  "@resvg/resvg-js": "^2.6.2",
53
53
  "@resvg/resvg-wasm": "^2.6.2",
54
- "@shotstack/schemas": "1.9.0",
54
+ "@shotstack/schemas": "1.9.1",
55
55
  "bidi-js": "^1.0.3",
56
56
  "canvas": "npm:@napi-rs/canvas@^0.1.54",
57
57
  "ffmpeg-static": "^5.2.0",