<?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>Forem: andy</title>
    <description>The latest articles on Forem by andy (@ajsmth).</description>
    <link>https://forem.com/ajsmth</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%2F200529%2F43543152-40bd-47b9-97e7-7e3c7607c6bb.jpeg</url>
      <title>Forem: andy</title>
      <link>https://forem.com/ajsmth</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ajsmth"/>
    <language>en</language>
    <item>
      <title>Building a pager component from scratch* -- part 2</title>
      <dc:creator>andy</dc:creator>
      <pubDate>Wed, 24 Jul 2019 01:33:47 +0000</pubDate>
      <link>https://forem.com/ajsmth/building-a-pager-component-from-scratch-part-2-557l</link>
      <guid>https://forem.com/ajsmth/building-a-pager-component-from-scratch-part-2-557l</guid>
      <description>&lt;p&gt;If you missed the first part of this lesson, it can be found here: &lt;a href="https://dev.to/ajsmth/building-a-pager-component-from-scratch-4nlh"&gt;https://dev.to/ajsmth/building-a-pager-component-from-scratch-4nlh&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this part, we'll add on to the pager component we've already created by handling animations and gestures to page between child views&lt;/p&gt;

&lt;p&gt;The first thing we'll add is spring animations when the &lt;code&gt;activeIndex&lt;/code&gt; prop changes. In order to do so, let's bring in &lt;code&gt;react-spring&lt;/code&gt; and import some of it's functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add react-spring 

import {animated, useSpring} from 'react-spring'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Adding spring page transitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Pager({ children, activeIndex, size }) {
  // the total offset of the container div -- based on activeIndex
  const translateX = `translateX(calc(${activeIndex * -100}%))`;

  // this will animate changes in activeIndex between pages:
  const animatedStyle = useSpring({ transform: translateX })

  return (
    &amp;lt;div ...&amp;gt;

      {/* Update to animated.div */}
      &amp;lt;animated.div
        style={{
          ...absoluteFill,
          // we will translate this container view to bring children into focus
          ...animatedStyle
        }}
      &amp;gt;
        {React.Children.map(children, (element, index) =&amp;gt; (
          &amp;lt;PageView index={index} width={size}&amp;gt;
            {element}
          &amp;lt;/PageView&amp;gt;
        ))}
      &amp;lt;/animated.div&amp;gt;

    &amp;lt;/div&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we have a spring animation that transitions between page changes&lt;/p&gt;

&lt;p&gt;Next, we'll want to add support for handling swipe gestures. Again, we'll need a helper library&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add react-use-gesture

import {useDrag} from 'react-use-gesture'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will help us track the drag value on the container view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Pager({ children, activeIndex, size }) {
  // ...

  const [{ dx }, set] = useSpring(() =&amp;gt; ({ dx: 0 }));

  const bind = useDrag(({ delta }) =&amp;gt; {
    const [dx] = delta;
    set({ dx: dx });
  });

  const dragX = dx.interpolate(dx =&amp;gt; `translateX(${dx}px)`);

  {/* Change this container to animated.div */}
  return (
    &amp;lt;animated.div
      {...bind()}
      style={{
        ...
        transform: dragX
      }}
    &amp;gt;
     {...}
    &amp;lt;/animated.div&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You'll notice that after releasing, the translation value needs to reset in order to recenter the view. To achieve this let's update the useDrag() callback we just wrote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const bind = useDrag(({ delta, last }) =&amp;gt; {
    const [dx] = delta;
    set({ dx: dx });

    // last means they've released from dragging
    if (last) {
      set({ dx: 0 });
    }
  });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now the view re-centers after release. &lt;/p&gt;

&lt;p&gt;So far, so good. What we need to consider now is how far the user has dragged, and if it's beyond a certain threshold, let's update the activeIndex so the next / previous view becomes focused. &lt;/p&gt;

&lt;p&gt;The first thing we'll want to do is determine the threshold for when we should change -- in our case I'll set it to an arbitrary value of +/- 100:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const bind = useDrag(({ delta, last }) =&amp;gt; {
    const [dx] = delta;
    set({ dx: dx });

    // last means they've released from dragging
    if (last) {
      if (dx &amp;gt; DX_THRESHOLD) {
        // transition to previous view
      }

      if (dx &amp;lt; -DX_THRESHOLD) {
        // transition to next view
      }

      set({ dx: 0 });
    }
  });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we can use a callback prop to update the &lt;code&gt;activeIndex&lt;/code&gt; prop and properly focus the previous / next page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// add an onChange prop:
function Pager({ ..., onChange }) {

  ...

  // callback to onChange prop with new active value:

  const bind = useDrag(({ delta, last }) =&amp;gt; {
    const [dx] = delta;
    set({ dx: dx });

    // last means they've released from dragging
    if (last) {
      if (dx &amp;gt; DX_THRESHOLD) {
        // transition to previous view
        onChange(activeIndex - 1)
      }

      if (dx &amp;lt; -DX_THRESHOLD) {
        // transition to next view
        onChange(activeIndex + 1)
      }

      set({ dx: 0 });
    }
  });

  ...
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The last thing that we can do is to remove the border around our container view (if you still have it in your styles) and add an &lt;code&gt;overflow: hidden&lt;/code&gt; style, if you'd like to hide the unfocused views. &lt;/p&gt;

&lt;p&gt;One last note -- in practice, we might want to compute the threshold as a percentage of the total width, or whatever value you think works best. &lt;/p&gt;

&lt;p&gt;The source for this can be viewed here: &lt;a href="https://codesandbox.io/s/intelligent-cache-5f366"&gt;https://codesandbox.io/s/intelligent-cache-5f366&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now have a serviceable pager component that handles gestures and animates page transitions&lt;/p&gt;

&lt;p&gt;What we'll look at next is opening up the pager API to work as a controlled and an uncontrolled component, as well as a psuedo-virtualization for child views which might help with with your app's performance. We'll also take a look at some jank that occurs in our existing implementation&lt;/p&gt;

</description>
      <category>react</category>
    </item>
    <item>
      <title>Building a pager component from scratch*</title>
      <dc:creator>andy</dc:creator>
      <pubDate>Tue, 23 Jul 2019 23:21:13 +0000</pubDate>
      <link>https://forem.com/ajsmth/building-a-pager-component-from-scratch-4nlh</link>
      <guid>https://forem.com/ajsmth/building-a-pager-component-from-scratch-4nlh</guid>
      <description>&lt;p&gt;In this tutorial we'll go through the steps of building a pager component from "scratch" (not entirely accurate -- we'll use a few dependencies to help with handling gestures and animations later on). This first article will capture the core behaviour of the pager component before moving onto things like animations and gestures.&lt;/p&gt;

&lt;p&gt;I originally implemented this in react-native, so an example of what the final product will (sort of) look like can be found here: &lt;a href="https://github.com/ajsmth/react-native-pager-component#readme"&gt;https://github.com/ajsmth/react-native-pager-component#readme&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I know that components for this kind of thing already exist -- this was made for my own learning, and hopefully yours too.&lt;/p&gt;

&lt;p&gt;Before getting into the code, let's think about what we want to do...&lt;/p&gt;

&lt;p&gt;We want a component that, when given a certain index, moves the matching child index into focus. So we'll be thinking in terms of indexes -- when the &lt;code&gt;activeIndex&lt;/code&gt; prop changes, we'll want to shift the focus to the child that matches that index: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;onChange(activeIndex) -&amp;gt; updateTranslation(activeIndex)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can achieve this by laying out all of our children horizontally inside a container view, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const absoluteFill = {
  position: "absolute",
  left: 0,
  right: 0,
  bottom: 0,
  top: 0
};

function Pager({ children, activeIndex, size }) {
  return (
    &amp;lt;div
      style={{
        display: "flex",
        alignSelf: "center",

        position: "relative",
        width: size,
        height: size,
        border: "2px solid green"
      }}
    &amp;gt;
      &amp;lt;div
        style={{
          ...absoluteFill
          // we will translate this container view
        }}
      &amp;gt;
        {React.Children.map(children, (element, index) =&amp;gt; (
          &amp;lt;PageView index={index} width={size}&amp;gt;
            {element}
          &amp;lt;/PageView&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function PageView({ children, index }) {
  // position each page side-by-side based on it's index
  const position = `translateX(calc(${100 * index}%))`;

  return (
    &amp;lt;div
      style={{
        ...absoluteFill,
        transform: position,
        border: "thin solid red"
      }}
    &amp;gt;
      {children}
    &amp;lt;/div&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now all we have to do is update the translation value of the inner container based on the &lt;code&gt;activeIndex&lt;/code&gt; prop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Pager({ children, activeIndex, size }) {
  // the total offset of the container div -- based on activeIndex
  const translateX = `translateX(${activeIndex * -100}%)`;

  return (
    &amp;lt;div ...&amp;gt;
      &amp;lt;div
        style={{
          ...absoluteFill,
          transform: translateX
        }}
      &amp;gt;
        {React.Children.map(children, (element, index) =&amp;gt; (
          &amp;lt;PageView index={index} width={size}&amp;gt;
            {element}
          &amp;lt;/PageView&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The inner container now shifts its focus by computing its offset from the &lt;code&gt;activeIndex&lt;/code&gt; prop. This is a pretty basic implementation so far, but hopefully you can see that it captures the core behaviour of what the final component will do. &lt;/p&gt;

&lt;p&gt;The next steps will be to animate page transitions, and handle gestures to update the &lt;code&gt;activeIndex&lt;/code&gt; based on user interaction. We'll also open up the component's API to make it fully controllable for use in other projects.&lt;/p&gt;

&lt;p&gt;To see a full example visit &lt;a href="https://codesandbox.io/s/modern-moon-o5etr"&gt;https://codesandbox.io/s/modern-moon-o5etr&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next article, we'll animate the transitions between pages: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/ajsmth/building-a-pager-component-from-scratch-part-2-557l"&gt;https://dev.to/ajsmth/building-a-pager-component-from-scratch-part-2-557l&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
    </item>
  </channel>
</rss>
