@tidepool/viz
Version:
Tidepool data visualization for diabetes device data.
699 lines (302 loc) • 24.3 kB
HTML
<html lang="" >
<head>
<meta charset="UTF-8">
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>GSAP · GitBook</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="description" content="">
<meta name="generator" content="GitBook 3.2.2">
<link rel="stylesheet" href="../../gitbook/style.css">
<link rel="stylesheet" href="../../gitbook/gitbook-plugin-highlight/website.css">
<link rel="stylesheet" href="../../gitbook/gitbook-plugin-search/search.css">
<link rel="stylesheet" href="../../gitbook/gitbook-plugin-fontsettings/website.css">
<meta name="HandheldFriendly" content="true"/>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="../../gitbook/images/apple-touch-icon-precomposed-152.png">
<link rel="shortcut icon" href="../../gitbook/images/favicon.ico" type="image/x-icon">
<link rel="next" href="Moment.html" />
<link rel="prev" href="D3.html" />
</head>
<body>
<div class="book">
<div class="book-summary">
<div id="book-search-input" role="search">
<input type="text" placeholder="Type to search" />
</div>
<nav role="navigation">
<ul class="summary">
<li class="chapter " data-level="1.1" data-path="../../">
<a href="../../">
Introduction
</a>
</li>
<li class="chapter " data-level="1.2" data-path="../StartHere.html">
<a href="../StartHere.html">
@tidepool/viz developer guide
</a>
<ul class="articles">
<li class="chapter " data-level="1.2.1" data-path="../Background.html">
<a href="../Background.html">
background
</a>
</li>
<li class="chapter " data-level="1.2.2" data-path="../FeatureOverview.html">
<a href="../FeatureOverview.html">
overview of features
</a>
</li>
<li class="chapter " data-level="1.2.3" data-path="../Architecture.html">
<a href="../Architecture.html">
planned architecture
</a>
</li>
<li class="chapter " data-level="1.2.4" data-path="../DirectoryStructure.html">
<a href="../DirectoryStructure.html">
app & directory structure
</a>
</li>
<li class="chapter " data-level="1.2.5" data-path="../CodeStyle.html">
<a href="../CodeStyle.html">
code style
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.3" data-path="../views/">
<a href="../views/">
per-view documentation
</a>
<ul class="articles">
<li class="chapter " data-level="1.3.1" data-path="../../src/components/settings/">
<a href="../../src/components/settings/">
Device Settings view
</a>
</li>
<li class="chapter " data-level="1.3.2" data-path="../views/Trends.html">
<a href="../views/Trends.html">
Trends view
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.4" data-path="../Storybook.html">
<a href="../Storybook.html">
use of React Storybook
</a>
</li>
<li class="chapter " data-level="1.5" data-path="./">
<a href="./">
usage of dependencies
</a>
<ul class="articles">
<li class="chapter " data-level="1.5.1" data-path="D3.html">
<a href="D3.html">
D3
</a>
</li>
<li class="chapter active" data-level="1.5.2" data-path="GSAP.html">
<a href="GSAP.html">
GSAP
</a>
</li>
<li class="chapter " data-level="1.5.3" data-path="Moment.html">
<a href="Moment.html">
Moment
</a>
</li>
<li class="chapter " data-level="1.5.4" data-path="React.html">
<a href="React.html">
React
</a>
</li>
<li class="chapter " data-level="1.5.5" data-path="ReactMotion.html">
<a href="ReactMotion.html">
React Motion
</a>
</li>
<li class="chapter " data-level="1.5.6" data-path="Redux.html">
<a href="Redux.html">
Redux
</a>
</li>
<li class="chapter " data-level="1.5.7" data-path="Webpack.html">
<a href="Webpack.html">
webpack
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.6" data-path="../../src/utils/">
<a href="../../src/utils/">
utilities
</a>
<ul class="articles">
<li class="chapter " data-level="1.6.1" data-path="../../src/utils/apidocs/">
<a href="../../src/utils/apidocs/">
API docs for utilities
</a>
<ul class="articles">
<li class="chapter " data-level="1.6.1.1" data-path="../../src/utils/apidocs/basal.html">
<a href="../../src/utils/apidocs/basal.html">
basal
</a>
</li>
<li class="chapter " data-level="1.6.1.2" data-path="../../src/utils/apidocs/bloodglucose.html">
<a href="../../src/utils/apidocs/bloodglucose.html">
blood glucose
</a>
</li>
<li class="chapter " data-level="1.6.1.3" data-path="../../src/utils/apidocs/bolus.html">
<a href="../../src/utils/apidocs/bolus.html">
bolus
</a>
</li>
<li class="chapter " data-level="1.6.1.4" data-path="../../src/utils/apidocs/datetime.html">
<a href="../../src/utils/apidocs/datetime.html">
datetime
</a>
</li>
<li class="chapter " data-level="1.6.1.5" data-path="../../src/utils/apidocs/format.html">
<a href="../../src/utils/apidocs/format.html">
format
</a>
</li>
<li class="chapter " data-level="1.6.1.6" data-path="../../src/utils/apidocs/misc.html">
<a href="../../src/utils/apidocs/misc.html">
misc
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="chapter " data-level="1.7" data-path="../misc/">
<a href="../misc/">
misc
</a>
<ul class="articles">
<li class="chapter " data-level="1.7.1" data-path="../misc/CommonProps.html">
<a href="../misc/CommonProps.html">
Common props
</a>
</li>
<li class="chapter " data-level="1.7.2" data-path="../misc/Docs.html">
<a href="../misc/Docs.html">
Docs setup & publishing
</a>
</li>
<li class="chapter " data-level="1.7.3" data-path="../misc/TimeRenderingModes.html">
<a href="../misc/TimeRenderingModes.html">
Time-rendering modes
</a>
</li>
</ul>
</li>
<li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul>
</nav>
</div>
<div class="book-body">
<div class="body-inner">
<div class="book-header" role="navigation">
<!-- Title -->
<h1>
<i class="fa fa-circle-o-notch fa-spin"></i>
<a href="../.." >GSAP</a>
</h1>
</div>
<div class="page-wrapper" tabindex="-1" role="main">
<div class="page-inner">
<div id="book-search-results">
<div class="search-noresults">
<section class="normal markdown-section">
<h2 id="tidepoolvizs-usage-of-gsap">@tidepool/viz's usage of GSAP</h2>
<p><a href="https://greensock.com/" title="GreenSock.com: GSAP" target="_blank">GSAP (GreenSock Animation Platform)</a> is a powerful library for animating HTML5 documents with JavaScript (largely as an alternative to CSS3). GSAP as a product started with <a href="https://greensock.com/gsap-as" title="GSAP ActionScript" target="_blank">a similar animation library</a> for <a href="https://en.wikipedia.org/wiki/Adobe_Flash" title="Wikipedia: Adobe Flash" target="_blank">Flash</a><sup><a href="#fn_a" id="reffn_a">a</a></sup>. The now more widely used JavaScript library has an extensive and yet still fairly intuitive API that allows for finely-grained control of animations, including an API for sequencing within complex animations that is far over and above what can be accomplished with CSS3's <code>@keyframes</code>. The library is available in several separate modules (TweenLite, TweenMax, TimelineLite, TimelineMax) to keep file sizes small (choose what you need), and even the combination of TweenMax + TimelineMax is less than 200KB minified (that's about the size of React + ReactDOM but much less than Angular 2<sup><a href="#fn_b" id="reffn_b">b</a></sup>).</p>
<h3 id="📚-resources">📚 resources</h3>
<ul>
<li><a href="https://greensock.com/docs/#/HTML5/" title="GSAP Docs" target="_blank">GSAP docs</a></li>
<li><a href="https://frontendmasters.com/courses/svg-animation/" title="Frontend Masters: Advanced SVG Animation" target="_blank">Advanced SVG Animation</a>, a Frontend Masters course by Sarah Drasner</li>
</ul>
<h3 id="🕰️-when-we-use-gsap">🕰️ when we use GSAP</h3>
<p>Our first choice for animating with React is <a href="ReactMotion.html">React Motion</a>, but we fall back to GSAP where React Motion's API doesn't provide what we need. So far the only circumstance that we've needed to pull GSAP in for is a staggered animation on revealing the daily CGM sensor traces via hover over a CGM time-slice segment in the CGM Trends view. This is due to the fact that React Motion's <code>StaggeredMotion</code> component only serves a subset of stagger animations—namely, those in which each value in the stagger is <strong>entirely</strong> dependent on the previous and next values. In the case of a CGM sensor trace for a day, while each value is <em>somewhat</em> dependent on its previous value, CGM values fail the <em>entirely dependent</em> criterion: while blood glucose can't jump from 68 mg/dL to 345 mg/dL in the space of a single five-minute span of time, all blood glucose values <em>do</em> fall within a certain "delta" of the previous value, though this delta varies (i.e., the previous and next values are <strong>not</strong> entirely dependent on each other).</p>
<h3 id="🍝-integrating-gsap-with-react">🍝 integrating GSAP with React</h3>
<p>The strategy for using GSAP with React is very similar to the <code>componentDidMount</code> strategy for integrating <a href="D3.html#the-componentdidmount-strategy">React and D3</a> but perhaps a little less offensive to the React purist. Instead of <em>rendering</em> to the DOM in the <code>componentDidMount</code> lifecycle method (<strong>after</strong> React's render cycle), to integrate GSAP with React we first render the component inside a <code>ReactTransitionGroup</code> container. Then in the <code>componentWillEnter</code> method (which is provided by <code>ReactTransitionGroups</code> and fires at the same point in the React lifecycle as <code>componentDidMount</code>) we simply gather the <a href="https://facebook.github.io/react/docs/refs-and-the-dom.html" title="React docs: Refs and the DOM" target="_blank">references to DOM nodes</a> that have just been rendered by React and then pass them to GSAP as the target(s) for animation. We do the same on exit only inside the <code>componentWillLeave</code> method also provided by <code>ReactTransitionGroup</code>. In this way, we're only modifying the <em>appearance</em> of DOM nodes via GSAP and not disrupting React's control of rendering to the DOM. (In addition, since animations are more-or-less a nice-to-have<sup><a href="#fn_c" id="reffn_c">c</a></sup> and not absolutely essential to our data visualization functionality, we don't write tests around them, and so the difficulty of writing tests around things that happen inside React lifecycle methods does not come into play.)</p>
<h3 id="✍️-example">✍️ example</h3>
<p>If your React component has rendered a series of SVG <code><circle></code>s that you want to reveal in a stagger animation, one-by-one, start with assigning a <code>ref</code> on each <code><circle></code> in the <code>render</code> method of the component:</p>
<pre><code class="lang-js">render() {
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">g</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"a-gaggle-of-circles"</span>></span>
{_.map(data, (d) => (
<span class="hljs-tag"><<span class="hljs-name">circle</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">{5}</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">{10}</span> <span class="hljs-attr">r</span>=<span class="hljs-string">{5}</span> <span class="hljs-attr">opacity</span>=<span class="hljs-string">{0}</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{(node)</span> =></span> { this[d.id] = node; }} />
))}
<span class="hljs-tag"></<span class="hljs-name">g</span>></span>
);
}
</span></code></pre>
<p>Notice that we also render initially with opacity zero; the trick to this staggered render of <code><circle></code>s is to render all the <code><circle></code>s at the same time (in the React component's <code>render</code> method) and then stagger the animation to <em>full</em> opacity in <a href="https://facebook.github.io/react/docs/animation.html#componentwillenter" title="React Animation Add-Ons: componentWillEnter" target="_blank"><code>componentWillEnter</code></a>:</p>
<pre><code class="lang-js">componentWillEnter(callback) {
<span class="hljs-keyword">const</span> { animationDuration, data } = <span class="hljs-keyword">this</span>.props;
<span class="hljs-keyword">const</span> targets = _.map(data, (d) => (<span class="hljs-keyword">this</span>[d.id]));
TweenMax.staggerTo(
targets, animationDuration, { opacity: <span class="hljs-number">1</span>, onComplete: callback }, animationDuration / targets.length
);
}
</code></pre>
<p>Also note the callback argument provided to <code>componentWillEnter</code>, which we specify as the <code>onComplete</code> in the <code>TweenMax.staggerTo</code> configuration.</p>
<hr>
<blockquote id="fn_a">
<sup>a</sup>. Link, of course, for the youngins soon to ask, "What the hell is Flash?" (Also, 🎩 to @krystophv for pointing out to @jebeck that GSAP <em>was</em> originally a Flash animation library.)<a href="#reffn_a" title="Jump back to footnote [a] in the text."> ↩</a>
</blockquote>
<blockquote id="fn_b">
<sup>b</sup>. Source for React + React DOM and Angular 2 sizes: <a href="https://gist.github.com/Restuta/cda69e50a853aa64912d" target="_blank">https://gist.github.com/Restuta/cda69e50a853aa64912d</a><a href="#reffn_b" title="Jump back to footnote [b] in the text."> ↩</a>
</blockquote>
<blockquote id="fn_c">
<sup>c</sup>. Though very valuable for <a href="https://css-tricks.com/the-importance-of-context-shifting-in-ux-patterns/" title="CSS Tricks: The Importance of Context-Shifting in UX Patterns" target="_blank">context-shifting</a>, which is arguably even <em>more</em> important in data visualization than general application UX, because context-shifting with data aids in understanding changes and relationships in data, which is often (if not always!) the primary communicative purpose of a data visualization.<a href="#reffn_c" title="Jump back to footnote [c] in the text."> ↩</a>
</blockquote>
</section>
</div>
<div class="search-results">
<div class="has-results">
<h1 class="search-results-title"><span class='search-results-count'></span> results matching "<span class='search-query'></span>"</h1>
<ul class="search-results-list"></ul>
</div>
<div class="no-results">
<h1 class="search-results-title">No results matching "<span class='search-query'></span>"</h1>
</div>
</div>
</div>
</div>
</div>
</div>
<a href="D3.html" class="navigation navigation-prev " aria-label="Previous page: D3">
<i class="fa fa-angle-left"></i>
</a>
<a href="Moment.html" class="navigation navigation-next " aria-label="Next page: Moment">
<i class="fa fa-angle-right"></i>
</a>
</div>
<script>
var gitbook = gitbook || [];
gitbook.push(function() {
gitbook.page.hasChanged({"page":{"title":"GSAP","level":"1.5.2","depth":2,"next":{"title":"Moment","level":"1.5.3","depth":2,"path":"docs/deps/Moment.md","ref":"docs/deps/Moment.md","articles":[]},"previous":{"title":"D3","level":"1.5.1","depth":2,"path":"docs/deps/D3.md","ref":"docs/deps/D3.md","articles":[]},"dir":"ltr"},"config":{"gitbook":"*","theme":"default","variables":{},"plugins":[],"pluginsConfig":{"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"}},"file":{"path":"docs/deps/GSAP.md","mtime":"2017-05-23T17:51:07.000Z","type":"markdown"},"gitbook":{"version":"3.2.2","time":"2017-05-31T15:26:30.892Z"},"basePath":"../..","book":{"language":""}});
});
</script>
</div>
<script src="../../gitbook/gitbook.js"></script>
<script src="../../gitbook/theme.js"></script>
<script src="../../gitbook/gitbook-plugin-search/search-engine.js"></script>
<script src="../../gitbook/gitbook-plugin-search/search.js"></script>
<script src="../../gitbook/gitbook-plugin-lunr/lunr.min.js"></script>
<script src="../../gitbook/gitbook-plugin-lunr/search-lunr.js"></script>
<script src="../../gitbook/gitbook-plugin-sharing/buttons.js"></script>
<script src="../../gitbook/gitbook-plugin-fontsettings/fontsettings.js"></script>
</body>
</html>