main#home-page(data-wrapper="", role="main")
  .container
    h2.font-sz-3xl.font-sans.font-400 Properties
    .animation-containers.jumbotron.jumbotron-fluid.my-2.p-4.rounded-lg.box-shadow.text-dark.bg-warning
      .motion-path-demo
        .flex.justify-evenly.flex-wrap
          .motion-path
            h2.text-center.text-lg.font-bold Motion Path/Offset Path
            .relative
              .small.square.shadow.el-initial
              .small.square.el.el-1
              svg(width="256", height="112", viewbox="0 0 256 112")
                path#follow-path(
                  fill="none",
                  stroke="currentColor",
                  stroke-width="1",
                  d="M8,56 C8,33.90861 25.90861,16 48,16 C70.09139,16 88,33.90861 88,56 C88,78.09139 105.90861,92 128,92 C150.09139,92 160,72 160,56 C160,40 148,24 128,24 C108,24 96,40 96,56 C96,72 105.90861,92 128,92 C154,93 168,78 168,56 C168,33.90861 185.90861,16 208,16 C230.09139,16 248,33.90861 248,56 C248,78.09139 230.09139,96 208,96 L48,96 C25.90861,96 8,78.09139 8,56 Z"
                )

          .motion-path
            h2.text-center.text-lg.font-bold getTotalLength()
            .relative
              .small.square.shadow.el-initial
              .small.square.el.el-2
              svg(width="256", height="112", viewbox="0 0 256 112")
                path#follow-path(
                  fill="none",
                  stroke="currentColor",
                  stroke-width="1",
                  d="M8,56 C8,33.90861 25.90861,16 48,16 C70.09139,16 88,33.90861 88,56 C88,78.09139 105.90861,92 128,92 C150.09139,92 160,72 160,56 C160,40 148,24 128,24 C108,24 96,40 96,56 C96,72 105.90861,92 128,92 C154,93 168,78 168,56 C168,33.90861 185.90861,16 208,16 C230.09139,16 248,33.90861 248,56 C248,78.09139 230.09139,96 208,96 L48,96 C25.90861,96 8,78.09139 8,56 Z"
                )

        .controls.row.mx-0.mt-5.align-items-center
          .col-2
            button#playstate-toggle.btn.btn-light(
              type="button",
              title="Play/Pause Toggle",
              data-playstate="paused"
            )
              i.fa.fa-play(aria-hidden="true")
              i.fa.fa-pause(aria-hidden="true")
              i.fa.fa-redo(aria-hidden="true")
            span.text-center(id=`progress-output`) 0
          input#progress.col-10.form-control-range(
            type="range",
            value="0",
            min="0",
            max="100",
            step="0.01"
          )
View Compiled
.div {
  @apply bg-blue-400 w-10 h-10 rounded relative m-2;
  --size: 8vmin;
  width: var(--size);
  height: var(--size);
}

.relative {
  position: relative;
  margin: 0 auto;
}

.flex-wrap {
  flex-wrap: wrap;
}

.motion-path {
  position: relative;
}

.el,
.el-initial {
  --size: 4vmin;
  width: var(--size);
  height: var(--size);
  /*     transform: scale(1); */
  // border-radius: 8%;
  margin: 5px;
  background: #616aff;
  position: relative;
}

.el-initial {
  opacity: 0.6;
  position: absolute;
  display: block;
  margin-top: 0;
  top: 0;
  left: 0;
}

.motion-path .el-1 {
  position: absolute;
  offset-path: path(
    "M8,56 C8,33.90861 25.90861,16 48,16 C70.09139,16 88,33.90861 88,56 C88,78.09139 105.90861,92 128,92 C150.09139,92 160,72 160,56 C160,40 148,24 128,24 C108,24 96,40 96,56 C96,72 105.90861,92 128,92 C154,93 168,78 168,56 C168,33.90861 185.90861,16 208,16 C230.09139,16 248,33.90861 248,56 C248,78.09139 230.09139,96 208,96 L48,96 C25.90861,96 8,78.09139 8,56 Z"
  );
  offset-path: url("#follow-path");
  offset-distance: 0;
}

.motion-path .el-2 {
  position: absolute;
  transform-origin: center center;
  top: calc(var(--size) / -2);
  left: calc(var(--size) / -2);
}

.animation-container {
  transition: background-color 0.5s ease;
  background-color: rgba(0, 0, 0, 0.085);
  border-radius: 5px;
  padding: 5px;
  cursor: pointer;

  &:hover {
    background-color: rgba(255, 255, 255, 0.25);
  }
}

.contain {
  position: relative;
}

.animation-container + .animation-container {
  margin-top: 2em;
}

input[type="range"] {
  width: 100%;
  padding: 0;
  margin: 0;
}
html:not(.unsupported) .support {
  display: none;
}

.col-2 {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
// Cool
#playstate-toggle[data-playstate="running"] {
  .fa-play,
  .fa-redo {
    display: none;
  }
}
#playstate-toggle[data-playstate="paused"] {
  .fa-pause,
  .fa-redo {
    display: none;
  }
}

#playstate-toggle[data-playstate="finished"] {
  .fa-play,
  .fa-pause {
    display: none;
  }
}

.flex.justify-evenly {
  display: flex;
  justify-content: space-evenly;
}
View Compiled
import { animate } from "https://cdn.skypack.dev/@okikio/animate";
import { methodCall } from "https://cdn.skypack.dev/@okikio/manager";

/* Properties Section */
// Motion Paths Demo
let playbackFn = (containerSel, anims) => {
  let playstateEl = document.querySelector(`${containerSel} #playstate-toggle`);
  let progressEl = document.querySelector(`${containerSel} #progress`);

  let progressOutputEl = document.querySelector(
    `${containerSel} #progress-output`
  );
  let oldState: AnimationPlayState;

  let updatePlayState = () => {
    oldState = anims[0].getPlayState();
    playstateEl.setAttribute("data-playstate", oldState);
  };

  anims[0].on("finish begin", updatePlayState).on("update", (progress) => {
    progressEl.value = `` + progress.toFixed(2);
    progressOutputEl.textContent = `${Math.round(progress)}%`;
  });

  playstateEl.addEventListener("click", () => {
    if (anims[0].is("running")) methodCall(anims, "pause");
    else if (anims[0].is("finished")) methodCall(anims, "reset");
    else methodCall(anims, "play");

    updatePlayState();
  });

  progressEl.addEventListener("input", (e) => {
    let percent = +progressEl.value;
    methodCall(anims, "pause");
    methodCall(anims, "setProgress", percent);
  });

  progressEl.addEventListener("change", () => {
    oldState !== "paused"
      ? methodCall(anims, "play")
      : methodCall(anims, "pause");

    updatePlayState();
  });
};

(() => {
  let options: IAnimationOptions = {
    padEndDelay: true,
    easing: "linear",
    duration: 2000,
    loop: 4,
    speed: 1
  };
  let containerSel = ".motion-path-demo";
  let el = document.querySelector(".motion-path .el-1") as HTMLElement;
  let motionPath = animate({
    target: el,
    offsetDistance: ["0%", "100%"],
    ...options
  });

  let path = document.querySelector(".motion-path path");
  let el2 = document.querySelector(".motion-path .el-2");

  let pts = new Set();
  let rotateArr = [];
  let len = path.getTotalLength();

  let ptAtZero = path.getPointAtLength(0);
  for (var i = 0; i < len; i++) {
    let { x, y } = path.getPointAtLength(i);
    pts.add([x, y]);

    let { x: x0, y: y0 } = i - 1 >= 1 ? path.getPointAtLength(i - 1) : ptAtZero;
    let { x: x1, y: y1 } = i + 1 >= 1 ? path.getPointAtLength(i + 1) : ptAtZero;
    let calc = +((Math.atan2(y0 - y1, x0 - x1) * 180) / Math.PI);
    rotateArr.push(calc);
  }

  let getTotalLength = animate({
    target: el2,
    translate: [...pts],
    rotate: rotateArr,
    fillMode: "both",
    ...options
  });

  playbackFn(containerSel, [motionPath, getTotalLength]);
})();
View Compiled

External CSS

  1. https://fonts.googleapis.com/css2?family=Manrope:[email protected];600;700;800&amp;family=Material+Icons&amp;family=Material+Icons+Round&amp;display=swap
  2. https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.min.js