色んなバー(プログレスバー、スライダーバー、コントロールバー)UIの細かい所

まぼろしのJS勉強会 #2 「細かすぎて伝わらないUI実装選手権」 @masuP9

プログレスバー

<progress>!! That's it!

🤓 < original design!

🤓 < animation!

😇

<progress> を隠蔽しつつ、
表示を <div> で行い、値を共有する。

HTML

<div class="Progressbar">
  <div class="Progressbar__value" style="width: 70%;"></div>
  <progress value="70" max="100">100%</progress>
</div>

or

<div
  role="progressbar"
  aria-valuenow="70"
  aria-valuemax="100"
  class="Progressbar"
>
  <div class="Progressbar__value" style="width: 70%;"></div>
</div>

CSS

.Progressbar {
  position: relative;
  height: 32px;
  border-radius: 1000px;
  background-color: #484848;
  clip-path: inset(0 0 0 0 round 1000px); // 終端の切り抜き
}

.Progressbar__value {
  height: 32px;
  transition: width 0.4s ease-in-out;
  border-radius: 1000px 0 0 1000px;
  background-color: #ff4c65;
}

.Progressbar > progress {
  opacity: 0;
  width: 1;
  height: 1;
  position: absolute;
  pointer-events: none;
}

JavaScript(React)

const { value } = this.props;

render(
  <div class="Progressbar">
    <div
      class="Progressbar__value"
      style={ width: `${value}`px }
    />
    <progress value={value} max="100">${value}%</progress>
  </div>
);

Progress bar - JSFiddle

さらに fallback

/**
  * HACK clip-path に対応していない IE/Edge でも value の端点を平らにしかつ
  * プログレスバー終点を round にするために背景色と同じ塗のSVGをかぶせる
  */
.Progressbar__endPointOuter {
  position: absolute;
  top: 0;
  right: 0;
  width: 16px;
  height: 32px;
}

/* clip-path に対応してるブラウザーでは表示しない */
@supports (clip-path: inset(0% 0% 0% 0% round 1000px)) {
  .Progressbar__endPointOuter {
    display: none;
  }
}

スライダーバー

<input type="range">!! That's it!

🤓 < original design!

🤓 < animation!

😇 < せやな

基本は、プログレスバー + スライド。スライドのマウス操作は置いておいて(自分がやってないので)キーボード操作について

onKeyDownSeekBar(e) {
  switch (e.key) {
    case 'ArrowLeft':
    case 'ArrowDown':
      e.preventDefault(); // event キャンセルしないと、横スクロールがあったらスクロールしちゃう
      this.setSeek(10);
      break;
    case 'ArrowRight':
    case 'ArrowUp':
      e.preventDefault();
      this.setSeek(30);
      break;
    case 'Home':
      e.preventDefault();
      setSeek(0);
      break;
    default:
  }
}

render(
  <div
    role="slider"
    tabIndex="0"
    aria-valuenow={valuePercent}
    aria-valuemin="0"
    aria-valuemax="100"
    aria-label={label}
    aria-valuetext={valueText}
    onKeyDown={this.onKeyDown}
  >
    <div style={{ width : `${valuePercent}%` }} />
  </div>
);

キーボード操作のキーと該当する操作のマッピングは WAI-ARIA Authoring Practices 1.1 を参考に。

  • Right Arrow: Increase the value of the slider by one step.
  • Up Arrow: Increase the value of the slider by one step.
  • Left Arrow: Decrease the value of the slider by one step.
  • Down Arrow: Decrease the value of the slider by one step.
  • Home: Set the slider to the first allowed value in its range.
  • End: Set the slider to the last allowed value in its range.
  • Page Up (Optional): Increment the slider by an amount larger than the step change made by Up Arrow.
  • Page Down (Optional): Decrement the slider by an amount larger than the step change made by Down Arrow.

コントロールバー

<video controls>!! That's it!

🤓 < original design!

🤓 < original feature!

😇 < これはしゃあない

Reactのfocusイベント

\(^o^)/オワリ