LoginSignup
1
0

More than 3 years have passed since last update.

フロントエンドマスターへの道 その4 Svelteでストライプナビゲーション

Last updated at Posted at 2020-01-02

上記記事の4つめ。チュートリアルのURLは以下

成果物は以下

Peek 2020-01-03 00-11.gif

ご覧の通り、作ったのはToDoリストではありません

Svelte

紹介されているURLよりも、まず最初に公式のチュートリアルが実際に手を動かしながら体験できて本当にわかりやすいので一通りやったほうがいいと思う、さらにここでやったあれってどうやるんだっけなって思ったときには Example で確認ができる

終えたところで気づくと思うんだけど、ToDoリストはすでにこの公式のチュートリアルにもあり題材としては微妙な気がするので、せっかくなのでSvelteを使って下記の記事

であげられている課題の6番目にあげられているストライプナビゲーションを作成することにします。チュートリアルは下記

インストール

npx degit sveltejs/template svelte-todo-example

degitを使うのが簡単。ただこのままだとTypeScriptではないし、とくにLintとかもないので他にいいテンプレートがあればそれを使ったほうがいいと思う。今回はほとんど使ったことのないrollupで消耗するのが嫌だったので、何もいじらずそのまま使ってます

globalなCSS

App.svelteのstyleに記述した

<style>
  @import url('https://fonts.googleapis.com/css?family=Open+Sans:400,600');

  :root {
    --padding: 16px 32px;
    --transition-duration: 0.3s;
  }

  :global(*) {
    box-sizing: border-box;
    font-family: 'Open Sans', sans-serif;
  }

  :global(body) {
    min-height: 100vh;
    font-family: 'Open Sans', sans-serif;
    background: linear-gradient(-45deg, #19C5FE, #4553FF);
    padding: 20px;
  }
</style>

場合によってはnormarize.cssみたいなものを普通にhtmlから読み込んでもいいと思う

要素の高さをとる

要素の高さをとるには、DOMに触らないといけない。そういうときには以下のような記述をする必要がある

  let dimension
  let elem
  onMount(() => {
    dimension = elem.getBoundingClientRect()
  })

Animation

Svelteのアニメーションはかなり使いやすそうで、transitionanimation のAPIが用意されている。ただこれらのAPIは、DOMのノードが削除されたり、入れ替わったりしたときのアニメーション用のAPIで、状態が変化したことによる特定要素のアニメーションについてどうこうするものではなさそうな気がする(たぶん)

なので、基本的にクラス名で、それができないのであればstyle属性に記述する必要がありそう。今回の場合はstyle属性で処理が必要になるところがいくつかあって

  <div
    class="content"
    style="
      {lastActiveMenu !== null ? `
        width: ${parseInt(dimensions[$menuList[lastActiveMenu]].width)}px;
        height: ${parseInt(dimensions[$menuList[lastActiveMenu]].height)}px;
      ` : ''
      }
      transform: translateX({lastActiveMenu * 120}px);
    "
  >
  {#if dimensions.products}
    <div
      class="background"
      style="
        width: {parseInt(dimensions.products.width)}px;
        height: {parseInt(dimensions.products.height)}px;
        transform:
          translateX({lastActiveMenu * 120}px)
          {lastActiveMenu !== null ? `
            scaleX(${dimensions[$menuList[lastActiveMenu]].width / dimensions.products.width})
            scaleY(${dimensions[$menuList[lastActiveMenu]].height / dimensions.products.height})
          ` : ''
          }
        ;
      "
    ></div>  
  {/if}

こんな感じで書いたら動いた

とりあえずこう書いたら動いた、というだけなのでこれが最適な記述ではないかもしれない

<h1 style={{color: '#fff'}}>Hello World!</h1> こういうのは動きそうだなと思って試してみたが動かなかった。なので今回のように動的に長さをとって、状態によってその取得した長さを計算してとかやるのであれば

transform:
  translateX({lastActiveMenu * 120}px)
  {calcBackgroundScale()}
;

こんな感じか

style="{fooStyle()}"

みたいな感じにしてしまったほうがよかったかも

Store

writableだけでも良かったと思うんだけど、せっかくなのでreadableもつかった

import { writable, readable} from 'svelte/store'

export const menuList = readable([
  'products',
  'developers',
  'company'
])

export const activeMenu = writable(null)

このreadableは試しに使っただけで、こういった不変のメニューリストとかであれば main.js のpropsとかに入れるもののような気がする

最後にホバーしたアイテムを取得する

現在のホバーされている状態はstoreのactiveMenuに保存したが、これにはホバーしていないときにはnull、何かにホバーしてるときにはnumberが入る

作っていく過程でアニメーションのために最後にホバーしたアイテムがどれであったのかを取得する必要がでてきた

let lastActiveMenu = null
$: if ($activeMenu !== null) {
  lastActiveMenu = $activeMenu
}

上記のように記述すると、$activeMenuがnullでないとき、lastActiveMenuの変更がリアクティブに反映される

感想

題材も悪かったのかもしれないが、Svelteの学習コストが低いというのはどういうところで学習コストが低いと判断されているのかはわからなかった。こう書いたら動くけど、この書き方がただしいのかは全然わからない、みたいな手探り状態だったのは情報量の少なさなのだろうか……VSCodeでのいい感じの環境も結局整えることはできなかった

公式チュートリアルはどのフレームワークよりもいい

途中まではLPみたいなアニメーションがそこそこあって、jQueryやVanillaで書かれるような小さいものであればその代替になるのではないかと思ったんだけど、正直ちょっと(自分のいまの理解度では)怖くて使えないかな

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0