-- <<<- ------------------------------------------------------------------------ ------------------------------------------------------------------------ -- -- Animation module: Yet Another Animation Handler for ScriptX. -- By Don Hopkins, Kaleida Labs. -- ------------------------------------------------------------------------ ------------------------------------------------------------------------ -- Animation Implementation Module module AnimationImplementation uses Animation uses ScriptX end in module AnimationImplementation ------------------------------------------------------------------------ -- Animation class definition. class Animation() class variables importMedia -- Import animation hook, function taking media as arg. instance variables animationActive -- Boolean, animate when true, pause when false. -- This can be set any time by startAnimation and -- stopAnimation. animationPaused -- Boolean, pause when true, resume when false. -- This can be set any time by startAnimation and -- stopAnimation. animationSpeed -- Number, speed of animation, 1 is normal. -- This can be set any time, to change the -- animation speed. animationLooping -- Boolean, repeat animation again if true. animationBackAndForth -- Boolean, repeat loop back and forth if true. animationTarget -- Object, target to notify about animation. -- This can be self (especially if you mix in -- the behavior), or another object to delegate to -- for behavior. animationBeginHook -- Function, called when action begins, passed -- three arguments: animationTarget self action. animationEndHook -- Function, called when action ends, passed -- three arguments: animationTarget self action. -- This can set up instance variables for the next -- action sequence. animationFrameHook -- Function, called each frame, passed -- three arguments: animationTarget self frame. animationAction -- Name, currently animating action, defined as -- a key in animationActions. -- Set to undefined after the action has ended. animationFrame -- Number, current frame number in active action, -- or 0 if no active action. animationFirstFrame -- Number, beginning of active action. animationLastFrame -- Number, end of active action. animationActions -- Keyed list, map of action names to opaque -- action descriptions. -- All references are by names, since action -- descriptions are private. -- Must be set by media subclass in init. end Animation.importMedia := (media -> false) method init self {class Animation} #rest args #key \ registration: (new Point) \ active: (true) \ paused: (false) \ speed: (1) \ looping: (false) \ backAndForth: (false) \ target: (undefined) \ beginHook: (undefined) \ endHook: (undefined) \ frameHook: (undefined) -> ( self.animationActive := active self.animationPaused := paused self.animationSpeed := speed self.animationLooping := looping self.animationBackAndForth := backAndForth self.animationTarget := target self.animationBeginHook := beginHook self.animationEndHook := endHook self.animationFrameHook := frameHook self.animationAction := undefined self.animationFrame := undefined self.animationFirstFrame := undefined self.animationLastFrame := undefined apply nextMethod self args ) class method findMediaClass self {class Animation} media -> ( local mediaClass := empty local mediaClassName := media[@MediaClassName] if (mediaClassName != empty) do ( local b := getNameBinding @Animation mediaClassName if (b == false) then ( mediaClass := empty ) else ( mediaClass := value b ) ) if (mediaClass == empty) do ( mediaClass := media[@MediaClass] ) load mediaClass ) class method mixMediaClass self {class Animation} media mixin #rest args -> ( local mediaClass := findMediaClass self media local AnonymousClass class AnonymousClass (mediaClass, mixin) end local o := apply new AnonymousClass media: media args ) class method importFromOpenPanel self {class Animation} -> ( local o := new OpenPanel openFilePanel o if (o.validReply) do ( local fname := o.fileName local f := getLast fname delete fname local dir := spawn theRootDir fname local tc := open TitleContainer \ dir: dir \ path: f \ mode: @readonly local media := tc[@media] if (media != empty) do ( Animation.importMedia media ) ) ) -- Start animating the action. method startAnimation self {class Animation} -> ( self.animationActive := true ) -- Stop animating the action. method stopAnimation self {class Animation} -> ( self.animationActive := false ) -- Pause the animation. method pauseAnimation self {class Animation} -> ( self.animationPaused := true ) -- Resume the animation. method resumeAnimation self {class Animation} -> ( self.animationPaused := false ) -- Return true if currently animating an action. method isAnimating self {class Animation} -> ( return (self.animationActive and (not self.animationPaused) and (self.animationAction != undefined)) ) -- Return a list of the names of actions we know how to do. method actionNames self {class Animation} -> ( local actions := self.animationActions if (isAKindOf actions ExplicitlyKeyedCollection) then ( local a := new Array initialSize: (size actions) growable: false foreachBinding actions (key val arg -> append a key) undefined return a ) else ( return #() ) ) -- Frame Hook method notifyFrame self {class Animation} frame -> ( if (self.animationFrameHook != undefined) do ( self.animationFrameHook self.animationTarget self frame ) ) -- Begin Action Hook method notifyBeginAction self {class Animation} act -> ( startAnimation self if (self.animationBeginHook != undefined) do ( self.animationBeginHook self.animationTarget self act ) ) -- End Action Hook method notifyEndAction self {class Animation} act frame -> ( stopAnimation self if (self.animationEndHook != undefined) do ( self.animationEndHook self.animationTarget self act ) ) -- Finished with action. method finishAction self {class Animation} -> ( cancelAction self ) -- Looping an action. method loopAction self {class Animation} -> ( local firstFrame := self.animationFirstFrame local lastFrame := self.animationLastFrame local frame := self.animationFrame local speed := self.animationSpeed local act := self.animationAction if (self.animationBackAndForth) then ( notifyEndAction self act frame speed := -speed self.animationSpeed := speed if (speed > 0) then ( self.animationFrame := min lastFrame \ (firstFrame + speed) ) else ( self.animationFrame := max firstFrame \ (lastFrame + speed) ) notifyBeginAction self act ) else ( notifyEndAction self act frame if (speed > 0) then ( self.animationFrame := firstFrame ) else ( self.animationFrame := lastFrame ) notifyBeginAction self act ) ) -- Cancel any currently active action. . method cancelAction self {class Animation} -> ( local actionName := self.animationAction if (actionName != undefined) then ( local frame := self.animationFrame self.animationAction := undefined self.animationFrame := undefined stopAnimation self notifyEndAction self actionName frame ) else ( stopAnimation self ) ) -- Start a new action, canceling any ongoing action. method startAction self {class Animation} actionName -> ( if (self.animationAction != undefined) do ( cancelAction self ) setAction self actionName notifyBeginAction self actionName ) -- Setup to begin a named action sequence. method setAction self {class Animation} actionName -> ( self.animationAction := actionName self.animationFrame := self.animationFirstFrame ) -- Display the current animation frame, and advance to the next frame. -- Arrange for this to be called at regular intervals, either by a clock -- callback, or the addChangedRegion method of the presenter. method tickleAnimation self {class Animation} -> ( if ((self.animationActive == false) or (self.animationPaused)) do ( return false ) local actionName := self.animationAction if (actionName == undefined) do (return false) local frame := self.animationFrame if (frame == undefined) do ( cancelAction self return false ) gotoFrame self frame notifyFrame self self.animationFrame frame := self.animationFrame + self.animationSpeed if ((frame > self.animationLastFrame) or (frame < self.animationFirstFrame)) then ( if (self.animationLooping) then ( loopAction self ) else ( finishAction self ) ) else ( self.animationFrame := frame ) return true ) -- Return the current frame of the active action, -- or 0 if there is no active action. method currentFrame self {class Animation} -> ( self.animationFrame ) -- Set the current frame of the active action. method gotoFrame self {class Animation} frame -> ( ) -- Move the animation to the given x,y location. method moveAnimation self {class Animation} x y -> ( self.x := x self.y := y ) -- Translate the animation by the given dx,dy offset. method translateAnimation self {class Animation} dx dy -> ( moveAnimation self (self.x + dx) (self.y + dy) ) ------------------------------------------------------------------------ ------------------------------------------------------------------------ ok -- >>>