Pull Down to Refresh Plane

by Chun

HTML

<div class="popularStoryWrapper">
<div class="popularStory">
  <div class="popularStory__top">
    <svg class="popularStory__top-svgBg" viewBox="0 0 366 256">
      <g class="svgBg__objects">
        <path class="svgBg__bg svgBg__bg1" fill="#86D7DB" d="M0,143 88,107 224,153 348,109 366,123 366,256 0,256z" />
        <path class="svgBg__bg svgBg__bg2" fill="#3C929A" d="M0,156 106,136 272,172 342,124 366,144 366,256 0,256z" />
        <path class="svgBg__bg svgBg__bg3" fill="#416175" d="M0,170 62,160 235,148 305,145 366,153 366,256 0,256z" />
        <g class="svgBg__tree svgBg__tree-1 m--left" data-id="1">
          <path class="svgBg__tree-leafs svgBg__tree-part" fill="#389296" d="M54,127 C77,127 62,95 54,63 C46,95 31,127 54,127" />
          <path class="svgBg__tree-trunk svgBg__tree-part" d="M56,147 Q55,115 54,83 Q53,115 52,146" />
        </g>
        <g class="svgBg__tree svgBg__tree-2 m--left" data-id="2">
          <path class="svgBg__tree-leafs svgBg__tree-part" fill="#389296" d="M67,124 C90,124 75,92 67,59 C59,92 44,124 67,124" />
          <path class="svgBg__tree-trunk svgBg__tree-part" d="M69,144 Q68,112 67,80 Q66,112 65,143" />
        </g>
        <g class="svgBg__tree svgBg__tree-3 m--right" data-id="3">
          <path class="svgBg__tree-leafs svgBg__tree-part" fill="#389296" d="M264,129 C287,129 272,97 264,64 C256,97 241,129 264,129" />
          <path class="svgBg__tree-trunk svgBg__tree-part" d="M266,149 Q265,117 264,85 Q263,117 262,148" />
        </g>
        <g class="svgBg__tree svgBg__tree-4 m--right" data-id="4">
          <path class="svgBg__tree-leafs svgBg__tree-part" fill="#207277" d="M287,128 C310,128 295,96 287,63 C279,96 264,128 287,128" />
          <path class="svgBg__tree-trunk svgBg__tree-part" d="M289,148 Q288,116 287,84 Q286,116 285,147" />
        </g>
        <g class="svgBg__tree svgBg__tree-5 m--right" data-id="5">
          <path class="svgBg__tree-leafs svgBg__tree-part" fill="#389296" d="M313,128 C336,128 321,96 313,63...

CSS

.popularStoryWrapper *, .popularStoryWrapper *:before, .popularStoryWrapper *:after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.popularStoryWrapper {

  width:300px;
}
@media (max-width: 768px) {
  .popularStoryWrapper {

  }
}

body {
  background: #EDEFF2;
}

.popularStory {
    width: 100%;
    height: 25rem;
    background: #FFFFFF;
    border-radius: 1.2rem;
    box-shadow: 0 2rem 2rem rgba(0, 0, 0, 0.15);
    overflow: hidden;
}
.popularStory__top {
  position: relative;
  height: 8.6rem;
  padding-bottom: 8px;
  background: linear-gradient(#7BCECA, #82D3CB);
  overflow: hidden;
}
.popularStory__body {
  position: relative;
  min-height: 56.8rem;
    padding-top: 3rem;
  will-change: transform;
}

.pull-down {
  position: absolute;
  left: 0;
  top: 1rem;
  width: 100%;
    padding-right: 4rem;
  text-align: right;
  color: rgba(84, 92, 103, 0.6);
  user-select: none;
  pointer-events: none;
}
.pull-down:before, .pull-down:after {
  content: "";
  position: absolute;
  top: 0;
  width: 1rem;
  height: 1rem;
  border: 1px solid rgba(84, 92, 103, 0.6);
  border-left: none;
  border-top: none;
  transform: rotate(45deg);
  animation: arrowAnim 1.5s infinite;
}
.pull-down:before {
  left: 8rem;
}
.pull-down:after {
  left: 16rem;
}

@keyframes arrowAnim {
  to {
    transform: translateY(1.3rem) rotate(45deg);
    opacity: 0;
  }
}
.itemPieces {
  position: relative;
}
.itemPieces.padded {
  transition: padding 0.3s;
  padding-top: 8rem;
}

.itemPiece {
    padding: 5px 15px;
  user-select: none;
  transition: opacity 0.3s;
}
.itemPiece.absPos {
  position: absolute;
  left: 0;
  top: 0;
}
.itemPiece.hidden {
  opacity: 0;
}
.itemPiece__icon {
  display: inline-block;
  vertical-align: top;
  width: 4rem;
  height: 4rem;
  margin-right: 2rem;
  border-radius: 50%;
}
.itemPiece__icon.animated {
  animation: animateIcon 0.6s forwards;
}
.itemPiece__icon.m--img img {
  width: 100%;
}
.itemPiece__name {
  line-height: 3rem;
  color:...

JavaScript

'use strict';

window.requestAnimFrame = (function() {
  return window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function(callback) {
    window.setTimeout(callback, 1000 / 60);
  };
})();

$(document).ready(function() {

  var $top = $(".popularStory__top");
  var $body = $(".popularStory__body");
  var $bg1 = $(".svgBg__bg1");
  var $bg2 = $(".svgBg__bg2");
  var $bg3 = $(".svgBg__bg3");
  // jQuery have problems with getting svg elements attrs, so I'm using vanillaJS
  var $trees = [].slice.call(document.querySelectorAll(".svgBg__tree"));
  var $treeParts = [].slice.call(document.querySelectorAll(".svgBg__tree-part"));
  var $leftTrees = $(".svgBg__tree.m--left");
  var $rightTrees = $(".svgBg__tree.m--right");
  var $planeYoRotater = $(".planeYo-rotater");
  var $planeYo = $(".planeYo");
  var isDesktop = window.matchMedia("(min-width: 769px)").matches;
  var topH = (isDesktop) ? 186 : 149;
  var bg1change, bg2change, bg3change;
  var bg1max = (isDesktop) ? 10 : 8;
  var bg2max = (isDesktop) ? 22 : 18;
  var bg3max = (isDesktop) ? 44 : 35;
  var pullDeltaY;
  var maxPullDeltaY = (isDesktop) ? 70 : 56;
  var treesData = {};
  var treeMaxX = (isDesktop) ? 18 : 14;
  var treeMaxCoef = treeMaxX / maxPullDeltaY;
  var treeChange;
  var planeYoMaxDeg = -45; // defines maximum planeYo rotation deg during pull event
  var planeYoMaxCoef = planeYoMaxDeg / maxPullDeltaY;
  var planeYoChange;
  var frame = 1000 / 60; // 60 frames per second
  // duration for release animation for all elements, except flying planeYo
  var releaseTime = 900;
  var animating = false;
  var planeYoAnimTime = 3500; // this value must be synced with SASS $planeYoAnimTime
  
  /* You can find these easing functions on this site
  http://timotheegroleau.com/Flash/experiments/easing_function_generator.htm
  Also, you can customize them with generator,
 ...