<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Joost Kiens</title>
    <description>The latest articles on DEV Community by Joost Kiens (@joostkiens).</description>
    <link>https://dev.to/joostkiens</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F315237%2Fdf5bcef0-a54f-457f-980d-23279d16f25e.jpeg</url>
      <title>DEV Community: Joost Kiens</title>
      <link>https://dev.to/joostkiens</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joostkiens"/>
    <language>en</language>
    <item>
      <title>Creating practical Instagram-like galleries and horizontal lists with CSS scroll snapping</title>
      <dc:creator>Joost Kiens</dc:creator>
      <pubDate>Tue, 05 May 2020 07:49:55 +0000</pubDate>
      <link>https://dev.to/joostkiens/creating-practical-instagram-like-galleries-and-horizontal-lists-with-css-scroll-snapping-580e</link>
      <guid>https://dev.to/joostkiens/creating-practical-instagram-like-galleries-and-horizontal-lists-with-css-scroll-snapping-580e</guid>
      <description>&lt;p&gt;What's the difference between carousels and &lt;a href="https://uxdesign.cc/best-practices-for-horizontal-lists-in-mobile-21480b9b73e5" rel="noopener noreferrer"&gt;horizontally&lt;/a&gt; scrollable &lt;a href="https://uxplanet.org/horizontal-scrolling-in-mobile-643c81901af3" rel="noopener noreferrer"&gt;lists&lt;/a&gt;? Is it the gestures, snapping, or the number of visible items? They are very similar, especially on touch devices.&lt;/p&gt;

&lt;p&gt;I looked at the Instagram iOS app to learn more and noticed 3 different elements you can scroll horizontally.&lt;/p&gt;

&lt;p&gt;I set out to build these 3 elements based on the same code, mainly CSS. Here’s what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three scrollable elements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Free-scrolling horizontal lists
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5ccnd91h921k3cpw0rtj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5ccnd91h921k3cpw0rtj.gif" alt="Screen capture of Free-scrolling horizontal list" width="720" height="249"&gt;&lt;/a&gt;&lt;br&gt;
A horizontal list that overflows its boundaries. You can freely scroll left and right. Netflix and Spotify use it everywhere on mobile, Instagram uses it for its stories.&lt;/p&gt;

&lt;p&gt;It uses a bit of old school CSS, like &lt;code&gt;overflow-x&lt;/code&gt;, and is improved with more experimental rules.&lt;/p&gt;
&lt;h3&gt;
  
  
  Snapping horizontal lists
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Falficj2bl1quglsi9qkx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Falficj2bl1quglsi9qkx.gif" alt="Screen capture of snapping horizontal list" width="720" height="252"&gt;&lt;/a&gt;&lt;br&gt;
The same as free-scrolling horizontal lists, but the nearest item in the list snaps into place. Like the “Suggested for You” section in the Instagram app.&lt;/p&gt;

&lt;p&gt;Here we have to add some newer CSS, like scroll-snapping. On older browsers, it degrades gracefully to the first version 👊. This makes it a very practical solution to use in production.&lt;/p&gt;
&lt;h3&gt;
  
  
  A gallery
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Feizi3b61l75v1v5q4l01.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Feizi3b61l75v1v5q4l01.gif" alt="Screen capture of the gallery" width="896" height="310"&gt;&lt;/a&gt;&lt;br&gt;
This is similar to snapping horizontal lists, but displaying one item at a time. An example is the Instagram Gallery. There's a row of dots below, one for each image, to indicate there are more images and which image we are currently viewing.&lt;/p&gt;

&lt;p&gt;The code is also identical to the second one. However, we don't need the gap and padding plus we add a few lines of JavaScript using the IntersectionObserver to show which dot corresponds to the currently visible image.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building the 3 different versions
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Free-scrolling horizontal lists
&lt;/h3&gt;

&lt;p&gt;We make a horizontal list with the list-items in a horizontal row by using flex-box and we allow the list to scroll horizontally. &lt;/p&gt;

&lt;p&gt;The list-items get an explicit size and a gap in between. &lt;/p&gt;

&lt;p&gt;We set padding, larger than the gap, so we can see when we've scrolled to the beginning or end of the list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;224px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;125px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-shrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.item&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:last-child&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see it here:&lt;br&gt;
&lt;iframe src="https://codesandbox.io/embed/horizontal-free-scrolling-list-basis-3lso3"&gt;
&lt;/iframe&gt;
&lt;br&gt;
It works, but we can improve it:&lt;/p&gt;
&lt;h4&gt;
  
  
  Contain overscrolling
&lt;/h4&gt;

&lt;p&gt;For some browsers, a swipe left is like using the back button. Try it out by forcefully scrolling to the beginning of the list. We can prevent this by setting the &lt;a href="https://css-tricks.com/almanac/properties/o/overscroll-behavior/" rel="noopener noreferrer"&gt;&lt;code&gt;overscroll-behavior&lt;/code&gt;&lt;/a&gt; to &lt;code&gt;contain&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Hide the scrollbar
&lt;/h4&gt;

&lt;p&gt;We can hide the scrollbar initially by setting &lt;code&gt;overflow-x&lt;/code&gt; to &lt;code&gt;auto&lt;/code&gt;. However, when you start scrolling it will appear again. We can set the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-width" rel="noopener noreferrer"&gt;&lt;code&gt;scrollbar-width&lt;/code&gt;&lt;/a&gt; to &lt;code&gt;none&lt;/code&gt; to completely hide it. At the time of writing, this only works in Firefox, so we add the following mess of unstandardized bastard CSS to hide it in other browsers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;-ms-overflow-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.list&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;small&gt;Looks much better, but if you feel this hurts accessibility or your CSS-purist-heart, you can leave it out and use &lt;code&gt;overflow-x: auto&lt;/code&gt; instead.&lt;/small&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Momentum scrolling
&lt;/h4&gt;

&lt;p&gt;In iOS it lacks the standard &lt;a href="https://css-tricks.com/snippets/css/momentum-scrolling-on-ios-overflow-elements/" rel="noopener noreferrer"&gt;momentum scrolling&lt;/a&gt;. We can tell the browser to scroll use momentum scrolling by setting the non-standard: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling" rel="noopener noreferrer"&gt;&lt;code&gt;-webkit-overflow-scrolling: touch;&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Prevent vertical scrolling
&lt;/h4&gt;

&lt;p&gt;We can scroll the page vertically while interacting with the list. We can disable this for touchscreen users by adding &lt;code&gt;touch-action: pan-x&lt;/code&gt; to the list. However, if your list covers the entire viewport, this will prevent the user from scrolling vertically. Best use it with caution!&lt;/p&gt;

&lt;h4&gt;
  
  
  List padding
&lt;/h4&gt;

&lt;p&gt;There's something weird going on with the padding of the &lt;code&gt;.list&lt;/code&gt;. It's on the start, but it has disappeared in the end 😕. To be honest, I have no idea why this occurs. There's a &lt;a href="https://www.brunildo.org/test/overscrollback.html" rel="noopener noreferrer"&gt;hacky fix though&lt;/a&gt;: an absolutely positioned (pseudo) element with a width of the padding peaking out of the scrolling items. &lt;/p&gt;

&lt;p&gt;It's ugly and it doesn't make any sense! How does this even work? However, it &lt;strong&gt;is&lt;/strong&gt; important that there is a padding, so it's clear that we've scrolled to the end of the list. With pain in our hearts, we'll add it.&lt;/p&gt;

&lt;p&gt;So now the CSS looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;overscroll-behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;scrollbar-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;touch-action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pan-x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;-ms-overflow-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;-webkit-overflow-scrolling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;touch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.list&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;224px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;125px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-shrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.item&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:last-child&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* hacky fix for padding at the end of the list */&lt;/span&gt;
&lt;span class="nc"&gt;.item&lt;/span&gt;&lt;span class="nd"&gt;:last-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.item&lt;/span&gt;&lt;span class="nd"&gt;:last-child::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it looks like this:&lt;br&gt;
&lt;iframe src="https://codesandbox.io/embed/horizontal-free-scrolling-list-enhanced-0muh1"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Snapping Horizontal lists
&lt;/h3&gt;

&lt;p&gt;Next we add &lt;a href="https://css-tricks.com/practical-css-scroll-snapping/" rel="noopener noreferrer"&gt;scroll snapping&lt;/a&gt;. First, we tell the list to always stop scrolling at a horizontal snapping point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;scroll-snap-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="n"&gt;mandatory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to the list-items we add &lt;code&gt;scroll-snap-align: start;&lt;/code&gt;, which means we snap to the start: on the left if you are using English or another left to right language*.&lt;/p&gt;

&lt;p&gt;If we look at “Suggested for You” on Instagram, the previous item is always peaking out a little bit. Turns out we can set the scroll padding on: &lt;code&gt;scroll-padding-inline-start: 20px;&lt;/code&gt;. (Note: I added &lt;code&gt;scroll-padding-left&lt;/code&gt;, since Safari lacks support for inline-start at the moment.)&lt;/p&gt;

&lt;p&gt;It is possible to swipe more items with one swipe. This is not possible on Instagram. We can add &lt;code&gt;scroll-snap-stop: always;&lt;/code&gt; to the list-items, but &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-stop#Browser_compatibility" rel="noopener noreferrer"&gt;browser support is is still spotty&lt;/a&gt; for now.&lt;/p&gt;

&lt;p&gt;That’s it!&lt;br&gt;
&lt;iframe src="https://codesandbox.io/embed/horizontal-scrolling-list-with-snapping-vulyn"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a&gt;*&lt;/a&gt;) On the right for the RTL homies out there 👋&lt;/small&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Instagram-like gallery
&lt;/h3&gt;

&lt;p&gt;If we make the list-items as wide as the scrolling area, and remove the padding and gap, it looks and behaves pretty much like the Instagram gallery. Except for the little indicator dots. Without the dots it will look like this:&lt;br&gt;
&lt;iframe src="https://codesandbox.io/embed/gallery-without-indicator-dots-cw8gq"&gt;
&lt;/iframe&gt;
&lt;br&gt;
We want to have these indicator dots, though. They are there for 3 reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Indicate that there is more to see, so it is clear a user can swipe to the next item.&lt;/li&gt;
&lt;li&gt;Indicate which image is currently visible.&lt;/li&gt;
&lt;li&gt;Indicate we have scrolled to the first or last item.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The easiest way is to let the browser take care of determining which item is visible by using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API" rel="noopener noreferrer"&gt;IntersectionObserver&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We make a list of dots, each dot corresponds to an image. When an item is visible (intersecting) in the list, we get that item’s index and set the indicator dot with the corresponding index to active.&lt;/p&gt;

&lt;p&gt;This is what it will look like, see the comments in the code above each section for an explanation of each step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// references to DOM elements&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;indicators&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.indicator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// create an observer with the list as intersection root&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onIntersectionObserved&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// observe each item&lt;/span&gt;
&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// when the observer detects an entry changing &lt;/span&gt;
&lt;span class="c1"&gt;// (item entering or exiting  list)&lt;/span&gt;
&lt;span class="c1"&gt;// and the entry is intersecting&lt;/span&gt;
&lt;span class="c1"&gt;// get the intersecting item’s index&lt;/span&gt;
&lt;span class="c1"&gt;// set the correct indicator to active&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onIntersectionObserved&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isIntersecting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intersectingIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;activateIndicator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intersectingIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// toggle an `active` class on the indicators&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;activateIndicator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;indicators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;indicator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;indicator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s how it looks&lt;br&gt;
&lt;iframe src="https://codesandbox.io/embed/gallery-with-indicator-dots-x1emk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A note on threshold&lt;/strong&gt;&lt;br&gt;
We set the threshold to 0.6. This means that if 60% of the item is visible, it counts as intersecting.&lt;/p&gt;

&lt;p&gt;If we set it to 1, we only count a completely visible item as intersecting. This would work fine with scroll snapping enabled, but doesn’t work as well with free-scrolling on older browsers without support for scroll snapping (perhaps with an IntersectionObserver polyfill).&lt;/p&gt;

&lt;p&gt;When we lower the threshold to somewhere below 1, we count a partly visible item as intersecting. If it’s 0.5 or below, multiple items could be intersecting. So 0.6 seems like a reasonable value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;h4&gt;
  
  
  The bad
&lt;/h4&gt;

&lt;p&gt;Since this uses native scrolling it is not possible to adjust the way the movement feels, we can’t control the ‘stickiness’ of the snapping or the ‘decay’ of the scroll motion. This is decided by the browser. If there’s a need to have control over this, I would choose a more JavaScript-heavy solution. Finally, it's definitely not the most pretty CSS with a hack and a few non-standard properties.&lt;/p&gt;

&lt;h4&gt;
  
  
  The good
&lt;/h4&gt;

&lt;p&gt;The small amount of code is pretty awesome. And the way it gracefully degrades in older browsers makes this a pretty solid technique in my opinion.&lt;/p&gt;

&lt;p&gt;I don't know the constraints which led to the decision to not use native scrolling for the Instagram gallery on their website, but I feel native scroll snapping feels more natural.&lt;/p&gt;

&lt;h4&gt;
  
  
  What about desktop?
&lt;/h4&gt;

&lt;p&gt;While horizontal scrolling feels very natural on touch devices, it is a little awkward and unintuitive on desktop. Buttons to move to the left and right help, the Instagram website does this as well.&lt;/p&gt;

&lt;p&gt;Happy hacking, and let me know if you would use this technique in production. 🤘&lt;/p&gt;

&lt;p&gt;Bonus tip: if you want to use the indicators as navigation, &lt;code&gt;scrollIntoView({ behavior: 'smooth', inline: 'start' })&lt;/code&gt; is a good place to start!&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>TIL a better way to handle in/out transitions</title>
      <dc:creator>Joost Kiens</dc:creator>
      <pubDate>Mon, 20 Apr 2020 06:26:34 +0000</pubDate>
      <link>https://dev.to/joostkiens/til-a-better-way-to-handle-in-out-transitions-2bc1</link>
      <guid>https://dev.to/joostkiens/til-a-better-way-to-handle-in-out-transitions-2bc1</guid>
      <description>&lt;p&gt;Using CSS grid &amp;amp; the hidden attribute to simplify in/out transitions.&lt;/p&gt;

&lt;p&gt;If I need to replace an element with another element, it's often a good user experience if there's a transition. A card gets replaced with another card, maybe a carousel, whatever. &lt;/p&gt;

&lt;p&gt;In other words, I want to animate something that disappears and, &lt;em&gt;at the same time and place&lt;/em&gt;, animate another thing that appears.&lt;/p&gt;

&lt;p&gt;I always found coding this to be a bit clunky because there are two annoying problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two elements need to be positioned in the same space. &lt;/li&gt;
&lt;li&gt;I will need to keep the disappearing element around during the animation; I can’t remove it at the moment it makes the most sense.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This morning I came across a &lt;a href="https://twitter.com/DavidKPiano/status/1207308369687699456" rel="noopener noreferrer"&gt;tweet&lt;/a&gt; by &lt;a href="https://twitter.com/DavidKPiano" rel="noopener noreferrer"&gt;David K. Piano&lt;/a&gt; that got me all excited. He offers solutions to both these problems, way better solutions than I’ve ever used before!&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1207308369687699456-7" src="https://platform.twitter.com/embed/Tweet.html?id=1207308369687699456"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1207308369687699456-7');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1207308369687699456&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  How to position 2 elements in the same space?
&lt;/h2&gt;

&lt;p&gt;CSS and the DOM aren’t really good at positioning 2 elements in the same place*. There are not many ways to accomplish this. &lt;/p&gt;

&lt;p&gt;Previously I’ve used  &lt;code&gt;position: absolute&lt;/code&gt; on those 2 elements. This works well, but now both elements are taken out of the layout flow, so don’t take any space anymore. To combat this you could read the height and width of these elements and set these on their parent. But what if the dimensions change? Add a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver" rel="noopener noreferrer"&gt;&lt;code&gt;ResizeObserver&lt;/code&gt;&lt;/a&gt;? The code gets quite complex for something so basic.&lt;/p&gt;

&lt;p&gt;That’s why I was so excited when I read David’s solution: using CSS Grid to create overlapping elements 😲.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.parent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;grid-template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.transitioningChildren&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  So what’s going on here?
&lt;/h3&gt;

&lt;p&gt;We are telling the parent to make a grid with one row and one column. The children are all positioned in the area of the grid, which occupies the 1st row and the 1st column. &lt;/p&gt;

&lt;p&gt;This will stack all the &lt;code&gt;.transitioningChildren&lt;/code&gt; on top of each other. 💥Boom💥.&lt;/p&gt;

&lt;p&gt;And what’s more: the grid will automatically expand to the width of its widest child and to the height of its highest child (weird sentence, but ok…). Freaking genius!!!&lt;/p&gt;

&lt;p&gt;I absolutely love how something designed for a completely different reason (grid layouts), fits so well in this use case (positioning elements on top of each other for animation).&lt;/p&gt;

&lt;p&gt;&lt;a&gt;*&lt;/a&gt;) Except for SVG, elements inside an SVG stack on top of each other by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to animate something that’s disappearing?
&lt;/h2&gt;

&lt;p&gt;It isn’t possible to animate something that’s not there. &lt;/p&gt;

&lt;p&gt;In order to work around this, I have seen solutions where both the appearing and disappearing elements are kept around during the animation. The disappearing element is removed after the animation is complete. React-spring’s &lt;a href="https://www.react-spring.io/docs/hooks/use-transition" rel="noopener noreferrer"&gt;&lt;code&gt;useTransition&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://reactcommunity.org/react-transition-group/" rel="noopener noreferrer"&gt;&lt;code&gt;ReactTransitionGroup&lt;/code&gt;&lt;/a&gt; work this way. &lt;/p&gt;

&lt;p&gt;But this technique isn’t without its drawbacks: screen readers see both elements, I could tab to a link or button in an element that’s disappearing, and so on.&lt;/p&gt;

&lt;p&gt;I could throw more code at it to solve these issues, but it’s a hassle.&lt;/p&gt;

&lt;p&gt;And what if I just want a simple CSS transition and don’t want the added complexity of these tools?&lt;/p&gt;

&lt;p&gt;The magic that these tools add is keeping the disappearing element around for long enough to finish the animation. But what if I could just leave the element in the DOM? If only it would not interfere with screen readers, keyboard navigation, layout, blah blah blah. This would make my life a lot easier.&lt;/p&gt;

&lt;p&gt;Turns out we can use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/hidden" rel="noopener noreferrer"&gt;&lt;code&gt;hidden&lt;/code&gt;&lt;/a&gt; attribute for all these requirements. And what’s even more impressive is how we can use the &lt;code&gt;hidden&lt;/code&gt; attribute as a &lt;strong&gt;selector&lt;/strong&gt; and transition from and to the hidden state.&lt;/p&gt;

&lt;p&gt;The hidden attribute sets &lt;code&gt;display: none&lt;/code&gt; in the browser's stylesheet. So we do need to explicitly declare another display property on the element to override it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.transitioningChild&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;.3s&lt;/span&gt; &lt;span class="m"&gt;.3s&lt;/span&gt; &lt;span class="n"&gt;cubic-bezier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;transition-property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.transitioningChild&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;hidden&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transition-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whuuut! Awesome!&lt;/p&gt;

&lt;p&gt;I would definitely use this in situations where I don’t mind keeping hidden elements around.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s okay to keep elements in the DOM. It’s not like you have to pay DOM rent for them to stay there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s a demo showing both these principles:&lt;br&gt;
&lt;iframe src="https://codesandbox.io/embed/funny-wind-p59ei"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser support
&lt;/h3&gt;

&lt;p&gt;This holds up surprisingly well in modern browsers, even in IE11! It uses an older spec for CSS Grid, but with a few tweaks, the result is the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-ms-grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;-ms-grid-rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;-ms-grid-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;grid-template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;-ms-grid-row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;-ms-grid-column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hidden attribute is supported as well in all modern browsers and IE11.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;I’ve used grid-areas, and -templates before, I knew about the hidden attribute, but I never put together how they could work together to help with in/out transitions.&lt;/p&gt;

&lt;p&gt;If you find this useful, follow &lt;a href="https://twitter.com/DavidKPiano" rel="noopener noreferrer"&gt;David&lt;/a&gt; (or &lt;a href="https://twitter.com/joostkiens" rel="noopener noreferrer"&gt;me&lt;/a&gt;😅) on Twitter for more tips like these.&lt;/p&gt;

</description>
      <category>css</category>
      <category>animations</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Transitioned from Ease to Spring Animations</title>
      <dc:creator>Joost Kiens</dc:creator>
      <pubDate>Tue, 03 Mar 2020 06:45:59 +0000</pubDate>
      <link>https://dev.to/joostkiens/how-i-transitioned-from-ease-to-spring-animations-50dk</link>
      <guid>https://dev.to/joostkiens/how-i-transitioned-from-ease-to-spring-animations-50dk</guid>
      <description>&lt;h1&gt;
  
  
  Lessons learned while building react-spring visualizer.
&lt;/h1&gt;

&lt;p&gt;At &lt;a href="https://kaliber.net" rel="noopener noreferrer"&gt;work&lt;/a&gt; we have been transitioning towards &lt;a href="https://www.react-spring.io/" rel="noopener noreferrer"&gt;react-spring&lt;/a&gt; and it has quickly become my favorite UI-animation library when working with React; I love its small footprint, modern hook-based API and performant animations without re-renders.&lt;/p&gt;

&lt;p&gt;One strong point of react-spring is that it supports physics-based animations, especially those based on springs. These types of animation feel very natural. However, coming from using eases and durations in CSS and libraries such as GSAP, there was &lt;strong&gt;a lot&lt;/strong&gt; that I didn't get at first.&lt;/p&gt;

&lt;p&gt;React-spring's configuration accepts properties such as &lt;strong&gt;tension&lt;/strong&gt;, &lt;strong&gt;mass&lt;/strong&gt;, &lt;strong&gt;friction&lt;/strong&gt; and &lt;strong&gt;velocity&lt;/strong&gt;. While I sort of understood the meaning of these words, I had no idea what they meant in the context of a spring animation. Which values would I need to give them to hit that sweet spot, where the animation feels just right?&lt;/p&gt;

&lt;p&gt;So I built a tool to help me visualize how these values impact an animation: &lt;a href="https://react-spring-visualizer.com/" rel="noopener noreferrer"&gt;react-spring visualizer&lt;/a&gt;. In the process of building this tool, I tried to understand how spring based animations work. Below are the things I've learned along the way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fs53946bcbyn6mx0467yo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fs53946bcbyn6mx0467yo.png" alt="UI of React-spring visualizer" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the way, the same things apply to any spring animation, not just react-spring. They are just as relevant if you use &lt;a href="https://github.com/chenglou/react-motion" rel="noopener noreferrer"&gt;React-Motion&lt;/a&gt;, &lt;a href="https://www.framer.com/motion/" rel="noopener noreferrer"&gt;Framer motion&lt;/a&gt; or &lt;a href="https://animejs.com/" rel="noopener noreferrer"&gt;Anime.js&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, a Refresher on Easing
&lt;/h2&gt;

&lt;p&gt;When you think of an object, for example a car, that goes from A to B, it will not immediately reach full speed and it won't stop immediately when it reaches its destination. Instead it will slowly increase speed, reach its top-speed and slow down before eventually coming to a halt. It will ease in and ease out.&lt;/p&gt;

&lt;p&gt;The same goes for UI elements, compare the two animations below:&lt;br&gt;
&lt;iframe src="https://codesandbox.io/embed/linear-vs-ease-in-out-v6ncs"&gt;
&lt;/iframe&gt;Tap to play again.
&lt;/p&gt;

&lt;p&gt;The top one, which moves at a constant speed, feels mechanical and a bit awkward. The bottom one eases in when starting, reaches full speed and eases out before arriving at its destination. Since this mimics the movement of the car above, it feels more natural.&lt;/p&gt;

&lt;p&gt;CSS, GSAP, anime.js, even jQuery come with some preset eases, so it's safe to say most frontend developers are familiar with them. A good list of commonly used eases can be found in this &lt;a href="https://easings.net/en" rel="noopener noreferrer"&gt;Easing Functions Cheat Sheet&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  So How do Ease-based Animations Work?
&lt;/h2&gt;

&lt;p&gt;An animation with easing always needs at least 2 things: a &lt;strong&gt;duration&lt;/strong&gt; usually expressed in seconds or milliseconds and an &lt;strong&gt;easing function&lt;/strong&gt; representing a &lt;em&gt;curve&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example of an easing curve named quadIn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;quadIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a quadratic function, that's why it's called &lt;em&gt;quadIn&lt;/em&gt;. If you plot the curve of this function it would look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqma81hl7mow04qkdqmhd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqma81hl7mow04qkdqmhd.png" alt="A graphic representation of a quadIn easing function" width="800" height="643"&gt;&lt;/a&gt;&lt;/p&gt;
A graphic representation of a quadIn easing function.



&lt;p&gt;The start of the progress of the animation is 0 and the end is 1. If you would move a variable called x from 0 to 400 pixels in 750 milliseconds, you would calculate the current position for each frame by interpolating between the values like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;750&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;quadIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Delta&lt;/em&gt; is the time since the start of the animation, so after after 200 milliseconds x would be roughly at 28.4 pixels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;750&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;750&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="c1"&gt;// ≈ 28.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After 700 milliseconds x is at 348.4 pixels, and so on. Below is an example of a quadIn animation.&lt;br&gt;
&lt;iframe src="https://codesandbox.io/embed/ease-quad-in-zmxfu"&gt;
&lt;/iframe&gt;Tap to play again.
&lt;/p&gt;

&lt;p&gt;By the way, above I stated we are &lt;em&gt;interpolating between values&lt;/em&gt;. An animator might express the same principle by saying they are inbetweening or &lt;em&gt;tweening&lt;/em&gt; between &lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/Key_frame" rel="noopener noreferrer"&gt;keyframes&lt;/a&gt;&lt;/em&gt;. It took me an embarrassingly long time to realize where all these terms originated 🤷‍♂️.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why use Spring Animations?
&lt;/h2&gt;

&lt;p&gt;While eases look a lot &lt;a href="https://dribbble.com/shots/2092963-Event-Website-Design" rel="noopener noreferrer"&gt;nicer&lt;/a&gt; than linear animations, there's still something unnatural about them; things in the real world rarely move according to a fixed duration and a mathematical function.&lt;/p&gt;

&lt;p&gt;For example, what would the easing function of a &lt;a href="https://giphy.com/gifs/falling-funny-alUHKvX7BJXwc" rel="noopener noreferrer"&gt;falling object&lt;/a&gt; be? Easing functions are an approximation of how things move in the real world. But we can do better!&lt;/p&gt;

&lt;p&gt;In order to mimic how physical objects move, we have to look at… well, physics. And one aspect of physics is especially useful when working with UI animations: the way springs move.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/winter-monad-y6gn8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Spring animations are very useful for UI animations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Physics-based animations feel more natural than ease-based animations.&lt;/li&gt;
&lt;li&gt;Spring animations are more intuitive compared to ease-based animations.&lt;/li&gt;
&lt;li&gt;Physics-based animations are interruptible!&lt;/li&gt;
&lt;li&gt;Because spring animations take an initial velocity, they are well adapted to go from a swipe or drag to an animation after release.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's no wonder they are used extensively in native apps on Android and especially iOS, but for some reason the web trails behind. If you are interested in learning why spring animations work so well, I can highly recommend this &lt;a href="https://developer.apple.com/videos/play/wwdc2018/803/" rel="noopener noreferrer"&gt;talk by Apple engineers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a comparison, see the two animations below:&lt;br&gt;
&lt;iframe src="https://codesandbox.io/embed/ease-vs-spring-d6p94"&gt;
&lt;/iframe&gt;Tap to play again.
&lt;/p&gt;
&lt;h2&gt;
  
  
  So How Do Spring Animations Work?
&lt;/h2&gt;

&lt;p&gt;Earlier we saw that for ease-based animations we check every frame: &lt;em&gt;what's my position based on this time and this function?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Spring animations work a bit differently: each frame we check: &lt;em&gt;based on the current position, velocity, mass, tension, and friction what will the position be in the next frame?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In order to do that we need to know a few properties, like &lt;strong&gt;mass&lt;/strong&gt;, &lt;strong&gt;tension&lt;/strong&gt;, &lt;strong&gt;friction&lt;/strong&gt; and &lt;strong&gt;velocity&lt;/strong&gt;. In react-spring these are defined in the &lt;strong&gt;&lt;a href="https://www.react-spring.io/docs/hooks/api#configs" rel="noopener noreferrer"&gt;config&lt;/a&gt;&lt;/strong&gt;. It looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSpring&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;tension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;170&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;mass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;friction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;precision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  So what do these terms mean?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl5y6hbfu47znwj83ccsh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl5y6hbfu47znwj83ccsh.png" alt="Parts of a spring animation" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine you have a spring hanging from a ceiling with a "bob" on the end. If the spring is at rest, it is at its resting point (or at equilibrium). Pull the bob down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mass&lt;/strong&gt; will be the mass of this bob. While not technically the same, you can think of it as the weight.&lt;/p&gt;

&lt;p&gt;The spring itself has a property called &lt;strong&gt;tension&lt;/strong&gt;. This is a constant and in the real world is determined by the spring's material, the thickness of the coils, etcetera. Sometimes this is also called &lt;strong&gt;rate&lt;/strong&gt;, &lt;strong&gt;spring constant&lt;/strong&gt; or &lt;strong&gt;stiffness&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Friction&lt;/strong&gt; is a force acting on the bob reducing the motion of the spring. I prefer to think of it as the density of the surrounding medium, for example air. Air would have lower friction than water or molasses. This is also sometimes referred to as &lt;strong&gt;damping&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then there's the &lt;strong&gt;velocity&lt;/strong&gt;, which is the initial velocity or speed the spring has when it is released. As if you give the bob a push. Or a pull for a negative velocity.&lt;/p&gt;

&lt;p&gt;When the spring reaches its resting point it will bounce around for a while. With each iteration the amplitude will be smaller. &lt;strong&gt;Precision&lt;/strong&gt; is the amplitude at which the animation is stopped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The physics
&lt;/h2&gt;

&lt;p&gt;So if our starting point is 0, how do we get to 400 with these config values?&lt;/p&gt;

&lt;p&gt;We first calculate the spring force (Fs), by using &lt;a href="https://en.wikipedia.org/wiki/Hooke's_law" rel="noopener noreferrer"&gt;Hooke's law&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnlka701j61aybe1hpose.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnlka701j61aybe1hpose.png" alt="Hooke's law" width="166" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where &lt;strong&gt;k&lt;/strong&gt; is the &lt;em&gt;tension&lt;/em&gt; of the spring and &lt;strong&gt;x&lt;/strong&gt; is the &lt;em&gt;displacement&lt;/em&gt;. The displacement is &lt;em&gt;the distance between the current length of the spring and the spring length at rest&lt;/em&gt; (see image above).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displacement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentPosition&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;toPosition&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;springForce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;tension&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;displacement&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we determine the friction force:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frictionForce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;friction&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;currentVelocity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;em&gt;currentVelocity&lt;/em&gt; is the velocity in the current frame, not the initial velocity in the configuration.&lt;/p&gt;

&lt;p&gt;From here we can calculate the acceleration, the new velocity and finally the new position, using the &lt;a href="https://en.wikipedia.org/wiki/Equations_of_motion#Constant_linear_acceleration_in_any_direction" rel="noopener noreferrer"&gt;equations of motion&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acceleration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;springForce&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;frictionForce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;mass&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newVelocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentVelocity&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;acceleration&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentPosition&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;newVelocity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the next frame the &lt;em&gt;newVelocity&lt;/em&gt; and &lt;em&gt;newPosition&lt;/em&gt; become the &lt;em&gt;currentVelocity&lt;/em&gt; and &lt;em&gt;currentPosition&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;All together it looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;velocity&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;
&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tensionForce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;tension&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentPosition&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;toPosition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dampingForce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;friction&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;velocity&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acceleration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tensionForce&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dampingForce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;mass&lt;/span&gt;
  &lt;span class="nx"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;acceleration&lt;/span&gt;
  &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;velocity&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;precision&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;    
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: this is a very simple example. The folks behind react-spring use a few tricks to make the results more accurate and performant. You can see their &lt;a href="https://github.com/react-spring/react-spring/blob/cd5548a987383b8023efd620f3726a981f9e18ea/src/animated/FrameLoop.ts#L78" rel="noopener noreferrer"&gt;code here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building react-spring visualizer
&lt;/h2&gt;

&lt;p&gt;When I set out to build &lt;a href="https://react-spring-visualizer.com/" rel="noopener noreferrer"&gt;react-spring visualizer&lt;/a&gt;, at first I wanted to use a chart, similar to the excellent &lt;a href="https://greensock.com/ease-visualizer/" rel="noopener noreferrer"&gt;ease visualizer from GSAP&lt;/a&gt;. But because we are not working with predefined curves and durations, it didn't feel right. So I chose to show the effects of the parameters in a spring.&lt;/p&gt;

&lt;p&gt;I have added some visualizers for the most common types of animations: transforms (opacity, translate and rotate) and opacity. Those are the properties I use most of the time when animating DOM elements, since they can be modified by &lt;a href="https://developers.google.com/web/fundamentals/performance/rendering" rel="noopener noreferrer"&gt;compositing&lt;/a&gt; alone and don't trigger expensive repaints.&lt;/p&gt;

&lt;p&gt;While building this tool, I had the chance to take deep-dive into spring animations, learning way more than I could ever have from reading the documentation (which is a bit sparse) and examples (which are gorgeous, but a bit complex).&lt;/p&gt;

&lt;p&gt;I built the app on top of &lt;a href="https://kaliber.net/" rel="noopener noreferrer"&gt;our&lt;/a&gt; own build stack: &lt;a href="https://kaliberjs.github.io/build/" rel="noopener noreferrer"&gt;kaliber/build&lt;/a&gt; based on webpack and PostCSS with SSR out of the box. It's highly opinionated, but requires zero configuration and it's &lt;a href="https://github.com/kaliberjs/build" rel="noopener noreferrer"&gt;open source&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;We use it for all our projects with React, but also for our WordPress based projects. One really cool feature is how it allows you to easily have smaller React applications inside a WordPress site with data from the CMS.&lt;/p&gt;

&lt;p&gt;I was allowed to spend some time at work writing this article, as long as I plugged our job vacancies 😁. If you have read this far you're obviously the kind of person we are looking for, so why not join us as &lt;a href="https://werkenbij.kaliber.net/" rel="noopener noreferrer"&gt;frontend developer&lt;/a&gt; at Kaliber?&lt;/p&gt;

&lt;p&gt;By the way, if you want to know more about how position, velocity and acceleration work together, I can highly recommend checking out &lt;a href="https://www.youtube.com/watch?v=uHusbFmq-4I&amp;amp;list=PLRqwX-V7Uu6ZwSmtE13iJBcoI-r4y7iEc&amp;amp;index=4" rel="noopener noreferrer"&gt;The Nature of Code&lt;/a&gt; by Daniel Shiffman. He also has a great &lt;a href="https://www.youtube.com/watch?v=cluKQOY92Dw" rel="noopener noreferrer"&gt;video about springs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>animations</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Get the most out of your react-spring configuration</title>
      <dc:creator>Joost Kiens</dc:creator>
      <pubDate>Mon, 13 Jan 2020 06:47:09 +0000</pubDate>
      <link>https://dev.to/joostkiens/get-the-most-out-of-your-react-spring-configuration-6o3</link>
      <guid>https://dev.to/joostkiens/get-the-most-out-of-your-react-spring-configuration-6o3</guid>
      <description>&lt;h1&gt;
  
  
  React-spring
&lt;/h1&gt;

&lt;p&gt;My favorite solution for UI animations when working with React is &lt;a href="https://www.react-spring.io/" rel="noopener noreferrer"&gt;react-spring&lt;/a&gt;, a spring-physics based animation library.&lt;/p&gt;

&lt;p&gt;I love it for its simple, declarative, hook-based API and animation updates without re-renders.&lt;/p&gt;

&lt;p&gt;In case you're not familiar, the code might look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;animated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useSpring&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-spring&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;visible&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;opacity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSpring&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;visible&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;mass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;friction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;animated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;opacity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;BTW, if you are not familiar, &lt;a href="https://www.react-spring.io/" rel="noopener noreferrer"&gt;check it out&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;However, as a newbie to spring-based animations, I've had a hard time knowing which effect the different config settings would have.&lt;/p&gt;

&lt;p&gt;I believe I know what &lt;code&gt;mass&lt;/code&gt; is, and I can sort of imagine what &lt;code&gt;tension&lt;/code&gt; is in the context of a spring. But how would these values impact my animation? I found myself often changing the parameters and replaying the animation in the hope it would look good.&lt;/p&gt;

&lt;p&gt;In order to take the guess-work out and get the most out of react-spring, I built a &lt;a href="https://react-spring-visualizer.com/" rel="noopener noreferrer"&gt;visualizer&lt;/a&gt; to help me find the optimal config for a specific animation.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://react-spring-visualizer.com/" rel="noopener noreferrer"&gt;React-spring visualizer&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8u8xs2qzawzhx0c1mjn7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8u8xs2qzawzhx0c1mjn7.gif" alt="React-spring visualizer in spring view" width="760" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the left side you can change the config values for spring animations, on the right side you can see the animation itself. &lt;/p&gt;

&lt;p&gt;In the default "spring" view, the impact of &lt;code&gt;mass&lt;/code&gt;, &lt;code&gt;tension&lt;/code&gt;, &lt;code&gt;friction&lt;/code&gt; and &lt;code&gt;clamp&lt;/code&gt; on a spring are visualized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Mass&lt;/code&gt; changes the size of the "bob" on the end of the spring.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Tension&lt;/code&gt; changes the amount the spring is pulled from its resting point.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Friction&lt;/code&gt; changes the scale of the downward arrow in the top left.&lt;/li&gt;
&lt;li&gt;Selecting &lt;code&gt;clamp&lt;/code&gt; adds a barrier just above the spring's resting point.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are 4 other displays to see how your config will look:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;translate&lt;/li&gt;
&lt;li&gt;scale&lt;/li&gt;
&lt;li&gt;rotate&lt;/li&gt;
&lt;li&gt;opacity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can access them with the buttons below the visualizer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlyh4dmg7wcjk979r2qf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlyh4dmg7wcjk979r2qf.gif" alt="React-spring visualizer in opacity view" width="635" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are happy with your configuration, use the copy-to-clipboard button to copy the settings.&lt;/p&gt;

&lt;p&gt;I would really appreciate it if you could have a look &amp;amp; let me know what you think!&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>animation</category>
    </item>
  </channel>
</rss>
