[{"data":1,"prerenderedAt":2145},["ShallowReactive",2],{"article_list_ui_":3},[4,389],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"image":11,"publishDate":12,"tags":13,"excerpt":10,"body":17,"_type":380,"_id":381,"_source":382,"_file":383,"_stem":384,"_extension":385,"author":386},"/bporter/2020-10/reanimated","2020-10",false,"","Re-animated","Last year, I posted here about an animation control framework called 'Friz' that works within the JUCE Application Framework.","/bporter/2020-10/img/module.png","2020-10-01",[14,15,16],"juce","ui","c++",{"type":18,"children":19,"toc":373},"root",[20,48,53,79,145,180,185,192,197,202,210,215,243,248,254,277,343,349,362,367],{"type":21,"tag":22,"props":23,"children":24},"element","p",{},[25,28,37,39,46],{"type":26,"value":27},"text","Last year, I ",{"type":21,"tag":29,"props":30,"children":34},"a",{"href":31,"rel":32},"https://artandlogic.com/2019/09/friz-and-the-illusion-of-life/",[33],"nofollow",[35],{"type":26,"value":36},"posted here",{"type":26,"value":38}," about an animation control framework called 'Friz' that works within the ",{"type":21,"tag":29,"props":40,"children":43},{"href":41,"rel":42},"https://www.juce.com",[33],[44],{"type":26,"value":45},"JUCE Application Framework",{"type":26,"value":47},".",{"type":21,"tag":22,"props":49,"children":50},{},[51],{"type":26,"value":52},"As I said in that post:",{"type":21,"tag":54,"props":55,"children":56},"blockquote",{},[57],{"type":21,"tag":22,"props":58,"children":59},{},[60,62,68,70,77],{"type":26,"value":61},"I sat down to sketch some things out and ended up making a small framework for generating dynamic data that can be used to animate pretty much any aspect of the user interface of a program written using the ",{"type":21,"tag":29,"props":63,"children":65},{"href":41,"rel":64},[33],[66],{"type":26,"value":67},"JUCE application framework",{"type":26,"value":69}," (which I've written about here ",{"type":21,"tag":29,"props":71,"children":74},{"href":72,"rel":73},"https://artandlogic.com/?s=juce",[33],[75],{"type":26,"value":76},"many times",{"type":26,"value":78}," before), and has the goals of being:",{"type":21,"tag":54,"props":80,"children":81},{},[82],{"type":21,"tag":83,"props":84,"children":85},"ul",{},[86,98,108,127],{"type":21,"tag":87,"props":88,"children":89},"li",{},[90,96],{"type":21,"tag":91,"props":92,"children":93},"strong",{},[94],{"type":26,"value":95},"Lightweight",{"type":26,"value":97},"—if there aren't any animations in progress, there's no runtime overhead.",{"type":21,"tag":87,"props":99,"children":100},{},[101,106],{"type":21,"tag":91,"props":102,"children":103},{},[104],{"type":26,"value":105},"Flexible",{"type":26,"value":107},"—it's easy to add new types of animation curves if you want to, or to chain multiple curves together into a more complex sequence.",{"type":21,"tag":87,"props":109,"children":110},{},[111,116,118,125],{"type":21,"tag":91,"props":112,"children":113},{},[114],{"type":26,"value":115},"Decoupled",{"type":26,"value":117},"—it doesn't need or want to know anything about your application. When your code creates an animation effect, you pass it a pair of ",{"type":21,"tag":119,"props":120,"children":122},"code",{"className":121},[],[123],{"type":26,"value":124},"std::function",{"type":26,"value":126}," objects; one to handle updates for each frame, and another one to handle the completion of the effect.",{"type":21,"tag":87,"props":128,"children":129},{},[130,135,137,144],{"type":21,"tag":91,"props":131,"children":132},{},[133],{"type":26,"value":134},"Modern",{"type":26,"value":136},"—written using current C++ techniques (defined for our purposes as C++11 and later). I spent some time away from C++ and have come back to find the language has undergone some seroius changes that require me to consciously update my habits, making me feel like the programmer version of SNL's ",{"type":21,"tag":29,"props":138,"children":141},{"href":139,"rel":140},"https://en.wikipedia.org/wiki/Unfrozen_Caveman_Lawyer",[33],[142],{"type":26,"value":143},"Unfrozen Caveman Lawyer",{"type":26,"value":47},{"type":21,"tag":54,"props":146,"children":147},{},[148],{"type":21,"tag":22,"props":149,"children":150},{},[151,153,160,162,169,171,178],{"type":26,"value":152},"As the code began to take shape, I decided to name it after the great Warner Bros. animator/director ",{"type":21,"tag":29,"props":154,"children":157},{"href":155,"rel":156},"https://en.wikipedia.org/wiki/Friz_Freleng",[33],[158],{"type":26,"value":159},"Friz Freling",{"type":26,"value":161},". I would like to have called it 'Chuck' after his colleague ",{"type":21,"tag":29,"props":163,"children":166},{"href":164,"rel":165},"https://en.wikipedia.org/wiki/Chuck_Jones",[33],[167],{"type":26,"value":168},"Chuck Jones",{"type":26,"value":170},", but \"",{"type":21,"tag":29,"props":172,"children":175},{"href":173,"rel":174},"https://chuck.cs.princeton.edu/",[33],[176],{"type":26,"value":177},"ChucK",{"type":26,"value":179},"\" already means something else in the electronic music software world.",{"type":21,"tag":22,"props":181,"children":182},{},[183],{"type":26,"value":184},"I found the time this past weekend to update the Friz code with two things that I've had on my list for a long time:",{"type":21,"tag":186,"props":187,"children":189},"h2",{"id":188},"juce-module",[190],{"type":26,"value":191},"JUCE Module",{"type":21,"tag":22,"props":193,"children":194},{},[195],{"type":26,"value":196},"The library is now packaged as a JUCE module, so it's straightforward to include in a JUCE project without needing to manually copy files around. In reality, I should have done this from day one (and now that I understand how trivial it is to work with JUCE modules, that's how I'll approach anything like this in the future).",{"type":21,"tag":22,"props":198,"children":199},{},[200],{"type":26,"value":201},"To use friz as a module, get it from the GitHub repo (see below) and then use the Projucer to add it to your project from that directory:",{"type":21,"tag":22,"props":203,"children":204},{},[205],{"type":21,"tag":206,"props":207,"children":209},"img",{"alt":208,"src":11},"projucer view",[],{"type":21,"tag":22,"props":211,"children":212},{},[213],{"type":26,"value":214},"Once you've done that, you can just add",{"type":21,"tag":216,"props":217,"children":221},"pre",{"className":218,"code":219,"language":220,"meta":8,"style":8},"language-cpp shiki shiki-themes github-light github-dark","#include \"friz.h\"\n","cpp",[222],{"type":21,"tag":119,"props":223,"children":224},{"__ignoreMap":8},[225],{"type":21,"tag":226,"props":227,"children":230},"span",{"class":228,"line":229},"line",1,[231,237],{"type":21,"tag":226,"props":232,"children":234},{"style":233},"--shiki-default:#D73A49;--shiki-dark:#F97583",[235],{"type":26,"value":236},"#include",{"type":21,"tag":226,"props":238,"children":240},{"style":239},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[241],{"type":26,"value":242}," \"friz.h\"\n",{"type":21,"tag":22,"props":244,"children":245},{},[246],{"type":26,"value":247},"...and start using it as described in my earlier post.",{"type":21,"tag":186,"props":249,"children":251},{"id":250},"new-parametric-easing-curves",[252],{"type":26,"value":253},"New 'parametric' easing curves",{"type":21,"tag":22,"props":255,"children":256},{},[257,259,266,268,275],{"type":26,"value":258},"Shortly after releasing the original version of this last year, I stumbled on an ",{"type":21,"tag":29,"props":260,"children":263},{"href":261,"rel":262},"https://easings.net",[33],[264],{"type":26,"value":265},"excellent site",{"type":26,"value":267}," that described itself as an \"Easing Curve Cheat Sheet\" that shows examples of a whole bunch of curves that are commonly found in the wild—libraries and frameworks like jQuery, Cinder, and Flutter all support a set of curves that all point back to functions defined by ",{"type":21,"tag":29,"props":269,"children":272},{"href":270,"rel":271},"http://robertpenner.com/easing/",[33],[273],{"type":26,"value":274},"Robert Penner",{"type":26,"value":276},", whose site you should check out.",{"type":21,"tag":22,"props":278,"children":279},{},[280,282,288,290,295,297,303,305,311,313,318,320,326,328,333,335,341],{"type":26,"value":281},"The new ",{"type":21,"tag":119,"props":283,"children":285},{"className":284},[],[286],{"type":26,"value":287},"Parametric",{"type":26,"value":289}," class may be created to implement any of those Penner curves, or you can use it to follow any curve that you design, by passing in a function (whether a ",{"type":21,"tag":119,"props":291,"children":293},{"className":292},[],[294],{"type":26,"value":124},{"type":26,"value":296},", lambda, or plain old C function) that accepts a single ",{"type":21,"tag":119,"props":298,"children":300},{"className":299},[],[301],{"type":26,"value":302},"float",{"type":26,"value":304}," parameter in the range ",{"type":21,"tag":119,"props":306,"children":308},{"className":307},[],[309],{"type":26,"value":310},"[0..1]",{"type":26,"value":312}," and returns a ",{"type":21,"tag":119,"props":314,"children":316},{"className":315},[],[317],{"type":26,"value":302},{"type":26,"value":319}," that is typically ",{"type":21,"tag":321,"props":322,"children":323},"em",{},[324],{"type":26,"value":325},"but not necessarily",{"type":26,"value":327}," also in the ",{"type":21,"tag":119,"props":329,"children":331},{"className":330},[],[332],{"type":26,"value":310},{"type":26,"value":334}," range. The animation framework will interpolate that output value between the start/end values used in your animation. Since these curves may have overshoot on either end (see for example, the ",{"type":21,"tag":119,"props":336,"children":338},{"className":337},[],[339],{"type":26,"value":340},"easeInOutElastic",{"type":26,"value":342}," curve, which has fairly complex under- and overshoot on both ends of the curve), the code that handles your animation must work correctly when passed values outside of your start/stop values (where only you can define what 'correctly' means other than 'don't crash').",{"type":21,"tag":186,"props":344,"children":346},{"id":345},"downloadlicenceetc",[347],{"type":26,"value":348},"Download/Licence/etc.",{"type":21,"tag":22,"props":350,"children":351},{},[352,354,360],{"type":26,"value":353},"You can get the library at ",{"type":21,"tag":29,"props":355,"children":358},{"href":356,"rel":357},"https://github.com/bgporter/animator",[33],[359],{"type":26,"value":356},{"type":26,"value":361},", and it's MIT licensed. Open source, closed source, it's all fine by me as long as you only use it for good and not evil.",{"type":21,"tag":22,"props":363,"children":364},{},[365],{"type":26,"value":366},"Go make cool stuff.",{"type":21,"tag":368,"props":369,"children":370},"style",{},[371],{"type":26,"value":372},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":8,"searchDepth":374,"depth":374,"links":375},3,[376,378,379],{"id":188,"depth":377,"text":191},2,{"id":250,"depth":377,"text":253},{"id":345,"depth":377,"text":348},"markdown","content:bporter:2020-10:reanimated.md","content","bporter/2020-10/reanimated.md","bporter/2020-10/reanimated","md",{"user":387,"name":388},"bporter","Brett Porter",{"_path":390,"_dir":391,"_draft":7,"_partial":7,"_locale":8,"title":392,"description":393,"excerpt":393,"image":394,"publishDate":395,"tags":396,"body":397,"_type":380,"_id":2141,"_source":382,"_file":2142,"_stem":2143,"_extension":385,"author":2144},"/bporter/2019-3/animator","2019-3","Friz: A flexible animation controller for JUCE","As is often the case, I found myself working on a personal project and had some UI elements that really wanted to have some life to them on the screen.","/bporter/2019-3/img/animator.png","2019-03-01",[14,15,16],{"type":18,"children":398,"toc":2120},[399,411,424,429,443,448,453,458,467,472,488,535,557,563,568,593,598,605,610,615,623,629,650,661,666,676,681,688,698,703,716,724,734,739,747,757,762,767,774,781,791,796,810,815,893,898,1718,1731,1736,1746,1759,1774,1787,1798,1819,1824,1835,1854,1859,1887,1897,1909,1914,1919,1966,1971,2018,2030,2036,2054,2076,2090,2095,2101,2106,2111,2116],{"type":21,"tag":22,"props":400,"children":401},{},[402,404,409],{"type":26,"value":403},"As is often the case, I found myself working on a personal project and had some UI elements that ",{"type":21,"tag":321,"props":405,"children":406},{},[407],{"type":26,"value":408},"really",{"type":26,"value":410}," wanted to have some life to them on the screen.",{"type":21,"tag":22,"props":412,"children":413},{},[414,416,422],{"type":26,"value":415},"I tucked into the JUCE documentation expecting to see something that I could use to easily add some personality to the interface, and...didn't find what I was looking for. There's a ",{"type":21,"tag":119,"props":417,"children":419},{"className":418},[],[420],{"type":26,"value":421},"ComponentAnimator",{"type":26,"value":423}," class that supports moving a single component from one set of coordinates/bounds to another linearly, and also modify the component's alpha value to have it fade in or out.",{"type":21,"tag":22,"props":425,"children":426},{},[427],{"type":26,"value":428},"I was looking for something...more expressive.",{"type":21,"tag":22,"props":430,"children":431},{},[432,434,441],{"type":26,"value":433},"Earlier this year, I read Ge Wang's book \"Artful Design\" (see my review ",{"type":21,"tag":29,"props":435,"children":438},{"href":436,"rel":437},"https://artandlogic.com/2019/02/book-review-artful-design/",[33],[439],{"type":26,"value":440},"here",{"type":26,"value":442},") and it's occupied a larger than expected chunk of my brain for a long time, especially some of the topics covered there in Chapter 3 on Visual Design.",{"type":21,"tag":22,"props":444,"children":445},{},[446],{"type":26,"value":447},"The ideas presented there on animation and design and interactivity resonated so strongly with me at least partly because I've always been a student and fan of animation as an art form, whether classic-era hand drawn or eventually computer-created. I have vivid memories from my time in college studying to become a composer of the look of shock and disappointment on my professor's face when I told him that most of my thinking about musical structure had its roots in the Warner Bros. cartoons of Chuck Jones.",{"type":21,"tag":22,"props":449,"children":450},{},[451],{"type":26,"value":452},"Somewhere in my house I still have an old copy of the book \"The Illusion of Life,\" written by Frank Thomas and Ollie Johnston, two of Disney's \"Nine Old Men,\" the animators who led the creation of Disney's post-war feature films. In that book they call out some principles that I wanted to be able to express at least in some small ways in my software.",{"type":21,"tag":22,"props":454,"children":455},{},[456],{"type":26,"value":457},"This video is a quick look at them:",{"type":21,"tag":22,"props":459,"children":460},{},[461],{"type":21,"tag":29,"props":462,"children":465},{"href":463,"rel":464},"https://www.youtube.com/watch?v=yiGY0qiy8fY",[33],[466],{"type":26,"value":463},{"type":21,"tag":22,"props":468,"children":469},{},[470],{"type":26,"value":471},"Not all of them are directly applicable to software that's restricting itself to living in a 2D plane, but let's take them as an inspiration, and something to aspire to.",{"type":21,"tag":22,"props":473,"children":474},{},[475,476,481,482,487],{"type":26,"value":61},{"type":21,"tag":29,"props":477,"children":479},{"href":41,"rel":478},[33],[480],{"type":26,"value":67},{"type":26,"value":69},{"type":21,"tag":29,"props":483,"children":485},{"href":72,"rel":484},[33],[486],{"type":26,"value":76},{"type":26,"value":78},{"type":21,"tag":83,"props":489,"children":490},{},[491,499,507,521],{"type":21,"tag":87,"props":492,"children":493},{},[494,498],{"type":21,"tag":91,"props":495,"children":496},{},[497],{"type":26,"value":95},{"type":26,"value":97},{"type":21,"tag":87,"props":500,"children":501},{},[502,506],{"type":21,"tag":91,"props":503,"children":504},{},[505],{"type":26,"value":105},{"type":26,"value":107},{"type":21,"tag":87,"props":508,"children":509},{},[510,514,515,520],{"type":21,"tag":91,"props":511,"children":512},{},[513],{"type":26,"value":115},{"type":26,"value":117},{"type":21,"tag":119,"props":516,"children":518},{"className":517},[],[519],{"type":26,"value":124},{"type":26,"value":126},{"type":21,"tag":87,"props":522,"children":523},{},[524,528,529,534],{"type":21,"tag":91,"props":525,"children":526},{},[527],{"type":26,"value":134},{"type":26,"value":136},{"type":21,"tag":29,"props":530,"children":532},{"href":139,"rel":531},[33],[533],{"type":26,"value":143},{"type":26,"value":47},{"type":21,"tag":22,"props":536,"children":537},{},[538,539,544,545,550,551,556],{"type":26,"value":152},{"type":21,"tag":29,"props":540,"children":542},{"href":155,"rel":541},[33],[543],{"type":26,"value":159},{"type":26,"value":161},{"type":21,"tag":29,"props":546,"children":548},{"href":164,"rel":547},[33],[549],{"type":26,"value":168},{"type":26,"value":170},{"type":21,"tag":29,"props":552,"children":554},{"href":173,"rel":553},[33],[555],{"type":26,"value":177},{"type":26,"value":179},{"type":21,"tag":186,"props":558,"children":560},{"id":559},"orbital-view",[561],{"type":26,"value":562},"Orbital View",{"type":21,"tag":22,"props":564,"children":565},{},[566],{"type":26,"value":567},"At the outermost level, all we want to be able to do is:",{"type":21,"tag":83,"props":569,"children":570},{},[571,576,581],{"type":21,"tag":87,"props":572,"children":573},{},[574],{"type":26,"value":575},"define one or more values that will change from one value to another value...",{"type":21,"tag":87,"props":577,"children":578},{},[579],{"type":26,"value":580},"...over some period of time...",{"type":21,"tag":87,"props":582,"children":583},{},[584,586,591],{"type":26,"value":585},"...using some set of curves that describe ",{"type":21,"tag":321,"props":587,"children":588},{},[589],{"type":26,"value":590},"how",{"type":26,"value":592}," the values change over time.",{"type":21,"tag":22,"props":594,"children":595},{},[596],{"type":26,"value":597},"It's just a question of how to create those pieces and plug them together. We'll start looking at the pieces from the bottom up, with a look at the curves we support, using a demo application that lets us play with things.",{"type":21,"tag":22,"props":599,"children":600},{},[601],{"type":21,"tag":206,"props":602,"children":604},{"alt":603,"src":394},"The Animator",[],{"type":21,"tag":22,"props":606,"children":607},{},[608],{"type":26,"value":609},"The Animator is mostly a blank window that you can click inside to create randomly colored squares that will animate away from the mouse using one of the pre-defined curves. After the animation of a square completes, the square will animate its color to fade away, until it disappears.",{"type":21,"tag":22,"props":611,"children":612},{},[613],{"type":26,"value":614},"The control panel on the right lets you tweak parameters for each of the animation types to explore the different behaviors. The control panel is itself animated:",{"type":21,"tag":22,"props":616,"children":617},{},[618],{"type":21,"tag":206,"props":619,"children":622},{"alt":620,"src":621},"panel","/bporter/2019-3/img/panel.mp4",[],{"type":21,"tag":186,"props":624,"children":626},{"id":625},"available-curves",[627],{"type":26,"value":628},"Available Curves",{"type":21,"tag":22,"props":630,"children":631},{},[632,634,640,642,648],{"type":26,"value":633},"All of the classes that are used to generate animated data are derived from the ",{"type":21,"tag":119,"props":635,"children":637},{"className":636},[],[638],{"type":26,"value":639},"friz::AnimatedValue",{"type":26,"value":641}," class (which you can also derive classes from to define any behavior makes sense for your application). Right now, the following are available as part of the ",{"type":21,"tag":119,"props":643,"children":645},{"className":644},[],[646],{"type":26,"value":647},"friz",{"type":26,"value":649}," module:",{"type":21,"tag":651,"props":652,"children":654},"h3",{"id":653},"constant",[655],{"type":21,"tag":119,"props":656,"children":658},{"className":657},[],[659],{"type":26,"value":660},"Constant",{"type":21,"tag":22,"props":662,"children":663},{},[664],{"type":26,"value":665},"Generates a single value for the duration of the effect. Perhaps it's not obviously useful to be able to generate unchanging values in an animation, this can be useful when building up sequences of multiple effects or when controlling the regeneration of cyclical effects.",{"type":21,"tag":651,"props":667,"children":669},{"id":668},"linear",[670],{"type":21,"tag":119,"props":671,"children":673},{"className":672},[],[674],{"type":26,"value":675},"Linear",{"type":21,"tag":22,"props":677,"children":678},{},[679],{"type":26,"value":680},"Performs a simple linear interpolation between two values over a specified amount of time.",{"type":21,"tag":22,"props":682,"children":683},{},[684],{"type":21,"tag":206,"props":685,"children":687},{"alt":668,"src":686},"/bporter/2019-3/img/linear.mp4",[],{"type":21,"tag":651,"props":689,"children":691},{"id":690},"easein",[692],{"type":21,"tag":119,"props":693,"children":695},{"className":694},[],[696],{"type":26,"value":697},"EaseIn",{"type":21,"tag":22,"props":699,"children":700},{},[701],{"type":26,"value":702},"Accelerates quickly away from the start value, decelerates smoothly into the end value. The amount of time for the animation is not specified, instead your code specifies a tolerance of how close to the end value it needs to be before the effect is complete.",{"type":21,"tag":22,"props":704,"children":705},{},[706,708,714],{"type":26,"value":707},"In his ",{"type":21,"tag":119,"props":709,"children":711},{"className":710},[],[712],{"type":26,"value":713},"Artful Design",{"type":26,"value":715}," book, Dr Wang refers to this as \"Zeno's Interpolator\" because it keeps moving some fraction of the way between the current value and the end value, never quite getting there. Our tolerance argument keeps us from calculating ever smaller slices.",{"type":21,"tag":22,"props":717,"children":718},{},[719],{"type":21,"tag":206,"props":720,"children":723},{"alt":721,"src":722},"easeIn","/bporter/2019-3/img/easeIn.mp4",[],{"type":21,"tag":651,"props":725,"children":727},{"id":726},"easeout",[728],{"type":21,"tag":119,"props":729,"children":731},{"className":730},[],[732],{"type":26,"value":733},"EaseOut",{"type":21,"tag":22,"props":735,"children":736},{},[737],{"type":26,"value":738},"Accelerates slowly away from the start values, accelerates into the end value. Also tolerance-based.",{"type":21,"tag":22,"props":740,"children":741},{},[742],{"type":21,"tag":206,"props":743,"children":746},{"alt":744,"src":745},"easeOut","/bporter/2019-3/img/easeOut.mp4",[],{"type":21,"tag":651,"props":748,"children":750},{"id":749},"spring",[751],{"type":21,"tag":119,"props":752,"children":754},{"className":753},[],[755],{"type":26,"value":756},"Spring",{"type":21,"tag":22,"props":758,"children":759},{},[760],{"type":26,"value":761},"Accelerates away from the start value, and may go past the end value; if so, it does a simplified model of the oscillation of a  dampened spring (...and if I wasn't worried that there might be physics professors reading this I'd insert some text here about Young's Modulus and how that affects the behavior here but instead will point out that this is in no way intended as a realistic physics modeling engine).",{"type":21,"tag":22,"props":763,"children":764},{},[765],{"type":26,"value":766},"A few examples of different damping values:",{"type":21,"tag":22,"props":768,"children":769},{},[770],{"type":21,"tag":206,"props":771,"children":773},{"alt":749,"src":772},"/bporter/2019-3/img/spring1.mp4",[],{"type":21,"tag":22,"props":775,"children":776},{},[777],{"type":21,"tag":206,"props":778,"children":780},{"alt":749,"src":779},"/bporter/2019-3/img/spring2.mp4",[],{"type":21,"tag":651,"props":782,"children":784},{"id":783},"sinusoid",[785],{"type":21,"tag":119,"props":786,"children":788},{"className":787},[],[789],{"type":26,"value":790},"Sinusoid",{"type":21,"tag":22,"props":792,"children":793},{},[794],{"type":26,"value":795},"Generates sin/cos wave values between any two arbitrary phase points. The data generated is always in the natural (-1.0..+1.0) range of those functions.",{"type":21,"tag":186,"props":797,"children":799},{"id":798},"the-animation-class",[800,802,808],{"type":26,"value":801},"The ",{"type":21,"tag":119,"props":803,"children":805},{"className":804},[],[806],{"type":26,"value":807},"Animation",{"type":26,"value":809}," class",{"type":21,"tag":22,"props":811,"children":812},{},[813],{"type":26,"value":814},"In code, the basic process is to",{"type":21,"tag":816,"props":817,"children":818},"ol",{},[819,824,837,849,861,881],{"type":21,"tag":87,"props":820,"children":821},{},[822],{"type":26,"value":823},"decide how many curves your animation needs -- perhaps one for a control panel opening/closing or a color/alpha change, two for moving a component around, more for changing location/size of a component",{"type":21,"tag":87,"props":825,"children":826},{},[827,829,835],{"type":26,"value":828},"Create ",{"type":21,"tag":119,"props":830,"children":832},{"className":831},[],[833],{"type":26,"value":834},"AnimatedValue",{"type":26,"value":836}," objects of the appropriate types and parameters",{"type":21,"tag":87,"props":838,"children":839},{},[840,842,847],{"type":26,"value":841},"Add those value objects to a new instance of the ",{"type":21,"tag":119,"props":843,"children":845},{"className":844},[],[846],{"type":26,"value":807},{"type":26,"value":848}," class.",{"type":21,"tag":87,"props":850,"children":851},{},[852,854,859],{"type":26,"value":853},"Configure the ",{"type":21,"tag":119,"props":855,"children":857},{"className":856},[],[858],{"type":26,"value":807},{"type":26,"value":860}," appropriately -- usually, this will require you to at least provide a function to call on each frame of the animation.",{"type":21,"tag":87,"props":862,"children":863},{},[864,866,871,873,879],{"type":26,"value":865},"Add that ",{"type":21,"tag":119,"props":867,"children":869},{"className":868},[],[870],{"type":26,"value":807},{"type":26,"value":872}," object to the appropriate ",{"type":21,"tag":119,"props":874,"children":876},{"className":875},[],[877],{"type":26,"value":878},"Animator",{"type":26,"value":880}," object, which will start executing the animation.",{"type":21,"tag":87,"props":882,"children":883},{},[884,886,891],{"type":26,"value":885},"When all of the values in the animation are complete, your completion function (if any) will be called, and the ",{"type":21,"tag":119,"props":887,"children":889},{"className":888},[],[890],{"type":26,"value":807},{"type":26,"value":892}," will be deleted.",{"type":21,"tag":22,"props":894,"children":895},{},[896],{"type":26,"value":897},"An example—this is the code inside the animator demo application that closes the control panel when the mouse is clicked:",{"type":21,"tag":216,"props":899,"children":901},{"className":218,"code":900,"language":220,"meta":8,"style":8},"void MainComponent::ClosePanel()\n{\n   // 1. get parameters in place \n   jassert(PanelState::kOpen == fPanelState);\n   int width = this->getWidth();\n   \n   int startX = fControls->getX();\n   int endX = width - kClosedPanelWidth;\n   \n   float accel = 1.4f;\n   float dampen = 0.4f;\n   \n   // 2. Create a Spring value\n   auto curve = std::make_unique\u003Cfriz::Spring>(startX, endX, 0.5, accel, dampen);\n   \n   // 3. Create an animation and add the curve to it. \n   auto animation = std::make_unique\u003Cfriz::Animation\u003C1>>(\n      friz::Animation\u003C1>::SourceList{std::move(curve)}, 0);\n   \n   // 4. Add a lambda to the animation that will move the panel\n   animation->OnUpdate([=] (int id, const friz::Animation\u003C1>::ValueList& val){\n      fControls->setTopLeftPosition(val[0], 0);\n   });\n   \n   // 5. Add a lambda to call when the effect is complete. \n   animation->OnCompletion([=] (int id) {\n      fPanelState = PanelState::kClosed;\n   });\n   \n   fPanelState = PanelState::kClosing;\n   // 6. Add the animation to the animator, starting it.  \n   fPanelAnimator.AddAnimation(std::move(animation));\n   \n}\n\n",[902],{"type":21,"tag":119,"props":903,"children":904},{"__ignoreMap":8},[905,935,943,952,986,1026,1035,1066,1097,1105,1138,1168,1176,1185,1249,1257,1266,1325,1384,1392,1401,1496,1532,1541,1549,1558,1596,1619,1627,1635,1657,1666,1701,1709],{"type":21,"tag":226,"props":906,"children":907},{"class":228,"line":229},[908,913,919,925,930],{"type":21,"tag":226,"props":909,"children":910},{"style":233},[911],{"type":26,"value":912},"void",{"type":21,"tag":226,"props":914,"children":916},{"style":915},"--shiki-default:#6F42C1;--shiki-dark:#B392F0",[917],{"type":26,"value":918}," MainComponent",{"type":21,"tag":226,"props":920,"children":922},{"style":921},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[923],{"type":26,"value":924},"::",{"type":21,"tag":226,"props":926,"children":927},{"style":915},[928],{"type":26,"value":929},"ClosePanel",{"type":21,"tag":226,"props":931,"children":932},{"style":921},[933],{"type":26,"value":934},"()\n",{"type":21,"tag":226,"props":936,"children":937},{"class":228,"line":377},[938],{"type":21,"tag":226,"props":939,"children":940},{"style":921},[941],{"type":26,"value":942},"{\n",{"type":21,"tag":226,"props":944,"children":945},{"class":228,"line":374},[946],{"type":21,"tag":226,"props":947,"children":949},{"style":948},"--shiki-default:#6A737D;--shiki-dark:#6A737D",[950],{"type":26,"value":951},"   // 1. get parameters in place \n",{"type":21,"tag":226,"props":953,"children":955},{"class":228,"line":954},4,[956,961,966,971,976,981],{"type":21,"tag":226,"props":957,"children":958},{"style":915},[959],{"type":26,"value":960},"   jassert",{"type":21,"tag":226,"props":962,"children":963},{"style":921},[964],{"type":26,"value":965},"(",{"type":21,"tag":226,"props":967,"children":968},{"style":915},[969],{"type":26,"value":970},"PanelState",{"type":21,"tag":226,"props":972,"children":973},{"style":921},[974],{"type":26,"value":975},"::kOpen ",{"type":21,"tag":226,"props":977,"children":978},{"style":233},[979],{"type":26,"value":980},"==",{"type":21,"tag":226,"props":982,"children":983},{"style":921},[984],{"type":26,"value":985}," fPanelState);\n",{"type":21,"tag":226,"props":987,"children":989},{"class":228,"line":988},5,[990,995,1000,1005,1011,1016,1021],{"type":21,"tag":226,"props":991,"children":992},{"style":233},[993],{"type":26,"value":994},"   int",{"type":21,"tag":226,"props":996,"children":997},{"style":921},[998],{"type":26,"value":999}," width ",{"type":21,"tag":226,"props":1001,"children":1002},{"style":233},[1003],{"type":26,"value":1004},"=",{"type":21,"tag":226,"props":1006,"children":1008},{"style":1007},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[1009],{"type":26,"value":1010}," this",{"type":21,"tag":226,"props":1012,"children":1013},{"style":921},[1014],{"type":26,"value":1015},"->",{"type":21,"tag":226,"props":1017,"children":1018},{"style":915},[1019],{"type":26,"value":1020},"getWidth",{"type":21,"tag":226,"props":1022,"children":1023},{"style":921},[1024],{"type":26,"value":1025},"();\n",{"type":21,"tag":226,"props":1027,"children":1029},{"class":228,"line":1028},6,[1030],{"type":21,"tag":226,"props":1031,"children":1032},{"style":921},[1033],{"type":26,"value":1034},"   \n",{"type":21,"tag":226,"props":1036,"children":1038},{"class":228,"line":1037},7,[1039,1043,1048,1052,1057,1062],{"type":21,"tag":226,"props":1040,"children":1041},{"style":233},[1042],{"type":26,"value":994},{"type":21,"tag":226,"props":1044,"children":1045},{"style":921},[1046],{"type":26,"value":1047}," startX ",{"type":21,"tag":226,"props":1049,"children":1050},{"style":233},[1051],{"type":26,"value":1004},{"type":21,"tag":226,"props":1053,"children":1054},{"style":921},[1055],{"type":26,"value":1056}," fControls->",{"type":21,"tag":226,"props":1058,"children":1059},{"style":915},[1060],{"type":26,"value":1061},"getX",{"type":21,"tag":226,"props":1063,"children":1064},{"style":921},[1065],{"type":26,"value":1025},{"type":21,"tag":226,"props":1067,"children":1069},{"class":228,"line":1068},8,[1070,1074,1079,1083,1087,1092],{"type":21,"tag":226,"props":1071,"children":1072},{"style":233},[1073],{"type":26,"value":994},{"type":21,"tag":226,"props":1075,"children":1076},{"style":921},[1077],{"type":26,"value":1078}," endX ",{"type":21,"tag":226,"props":1080,"children":1081},{"style":233},[1082],{"type":26,"value":1004},{"type":21,"tag":226,"props":1084,"children":1085},{"style":921},[1086],{"type":26,"value":999},{"type":21,"tag":226,"props":1088,"children":1089},{"style":233},[1090],{"type":26,"value":1091},"-",{"type":21,"tag":226,"props":1093,"children":1094},{"style":921},[1095],{"type":26,"value":1096}," kClosedPanelWidth;\n",{"type":21,"tag":226,"props":1098,"children":1100},{"class":228,"line":1099},9,[1101],{"type":21,"tag":226,"props":1102,"children":1103},{"style":921},[1104],{"type":26,"value":1034},{"type":21,"tag":226,"props":1106,"children":1108},{"class":228,"line":1107},10,[1109,1114,1119,1123,1128,1133],{"type":21,"tag":226,"props":1110,"children":1111},{"style":233},[1112],{"type":26,"value":1113},"   float",{"type":21,"tag":226,"props":1115,"children":1116},{"style":921},[1117],{"type":26,"value":1118}," accel ",{"type":21,"tag":226,"props":1120,"children":1121},{"style":233},[1122],{"type":26,"value":1004},{"type":21,"tag":226,"props":1124,"children":1125},{"style":1007},[1126],{"type":26,"value":1127}," 1.4",{"type":21,"tag":226,"props":1129,"children":1130},{"style":233},[1131],{"type":26,"value":1132},"f",{"type":21,"tag":226,"props":1134,"children":1135},{"style":921},[1136],{"type":26,"value":1137},";\n",{"type":21,"tag":226,"props":1139,"children":1141},{"class":228,"line":1140},11,[1142,1146,1151,1155,1160,1164],{"type":21,"tag":226,"props":1143,"children":1144},{"style":233},[1145],{"type":26,"value":1113},{"type":21,"tag":226,"props":1147,"children":1148},{"style":921},[1149],{"type":26,"value":1150}," dampen ",{"type":21,"tag":226,"props":1152,"children":1153},{"style":233},[1154],{"type":26,"value":1004},{"type":21,"tag":226,"props":1156,"children":1157},{"style":1007},[1158],{"type":26,"value":1159}," 0.4",{"type":21,"tag":226,"props":1161,"children":1162},{"style":233},[1163],{"type":26,"value":1132},{"type":21,"tag":226,"props":1165,"children":1166},{"style":921},[1167],{"type":26,"value":1137},{"type":21,"tag":226,"props":1169,"children":1171},{"class":228,"line":1170},12,[1172],{"type":21,"tag":226,"props":1173,"children":1174},{"style":921},[1175],{"type":26,"value":1034},{"type":21,"tag":226,"props":1177,"children":1179},{"class":228,"line":1178},13,[1180],{"type":21,"tag":226,"props":1181,"children":1182},{"style":948},[1183],{"type":26,"value":1184},"   // 2. Create a Spring value\n",{"type":21,"tag":226,"props":1186,"children":1188},{"class":228,"line":1187},14,[1189,1194,1199,1203,1208,1212,1217,1222,1226,1230,1234,1239,1244],{"type":21,"tag":226,"props":1190,"children":1191},{"style":233},[1192],{"type":26,"value":1193},"   auto",{"type":21,"tag":226,"props":1195,"children":1196},{"style":921},[1197],{"type":26,"value":1198}," curve ",{"type":21,"tag":226,"props":1200,"children":1201},{"style":233},[1202],{"type":26,"value":1004},{"type":21,"tag":226,"props":1204,"children":1205},{"style":915},[1206],{"type":26,"value":1207}," std",{"type":21,"tag":226,"props":1209,"children":1210},{"style":921},[1211],{"type":26,"value":924},{"type":21,"tag":226,"props":1213,"children":1214},{"style":915},[1215],{"type":26,"value":1216},"make_unique",{"type":21,"tag":226,"props":1218,"children":1219},{"style":921},[1220],{"type":26,"value":1221},"\u003C",{"type":21,"tag":226,"props":1223,"children":1224},{"style":915},[1225],{"type":26,"value":647},{"type":21,"tag":226,"props":1227,"children":1228},{"style":921},[1229],{"type":26,"value":924},{"type":21,"tag":226,"props":1231,"children":1232},{"style":915},[1233],{"type":26,"value":756},{"type":21,"tag":226,"props":1235,"children":1236},{"style":921},[1237],{"type":26,"value":1238},">(startX, endX, ",{"type":21,"tag":226,"props":1240,"children":1241},{"style":1007},[1242],{"type":26,"value":1243},"0.5",{"type":21,"tag":226,"props":1245,"children":1246},{"style":921},[1247],{"type":26,"value":1248},", accel, dampen);\n",{"type":21,"tag":226,"props":1250,"children":1252},{"class":228,"line":1251},15,[1253],{"type":21,"tag":226,"props":1254,"children":1255},{"style":921},[1256],{"type":26,"value":1034},{"type":21,"tag":226,"props":1258,"children":1260},{"class":228,"line":1259},16,[1261],{"type":21,"tag":226,"props":1262,"children":1263},{"style":948},[1264],{"type":26,"value":1265},"   // 3. Create an animation and add the curve to it. \n",{"type":21,"tag":226,"props":1267,"children":1269},{"class":228,"line":1268},17,[1270,1274,1279,1283,1287,1291,1295,1299,1303,1307,1311,1315,1320],{"type":21,"tag":226,"props":1271,"children":1272},{"style":233},[1273],{"type":26,"value":1193},{"type":21,"tag":226,"props":1275,"children":1276},{"style":921},[1277],{"type":26,"value":1278}," animation ",{"type":21,"tag":226,"props":1280,"children":1281},{"style":233},[1282],{"type":26,"value":1004},{"type":21,"tag":226,"props":1284,"children":1285},{"style":915},[1286],{"type":26,"value":1207},{"type":21,"tag":226,"props":1288,"children":1289},{"style":921},[1290],{"type":26,"value":924},{"type":21,"tag":226,"props":1292,"children":1293},{"style":915},[1294],{"type":26,"value":1216},{"type":21,"tag":226,"props":1296,"children":1297},{"style":921},[1298],{"type":26,"value":1221},{"type":21,"tag":226,"props":1300,"children":1301},{"style":915},[1302],{"type":26,"value":647},{"type":21,"tag":226,"props":1304,"children":1305},{"style":921},[1306],{"type":26,"value":924},{"type":21,"tag":226,"props":1308,"children":1309},{"style":915},[1310],{"type":26,"value":807},{"type":21,"tag":226,"props":1312,"children":1313},{"style":921},[1314],{"type":26,"value":1221},{"type":21,"tag":226,"props":1316,"children":1317},{"style":1007},[1318],{"type":26,"value":1319},"1",{"type":21,"tag":226,"props":1321,"children":1322},{"style":921},[1323],{"type":26,"value":1324},">>(\n",{"type":21,"tag":226,"props":1326,"children":1328},{"class":228,"line":1327},18,[1329,1334,1338,1342,1346,1350,1355,1360,1364,1369,1374,1379],{"type":21,"tag":226,"props":1330,"children":1331},{"style":915},[1332],{"type":26,"value":1333},"      friz",{"type":21,"tag":226,"props":1335,"children":1336},{"style":921},[1337],{"type":26,"value":924},{"type":21,"tag":226,"props":1339,"children":1340},{"style":915},[1341],{"type":26,"value":807},{"type":21,"tag":226,"props":1343,"children":1344},{"style":921},[1345],{"type":26,"value":1221},{"type":21,"tag":226,"props":1347,"children":1348},{"style":1007},[1349],{"type":26,"value":1319},{"type":21,"tag":226,"props":1351,"children":1352},{"style":921},[1353],{"type":26,"value":1354},">::SourceList{",{"type":21,"tag":226,"props":1356,"children":1357},{"style":915},[1358],{"type":26,"value":1359},"std",{"type":21,"tag":226,"props":1361,"children":1362},{"style":921},[1363],{"type":26,"value":924},{"type":21,"tag":226,"props":1365,"children":1366},{"style":915},[1367],{"type":26,"value":1368},"move",{"type":21,"tag":226,"props":1370,"children":1371},{"style":921},[1372],{"type":26,"value":1373},"(curve)}, ",{"type":21,"tag":226,"props":1375,"children":1376},{"style":1007},[1377],{"type":26,"value":1378},"0",{"type":21,"tag":226,"props":1380,"children":1381},{"style":921},[1382],{"type":26,"value":1383},");\n",{"type":21,"tag":226,"props":1385,"children":1387},{"class":228,"line":1386},19,[1388],{"type":21,"tag":226,"props":1389,"children":1390},{"style":921},[1391],{"type":26,"value":1034},{"type":21,"tag":226,"props":1393,"children":1395},{"class":228,"line":1394},20,[1396],{"type":21,"tag":226,"props":1397,"children":1398},{"style":948},[1399],{"type":26,"value":1400},"   // 4. Add a lambda to the animation that will move the panel\n",{"type":21,"tag":226,"props":1402,"children":1404},{"class":228,"line":1403},21,[1405,1410,1415,1420,1424,1429,1434,1440,1445,1450,1455,1459,1463,1467,1471,1476,1481,1486,1491],{"type":21,"tag":226,"props":1406,"children":1407},{"style":921},[1408],{"type":26,"value":1409},"   animation->",{"type":21,"tag":226,"props":1411,"children":1412},{"style":915},[1413],{"type":26,"value":1414},"OnUpdate",{"type":21,"tag":226,"props":1416,"children":1417},{"style":921},[1418],{"type":26,"value":1419},"([",{"type":21,"tag":226,"props":1421,"children":1422},{"style":233},[1423],{"type":26,"value":1004},{"type":21,"tag":226,"props":1425,"children":1426},{"style":921},[1427],{"type":26,"value":1428},"] (",{"type":21,"tag":226,"props":1430,"children":1431},{"style":233},[1432],{"type":26,"value":1433},"int",{"type":21,"tag":226,"props":1435,"children":1437},{"style":1436},"--shiki-default:#E36209;--shiki-dark:#FFAB70",[1438],{"type":26,"value":1439}," id",{"type":21,"tag":226,"props":1441,"children":1442},{"style":921},[1443],{"type":26,"value":1444},", ",{"type":21,"tag":226,"props":1446,"children":1447},{"style":233},[1448],{"type":26,"value":1449},"const",{"type":21,"tag":226,"props":1451,"children":1452},{"style":915},[1453],{"type":26,"value":1454}," friz",{"type":21,"tag":226,"props":1456,"children":1457},{"style":921},[1458],{"type":26,"value":924},{"type":21,"tag":226,"props":1460,"children":1461},{"style":915},[1462],{"type":26,"value":807},{"type":21,"tag":226,"props":1464,"children":1465},{"style":921},[1466],{"type":26,"value":1221},{"type":21,"tag":226,"props":1468,"children":1469},{"style":1007},[1470],{"type":26,"value":1319},{"type":21,"tag":226,"props":1472,"children":1473},{"style":921},[1474],{"type":26,"value":1475},">::",{"type":21,"tag":226,"props":1477,"children":1478},{"style":915},[1479],{"type":26,"value":1480},"ValueList",{"type":21,"tag":226,"props":1482,"children":1483},{"style":233},[1484],{"type":26,"value":1485},"&",{"type":21,"tag":226,"props":1487,"children":1488},{"style":1436},[1489],{"type":26,"value":1490}," val",{"type":21,"tag":226,"props":1492,"children":1493},{"style":921},[1494],{"type":26,"value":1495},"){\n",{"type":21,"tag":226,"props":1497,"children":1499},{"class":228,"line":1498},22,[1500,1505,1510,1515,1519,1524,1528],{"type":21,"tag":226,"props":1501,"children":1502},{"style":921},[1503],{"type":26,"value":1504},"      fControls->",{"type":21,"tag":226,"props":1506,"children":1507},{"style":915},[1508],{"type":26,"value":1509},"setTopLeftPosition",{"type":21,"tag":226,"props":1511,"children":1512},{"style":921},[1513],{"type":26,"value":1514},"(val[",{"type":21,"tag":226,"props":1516,"children":1517},{"style":1007},[1518],{"type":26,"value":1378},{"type":21,"tag":226,"props":1520,"children":1521},{"style":921},[1522],{"type":26,"value":1523},"], ",{"type":21,"tag":226,"props":1525,"children":1526},{"style":1007},[1527],{"type":26,"value":1378},{"type":21,"tag":226,"props":1529,"children":1530},{"style":921},[1531],{"type":26,"value":1383},{"type":21,"tag":226,"props":1533,"children":1535},{"class":228,"line":1534},23,[1536],{"type":21,"tag":226,"props":1537,"children":1538},{"style":921},[1539],{"type":26,"value":1540},"   });\n",{"type":21,"tag":226,"props":1542,"children":1544},{"class":228,"line":1543},24,[1545],{"type":21,"tag":226,"props":1546,"children":1547},{"style":921},[1548],{"type":26,"value":1034},{"type":21,"tag":226,"props":1550,"children":1552},{"class":228,"line":1551},25,[1553],{"type":21,"tag":226,"props":1554,"children":1555},{"style":948},[1556],{"type":26,"value":1557},"   // 5. Add a lambda to call when the effect is complete. \n",{"type":21,"tag":226,"props":1559,"children":1561},{"class":228,"line":1560},26,[1562,1566,1571,1575,1579,1583,1587,1591],{"type":21,"tag":226,"props":1563,"children":1564},{"style":921},[1565],{"type":26,"value":1409},{"type":21,"tag":226,"props":1567,"children":1568},{"style":915},[1569],{"type":26,"value":1570},"OnCompletion",{"type":21,"tag":226,"props":1572,"children":1573},{"style":921},[1574],{"type":26,"value":1419},{"type":21,"tag":226,"props":1576,"children":1577},{"style":233},[1578],{"type":26,"value":1004},{"type":21,"tag":226,"props":1580,"children":1581},{"style":921},[1582],{"type":26,"value":1428},{"type":21,"tag":226,"props":1584,"children":1585},{"style":233},[1586],{"type":26,"value":1433},{"type":21,"tag":226,"props":1588,"children":1589},{"style":1436},[1590],{"type":26,"value":1439},{"type":21,"tag":226,"props":1592,"children":1593},{"style":921},[1594],{"type":26,"value":1595},") {\n",{"type":21,"tag":226,"props":1597,"children":1599},{"class":228,"line":1598},27,[1600,1605,1609,1614],{"type":21,"tag":226,"props":1601,"children":1602},{"style":921},[1603],{"type":26,"value":1604},"      fPanelState ",{"type":21,"tag":226,"props":1606,"children":1607},{"style":233},[1608],{"type":26,"value":1004},{"type":21,"tag":226,"props":1610,"children":1611},{"style":915},[1612],{"type":26,"value":1613}," PanelState",{"type":21,"tag":226,"props":1615,"children":1616},{"style":921},[1617],{"type":26,"value":1618},"::kClosed;\n",{"type":21,"tag":226,"props":1620,"children":1622},{"class":228,"line":1621},28,[1623],{"type":21,"tag":226,"props":1624,"children":1625},{"style":921},[1626],{"type":26,"value":1540},{"type":21,"tag":226,"props":1628,"children":1630},{"class":228,"line":1629},29,[1631],{"type":21,"tag":226,"props":1632,"children":1633},{"style":921},[1634],{"type":26,"value":1034},{"type":21,"tag":226,"props":1636,"children":1638},{"class":228,"line":1637},30,[1639,1644,1648,1652],{"type":21,"tag":226,"props":1640,"children":1641},{"style":921},[1642],{"type":26,"value":1643},"   fPanelState ",{"type":21,"tag":226,"props":1645,"children":1646},{"style":233},[1647],{"type":26,"value":1004},{"type":21,"tag":226,"props":1649,"children":1650},{"style":915},[1651],{"type":26,"value":1613},{"type":21,"tag":226,"props":1653,"children":1654},{"style":921},[1655],{"type":26,"value":1656},"::kClosing;\n",{"type":21,"tag":226,"props":1658,"children":1660},{"class":228,"line":1659},31,[1661],{"type":21,"tag":226,"props":1662,"children":1663},{"style":948},[1664],{"type":26,"value":1665},"   // 6. Add the animation to the animator, starting it.  \n",{"type":21,"tag":226,"props":1667,"children":1669},{"class":228,"line":1668},32,[1670,1675,1680,1684,1688,1692,1696],{"type":21,"tag":226,"props":1671,"children":1672},{"style":921},[1673],{"type":26,"value":1674},"   fPanelAnimator.",{"type":21,"tag":226,"props":1676,"children":1677},{"style":915},[1678],{"type":26,"value":1679},"AddAnimation",{"type":21,"tag":226,"props":1681,"children":1682},{"style":921},[1683],{"type":26,"value":965},{"type":21,"tag":226,"props":1685,"children":1686},{"style":915},[1687],{"type":26,"value":1359},{"type":21,"tag":226,"props":1689,"children":1690},{"style":921},[1691],{"type":26,"value":924},{"type":21,"tag":226,"props":1693,"children":1694},{"style":915},[1695],{"type":26,"value":1368},{"type":21,"tag":226,"props":1697,"children":1698},{"style":921},[1699],{"type":26,"value":1700},"(animation));\n",{"type":21,"tag":226,"props":1702,"children":1704},{"class":228,"line":1703},33,[1705],{"type":21,"tag":226,"props":1706,"children":1707},{"style":921},[1708],{"type":26,"value":1034},{"type":21,"tag":226,"props":1710,"children":1712},{"class":228,"line":1711},34,[1713],{"type":21,"tag":226,"props":1714,"children":1715},{"style":921},[1716],{"type":26,"value":1717},"}\n",{"type":21,"tag":22,"props":1719,"children":1720},{},[1721,1723,1729],{"type":26,"value":1722},"Each animation may also be given an optional ",{"type":21,"tag":119,"props":1724,"children":1726},{"className":1725},[],[1727],{"type":26,"value":1728},"id",{"type":26,"value":1730}," value.",{"type":21,"tag":22,"props":1732,"children":1733},{},[1734],{"type":26,"value":1735},"This id will be passed as an argument to the Update and Completion functions registered with the animation, and may also be used to cancel an animation that's in progress.",{"type":21,"tag":651,"props":1737,"children":1739},{"id":1738},"sequence",[1740],{"type":21,"tag":119,"props":1741,"children":1743},{"className":1742},[],[1744],{"type":26,"value":1745},"Sequence",{"type":21,"tag":22,"props":1747,"children":1748},{},[1749,1751,1757],{"type":26,"value":1750},"There's also a separate ",{"type":21,"tag":119,"props":1752,"children":1754},{"className":1753},[],[1755],{"type":26,"value":1756},"friz::Sequence",{"type":26,"value":1758}," class that can hold a series of Animation objects and execute them in order as if they were a single long animation. Here's an example of EaseIn and EaseOut combined into a single effect:",{"type":21,"tag":1760,"props":1761,"children":1763},"h4",{"id":1762},"about-those-lambda-callbacks",[1764,1766,1772],{"type":26,"value":1765},"About Those ",{"type":21,"tag":119,"props":1767,"children":1769},{"className":1768},[],[1770],{"type":26,"value":1771},"lambda",{"type":26,"value":1773}," Callbacks",{"type":21,"tag":22,"props":1775,"children":1776},{},[1777,1779,1786],{"type":26,"value":1778},"You will want to provide at least one (a per-frame callback) and possibly two (an animation complete callback) lambda(s) to each animation you create. The example code in the demo app gives several models for how these functions should be declared and used. If you, like me, have a bit of Unfrozen Caveman Programmer in you, there are tons of references out there on using lambdas in modern C++; my favorite in terms of depth can be found in Meyers' ",{"type":21,"tag":29,"props":1780,"children":1783},{"href":1781,"rel":1782},"https://www.aristeia.com/books.html",[33],[1784],{"type":26,"value":1785},"EffectiveModern C++",{"type":26,"value":47},{"type":21,"tag":22,"props":1788,"children":1789},{},[1790,1791,1796],{"type":26,"value":801},{"type":21,"tag":91,"props":1792,"children":1793},{},[1794],{"type":26,"value":1795},"per-frame update callback",{"type":26,"value":1797}," will always have two arguments:",{"type":21,"tag":83,"props":1799,"children":1800},{},[1801,1806],{"type":21,"tag":87,"props":1802,"children":1803},{},[1804],{"type":26,"value":1805},"The ID for the animation being performed",{"type":21,"tag":87,"props":1807,"children":1808},{},[1809,1811,1817],{"type":26,"value":1810},"an ",{"type":21,"tag":119,"props":1812,"children":1814},{"className":1813},[],[1815],{"type":26,"value":1816},"std::array",{"type":26,"value":1818}," of float values for this frame.",{"type":21,"tag":22,"props":1820,"children":1821},{},[1822],{"type":26,"value":1823},"Your callback should do whatever it needs to do to implement the effect as quickly as it can—remember that another callback will be coming when the timer elapses again.",{"type":21,"tag":22,"props":1825,"children":1826},{},[1827,1828,1833],{"type":26,"value":801},{"type":21,"tag":91,"props":1829,"children":1830},{},[1831],{"type":26,"value":1832},"completion",{"type":26,"value":1834}," callback will have a single argument, the ID of the animation being completed.",{"type":21,"tag":22,"props":1836,"children":1837},{},[1838,1840,1845,1847,1852],{"type":26,"value":1839},"Note that if you are combining multiple ",{"type":21,"tag":119,"props":1841,"children":1843},{"className":1842},[],[1844],{"type":26,"value":807},{"type":26,"value":1846}," objects together into a single ",{"type":21,"tag":119,"props":1848,"children":1850},{"className":1849},[],[1851],{"type":26,"value":1745},{"type":26,"value":1853},", your callbacks will receive updates using the ID assigned to the sequence, not to the individual animation objects.",{"type":21,"tag":22,"props":1855,"children":1856},{},[1857],{"type":26,"value":1858},"You should also be very careful to watch the lifetimes of variables carried inside of the lambda objects via capture. It's tempting to think that \"hey, we're using smart pointers because we're all modern here so object lifetime is handled for us!\"",{"type":21,"tag":22,"props":1860,"children":1861},{},[1862,1864,1869,1871,1877,1879,1885],{"type":26,"value":1863},"That's not always true with constructs like these -- make sure that you puzzle out all the ownership and lifetime issues (and test, test, test!). A good pattern to follow is to make sure that the object that owns the ",{"type":21,"tag":119,"props":1865,"children":1867},{"className":1866},[],[1868],{"type":26,"value":878},{"type":26,"value":1870}," object driving an animation also owns any ",{"type":21,"tag":119,"props":1872,"children":1874},{"className":1873},[],[1875],{"type":26,"value":1876},"Component",{"type":26,"value":1878}," objects that will be controlled by an animation, and that in the destructor of that object, you ",{"type":21,"tag":119,"props":1880,"children":1882},{"className":1881},[],[1883],{"type":26,"value":1884},"CancelAllAnimations()",{"type":26,"value":1886}," to prevent stray timer updates from trying to update a component that's about to be deleted.",{"type":21,"tag":186,"props":1888,"children":1890},{"id":1889},"the-animator",[1891,1892],{"type":26,"value":801},{"type":21,"tag":119,"props":1893,"children":1895},{"className":1894},[],[1896],{"type":26,"value":878},{"type":21,"tag":22,"props":1898,"children":1899},{},[1900,1902,1907],{"type":26,"value":1901},"Your app will need one or more instances of the ",{"type":21,"tag":119,"props":1903,"children":1905},{"className":1904},[],[1906],{"type":26,"value":878},{"type":26,"value":1908}," object. The easiest approach is likely to be having any component that wants to control some aspect of one or more child components own an Animator.",{"type":21,"tag":22,"props":1910,"children":1911},{},[1912],{"type":26,"value":1913},"To start an animation, pass it to an Animator, which will take ownership of it and control its lifecycle from there. Ordinarily, the animation will run to its completion and be destroyed.",{"type":21,"tag":22,"props":1915,"children":1916},{},[1917],{"type":26,"value":1918},"You may, however, want to explicitly cancel a single animation that's in progress (or perhaps a group of animations). The Animator exposes two methods to cancel in-progress animations:",{"type":21,"tag":216,"props":1920,"children":1922},{"className":218,"code":1921,"language":220,"meta":8,"style":8},"bool Animator::CancelAllAnimations(bool moveToEndPosition)\n",[1923],{"type":21,"tag":119,"props":1924,"children":1925},{"__ignoreMap":8},[1926],{"type":21,"tag":226,"props":1927,"children":1928},{"class":228,"line":229},[1929,1934,1939,1943,1948,1952,1956,1961],{"type":21,"tag":226,"props":1930,"children":1931},{"style":233},[1932],{"type":26,"value":1933},"bool",{"type":21,"tag":226,"props":1935,"children":1936},{"style":915},[1937],{"type":26,"value":1938}," Animator",{"type":21,"tag":226,"props":1940,"children":1941},{"style":921},[1942],{"type":26,"value":924},{"type":21,"tag":226,"props":1944,"children":1945},{"style":915},[1946],{"type":26,"value":1947},"CancelAllAnimations",{"type":21,"tag":226,"props":1949,"children":1950},{"style":921},[1951],{"type":26,"value":965},{"type":21,"tag":226,"props":1953,"children":1954},{"style":233},[1955],{"type":26,"value":1933},{"type":21,"tag":226,"props":1957,"children":1958},{"style":1436},[1959],{"type":26,"value":1960}," moveToEndPosition",{"type":21,"tag":226,"props":1962,"children":1963},{"style":921},[1964],{"type":26,"value":1965},")\n",{"type":21,"tag":22,"props":1967,"children":1968},{},[1969],{"type":26,"value":1970},"will cancel all animations that are currently executing, optionally advancing them all to their final positions.",{"type":21,"tag":216,"props":1972,"children":1974},{"className":218,"code":1973,"language":220,"meta":8,"style":8},"bool CancelAnimation(int id, bool moveToEndPosition);\n",[1975],{"type":21,"tag":119,"props":1976,"children":1977},{"__ignoreMap":8},[1978],{"type":21,"tag":226,"props":1979,"children":1980},{"class":228,"line":229},[1981,1985,1990,1994,1998,2002,2006,2010,2014],{"type":21,"tag":226,"props":1982,"children":1983},{"style":233},[1984],{"type":26,"value":1933},{"type":21,"tag":226,"props":1986,"children":1987},{"style":915},[1988],{"type":26,"value":1989}," CancelAnimation",{"type":21,"tag":226,"props":1991,"children":1992},{"style":921},[1993],{"type":26,"value":965},{"type":21,"tag":226,"props":1995,"children":1996},{"style":233},[1997],{"type":26,"value":1433},{"type":21,"tag":226,"props":1999,"children":2000},{"style":1436},[2001],{"type":26,"value":1439},{"type":21,"tag":226,"props":2003,"children":2004},{"style":921},[2005],{"type":26,"value":1444},{"type":21,"tag":226,"props":2007,"children":2008},{"style":233},[2009],{"type":26,"value":1933},{"type":21,"tag":226,"props":2011,"children":2012},{"style":1436},[2013],{"type":26,"value":1960},{"type":21,"tag":226,"props":2015,"children":2016},{"style":921},[2017],{"type":26,"value":1383},{"type":21,"tag":22,"props":2019,"children":2020},{},[2021,2023,2028],{"type":26,"value":2022},"cancels any animations with an ",{"type":21,"tag":119,"props":2024,"children":2026},{"className":2025},[],[2027],{"type":26,"value":1728},{"type":26,"value":2029}," value that matches the argument. Since there's no requirement that animations use unique id values (or any id at all), you can use this knowledge to group animations together with a shared id value if that is useful to you.",{"type":21,"tag":186,"props":2031,"children":2033},{"id":2032},"technical-details",[2034],{"type":26,"value":2035},"Technical Details",{"type":21,"tag":22,"props":2037,"children":2038},{},[2039,2041,2046,2048,2053],{"type":26,"value":2040},"You can grab the demo application that includes the ",{"type":21,"tag":119,"props":2042,"children":2044},{"className":2043},[],[2045],{"type":26,"value":647},{"type":26,"value":2047}," code from GitHub at ",{"type":21,"tag":29,"props":2049,"children":2051},{"href":356,"rel":2050},[33],[2052],{"type":26,"value":356},{"type":26,"value":47},{"type":21,"tag":22,"props":2055,"children":2056},{},[2057,2059,2066,2068,2074],{"type":26,"value":2058},"If you have ",{"type":21,"tag":29,"props":2060,"children":2063},{"href":2061,"rel":2062},"http://www.doxygen.nl/",[33],[2064],{"type":26,"value":2065},"Doxygen",{"type":26,"value":2067}," installed, you can use the included ",{"type":21,"tag":119,"props":2069,"children":2071},{"className":2070},[],[2072],{"type":26,"value":2073},"doxygen/Doxyfile",{"type":26,"value":2075}," to generate a local copy of HTML documentation that should prove useful.",{"type":21,"tag":22,"props":2077,"children":2078},{},[2079,2081,2088],{"type":26,"value":2080},"It's licensed under the terms of the ",{"type":21,"tag":29,"props":2082,"children":2085},{"href":2083,"rel":2084},"https://github.com/bgporter/animator/blob/master/LICENSE",[33],[2086],{"type":26,"value":2087},"MIT license",{"type":26,"value":2089},"—if it's useful to you, use it as you like. Open source, closed source, commercial, free, whatever, as long as the terms of that license are amenable to you (and perhaps to whoever writes the checks funding your project).",{"type":21,"tag":22,"props":2091,"children":2092},{},[2093],{"type":26,"value":2094},"At some point I will package it up as a JUCE user module to make it easier to work with.",{"type":21,"tag":186,"props":2096,"children":2098},{"id":2097},"aspirations",[2099],{"type":26,"value":2100},"Aspirations",{"type":21,"tag":22,"props":2102,"children":2103},{},[2104],{"type":26,"value":2105},"Part of me felt a strange obligation to say something about how animation is like a seasoning where a little goes a long way, and about the value and importance of subtlety. Part of me actually believes those things.",{"type":21,"tag":22,"props":2107,"children":2108},{},[2109],{"type":26,"value":2110},"A different part of me walks around the show floor at NAMM every January, checking out new music software releases and dies a little inside from the sameness of everything. That part of me is hoping that someone will use this and create something that's way outside the lines.",{"type":21,"tag":22,"props":2112,"children":2113},{},[2114],{"type":26,"value":2115},"Make cool things.",{"type":21,"tag":368,"props":2117,"children":2118},{},[2119],{"type":26,"value":372},{"title":8,"searchDepth":374,"depth":374,"links":2121},[2122,2123,2131,2138,2139,2140],{"id":559,"depth":377,"text":562},{"id":625,"depth":377,"text":628,"children":2124},[2125,2126,2127,2128,2129,2130],{"id":653,"depth":374,"text":660},{"id":668,"depth":374,"text":675},{"id":690,"depth":374,"text":697},{"id":726,"depth":374,"text":733},{"id":749,"depth":374,"text":756},{"id":783,"depth":374,"text":790},{"id":798,"depth":377,"text":2132,"children":2133},"The Animation class",[2134],{"id":1738,"depth":374,"text":1745,"children":2135},[2136],{"id":1762,"depth":954,"text":2137},"About Those lambda Callbacks",{"id":1889,"depth":377,"text":603},{"id":2032,"depth":377,"text":2035},{"id":2097,"depth":377,"text":2100},"content:bporter:2019-3:animator.md","bporter/2019-3/animator.md","bporter/2019-3/animator",{"user":387,"name":388},1780330276280]