<?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: John Wika Haakseth</title>
    <description>The latest articles on DEV Community by John Wika Haakseth (@haakseth).</description>
    <link>https://dev.to/haakseth</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%2F320383%2Fca2d1ac5-93d7-45e7-bebe-69ea2f37b069.jpg</url>
      <title>DEV Community: John Wika Haakseth</title>
      <link>https://dev.to/haakseth</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/haakseth"/>
    <language>en</language>
    <item>
      <title>Creating custom webmap animations - Adding easing</title>
      <dc:creator>John Wika Haakseth</dc:creator>
      <pubDate>Sun, 02 Feb 2020 17:18:18 +0000</pubDate>
      <link>https://dev.to/haakseth/creating-custom-webmap-animations-adding-easing-2n5g</link>
      <guid>https://dev.to/haakseth/creating-custom-webmap-animations-adding-easing-2n5g</guid>
      <description>&lt;p&gt;From the previous parts of this series, we have a pretty good animation. There is still one more thing to do to create an even more pleasant animation: Easing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add easing
&lt;/h2&gt;

&lt;p&gt;Right now our animation is linear. In nature, most motion has an acceleration component to it. This mean that we as humans are used to it, and we should use it in web-animations as well, to make our interfaces more pleasant to use.&lt;/p&gt;

&lt;p&gt;I found a good source of easing functions for javascript &lt;a href="https://gist.github.com/gre/1650294"&gt;here&lt;/a&gt;, and some nice visualizations &lt;a href="https://easings.net/en"&gt;here&lt;/a&gt;. &lt;a href="https://developers.google.com/web/fundamentals/design-and-ux/animations/the-basics-of-easing"&gt;Google Developers&lt;/a&gt; also has a well-written article on the subject. For this example, let’s try the following function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const easeInOutCubic = t =&amp;gt;
  t &amp;lt; 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;We can then rewrite our &lt;code&gt;createNormalizedLineFeature()&lt;/code&gt; function to vary the along length like so:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const createNormalizedLineFeature = (feature, res = 60) =&amp;gt; {
  const alongDist = length(feature) / res;
  const normalizedFeature = {
    ...feature,
    features: [
      {
        ...feature.features[0],
        geometry: { ...feature.features[0].geometry, coordinates: [] }
      }
    ]
  };
  for (var i = 0; i &amp;lt; res; i++) {
    const newCoord = along(
      feature.features[0].geometry,
      alongDist * (i * easeInOutCubic(i / res))
    );
    normalizedFeature.features[0].geometry.coordinates.push(
      newCoord.geometry.coordinates
    );
  }
  return normalizedFeature;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;By varying the distance between the points on our route, we get a more pleasing result:&lt;/p&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/animated-route-5?previewSize=100&amp;amp;path=index.html" alt="animated-route-5 on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;p&gt;In the &lt;a href="https://dev.to/posts/custom-webmap-animations-1"&gt;first part&lt;/a&gt; of this series, I said that this method works equally well for other map libraries. To put my money where my mouth is, here is the same example in OpenLayers. OpenLayers even provide their own easing functions we can use.&lt;/p&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/animated-route-5b-ol?previewSize=100&amp;amp;path=index.html" alt="animated-route-5b-ol on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;


&lt;p&gt;There are still more things to do for us to improve these animations, but this post is getting long enough. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Control duration of animation based on actual time in stead of just frames.&lt;/li&gt;
&lt;li&gt;Handle framedrops. For instance, if the browser is struggling, fall back to just showing the route.&lt;/li&gt;
&lt;li&gt;Handle different types of geometries. Routes may consist of multiLineStrings. Also, maybe we want to animate polygons or points?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you enjoyed this series, or have any feedback, feel free to comment, or contact me on &lt;a href="https://twitter.com/haakseth"&gt;twitter&lt;/a&gt;. My website is at &lt;a href="https://www.haakseth.com/"&gt;haakseth.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>openlayers</category>
      <category>animation</category>
      <category>maps</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Creating custom webmap animations - Controlling duration</title>
      <dc:creator>John Wika Haakseth</dc:creator>
      <pubDate>Sun, 02 Feb 2020 17:14:48 +0000</pubDate>
      <link>https://dev.to/haakseth/creating-custom-webmap-animations-controlling-duration-55k0</link>
      <guid>https://dev.to/haakseth/creating-custom-webmap-animations-controlling-duration-55k0</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Approach&lt;/li&gt;
&lt;li&gt;Calculating length and finding points along the line using turf.js&lt;/li&gt;
&lt;li&gt;Result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the previous part, we set up the application, and added animation to the feature we’re adding to the map. Since we added only one point to the line feature for each animation frame, we saw that for longer routes, this could make the animation unbearably long. In addition, due to the line’s topology, some parts of the line might have more points than others, thus making the animation vary in speed:&lt;/p&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/animated-route-3-long?previewSize=100&amp;amp;path=index.html" alt="animated-route-3-long on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;


&lt;p&gt;Ouch! Let’s bring out &lt;a href="https://turfjs.org/"&gt;turf.js&lt;/a&gt; to work on the line to make a smooth, predictable animation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Chop the line into equal parts of line length divided by our desired line resolution.&lt;/li&gt;
&lt;li&gt;In stead of adding a single point per animation frame, we’ll add number of points divided by number of desired animation frames per animation frame. This lets us control the length of the animation.&lt;/li&gt;
&lt;li&gt;Finally, when the animation is done, we’ll replace the animated feature with the original one, as doing the previous steps will change its topology some, depending on our line resolution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Calculating length and finding points along the line using turf.js
&lt;/h2&gt;

&lt;p&gt;In order to get a smooth line of a given resolution we’ll have to do some work on the feature. For this we’ll use turf’s functions &lt;a href="https://turfjs.org/docs/#length"&gt;length&lt;/a&gt; and &lt;a href="https://turfjs.org/docs/#along"&gt;along&lt;/a&gt;. Let’s define a function &lt;code&gt;createNormalizedLineFeature(feature, resolution)&lt;/code&gt; that takes a line feature and does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calculate the feature’s distance&lt;/li&gt;
&lt;li&gt;Based on distance and resolution, calculate “along-distance” for feature (how far between each point). Higher resolution will make the line more detailed, but will demand more computing power.&lt;/li&gt;
&lt;li&gt;Create feature without coordinates.&lt;/li&gt;
&lt;li&gt;Use the along function for turf to add coordinates to feature along the original line.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const createNormalizedLineFeature = (feature, resolution = 60) =&amp;gt; {
  const alongDist = length(feature) / resolution;
  // create base feature, without coordinates
  const normalizedFeature = {
    ...feature,
    features: [
      {
        ...feature.features[0],
        geometry: { ...feature.features[0].geometry, coordinates: [] }
      }
    ]
  };
  // add coordinates
  for (var i = 0; i &amp;lt; resolution; i++) {
    const newCoord = along(feature.features[0].geometry, alongDist * i);
    normalizedFeature.features[0].geometry.coordinates.push(
      newCoord.geometry.coordinates
    );
  }
  return normalizedFeature;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/animated-route-4?previewSize=100&amp;amp;path=index.html" alt="animated-route-4 on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;p&gt;Great! Now our animation will be just as fast, no matter how long the feature is. We can use the resolution parameter to make it more or less detailed during animation. Just remember to replace the animation feature with the original route feature when the animation is complete, as we might lose some detail depending on our resolution.&lt;/p&gt;

&lt;p&gt;In the next and final part, we’ll try to make the animation feel more natural by adding easing.&lt;/p&gt;

</description>
      <category>mapbox</category>
      <category>animation</category>
      <category>maps</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Creating custom webmap animations</title>
      <dc:creator>John Wika Haakseth</dc:creator>
      <pubDate>Sun, 02 Feb 2020 17:10:19 +0000</pubDate>
      <link>https://dev.to/haakseth/creating-custom-webmap-animations-4379</link>
      <guid>https://dev.to/haakseth/creating-custom-webmap-animations-4379</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Result&lt;/li&gt;
&lt;li&gt;The base app&lt;/li&gt;
&lt;li&gt;Adding motion using requestAnimationFrame&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Micro animations in web applications are useful to guide users’ attention. They can even give the users extra information! Web map libraries come with a fixed set of built-in animations, like panning, zooming, and sometimes fading styles. What if we want to do more? Using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame"&gt;requestAnimationFrame()&lt;/a&gt;, we can create our own, custom animations.&lt;/p&gt;

&lt;p&gt;I did a talk on this topic at Kortdage 2019, you can find the slides on my &lt;a href="https://www.haakseth.com/pages/talks"&gt;talks&lt;/a&gt; page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;This is where we’ll end up in these posts. The idea is an app that shows the user a route on the map. In stead of just having the route appear, we can have it grow from start to stop. Not only is this more pleasing and delightful for the user, it also makes the route direction obvious.&lt;/p&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/animated-route-5?previewSize=100&amp;amp;path=index.html" alt="animated-route-5 on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;


&lt;p&gt;Awesome, right? Let’s go through the process of developing this animation step-by-step. This example is using Mapbox GL, but the same technique can be used in OpenLayers or Leaflet as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  The base app
&lt;/h2&gt;

&lt;p&gt;So our app simply fetches a route for the user and shows it to the user. The examples in this post are simplified to not take moving the map into consideration for the animation and only focus on the route itself.&lt;/p&gt;

&lt;p&gt;This is what we’re starting with. Pressing the button simply puts the route on the map. If the users blink they might not even notice something changed.&lt;/p&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/animated-route-1?previewSize=100&amp;amp;path=index.html" alt="animated-route-1 on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Adding motion using requestAnimationFrame
&lt;/h2&gt;

&lt;p&gt;Let’s add some motion. In stead of just adding the route itself to the map, we want to store it in a variable, and add it coordinate by coordinate to the map. But how do we know how often to add coordinates?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame"&gt;requestAnimationFrame()&lt;/a&gt; is good for this. The usage is simple: Define a method to run for each frame. To start simply call requestAnimationFrame with your method as a callback. Then, in your method have a clause that says whether it should be called again, if so do it!&lt;/p&gt;

&lt;p&gt;Say we want to animate something 10 times:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let count = 0;
function yourMethod() {
  count++;
  // Do stuff
  if (count &amp;gt; 10) {
    requestAnimationFrame(yourMethod);
  } else {
    count = 0;
  }
}
requestAnimationFrame(yourMethod);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In the our base example above, we simply add our feature by calling:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;map.getSource("line-animation").setData(routeFeature);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;To animate it, we define an animateLine() method that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Counts the number of coordinates in our route LineFeature and how many times it has been called.&lt;/li&gt;
&lt;li&gt;In addition to our route feature, we define an animationFeature, without coordinates in it.&lt;/li&gt;
&lt;li&gt;Add another coordinate to the animationfeature and calls &lt;code&gt;requestAnimationFrame(animateLine)&lt;/code&gt; until we’re finished.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It will look something like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let animationFeature = {
  type: "FeatureCollection",
  features: [
    {
      type: "Feature",
      geometry: {
        type: "LineString",
        coordinates: []
      }
    }
  ]
};
let progress = 0;
const animateLine = () =&amp;gt; {
  const numberOfPoints = route.features[0].geometry.coordinates.length;
  if (progress &amp;lt; numberOfPoints) {
    // append next coordinate pair to the lineString
    animationFeature.features[0].geometry.coordinates.push(
      route.features[0].geometry.coordinates[progress]
    );
    map.getSource("line-animation").setData(animationFeature);
    progress++;
    // Request the next frame of the animation.
    requestAnimationFrame(animateLine);
  } else {
    progress = 0;
  }
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/animated-route-2?previewSize=100&amp;amp;path=index.html" alt="animated-route-2 on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;p&gt;Nice! This works pretty well. However, we’re not quite finished. Since we’re adding a single coordinate for each frame, the duration of the animation will vary greatly depending on how long our feature is. Take a look at what happens if we want to show a longer route:&lt;/p&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/animated-route-3-long?previewSize=100&amp;amp;path=index.html" alt="animated-route-3-long on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;


&lt;p&gt;Probably not what we want! Also, since there will be more points where the line curves, the speed of the animation will slow down there, which might be undesirable. Go on to &lt;a href="https://dev.to/haakseth/custom-webmap-animations-2"&gt;part 2&lt;/a&gt; of this series to see how we can work with that.&lt;/p&gt;

</description>
      <category>mapbox</category>
      <category>animation</category>
      <category>maps</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
