Changes between Version 13 and Version 14 of animations


Ignore:
Timestamp:
04/16/2021 08:57:29 AM (14 months ago)
Author:
marcel.taeumel
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • animations

    v13 v14  
    1 [[PageOutline(1-3)]]
    2 
    3 = Animations =
    4 
    5 In its current development state, the Morphic implementation of Squeak does not support an extensible mechanism that allows visually appealing transitions whenever a morph's state changes, e.g., positon, rotation, color.
    6 
    7 This project provides such an extension to Morphic with the following key-features:
    8 
    9  * respect timeliness no matter how high the cpu load is
    10  * support any property of a morph that has an accessor like `#position:`
    11  * allow graphic transitions even without the need to change the state of a morph
    12 
    13 = How to Install =
    14 
    15 {{{
    16 #!div class="wiki_infotable" style="float:right;"
    17 ||'''Environment'''|| ||
    18 || [[Image(media/icons/custom:squeak_16.png, title="Recommended Squeak Version", nolink, right)]] || 4.1, 4.2 Alpha ||
    19 || [[Image(media/icons/silk:application_home.png, title="Recommended Squeak VM Version", nolink, right)]] || 4.0.2 (Win), ? (Mac) ||
    20 || [[Image(media/icons/silk:cog.png, title="Recommended Cog VM Version", nolink, right)]] || r2316 ||
    21 ||'''Sources'''|| ||
    22 || [[Image(media/icons/silk:script_gear.png, title="Metacello Configuration", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/MetacelloRepository/ ConfigurationOfAnimations] ||
    23 || [[Image(media/icons/silk:database.png, title="Repository", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/SwaUtilities/ SwaUtilities] ||
    24 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || Animations ||
    25 ||'''Misc'''|| ||
    26 || [[Image(media/icons/silk:world.png, title="Website", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/SwaUtilities.html SwaUtilities@SqueakSource] ||
    27 }}}
    28 
    29 Just load the `Animations` package into your Squeak image.
    30 
    31 '''Warning:''' Once installed, unloading will probably cause your image to stop rendering, which means it will hang. That is because some very important messages (like `WorldState>>doOneCycleFor:`) were overridden and could get lost on unloading.
    32 
    33 There are the following sub-packages:
    34  * Animations-Core ... core implementation
    35  * Animations-Canvas ... canvases to be used in graphics animations
    36  * Animations-Animations ... some example graphics animations
    37  * Animations-Tests ... tests for all packages
    38 
    39 {{{
    40 #!div style="clear:both;"
    41 }}}
    42 
    43 = How to Use =
    44 
    45 == Simple Example ==
    46 
    47 Open a workspace and create a new morph:
    48 
    49 {{{
    50 | myMorph |
    51 myMorph := Morph new topLeft: 100@100; extent: 400@400; openInWorld.
    52 }}}
    53 
    54 Now let this morph disappear. Try the close all unnecessary morphs for performance reasons:
    55 
    56 {{{
    57 myMorph fadeOut.
    58 }}}
    59 
    60 It's gone! Now get it back:
    61 
    62 {{{
    63 myMorph fadeIn.
    64 }}}
    65 
    66 This animation is about 200 milliseconds. If your Squeak image is quite busy it will be not that smooth.
    67 
    68 == Basic Animation Concept ==
    69 
    70 In principle, an animation is a timer that has a duration and can run several times to produce loops.
    71 
    72 {{{
    73 AnimAnimation new
    74    duration: 500; "milliseconds"
    75    start.
    76 }}}
    77 
    78 You may inspect this animation and look at `#currentTime` but nothing will change. There are no extra processes involved to keep the animation running. You need to call `#updateCurrentTime:` with an increasing time value frequently to achieve this.
    79 
    80 Animations were designed to be used in the Squeak UI process. Therefore, the best reference time to be used is:
    81 
    82 {{{
    83 WorldState lastCycleTime.
    84 }}}
    85 
    86 One possibility (there is a better one) could be to use morph's stepping or a custom process:
    87 
    88 {{{
    89 "Using morph stepping."
    90 MyMorph>>stepTime
    91    ^ 16 "60 steps per second"
    92 
    93 MyMorph>>step
    94    myAnimations do: [:anim | anim updateCurrentTime].
    95 
    96 "Using an extra process."
    97 [
    98    myAnimations do: [:anim | anim updateCurrentTime].
    99    (Delay forMilliseconds: 16) wait. "Avoid high load. Get 60 cycles per second."
    100 ] fork.
    101 }}}
    102 
    103 Having this, the animation `AnimAnimation` handles just simple time interpretation. You can control the animation with `#start`, `#stop`, `#pause`, `#resume`. Here are some other examples:
    104 
    105 {{{
    106 AnimAnimation
    107    duration: 500; "Always needed!"
    108    loopCount: 5;
    109    direction: #backward; "Not used in base class."
    110    start.
    111 
    112 AnimAnimation
    113    duration: 1000;
    114    loopCount: -1; "Infinite."
    115    start: #keepWhenFinished. "Memory management. Not needed for infinite animations.
    116                               Not used in base class."
    117 }}}
    118 
    119 You can perform an action after the animation is finished using a block:
    120 
    121 {{{
    122 AnimAnimation
    123    duration: 500;
    124    finishBlock: [Transcript cr; show: 'Animation finished!'];
    125    start.
    126 }}}
    127 
    128 == Variant Animations ==
    129 
    130 Variant animations add value interpolation behaviour to animations. There is a start and an end value. During one animation loop `#currentValue` changes in this range including the start and the end value itself.
    131 
    132 {{{
    133 AnimVariantAnimation new
    134    duration: 500;
    135    startValue: 1;
    136    endValue: 10;
    137    start.
    138 }}}
    139 
    140 Having `#updateCurrentTime:` called frequently somehow, `#updateCurrentValue` can be called frequently too to trigger a callback that allows variant animations to change their internal state or perform other operations:
    141 
    142 {{{
    143 MyVariantAnimation>>updateCurrentValue: newValue
    144    Transcript cr; show: newValue asString.
    145 }}}
    146 
    147 The value interpolation uses an easing curve that maps a value between 0.0 and 1.0 to another value between 0.0 and 1.0 or maybe more. This can be used to modify the normal linear interpolation and get some more pleasing effects. Overshooting is possible but 1.0 should map to 1.0 because the loop ends there. Here is an example for a custom easing curve:
    148 
    149 {{{
    150 MyEasingCurve>>valueForProgress: aFloat
    151    ^ aFloat * aFloat
    152 
    153 AnimVariantAnimation new
    154    duration: 500;
    155    startValue: 1;
    156    endValue: 10;
    157    easingCurve: MyEasingCurve new;
    158    start.
    159 }}}
    160 
    161 Variant animations make use of the `#direction` attribute which means the value goes from `#endValue` to `#startValue` if backwards. An offset can be specified to allow relative value changes:
    162 
    163 {{{
    164 AnimVariantAnimation new
    165    duration: 500;
    166    startValue: 1@1;
    167    endValue: 10@10;
    168    offsetBlock: [ActiveHand position]; "or just #offset:"
    169    start.
    170 }}}
    171 
    172 == Property Animations ==
    173 
    174 Property animations are variant animations that are bound to an object and a property. The `#updateCurrentValue:` callback will try to send a keyword message to the object with one argument using the property name:
    175 
    176 {{{
    177 AnimPropertyAnimation new
    178    duration: 500;
    179    target: myMorph;
    180    property: #position; "There should be a message called #position:."
    181    startValue: 10@10;
    182    endValue: 100@100;
    183    start.
    184 }}}
    185 
    186 == Let them run! — How to register Animations ==
    187 
    188 Animations are meant to be used in the Squeak UI process. There is a reference time called `WorldState class>>lastCycleTime` and some animations can use the world's main loop to keep themselves running. This is achieved by '''registering''' the animation in the `AnimAnimationRegistry`:
    189 
    190 {{{
    191 AnimPropertyAnimation new
    192    duration: 500;
    193    target: myMorph;
    194    property: #position;
    195    startValue: 10@10;
    196    endValue: 100@100;
    197    start: #deleteWhenFinished; "Automatic registry clean-up. No need to unregister."
    198    register. "Add to animation registry."
    199 }}}
    200 
    201 Only `AnimPropertyAnimation` and `AnimGraphicsAnimation` can be registered.
    202 
    203 If you want to keep animations after they finished, you need to unregister them manually, e.g., if it has stopped:
    204 {{{
    205 myAnimation isStopped
    206    ifTrue: [myAnimation unregister].
    207 }}}
    208 
    209 === Using Processes ===
    210 
    211 The animation registry is thread-safe which means that `#register` and `#unregister` operations are secured and can be called from within any process. However, that process should have a higher priority than the Squeak UI process. Otherwise it could be problematic to acquire the mutex because every world cycle needs it too.
    212 
    213 == Graphics Animations ==
    214 
    215 Graphics animations are variant animations that modify the visual appearance of a morph and all its submorphs doing simple color mappings. ''Graphic animations need to be registered.''
    216 
    217 {{{
    218 AnimAlphaBlendAnimation new
    219    morph: myMorph;
    220    duration: 500;
    221    startValue: 0.0;
    222    endValue: 1.0;
    223    start;
    224    register. "Always needed for graphics animations!"
    225 }}}
    226 
    227 There is no need to reimplement `#updateCurrentValue:` but `#transformedCanvas:` which returns a custom `AnimColorMappingCanvas` to be used during the drawing routine of morphs:
    228 
    229 {{{
    230 MyAlphaBlendingAnimation>>transformedCanvas: aCanvas
    231    ^ (MyAlphaBlendingCanvas
    232       on: aCanvas)
    233       alpha: self currentValue "Interpolated alpha value."
    234 }}}
    235 
    236 Having this, a simple fade-out animation for morphs can be implemented as follows:
    237 
    238 {{{
    239 MyMorph>>fadeOut
    240    AnimAlphaBlendAnimation new
    241       morph: self;
    242       startValue: 1.0; "totally visible"
    243       endValue: 0.0; "invisible"
    244       duration: 200;
    245       finishBlock: [self hide]; "Executed when animation finished."
    246       register;
    247       start: #deleteWhenFinished.
    248 }}}
    249 
    250 Color mappings apply to all submorphs in a morph. To prevent a morph from being color-mapped by its owner use the property `#ignoresColorMappings`.
    251 
    252 If you want to hold a certain color mapping state, you must not delete an animation when it has finished. Otherwise the color mapping will disappear. An example would be to gray-out or darken a morph using `AnimBrightnessAnimation` or `AnimGrayscaleAnimation`.
    253 
    254 = How to Extend =
    255 
    256 Take a look at:
    257 
    258  * memory management of `AnimAnimationRegistry`
    259     * `#garbageCollect`
    260  * `Morph>>#fullDrawOn:`
    261  * `WorldState>>#doOneCycleFor:`
    262 
    263 = Acknowledgments =
    264 
    265 The ''Animations'' package was inspired by the animation framework in the [http://qt.nokia.com Nokia Qt Framework].
    266 
    267 [[Image(media/icons/silk:user.png, title="Target Group", nolink)]] To date the following people contributed to this project:
    268  * Marcel Taeumel
     1This project, including all sources and documentation, moved to [https://github.com/hpi-swa/animations]