はじめに

ユーザーエクスペリエンスデザインでは、マイクロインタラクションは、ユーザーがインターフェイスをナビゲートするのに役立つ小さなフィードバックの瞬間です。 多くの場合、マイクロインタラクションはウェブサイトのデザインの微妙なアニメーションで行われます。

このチュートリアルでは、マイクロインタラクションを備えた機能的なダウンロードボタンを作成します。 それを機能させるために、CSSトランジションとアニメーションを使用し、SVG用の軽量アニメーションライブラリanime.jssegment.jsを使用します path アニメーション。

チュートリアルの最後に、次のようなダウンロードボタンが表示されます。

ダウンロードボタンのオリジナルデザインはPedroAquinoのもので、このドリブルショットにあります。 完全なコードはこのGithubリポジトリにあり、ここにデモページがあります。

ステップ1—HTML構造を作成する

使用するHTMLコードを見てみましょう。

<!-- Button container -->
<div class="download-button-container">
    <!-- The real button -->
    <button class="download-button">
        <span class="button-text-real hidden">download</span>
        <!-- Extra elements to perform the animations -->
        <span class="button-icon">
            <span class="button-linear-progress">
                <span class="button-linear-progress-bar"></span>
            </span>
            <svg class="button-icon-svg" viewBox="0 0 60 60">
                <path class="button-icon-path button-icon-path-square" d="M 20 40 l 0 -20 l 20 0 l 0 20 Z"></path>
                <path class="button-icon-path button-icon-path-line" d="M 40 20 l -20 20"></path>
            </svg>
        </span>
    </button>
    <!-- Extra elements to perform the animations -->
    <svg class="border-svg" width="240px" height="100px" viewBox="0 0 240 100">
        <path class="border-path hidden" d="M 40 3.5 a 36.5 36.5 0 0 0 -36.5 36.5 a 36.5 36.5 0 0 0 36.5 36.5 C 70 76.5 90 76.5 120 76.5 S 170 76.5 200 76.5 a 36.5 36.5 0 0 0 36.5 -36.5 a 36.5 36.5 0 0 0 -36.5 -36.5 Z"></path>
    </svg>
    <span class="button-text button-text-download">download</span>
    <span class="button-text button-text-done">done!</span>
    <div class="button-wave"></div>
    <div class="button-progress-container">
        <svg class="button-svg">
            <path class="button-circular-progress" d="M 50 50 m 0 -32.5 a 32.5 32.5 0 0 1 0 65 a 32.5 32.5 0 0 1 0 -65"></path>
        </svg>
        <span class="button-ball"></span>
    </div>
</div>

SVGに注意することが重要です path 要素は、必要な結果を得るために手作業で描画されています。 たとえば、ある時点で、ボタンの境界線に弾力性のあるアニメーションを実行させたいので、SVGが必要です path Anime.jsでそのモーフィングアニメーションの準備ができています(両方で同じ構造 paths):

ステップ2—スタイルを追加する

マークアップの準備ができたら、ボタンのスタイルを設定しましょう。 ここにはスタイルシート全体ではなく、最も重要な部分が含まれていることに注意してください。 コード全体はGithubリポジトリにあります。 理解を深めるために、コードは完全にコメント化されています。

定義したSCSS変数と、要素を非表示にするヘルパークラスを見てみましょう。

// Some variables to use later
$button-width: 300px;
$button-height: 70px;
$button-border: 3px;
$icon-padding: 5px;
$icon-width: $button-height - ($icon-padding * 2);
$ball-width: 18px;

// Helper class to hide elements
.hidden {
  visibility: hidden !important;
  opacity: 0 !important;
}

本物のスタイル button エレメント:

// Real button styles
.download-button {
  position: relative;
  display: inline-block;
  width: $button-width;
  height: $button-height;
  background-color: #2C2E2F;
  border: none;
  box-shadow: 0 0 0 $button-border #02D1FF; // This will be our 'border'
  border-radius: 100px;
  cursor: pointer;
  transition: 1s width, 0.3s box-shadow;

  // Remove the custom behavior in some browsers
  &, &:focus {
    padding: 0;
    outline: none;
  }
  &::-moz-focus-inner {
    border: 0;
  }

  // Styles for the different states of the button
  &:hover, &:active, &:focus {
    box-shadow: 0 0 0 $button-border #02D1FF, 0 0 20px $button-border darken(#02D1FF, 20%);
  }
}

ボタンは3つの異なる状態にすることができます。 downloading, progressing、 と completed. したがって、次の構造を使用して、各状態に必要なスタイルを定義しました。

// Button container
.download-button-container {
  // ...CODE...

  // Following are the different states for the button: downloading, progressing and completed
  // We have defined the states in the container to have access to all descendants in CSS

  // Downloading: The download button has been pressed
  &.downloading {
    // ...CODE...
  }

  // Progressing: The progress starts
  &.progressing {
    // ...CODE...
  }

  // Completed: The progress ends
  &.completed {
    // ...CODE...
  }
}

ダウンロードが終了したときにボールアニメーションを実現するために、もう1つの興味深いコードが使用されます。

.button-ball {
  left: 50%;
  transition: none;
  // CSS animations for the ball. All of them start at the same time, so we need to take care of delays
  animation:
          ball-throw-up 0.5s ease-out forwards, // Throw up the ball for 0.5s
          ball-throw-down 0.5s 0.5s ease-in forwards, // Wait 0.5 seconds (throw up), and throw down the ball for 0.5s
          ball-rubber 1s forwards; // Move the ball like a rubber deformation during 1s (throw up + throw down)
}

// Throw up animation
@keyframes ball-throw-up {
  from {
    transform: translate(-50%, 17.5px);
  }
  to {
    transform: translate(-50%, -60px);
    background-color: #00FF8D;
  }
}

// Throw down animation
@keyframes ball-throw-down {
  from {
    transform: translate(-50%, -60px);
  }
  to {
    transform: translate(-50%, 80px);
  }
}

// Rubber animation
@keyframes ball-rubber {
  from {
    width: $ball-width;
  }
  25% {
    width: $ball-width * 0.75;
  }
  50% {
    width: $ball-width;
  }
  to {
    width: $ball-width / 2;
  }
}

使用されている他のすべてのスタイルは、Githubリポジトリにあります。

ステップ3—Javascriptを使用したアニメーション

アニメーションを支援する軽量ライブラリであるanime.jssegment.jsを使用します。

わかりやすくするために、以下のコードスニペットには一部の変数宣言を含めないことに注意してください。 疑問がある場合は、Githubリポジトリを確認してください。

これが、クリックイベントをキャプチャするために使用している基本的なコードです。 button 必要な動作を実行します。

// Capture click events
button.addEventListener('click', function () {
    if (!completed) { // Don't do anything if downloading has been completed
        if (downloading) { // If it's downloading, stop the download
            stopDownload();
        } else { // Start the download
            startDownload();
        }
    }
});

// Start the download
function startDownload() {
    // Update variables and CSS classes
    downloading = true;
    buttonContainer.classList.add('downloading');
    animateIcon();
    // Update progress after 1s
    progressTimer = setTimeout(function () {
        buttonContainer.classList.add('progressing');
        animateProgress();
    }, 1000);
}

// Stop the download
function stopDownload() {
    // Update variables and CSS classes
    downloading = false;
    clearTimeout(progressTimer);
    buttonContainer.classList.remove('downloading');
    buttonContainer.classList.remove('progressing');
    // Stop progress and draw icons back to initial state
    stopProgress();
    iconLine.draw(0, '100%', 1, {easing: anime.easings['easeOutCubic']});
    iconSquare.draw('30%', '70%', 1, {easing: anime.easings['easeOutQuad']});
}

アニメーションの進行状況はデモで偽造されています。 実際のユースケースでは、実際の進捗データに置き換えられます。 これは、進行状況を処理する関数です。

// Progress animation
function animateProgress() {
    // Fake progress animation from 0 to 100%
    // This should be replaced with real progress data (real progress percent instead '100%'), and maybe called multiple times
    circularProgressBar.draw(0, '100%', 2.5, {easing: anime.easings['easeInQuart'], update: updateProgress, callback: completedAnimation});
}

最後に、ダウンロードが完了したときにアニメーションを実行するために使用されるコードを次に示します。ここで、ボールアニメーションがトリガーされ、 path 要素。

// Animation performed when download has been completed
function completedAnimation() {
    // Update variables and CSS classes
    completed = true;
    buttonContainer.classList.add('completed');
    // Wait 1s for the ball animation
    setTimeout(function () {
        button.classList.add('button-hidden');
        ball.classList.add('hidden');
        borderPath.classList.remove('hidden');
        // Morphing the path to the second shape
        var morph = anime({
            targets: borderPath,
            d: 'M 40 3.5 a 36.5 36.5 0 0 0 -36.5 36.5 a 36.5 36.5 0 0 0 10.5 26.5 C 35 86.5 90 91.5 120 91.5 S 205 86.5 226 66.5 a 36.5 36.5 0 0 0 10.5 -26.5 a 36.5 36.5 0 0 0 -36.5 -36.5 Z',
            duration: 100,
            easing: 'linear',
            complete: function () {
                // Morphing the path back to the original shape with elasticity
                morph = anime({
                    targets: borderPath,
                    d: 'M 40 3.5 a 36.5 36.5 0 0 0 -36.5 36.5 a 36.5 36.5 0 0 0 36.5 36.5 C 70 76.5 90 76.5 120 76.5 S 170 76.5 200 76.5 a 36.5 36.5 0 0 0 36.5 -36.5 a 36.5 36.5 0 0 0 -36.5 -36.5 Z',
                    duration: 1000,
                    elasticity: 600,
                    complete: function () {
                        // Update variables and CSS classes, and return the button to the original state
                        completed = false;
                        setTimeout(function () {
                            buttonContainer.classList.remove('completed');
                            button.classList.remove('button-hidden');
                            ball.classList.remove('hidden');
                            borderPath.classList.add('hidden');
                            stopDownload();
                        }, 500);
                    }
                });
            }
        });
    }, 1000);
}

結論

この記事では、このダウンロードボタンの作成に使用される主なコードを示しました。

ライブデモで遊ぶか、Githubで完全なコードを取得できます。 また、このコンポーネントは、実際の進捗データと、バックエンドがマイクロインタラクションにどのように影響するかについての考慮事項が必要なため、完全に本番環境に対応できる状態ではないことにも注意してください。