@pixodesk/svg-animator-web 1.0.27 → 1.0.29

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.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PxAnimatorAPI as PxAnimatorAPI$1, PxBasicAnimatorAPI as PxBasicAnimatorAPI$1, PxAnimatedSvgDocument, PxPlatformAdapter, PxAnimatorCallbacksConfig, PxTrigger, PxNode, PxDefs } from '@pixodesk/svg-animator-core';
2
- export { ApplyResult, COLOUR_ATTR_NAMES, ExtendPathOpts, ExtendedPath, FillMode, GlyphCharBox, GlyphCharBoxAlongPath, GlyphMaterialiseOpts, JsMode, KeysMatch, MaterialiseAllOptions, MotionPathMaterialisationOptions, MotionPathSample, OutAction, PX_ANIM_ATTR_NAME, PX_ANIM_SRC_ATTR_NAME, PX_TRANSFORM_PART_KEYS, PathPoint, PathSampler, PlaybackDirection, PxAnimatable, PxAnimatedSvgDocument, PxAnimatedSvgDocumentSchema, PxAnimationDefinition, PxAnimationDefinitionSchema, PxAnimatorCallbacksConfig, PxAnimatorConfig, PxAnimatorConfigSchema, PxAnimatorEngine, PxAnimatorMode, PxAttrValue, PxAttrValueSchema, PxBezierPath, PxBezierPathSchema, PxBinding, PxBindingSchema, PxCloneEffect, PxCloneEffectSchema, PxCreateElement, PxDefs, PxDefsSchema, PxEasingOrRefSchema, PxEffects, PxEffectsSchema, PxElementAnimation, PxElementAnimationSchema, PxFillGradientEffect, PxFillGradientEffectSchema, PxGlyph, PxGlyphFont, PxGradientSpreadMethod, PxGradientStop, PxGradientStopSchema, PxGradientType, PxGradientUnits, PxInfer, PxKeyframe, PxKeyframeSchema, PxKeyframeValueSchema, PxLoop, PxLoopSchema, PxMaskedByEffect, PxMaskedByEffectSchema, PxNode, PxNodeBase, PxNodeSchema, PxPlatformAdapter, PxPropertyAnimation, PxPropertyAnimationSchema, PxRepeaterEffect, PxRepeaterEffectSchema, PxRetimeEffect, PxRetimeEffectSchema, PxSchema, PxSchemaDesc, PxStrokeGradientEffect, PxStrokeGradientEffectSchema, PxSvgNode, PxSvgNodeExtra, PxTextEffectSchema, PxTextPathEffect, PxTextPathEffectSchema, PxTransformByEffect, PxTransformByEffectSchema, PxTransformPartKey, PxTransformParts, PxTransformPartsSchema, PxTransformValue, PxTransformValueSchema, PxTrigger, PxTriggerSchema, PxTrimPathEffect, PxTrimPathEffectSchema, PxValidationContext, PxValidationResult, RemoveIndex, STYLE_ATTR_NAMES, StartOn, TRANSFORM_FN_NAMES, Vec2, applyPlayerEffects, calcAnimationValues, camelCaseToKebabWordIfNeeded, collectSampleTimes, createBasicFrameLoopAnimator, createPathSampler, describeSchema, diffInEffect, evaluateMotionPathSegment, extendedPathForBrowser, generateNewIds, getAnimatorConfig, getBindings, getChildren, getDefs, getNormalizedProps, isPxElementFileFormat, isPxElementFileFormatDeep, jsonElementFactory, layoutGlyphTextChars, materialiseAllInTree, materialiseAnimatedUseInstances, materialiseGlyphText, materialiseGlyphTextAlongPath, materialiseGlyphTextHorizontal, materialiseInternalLoopsInPropAnim, materialiseInternalLoopsInTree, materialiseMotionPathInPropAnim, materialiseMotionPathsInTree, getNormalisedBindings as normalizeDocument, propAnimIsMotionPath, px, schemaKeys, shiftAnimatable, toRGBA, validateNodeEffects, visualModelAt } from '@pixodesk/svg-animator-core';
2
+ export { ApplyResult, COLOUR_ATTR_NAMES, ExtendPathOpts, ExtendedPath, FillMode, GlyphCharBox, GlyphCharBoxAlongPath, GlyphMaterialiseOpts, JsMode, KeysMatch, MISSING_GLYPH_CLASS_NAME, MaterialiseAllOptions, MotionPathMaterialisationOptions, MotionPathSample, OutAction, PX_ANIM_ATTR_NAME, PX_ANIM_SRC_ATTR_NAME, PX_TRANSFORM_PART_KEYS, PathPoint, PathSampler, PlaybackDirection, PxAnimatable, PxAnimatedSvgDocument, PxAnimatedSvgDocumentSchema, PxAnimationDefinition, PxAnimationDefinitionSchema, PxAnimatorCallbacksConfig, PxAnimatorConfig, PxAnimatorConfigSchema, PxAnimatorEngine, PxAnimatorMode, PxAttrValue, PxAttrValueSchema, PxBezierPath, PxBezierPathSchema, PxBinding, PxBindingSchema, PxCloneEffect, PxCloneEffectSchema, PxCreateElement, PxDefs, PxDefsSchema, PxEasingOrRefSchema, PxEffects, PxEffectsSchema, PxElementAnimation, PxElementAnimationSchema, PxFillGradientEffect, PxFillGradientEffectSchema, PxGlyph, PxGlyphFont, PxGradientSpreadMethod, PxGradientStop, PxGradientStopSchema, PxGradientType, PxGradientUnits, PxInfer, PxKeyframe, PxKeyframeSchema, PxKeyframeValueSchema, PxLoop, PxLoopSchema, PxMaskedByEffect, PxMaskedByEffectSchema, PxNode, PxNodeBase, PxNodeSchema, PxPlatformAdapter, PxPropertyAnimation, PxPropertyAnimationSchema, PxRepeaterEffect, PxRepeaterEffectSchema, PxRetimeEffect, PxRetimeEffectSchema, PxSchema, PxSchemaDesc, PxStrokeGradientEffect, PxStrokeGradientEffectSchema, PxSvgNode, PxSvgNodeExtra, PxTextEffectSchema, PxTextPathEffect, PxTextPathEffectSchema, PxTransformByEffect, PxTransformByEffectSchema, PxTransformPartKey, PxTransformParts, PxTransformPartsSchema, PxTransformValue, PxTransformValueSchema, PxTrigger, PxTriggerSchema, PxTrimPathEffect, PxTrimPathEffectSchema, PxValidationContext, PxValidationResult, RemoveIndex, STYLE_ATTR_NAMES, StartOn, TRANSFORM_FN_NAMES, Vec2, applyPlayerEffects, calcAnimationValues, camelCaseToKebabWordIfNeeded, collectSampleTimes, createBasicFrameLoopAnimator, createPathSampler, describeSchema, diffInEffect, evaluateMotionPathSegment, extendedPathForBrowser, generateNewIds, getAnimatorConfig, getBindings, getChildren, getDefs, getNormalizedProps, isPxElementFileFormat, isPxElementFileFormatDeep, jsonElementFactory, layoutGlyphTextChars, materialiseAllInTree, materialiseAnimatedUseInstances, materialiseGlyphText, materialiseGlyphTextAlongPath, materialiseGlyphTextHorizontal, materialiseInternalLoopsInPropAnim, materialiseInternalLoopsInTree, materialiseMotionPathInPropAnim, materialiseMotionPathsInTree, getNormalisedBindings as normalizeDocument, propAnimIsMotionPath, px, schemaKeys, shiftAnimatable, toRGBA, validateNodeEffects, visualModelAt } from '@pixodesk/svg-animator-core';
3
3
 
4
4
  /**
5
5
  * DOM specialisations of the platform-neutral animator API types.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PxAnimatorAPI as PxAnimatorAPI$1, PxBasicAnimatorAPI as PxBasicAnimatorAPI$1, PxAnimatedSvgDocument, PxPlatformAdapter, PxAnimatorCallbacksConfig, PxTrigger, PxNode, PxDefs } from '@pixodesk/svg-animator-core';
2
- export { ApplyResult, COLOUR_ATTR_NAMES, ExtendPathOpts, ExtendedPath, FillMode, GlyphCharBox, GlyphCharBoxAlongPath, GlyphMaterialiseOpts, JsMode, KeysMatch, MaterialiseAllOptions, MotionPathMaterialisationOptions, MotionPathSample, OutAction, PX_ANIM_ATTR_NAME, PX_ANIM_SRC_ATTR_NAME, PX_TRANSFORM_PART_KEYS, PathPoint, PathSampler, PlaybackDirection, PxAnimatable, PxAnimatedSvgDocument, PxAnimatedSvgDocumentSchema, PxAnimationDefinition, PxAnimationDefinitionSchema, PxAnimatorCallbacksConfig, PxAnimatorConfig, PxAnimatorConfigSchema, PxAnimatorEngine, PxAnimatorMode, PxAttrValue, PxAttrValueSchema, PxBezierPath, PxBezierPathSchema, PxBinding, PxBindingSchema, PxCloneEffect, PxCloneEffectSchema, PxCreateElement, PxDefs, PxDefsSchema, PxEasingOrRefSchema, PxEffects, PxEffectsSchema, PxElementAnimation, PxElementAnimationSchema, PxFillGradientEffect, PxFillGradientEffectSchema, PxGlyph, PxGlyphFont, PxGradientSpreadMethod, PxGradientStop, PxGradientStopSchema, PxGradientType, PxGradientUnits, PxInfer, PxKeyframe, PxKeyframeSchema, PxKeyframeValueSchema, PxLoop, PxLoopSchema, PxMaskedByEffect, PxMaskedByEffectSchema, PxNode, PxNodeBase, PxNodeSchema, PxPlatformAdapter, PxPropertyAnimation, PxPropertyAnimationSchema, PxRepeaterEffect, PxRepeaterEffectSchema, PxRetimeEffect, PxRetimeEffectSchema, PxSchema, PxSchemaDesc, PxStrokeGradientEffect, PxStrokeGradientEffectSchema, PxSvgNode, PxSvgNodeExtra, PxTextEffectSchema, PxTextPathEffect, PxTextPathEffectSchema, PxTransformByEffect, PxTransformByEffectSchema, PxTransformPartKey, PxTransformParts, PxTransformPartsSchema, PxTransformValue, PxTransformValueSchema, PxTrigger, PxTriggerSchema, PxTrimPathEffect, PxTrimPathEffectSchema, PxValidationContext, PxValidationResult, RemoveIndex, STYLE_ATTR_NAMES, StartOn, TRANSFORM_FN_NAMES, Vec2, applyPlayerEffects, calcAnimationValues, camelCaseToKebabWordIfNeeded, collectSampleTimes, createBasicFrameLoopAnimator, createPathSampler, describeSchema, diffInEffect, evaluateMotionPathSegment, extendedPathForBrowser, generateNewIds, getAnimatorConfig, getBindings, getChildren, getDefs, getNormalizedProps, isPxElementFileFormat, isPxElementFileFormatDeep, jsonElementFactory, layoutGlyphTextChars, materialiseAllInTree, materialiseAnimatedUseInstances, materialiseGlyphText, materialiseGlyphTextAlongPath, materialiseGlyphTextHorizontal, materialiseInternalLoopsInPropAnim, materialiseInternalLoopsInTree, materialiseMotionPathInPropAnim, materialiseMotionPathsInTree, getNormalisedBindings as normalizeDocument, propAnimIsMotionPath, px, schemaKeys, shiftAnimatable, toRGBA, validateNodeEffects, visualModelAt } from '@pixodesk/svg-animator-core';
2
+ export { ApplyResult, COLOUR_ATTR_NAMES, ExtendPathOpts, ExtendedPath, FillMode, GlyphCharBox, GlyphCharBoxAlongPath, GlyphMaterialiseOpts, JsMode, KeysMatch, MISSING_GLYPH_CLASS_NAME, MaterialiseAllOptions, MotionPathMaterialisationOptions, MotionPathSample, OutAction, PX_ANIM_ATTR_NAME, PX_ANIM_SRC_ATTR_NAME, PX_TRANSFORM_PART_KEYS, PathPoint, PathSampler, PlaybackDirection, PxAnimatable, PxAnimatedSvgDocument, PxAnimatedSvgDocumentSchema, PxAnimationDefinition, PxAnimationDefinitionSchema, PxAnimatorCallbacksConfig, PxAnimatorConfig, PxAnimatorConfigSchema, PxAnimatorEngine, PxAnimatorMode, PxAttrValue, PxAttrValueSchema, PxBezierPath, PxBezierPathSchema, PxBinding, PxBindingSchema, PxCloneEffect, PxCloneEffectSchema, PxCreateElement, PxDefs, PxDefsSchema, PxEasingOrRefSchema, PxEffects, PxEffectsSchema, PxElementAnimation, PxElementAnimationSchema, PxFillGradientEffect, PxFillGradientEffectSchema, PxGlyph, PxGlyphFont, PxGradientSpreadMethod, PxGradientStop, PxGradientStopSchema, PxGradientType, PxGradientUnits, PxInfer, PxKeyframe, PxKeyframeSchema, PxKeyframeValueSchema, PxLoop, PxLoopSchema, PxMaskedByEffect, PxMaskedByEffectSchema, PxNode, PxNodeBase, PxNodeSchema, PxPlatformAdapter, PxPropertyAnimation, PxPropertyAnimationSchema, PxRepeaterEffect, PxRepeaterEffectSchema, PxRetimeEffect, PxRetimeEffectSchema, PxSchema, PxSchemaDesc, PxStrokeGradientEffect, PxStrokeGradientEffectSchema, PxSvgNode, PxSvgNodeExtra, PxTextEffectSchema, PxTextPathEffect, PxTextPathEffectSchema, PxTransformByEffect, PxTransformByEffectSchema, PxTransformPartKey, PxTransformParts, PxTransformPartsSchema, PxTransformValue, PxTransformValueSchema, PxTrigger, PxTriggerSchema, PxTrimPathEffect, PxTrimPathEffectSchema, PxValidationContext, PxValidationResult, RemoveIndex, STYLE_ATTR_NAMES, StartOn, TRANSFORM_FN_NAMES, Vec2, applyPlayerEffects, calcAnimationValues, camelCaseToKebabWordIfNeeded, collectSampleTimes, createBasicFrameLoopAnimator, createPathSampler, describeSchema, diffInEffect, evaluateMotionPathSegment, extendedPathForBrowser, generateNewIds, getAnimatorConfig, getBindings, getChildren, getDefs, getNormalizedProps, isPxElementFileFormat, isPxElementFileFormatDeep, jsonElementFactory, layoutGlyphTextChars, materialiseAllInTree, materialiseAnimatedUseInstances, materialiseGlyphText, materialiseGlyphTextAlongPath, materialiseGlyphTextHorizontal, materialiseInternalLoopsInPropAnim, materialiseInternalLoopsInTree, materialiseMotionPathInPropAnim, materialiseMotionPathsInTree, getNormalisedBindings as normalizeDocument, propAnimIsMotionPath, px, schemaKeys, shiftAnimatable, toRGBA, validateNodeEffects, visualModelAt } from '@pixodesk/svg-animator-core';
3
3
 
4
4
  /**
5
5
  * DOM specialisations of the platform-neutral animator API types.
package/dist/index.js CHANGED
@@ -1207,6 +1207,13 @@ var PxScrollSchema = implementsInterface()(px.object({
1207
1207
  kind: px.enum(["view", "scroll"]).optional(),
1208
1208
  axis: px.enum(["block", "inline", "x", "y"]).optional(),
1209
1209
  source: px.enum(["nearest", "root"]).optional(),
1210
+ // Free-form: the two keywords `parent`/`scroller` plus any CSS selector.
1211
+ subject: px.string().optional(),
1212
+ smoothing: px.number().optional(),
1213
+ pin: px.boolean().optional(),
1214
+ pinAlign: px.enum(["top", "center", "bottom"]).optional(),
1215
+ pinTop: px.number().optional(),
1216
+ pinDistance: px.number().optional(),
1210
1217
  range: PxScrollRangeSchema.optional()
1211
1218
  }));
1212
1219
  var PxAnimatorConfigSchema = implementsInterface()(px.object({
@@ -4439,6 +4446,19 @@ var TEXT_ATTR_KEYS = [
4439
4446
  TEXT_CONTENT_ATTR,
4440
4447
  "xml:space"
4441
4448
  ];
4449
+ var PAINT_STATIC_KEYS = [
4450
+ "fillOpacity",
4451
+ "fillRule",
4452
+ "strokeOpacity",
4453
+ "strokeDasharray",
4454
+ "strokeDashoffset",
4455
+ "strokeLinecap",
4456
+ "strokeLinejoin",
4457
+ "strokeMiterlimit",
4458
+ "mixBlendMode",
4459
+ "filter"
4460
+ ];
4461
+ var PAINT_ANIMATE_KEYS = ["fill", "stroke", "strokeWidth", "opacity", "fillOpacity", "strokeOpacity", "strokeDasharray", "strokeDashoffset"];
4442
4462
  function parseLen(v) {
4443
4463
  if (typeof v === "number") return v;
4444
4464
  if (typeof v === "string") {
@@ -4450,17 +4470,39 @@ function parseLen(v) {
4450
4470
  function str(v) {
4451
4471
  return typeof v === "string" ? v : void 0;
4452
4472
  }
4473
+ function paintAnimateOf(node) {
4474
+ const bag = node.animate;
4475
+ if (!bag || typeof bag !== "object") return void 0;
4476
+ let out;
4477
+ for (const k of PAINT_ANIMATE_KEYS) {
4478
+ if (bag[k] !== void 0) (out != null ? out : out = {})[k] = bag[k];
4479
+ }
4480
+ return out;
4481
+ }
4453
4482
  function resolveStyle2(node, parent) {
4454
- var _a, _b, _c, _d, _e, _f, _g;
4455
- return {
4483
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
4484
+ const isTextRoot = node.type === "text";
4485
+ const nodeStyle = node.style;
4486
+ const own = (key) => {
4487
+ var _a2;
4488
+ return isTextRoot ? void 0 : (_a2 = node[key]) != null ? _a2 : nodeStyle == null ? void 0 : nodeStyle[key];
4489
+ };
4490
+ const res = {
4456
4491
  fontFamily: (_a = str(node.fontFamily)) != null ? _a : parent.fontFamily,
4457
4492
  fontSize: (_b = parseLen(node.fontSize)) != null ? _b : parent.fontSize,
4458
4493
  fill: (_c = node.fill) != null ? _c : parent.fill,
4459
4494
  stroke: (_d = node.stroke) != null ? _d : parent.stroke,
4460
4495
  strokeWidth: (_e = node.strokeWidth) != null ? _e : parent.strokeWidth,
4461
4496
  letterSpacing: (_f = parseLen(node.letterSpacing)) != null ? _f : parent.letterSpacing,
4462
- wordSpacing: (_g = parseLen(node.wordSpacing)) != null ? _g : parent.wordSpacing
4497
+ wordSpacing: (_g = parseLen(node.wordSpacing)) != null ? _g : parent.wordSpacing,
4498
+ opacity: (_h = parseLen(own("opacity"))) != null ? _h : parent.opacity,
4499
+ animate: (_i = isTextRoot ? void 0 : paintAnimateOf(node)) != null ? _i : parent.animate
4463
4500
  };
4501
+ for (const key of PAINT_STATIC_KEYS) {
4502
+ const v = (_j = own(key)) != null ? _j : parent[key];
4503
+ if (v !== void 0) res[key] = v;
4504
+ }
4505
+ return res;
4464
4506
  }
4465
4507
  function rootStyleOf(node) {
4466
4508
  var _a, _b, _c;
@@ -4479,6 +4521,11 @@ function paintOf(s) {
4479
4521
  if (s.fill !== void 0) p.fill = s.fill;
4480
4522
  if (s.stroke !== void 0) p.stroke = s.stroke;
4481
4523
  if (s.strokeWidth !== void 0) p.strokeWidth = s.strokeWidth;
4524
+ if (s.opacity !== void 0 && s.opacity !== 1) p.opacity = s.opacity;
4525
+ for (const key of PAINT_STATIC_KEYS) {
4526
+ if (s[key] !== void 0) p[key] = s[key];
4527
+ }
4528
+ if (s.animate !== void 0) p.animate = s.animate;
4482
4529
  return p;
4483
4530
  }
4484
4531
  function glyphFontFor(s, glyphs, soleFont, warnings) {
@@ -4492,6 +4539,7 @@ function soleFontOf(glyphs) {
4492
4539
  return names.length === 1 ? glyphs[names[0]] : void 0;
4493
4540
  }
4494
4541
  var MISSING_GLYPH_ADVANCE_EM = 0.6;
4542
+ var MISSING_GLYPH_CLASS_NAME = "px-missing-glyph";
4495
4543
  function missingGlyphBoxEm(advanceEm, ascentEm) {
4496
4544
  if (advanceEm <= 0 || ascentEm <= 0) return "";
4497
4545
  const inset = 0.08;
@@ -4527,7 +4575,7 @@ function materialiseGlyphTextHorizontal(node, opts) {
4527
4575
  pen.x += g.width * scale;
4528
4576
  } else if (/\S/.test(ch)) {
4529
4577
  const advEm = g && g.width > 0 ? g.width : MISSING_GLYPH_ADVANCE_EM * upm;
4530
- placements.push({ glyphD: missingGlyphBoxEm(advEm, ascentEm), m: [scale, 0, 0, scale, pen.x, pen.y], paint, line, x: pen.x, y: pen.y, scale });
4578
+ placements.push({ glyphD: missingGlyphBoxEm(advEm, ascentEm), m: [scale, 0, 0, scale, pen.x, pen.y], paint, isMissing: true, line, x: pen.x, y: pen.y, scale });
4531
4579
  pen.x += advEm * scale;
4532
4580
  } else {
4533
4581
  pen.x += (g ? g.width : 0) * scale;
@@ -4718,7 +4766,7 @@ function collectAlongPathCells(node, glyphs, soleFont, warnings) {
4718
4766
  } else if (/\S/.test(ch)) {
4719
4767
  const wEm = g && g.width > 0 ? g.width : MISSING_GLYPH_ADVANCE_EM * gf.unitsPerEm;
4720
4768
  const boxAdv = wEm * scale;
4721
- cells.push({ glyphD: missingGlyphBoxEm(wEm, ascentEm), widthEm: wEm, scale, paint, midBase: adv + boxAdv / 2 });
4769
+ cells.push({ glyphD: missingGlyphBoxEm(wEm, ascentEm), widthEm: wEm, scale, paint, isMissing: true, midBase: adv + boxAdv / 2 });
4722
4770
  adv += boxAdv;
4723
4771
  } else {
4724
4772
  adv += (g ? g.width : 0) * scale;
@@ -4774,6 +4822,7 @@ function materialiseGlyphTextAlongPath(node, pathD, startOffset, opts, textLengt
4774
4822
  const placements = placeCells.map((c) => ({
4775
4823
  glyphD: c.glyphD,
4776
4824
  paint: c.paint,
4825
+ isMissing: c.isMissing,
4777
4826
  m: alongAffine(sampler, base + c.midBase * k, c.scale, c.widthEm, perp)
4778
4827
  }));
4779
4828
  return toGroup(node, buildPaths(placements, create, warnings), create);
@@ -4862,12 +4911,13 @@ function buildAnimatedAlongPath(cells, sampler, distOf, times, loop, create, isC
4862
4911
  const transform = { keyframes: kfs };
4863
4912
  if (loop !== void 0) transform.loop = loop;
4864
4913
  const animate = { transform };
4914
+ if (c.paint.animate) Object.assign(animate, c.paint.animate);
4865
4915
  if (isClip && opKfs.some((k) => k.value === 0)) {
4866
4916
  const op = { keyframes: opKfs };
4867
4917
  if (loop !== void 0) op.loop = loop;
4868
4918
  animate.opacity = op;
4869
4919
  }
4870
- out.push(create("path", __spreadProps2(__spreadValues2({ d }, paintProps(c.paint)), { animate }), []));
4920
+ out.push(create("path", __spreadProps2(__spreadValues2(__spreadValues2({ d }, paintProps(c.paint)), missingGlyphProps(c.isMissing)), { animate }), []));
4871
4921
  }
4872
4922
  return out;
4873
4923
  }
@@ -4876,24 +4926,34 @@ function paintProps(paint) {
4876
4926
  if (paint.fill !== void 0) p.fill = paint.fill;
4877
4927
  if (paint.stroke !== void 0) p.stroke = paint.stroke;
4878
4928
  if (paint.strokeWidth !== void 0) p.strokeWidth = paint.strokeWidth;
4929
+ if (paint.opacity !== void 0) p.opacity = paint.opacity;
4930
+ for (const key of PAINT_STATIC_KEYS) {
4931
+ if (paint[key] !== void 0) p[key] = paint[key];
4932
+ }
4879
4933
  return p;
4880
4934
  }
4935
+ function missingGlyphProps(isMissing) {
4936
+ return isMissing ? { class: MISSING_GLYPH_CLASS_NAME } : {};
4937
+ }
4881
4938
  function buildPaths(placements, create, warnings) {
4882
- var _a, _b, _c;
4883
4939
  if (!placements.length) {
4884
4940
  warnings == null ? void 0 : warnings.push("textGlyphs: nothing to render");
4885
4941
  return [];
4886
4942
  }
4887
4943
  const byPaint = /* @__PURE__ */ new Map();
4888
4944
  for (const p of placements) {
4889
- const key = JSON.stringify([(_a = p.paint.fill) != null ? _a : null, (_b = p.paint.stroke) != null ? _b : null, (_c = p.paint.strokeWidth) != null ? _c : null]);
4945
+ const key = JSON.stringify([p.paint, !!p.isMissing]);
4890
4946
  const baked = transformPathData(p.glyphD, p.m);
4891
4947
  const entry = byPaint.get(key);
4892
4948
  if (entry) entry.d += baked;
4893
- else byPaint.set(key, { paint: p.paint, d: baked });
4949
+ else byPaint.set(key, { paint: p.paint, d: baked, isMissing: p.isMissing });
4894
4950
  }
4895
4951
  const out = [];
4896
- for (const { paint, d } of byPaint.values()) out.push(create("path", __spreadValues2({ d }, paintProps(paint)), []));
4952
+ for (const { paint, d, isMissing } of byPaint.values()) {
4953
+ out.push(create("path", __spreadValues2(__spreadValues2(__spreadValues2({
4954
+ d
4955
+ }, paintProps(paint)), missingGlyphProps(isMissing)), paint.animate !== void 0 ? { animate: __spreadValues2({}, paint.animate) } : {}), []));
4956
+ }
4897
4957
  return out;
4898
4958
  }
4899
4959
  function toGroup(node, children, create) {
@@ -6431,6 +6491,10 @@ function createNativeScrollTimeline(subject, config) {
6431
6491
  if (!config || !isScrollTimeline(config)) return null;
6432
6492
  const scroll = config.scroll || {};
6433
6493
  const kind = (_a = scroll.kind) != null ? _a : "view";
6494
+ if (scroll.smoothing) {
6495
+ console.warn('scroll timeline: `smoothing` needs the built-in driver \u2014 ignoring `driver: "native"`');
6496
+ return null;
6497
+ }
6434
6498
  const g = globalThis;
6435
6499
  const view = kind === "view";
6436
6500
  const Ctor = view ? g.ViewTimeline : g.ScrollTimeline;
@@ -6439,7 +6503,7 @@ function createNativeScrollTimeline(subject, config) {
6439
6503
  let timeline;
6440
6504
  try {
6441
6505
  if (view) {
6442
- timeline = new Ctor({ subject, axis });
6506
+ timeline = new Ctor({ subject: resolveScrollSubject(subject, scroll.subject), axis });
6443
6507
  } else {
6444
6508
  const source = scroll.source === "root" ? documentScroller() : findNearestScroller(subject, "y") || findNearestScroller(subject, "x") || documentScroller();
6445
6509
  timeline = new Ctor({ source, axis });
@@ -6455,7 +6519,10 @@ function createNativeScrollTimeline(subject, config) {
6455
6519
  };
6456
6520
  }
6457
6521
  function findNearestScroller(el, axis) {
6522
+ const body = document.body;
6523
+ const root = document.documentElement;
6458
6524
  for (let p = el.parentElement; p; p = p.parentElement) {
6525
+ if (p === body || p === root) return null;
6459
6526
  const style = getComputedStyle(p);
6460
6527
  const overflow = axis === "y" ? style.overflowY : style.overflowX;
6461
6528
  if (overflow === "auto" || overflow === "scroll" || overflow === "hidden" || overflow === "overlay") {
@@ -6467,11 +6534,42 @@ function findNearestScroller(el, axis) {
6467
6534
  function documentScroller() {
6468
6535
  return document.scrollingElement || document.documentElement;
6469
6536
  }
6537
+ var SUBJECT_PARENT = "parent";
6538
+ var SUBJECT_SCROLLER = "scroller";
6539
+ function resolveScrollSubject(svgRoot, subject) {
6540
+ var _a, _b;
6541
+ const spec = subject == null ? void 0 : subject.trim();
6542
+ if (!spec) return svgRoot;
6543
+ if (spec === SUBJECT_PARENT) {
6544
+ let outermostPinned = null;
6545
+ for (let p = svgRoot.parentElement; p && p !== document.body; p = p.parentElement) {
6546
+ const position = getComputedStyle(p).position;
6547
+ if (position === "sticky" || position === "fixed") outermostPinned = p;
6548
+ }
6549
+ return (_b = (_a = outermostPinned == null ? void 0 : outermostPinned.parentElement) != null ? _a : svgRoot.parentElement) != null ? _b : svgRoot;
6550
+ }
6551
+ if (spec === SUBJECT_SCROLLER) {
6552
+ return findNearestScroller(svgRoot, "y") || findNearestScroller(svgRoot, "x") || documentScroller();
6553
+ }
6554
+ let found = null;
6555
+ try {
6556
+ found = document.querySelector(spec);
6557
+ } catch (e) {
6558
+ console.warn('scroll timeline: subject "' + spec + '" is not a valid selector \u2014 measuring the SVG itself');
6559
+ return svgRoot;
6560
+ }
6561
+ if (!found) {
6562
+ console.warn('scroll timeline: subject "' + spec + '" matched no element \u2014 measuring the SVG itself');
6563
+ return svgRoot;
6564
+ }
6565
+ return found;
6566
+ }
6470
6567
  function createScrollDriver(subject, config, onProgress) {
6471
- var _a;
6568
+ var _a, _b;
6472
6569
  if (!config || !isScrollTimeline(config)) return null;
6473
6570
  const scroll = config.scroll || {};
6474
6571
  const kind = (_a = scroll.kind) != null ? _a : "view";
6572
+ const measured = resolveScrollSubject(subject, scroll.subject);
6475
6573
  const nearest = findNearestScroller(subject, "y") || findNearestScroller(subject, "x");
6476
6574
  const scroller = kind === "scroll" && scroll.source === "root" ? documentScroller() : nearest || documentScroller();
6477
6575
  const isRootScroller = scroller === documentScroller();
@@ -6482,7 +6580,7 @@ function createScrollDriver(subject, config, onProgress) {
6482
6580
  const maxOffset = axis === "y" ? scroller.scrollHeight - scroller.clientHeight : scroller.scrollWidth - scroller.clientWidth;
6483
6581
  return scrollOffsetProgress(offset, maxOffset, scroll.range);
6484
6582
  }
6485
- const subjectRect = subject.getBoundingClientRect();
6583
+ const subjectRect = measured.getBoundingClientRect();
6486
6584
  let portStart, portSize;
6487
6585
  if (isRootScroller) {
6488
6586
  portStart = 0;
@@ -6496,12 +6594,43 @@ function createScrollDriver(subject, config, onProgress) {
6496
6594
  const subjectSize = axis === "y" ? subjectRect.height : subjectRect.width;
6497
6595
  return scrollViewProgress(subjectStart, subjectSize, portSize, scroll.range);
6498
6596
  };
6499
- let rafId = null;
6597
+ const smoothingSec = Math.max(0, (_b = scroll.smoothing) != null ? _b : 0) / 1e3;
6598
+ const SETTLE_EPSILON = 1e-3;
6500
6599
  let destroyed = false;
6600
+ let smoothed = null;
6601
+ let smoothRaf = null;
6602
+ let lastFrameMs = 0;
6603
+ const emit = (target) => {
6604
+ if (!smoothingSec) {
6605
+ onProgress(target);
6606
+ return;
6607
+ }
6608
+ if (smoothed === null) {
6609
+ smoothed = target;
6610
+ onProgress(target);
6611
+ return;
6612
+ }
6613
+ if (smoothRaf !== null) return;
6614
+ lastFrameMs = 0;
6615
+ const step = (nowMs) => {
6616
+ smoothRaf = null;
6617
+ if (destroyed) return;
6618
+ const dtSec = lastFrameMs ? Math.min(0.1, (nowMs - lastFrameMs) / 1e3) : 1 / 60;
6619
+ lastFrameMs = nowMs;
6620
+ const goal = compute();
6621
+ const k = 1 - Math.exp(-dtSec / smoothingSec);
6622
+ smoothed = smoothed + (goal - smoothed) * k;
6623
+ if (Math.abs(goal - smoothed) < SETTLE_EPSILON) smoothed = goal;
6624
+ onProgress(smoothed);
6625
+ if (smoothed !== goal) smoothRaf = requestAnimationFrame(step);
6626
+ };
6627
+ smoothRaf = requestAnimationFrame(step);
6628
+ };
6629
+ let rafId = null;
6501
6630
  const tick = () => {
6502
6631
  rafId = null;
6503
6632
  if (destroyed) return;
6504
- onProgress(compute());
6633
+ emit(compute());
6505
6634
  };
6506
6635
  const schedule = () => {
6507
6636
  if (destroyed || rafId !== null) return;
@@ -6513,7 +6642,8 @@ function createScrollDriver(subject, config, onProgress) {
6513
6642
  let resizeObserver;
6514
6643
  if (typeof ResizeObserver !== "undefined") {
6515
6644
  resizeObserver = new ResizeObserver(schedule);
6516
- resizeObserver.observe(subject);
6645
+ resizeObserver.observe(measured);
6646
+ if (measured !== subject) resizeObserver.observe(subject);
6517
6647
  if (!isRootScroller) resizeObserver.observe(scroller);
6518
6648
  }
6519
6649
  const driver = {
@@ -6527,14 +6657,76 @@ function createScrollDriver(subject, config, onProgress) {
6527
6657
  cancelAnimationFrame(rafId);
6528
6658
  rafId = null;
6529
6659
  }
6660
+ if (smoothRaf !== null) {
6661
+ cancelAnimationFrame(smoothRaf);
6662
+ smoothRaf = null;
6663
+ }
6530
6664
  },
6665
+ // `refresh` is a deliberate JUMP (attach, host relayout) — never eased.
6531
6666
  refresh: () => {
6532
- if (!destroyed) onProgress(compute());
6667
+ if (!destroyed) {
6668
+ smoothed = compute();
6669
+ onProgress(smoothed);
6670
+ }
6533
6671
  }
6534
6672
  };
6535
6673
  driver.refresh();
6536
6674
  return driver;
6537
6675
  }
6676
+ function pinScrollportHeight(svgRoot) {
6677
+ const scroller = findNearestScroller(svgRoot, "y");
6678
+ return scroller ? scroller.clientHeight : document.documentElement.clientHeight;
6679
+ }
6680
+ function applyScrollPin(svgRoot, scroll) {
6681
+ const styled = svgRoot;
6682
+ if (!(scroll == null ? void 0 : scroll.pin) || !styled.style) return () => {
6683
+ };
6684
+ const style = styled.style;
6685
+ const prevPosition = style.position;
6686
+ const prevTop = style.top;
6687
+ style.position = "sticky";
6688
+ const alignFactor = scroll.pinAlign === "center" ? 0.5 : scroll.pinAlign === "bottom" ? 1 : 0;
6689
+ const applyTop = () => {
6690
+ var _a;
6691
+ const extra = (_a = scroll.pinTop) != null ? _a : 0;
6692
+ if (!alignFactor) {
6693
+ style.top = extra + "px";
6694
+ return;
6695
+ }
6696
+ const portSize = pinScrollportHeight(svgRoot);
6697
+ const ownSize = svgRoot.getBoundingClientRect().height;
6698
+ style.top = Math.round((portSize - ownSize) * alignFactor + extra) + "px";
6699
+ };
6700
+ applyTop();
6701
+ let resizeObserver = null;
6702
+ const onWindowResize = alignFactor ? applyTop : null;
6703
+ if (alignFactor) {
6704
+ if (onWindowResize) window.addEventListener("resize", onWindowResize);
6705
+ if (typeof ResizeObserver !== "undefined") {
6706
+ resizeObserver = new ResizeObserver(applyTop);
6707
+ resizeObserver.observe(svgRoot);
6708
+ }
6709
+ }
6710
+ let wrapper = null;
6711
+ const parent = svgRoot.parentElement;
6712
+ if (scroll.pinDistance && scroll.pinDistance > 0 && parent) {
6713
+ wrapper = document.createElement("div");
6714
+ wrapper.setAttribute("data-px-pin", "");
6715
+ wrapper.style.height = scroll.pinDistance * 100 + "vh";
6716
+ parent.insertBefore(wrapper, svgRoot);
6717
+ wrapper.appendChild(svgRoot);
6718
+ }
6719
+ return () => {
6720
+ if (onWindowResize) window.removeEventListener("resize", onWindowResize);
6721
+ resizeObserver == null ? void 0 : resizeObserver.disconnect();
6722
+ style.position = prevPosition;
6723
+ style.top = prevTop;
6724
+ if (wrapper == null ? void 0 : wrapper.parentElement) {
6725
+ wrapper.parentElement.insertBefore(svgRoot, wrapper);
6726
+ wrapper.remove();
6727
+ }
6728
+ };
6729
+ }
6538
6730
 
6539
6731
  // src/PxAnimatorBind.ts
6540
6732
  function finaliseAnimator(animatorConfig, callbacks, make) {
@@ -6561,7 +6753,10 @@ function bindWithEngineChoice(doc, adapter, callbacks, rootElement) {
6561
6753
  if (isScrollTimeline(animatorConfig)) {
6562
6754
  return finaliseAnimator(animatorConfig, callbacks, (cb) => {
6563
6755
  var _a, _b;
6756
+ let unpin = () => {
6757
+ };
6564
6758
  if (animatorConfig.mode !== PxAnimatorMode.frames && ((_a = animatorConfig.scroll) == null ? void 0 : _a.driver) === "native" && rootElement) {
6759
+ unpin = applyScrollPin(rootElement, animatorConfig.scroll);
6565
6760
  const native = createNativeScrollTimeline(rootElement, animatorConfig);
6566
6761
  if (native) {
6567
6762
  const api2 = createWebApiAnimator(
@@ -6571,12 +6766,23 @@ function bindWithEngineChoice(doc, adapter, callbacks, rootElement) {
6571
6766
  animatorConfig.mode === PxAnimatorMode.waapi,
6572
6767
  native
6573
6768
  );
6574
- if (api2) return api2;
6769
+ if (api2) {
6770
+ const destroyNative = api2.destroy.bind(api2);
6771
+ api2.destroy = () => {
6772
+ unpin();
6773
+ destroyNative();
6774
+ };
6775
+ return api2;
6776
+ }
6575
6777
  }
6778
+ unpin();
6779
+ unpin = () => {
6780
+ };
6576
6781
  }
6577
6782
  const api = (animatorConfig.mode !== PxAnimatorMode.frames ? createWebApiAnimator(doc, cb, rootElement, animatorConfig.mode === PxAnimatorMode.waapi) : null) || createFrameLoopAnimator(doc, adapter, cb, rootElement);
6578
6783
  const subject = ((_b = api.getRootElement) == null ? void 0 : _b.call(api)) || rootElement;
6579
6784
  if (subject) {
6785
+ unpin = applyScrollPin(subject, animatorConfig.scroll);
6580
6786
  const totalMs = scrollTotalDurationMs(animatorConfig);
6581
6787
  const driver = createScrollDriver(
6582
6788
  subject,
@@ -6587,6 +6793,7 @@ function bindWithEngineChoice(doc, adapter, callbacks, rootElement) {
6587
6793
  const destroy = api.destroy.bind(api);
6588
6794
  api.destroy = () => {
6589
6795
  driver.destroy();
6796
+ unpin();
6590
6797
  destroy();
6591
6798
  };
6592
6799
  }
@@ -6778,6 +6985,7 @@ if (typeof window !== "undefined") {
6778
6985
  }
6779
6986
  export {
6780
6987
  COLOUR_ATTR_NAMES,
6988
+ MISSING_GLYPH_CLASS_NAME,
6781
6989
  PX_ANIMATOR_DATA_KEY,
6782
6990
  PX_ANIM_ATTR_NAME,
6783
6991
  PX_ANIM_SRC_ATTR_NAME,