Source code for camtasia.effects.behaviors
"""GenericBehaviorEffect — Camtasia's text animation/behaviors system."""
from __future__ import annotations
from typing import Any, Union
from camtasia.types import _BehaviorEffectData, _BehaviorPhaseData
[docs]
class BehaviorPhase:
"""Wraps a single behavior phase dict (``in``, ``center``, or ``out``).
Each phase has ``attributes`` controlling character-level animation
timing and physics, plus ``parameters`` for direction/style keyframes.
Args:
data: The raw phase dict from the behavior effect.
"""
def __init__(self, data: dict[str, Any]) -> None:
self._data: _BehaviorPhaseData = data # type: ignore[assignment]
@property
def data(self) -> _BehaviorPhaseData:
"""The underlying raw dict."""
return self._data
@property
def _attrs(self) -> dict[str, Any]:
return self._data["attributes"]
@property
def name(self) -> str:
"""Behavior name (e.g. ``'reveal'``, ``'none'``)."""
return str(self._attrs["name"])
@name.setter
def name(self, value: str) -> None:
"""Set the behavior name."""
self._attrs["name"] = value
@property
def phase_type(self) -> int:
"""Animation granularity: 0 = whole-object, 1 = per-character."""
return int(self._attrs["type"])
@property
def character_order(self) -> int:
"""Order in which characters animate (e.g. left-to-right, random)."""
return int(self._attrs["characterOrder"])
@character_order.setter
def character_order(self, value: int) -> None:
"""Set the character animation order."""
self._attrs["characterOrder"] = value
@property
def offset_between_characters(self) -> int:
"""Delay between characters in ticks."""
return int(self._attrs.get("offsetBetweenCharacters", 0))
@offset_between_characters.setter
def offset_between_characters(self, value: int) -> None:
"""Set the delay between characters in ticks."""
self._attrs["offsetBetweenCharacters"] = value
@property
def suggested_duration_per_character(self) -> int:
"""Suggested duration per character in ticks."""
return int(self._attrs.get("suggestedDurationPerCharacter", 0))
@suggested_duration_per_character.setter
def suggested_duration_per_character(self, value: int) -> None:
"""Set the suggested duration per character in ticks."""
self._attrs["suggestedDurationPerCharacter"] = value
@property
def overlap_proportion(self) -> int | float | str:
"""Overlap proportion — may be int, float, or string fraction (e.g. ``'1/2'``)."""
return self._attrs.get("overlapProportion", 0) # type: ignore[no-any-return]
@overlap_proportion.setter
def overlap_proportion(self, value: int | float | str) -> None:
"""Set the overlap proportion value."""
self._attrs["overlapProportion"] = value
@property
def movement(self) -> int:
"""Movement enum for animation direction/style."""
return int(self._attrs.get("movement", 0))
@movement.setter
def movement(self, value: int) -> None:
"""Set the movement enum value."""
self._attrs["movement"] = value
@property
def spring_damping(self) -> float:
"""Spring damping coefficient for bounce animations."""
return float(self._attrs.get("springDamping", 0.0))
@spring_damping.setter
def spring_damping(self, value: float) -> None:
"""Set the spring damping coefficient."""
self._attrs["springDamping"] = value
@property
def spring_stiffness(self) -> float:
"""Spring stiffness coefficient for bounce animations."""
return float(self._attrs.get("springStiffness", 0.0))
@spring_stiffness.setter
def spring_stiffness(self, value: float) -> None:
"""Set the spring stiffness coefficient."""
self._attrs["springStiffness"] = value
@property
def bounce_bounciness(self) -> float:
"""Bounciness factor for bounce animations."""
return float(self._attrs.get("bounceBounciness", 0.0))
@bounce_bounciness.setter
def bounce_bounciness(self, value: float) -> None:
"""Set the bounciness factor."""
self._attrs["bounceBounciness"] = value
@property
def parameters(self) -> dict[str, Any]:
"""Raw parameters dict (direction keyframes, etc.)."""
return self._data.get("parameters", {})
def __repr__(self) -> str:
"""Return a developer-friendly string representation."""
return f"BehaviorPhase(name={self.name!r}, type={self.phase_type})"
[docs]
class GenericBehaviorEffect:
"""Wraps a ``GenericBehaviorEffect`` dict — Camtasia's text behavior system.
Unlike regular effects, behavior effects have a ``_type`` field set to
``'GenericBehaviorEffect'`` and contain three animation phases
(``in``, ``center``, ``out``) instead of flat parameters.
Args:
data: The raw behavior effect dict from the project JSON.
"""
def __init__(self, data: dict[str, Any]) -> None:
self._data: _BehaviorEffectData = data # type: ignore[assignment]
@property
def data(self) -> _BehaviorEffectData:
"""The underlying raw dict."""
return self._data
@property
def effect_name(self) -> str:
"""Effect name identifier."""
return self._data["effectName"]
@property
def bypassed(self) -> bool:
"""Whether the effect is bypassed (disabled)."""
return bool(self._data.get("bypassed", False))
@bypassed.setter
def bypassed(self, value: bool) -> None:
"""Set whether the effect is bypassed."""
self._data["bypassed"] = value
@property
def start(self) -> int:
"""Start time in ticks."""
return int(self._data["start"])
@start.setter
def start(self, value: int) -> None:
"""Set the start time in ticks."""
self._data["start"] = value
@property
def duration(self) -> int:
"""Duration in ticks."""
return int(self._data["duration"])
@duration.setter
def duration(self, value: int) -> None:
"""Set the duration in ticks."""
self._data["duration"] = value
@property
def entrance(self) -> BehaviorPhase:
"""The ``in`` (entrance) phase."""
return BehaviorPhase(self._data["in"]) # type: ignore[typeddict-item]
@property
def center(self) -> BehaviorPhase:
"""The ``center`` (sustain/loop) phase."""
return BehaviorPhase(self._data["center"]) # type: ignore[typeddict-item]
@property
def exit(self) -> BehaviorPhase:
"""The ``out`` (exit) phase."""
return BehaviorPhase(self._data["out"]) # type: ignore[typeddict-item]
@property
def preset_name(self) -> str:
"""Preset name from metadata (e.g. ``'Reveal'``)."""
return self._data.get("metadata", {}).get("presetName", "") # type: ignore[no-any-return]
def __repr__(self) -> str:
"""Return a developer-friendly string representation."""
return f"GenericBehaviorEffect(name={self.effect_name!r}, preset={self.preset_name!r})"