20190828のCSSに関する記事は3件です。

雨の日なので CSS だけで雨のアニメーションを実装する

定番ですが雨の日の気晴らしに。
JavaScript は使わずに CSS ( SCSS )だけで雨が降るアニメーションを実装してみました。

See the Pen SCSS Rain (No JS) by mimonelu (@mimonelu) on CodePen.

コード( SCSS )

.rain {

  $rain_drop_height: 20vh;

  background-color: #102030;
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;

  & > div {
    animation: rain 500ms linear infinite;
    background-color: rgba(#ffffff, 0.25);
    position: absolute;
    left: 0;
    top: -$rain_drop_height;
    width: 1px;
    height: $rain_drop_height;
    transform-origin: center center;
  }
  @for $i from 1 through 25 {
    & > div:nth-child(#{$i}) {
      animation-delay: #{$i * 100}ms;
      animation-duration: random(250) + 375ms;
      left: random(100) + vw;
    }
  }

  @keyframes rain {
    from {
      transform: rotate(-5deg) translateY(-$rain_drop_height);
    }
    to {
      transform: rotate(-5deg) translateY(100vh + $rain_drop_height);
    }
  }

}

ミソ

乱数どうするの問題

雨粒の x 座標やアニメーションの再生時間などはランダムな値にしたいところですが、 CSS に乱数を生成する仕様はないため、 SCSS の random() 関数で生成しています。つまり、コンパイルした時に乱数が定数として埋め込まれるだけなので、何度リロードしても同じアニメーションが再生されますが、そこは妥協しました。だって他に方法ないし(´・ω・`)

transform の順番

雨粒を斜めに降らせるにはどうすれば良いでしょう?ここでは transform: rotate(...) translateY(...); とすることで、雨粒が「回転した方向へ移動」するようにしました。これを入れ替えると「角度は無視して移動」することになり、要するに行列計算の順番を制御することで、あ、ご存知で?あ、はい。

おわりに

紙吹雪が瞬きながら舞うアニメーションなんかも実装できますが、無茶せずに JavaScript を、できれば Canvas も使って実装した方が良いでしょうね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】子から親コンポーネントのイベント実行・データ受け渡し

子から親コンポーネントのデータを更新

初心者用記事です。Vue.jsに少し慣れたので忘れないうちに記事にします。

今回、二つの方法に触れます。
$emitで子から親コンポーネントの処理を呼び出す
$emitで子から親コンポーネントへ値を渡す

以下の記事を参考に追加機能付きで作成しました。
https://dev83.com/vue-emit/
分かりやすい記事なのでお勧めです。

親から子の受け渡しを行うアプリで、ボタンを押すと数が増減する簡単なアプリ作っていきます。
↓ 例
image.png

実際に作ってもいいですが、JSFiddleで動作テストしてもいいと思います。

<div id="app">
</div>

new Vue({
        el: '#app',
        data: {
          // 下部に出てる合計個数
          totalCount: 0,
          items: [
            {
              name: 'りんご',
              price: 100,
              imgUrl: 'https://4.bp.blogspot.com/-uY6ko43-ABE/VD3RiIglszI/AAAAAAAAoEA/kI39usefO44/s800/fruit_ringo.png'
            },
            {
              name: 'バナナ',
              price: 200,
             imgUrl: 'https://1.bp.blogspot.com/-JaRzIloEZw4/UgSMOL-UzYI/AAAAAAAAW_A/vOiX6Tz5oO4/s800/fruit_banana.png'
            },
          ],
        },
      });

style部分

  .item-wrap {
    list-style-type: none;
    display: flex;
    flex-wrap: wrap;
    margin: 0;
    padding: 0;
  }
  .item-wrap li {
    padding: 1em;
  }
  .item-wrap img {
    border-radius: 50%;
  }
  .total-count {
    padding: 1em;
    background-color: #222;
    color: #fff;
  }

現状では何も表示されません。

簡単アプリを追加

本体に追加

<div id="app">
  <ul class="item-wrap">
    <item v-for="item in items" :item="item"></item>
  </ul>
  <div class="total-count">合計:{{ totalCount }}個</div>
</div>

子コンポーネント追加。ファイル読み込んだ方がいいですが今回はjsFiddle使って確認したので一つにまとめて書いてます。

Vue.component('item', {
        // 親から値もらう
        props: ['item'],
        // 子コンポーネントのテンプレート部分
        template: `
          <li>
            <img :src="item.imgUrl" style="height: 200px;">
            <div>{{ item.name }} = {{ childTotal }}個</div>
            <button @click="addCart">増やす</button>
            <button @click="removeCart">減らす</button>
          </li>
        `,
        // 子コンポーネントデータ
        data: function () {
          return {
            childTotal: 0
          };
        },
        // ボタン押すと発火する処理
        methods: {
          addCart: function () {
            this.childTotal += 1;
          },
          removeCart: function () {
            if (this.childTotal > 0) {
              this.childTotal -= 1;
            }
          },
        },
      });

new Vue({
        // 省略。さっき書いたところ
      });

ここまでの状態だと子コンポーネントから親コンポーネントに値を渡していないので
「合計:個」には変化が起きません。

親コンポーネントのデータを更新

子コンポーネントのデータが更新されたら親コンポーネントのデータが更新されるようにします。

子コンポーネントのmethodsを修正。

methods: {
          addCart: function () {
            this.childTotal += 1;
            // ↓追加。親の合計個数増やす
            this.$emit('addbtn');
          },
          removeCart: function () {
            if (this.childTotal > 0) {
              this.childTotal -= 1;
              // ↓追加。親の合計個数減らす
              this.$emit('removebtn');
            }
          },
        },

親コンポーネントを修正
itemコンポーネント部分
methods追加

<div id="app">
  <ul class="item-wrap">
    <item
          v-for="item in items"
          :item="item"
          @addbtn="addParentTotal"
          @removebtn="removeParentTotal"
          >
    </item>
  </ul>
  <div class="total-count">合計:{{ totalCount }}個</div>
</div>
new Vue({
        el: '#app',
        data: {
          totalCount: 0,
          items: [
            {
              name: 'RedApple',
              imgUrl: 'https://4.bp.blogspot.com/-uY6ko43-ABE/VD3RiIglszI/AAAAAAAAoEA/kI39usefO44/s800/fruit_ringo.png'
            },
            {
              name: 'GreenApple',
              imgUrl: 'https://1.bp.blogspot.com/-JaRzIloEZw4/UgSMOL-UzYI/AAAAAAAAW_A/vOiX6Tz5oO4/s800/fruit_banana.png'
            },
          ],
        },
        methods: {
          addParentTotal: function () {
            this.totalCount += 1;
          },
          removeParentTotal: function () {
            this.totalCount -= 1;
          },
        },
      });

最終的な子コンポーネント

      Vue.component('item', {
        props: ['item'],
        template: `
          <li>
            <img :src="item.imgUrl" style="height: 200px;">
            <div>{{ item.name }} = {{ childTotal }}個</div>
            <button @click="addCart">増やす</button>
            <button @click="removeCart">減らす</button>
          </li>
        `,
        data: function () {
          return {
            childTotal: 0
          };
        },
        methods: {
          addCart: function () {
            this.childTotal += 1;
            this.$emit('addbtn')
          },
          removeCart: function () {
            if (this.childTotal > 0) {
              this.childTotal -= 1;
              this.$emit('removebtn');
            }
          },
        },
      });

ひとまず完成

vue-emit.gif

@addbtn=”addParentTotal”
@removebtn=”removeParentTotal”

親コンポーネントの<item>~~~</item>に追加したのはこのふたつ。

子コンポーネントのthis.$emit('addbtn')
もしくはthis.$emit('removebtn');が発火したら
親コンポーネントのmethodsにあるaddParentTotal removeParentTotal
を呼び出してくださいという処理です。

子から親に値を受け渡す

現状だと親コンポーネントで合計を計算していますが、子コンポーネントからの値を直接受け取り、計算したい場合もあると思います。

サンプル:値段を計算

親コンポーネントに値段を定義して、子コンポーネントから受け取った個数をもとに合計金額を計算する機能を追加します

受け渡しは引数を追加してやります。

this.$emit('addbtn', this.childTotal, name)

この場合だとthis.childTotalとnameを渡しています。
親の受け取る側

addParentTotal: function (total, name){
}

これで子から親へデータを渡すことができます。

サンプルを作ったのですが、 改造してたら意外とシンプルにならずに申し訳ないです…

<div id="app">
  <ul class="item-wrap">
    <item
          v-for="item in items"
          :item="item"
          @addbtn="addParentTotal"
          @removebtn="removeParentTotal"
          >
    </item>
  </ul>
  <div class="total-count">合計:{{ totalCount }}個</div>
  <p class="total-count" v-for="item in items">
    {{ item.name }}の合計金額:¥{{ item.totalYen }}
  </p>
</div>

子コンポーネント

Vue.component('item', {
  props: ['item'],
  template: `
    <li>
      <img :src="item.imgUrl" style="height: 200px;">
      <div>{{ item.name }} = {{ childTotal }}個</div>
      <button @click="addCart(item.name)">増やす</button>
      <button @click="removeCart(item.name)">減らす</button>
    </li>
  `,
  data: function () {
    return {
      childTotal: 0
    };
  },
  methods: {
    addCart: function (name) {
      this.childTotal += 1;
      this.$emit('addbtn', this.childTotal, name)
    },
    removeCart: function (name) {
      if (this.childTotal > 0) {
        this.childTotal -= 1;
        this.$emit('removebtn', this.childTotal, name);
      }
    },
  },
});

親コンポーネント

new Vue({
        el: '#app',
        data: {
          totalCount: 0,
          items: [
            {
              name: 'リンゴ',
              price: 100,
              imgUrl: 'https://4.bp.blogspot.com/-uY6ko43-ABE/VD3RiIglszI/AAAAAAAAoEA/kI39usefO44/s800/fruit_ringo.png',
              number: 0,
              totalYen: 0
            },
            {
              name: 'バナナ',
              price: 200,
              imgUrl: 'https://1.bp.blogspot.com/-JaRzIloEZw4/UgSMOL-UzYI/AAAAAAAAW_A/vOiX6Tz5oO4/s800/fruit_banana.png',
              number: 0,
              totalYen: 0
            },
          ],
        },
        methods: {
          addParentTotal: function (total, name) {
            this.totalCount += 1;
            // 名前一致する該当の要素をオブジェクトから入れる
            const item = this.items.find(ele => ele.name === name)
            item.number = total
            item.totalYen = total * item.price
          },
          removeParentTotal: function (total, name) {
            this.totalCount -= 1;
            // 名前一致する該当の要素をオブジェクトから入れる
            const item = this.items.find(ele => ele.name === name)
            item.number = total
            item.totalYen = total * item.price
          },
        },
      });
  .item-wrap {
    list-style-type: none;
    display: flex;
    flex-wrap: wrap;
    margin: 0;
    padding: 0;
  }
  .item-wrap li {
    padding: 1em;
  }
  .item-wrap img {
    border-radius: 50%;
  }
  .total-count {
    padding: 1em;
    background-color: #222;
    color: #fff;
  }

それぞれのオブジェクトに個数を操作。
また、1個当たりの値段から金額を計算するアプリ完成。
vue-emit2.gif

これなにに使えるの?

色々ありますけど、モーダルなどが分かりやすい例です。
モーダルを子コンポーネントにして、フォームの入力をしてもらうと親の値も変わるみたいな感じです。

それについては次の機会に

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cssのclass名の付け方って、どんな名前をつかたらいいか、分からないよね。についての回答。

WEB業界に入ってhtml/cssを学び出した時、えっclass名の付け方って、どうしたらいいの?
っていう漠然とした問題が自分の中でずっとあったんで、同じように思っている人がいたら
少しは参考になればいいなと思ってこの記事を書いています。

当時の僕はこう思っていました。

「どんなclass名をつけたら正解なんかが分からん」

周りに聞いても結局、

「とりあえず何でもいいねん」

「後で見た時に分かりやすい名前をつけるんよ」

と言いながら、さらっと名前をつけてしまう。

で、こう思うわけです。

「えっなんでその名前にしたん?」

この疑問に嫌味な意味はなく、本当に単純に理由が知りたい。

経験者はわかってるんですね。染み付いているんです。
class名をこんな風につけおけば、後でこういう風にcssが書ける。と。

でも、初心者からするとそれじゃ分かんないので、
僕の名前のつけかたを少しだけ書いておきます。ヒントになればこれ幸い。
※あっ独自ルールのやつです。

僕はアンスコを2つ(__)使います。
styleに使用しているよ。っていうのが一目でわかるため。

※jsとかで使用するclass名は以下の場合があります。
・アンスコ一つ(_)
・冠にjsってつける
・キャメルで書く(modeShowみたいな)


大枠、レイアウト

〇〇__area とか、〇〇__box、〇〇__card、〇〇__contents

要素を囲いたいときは、親要素に

〇〇__aere__wrap、〇〇__box__wrap

ヘッダー部分

〇〇__header(囲いたいときは親要素に〇〇__header__wrap)

header の入れ子の要素は、

〇〇__title -> title 内の要素は p とか span とかで囲ってもいい
〇〇__side -> side 内の要素は header__side__item(itemを使う)
※切り分けるために item にもう一つclass名をつける場合は、type__〇〇みたいな感じ。
※item じゃなくてボタンとかの場合も type__submit type__cancel とかで切り分ける。
※他には mode__〇〇 とかよく使います。

内容部分

〇〇__body
〇〇__content
※内包する要素のclass名としては、〇〇__areaとか、〇〇__box、〇〇__card、
とか形状によってそれぞれ名前を変更したりします。

フッター部分

〇〇__footer(囲いたいときは親要素に〇〇__footer__wrap)


大まかなレイアウト時はこんな感じです。

「〇〇__の部分ってなんて名前にすればいいの?」

例えばサイトTOPページなら site-top
サイトの概要のページだと site-info みたいな。
あとは site__title とか info__title、site-info__title にしてもいい

とりあえずは自分なりのルールを作って、統一性を持たせたclass名を心がける。
慣れてきたらBEMとかの玄人さんたちが考え出した、命名規則を勉強するといいかも。

「あっ最後にこれだけは!」

シンプルな単語のclass名は極力やめたほうがいいよ。
後でcss記述する時に、class名もろ被りして大変なことになる
content とか、text とか、
あとは left とか right とか、場所が固定されるものは極力使用しないように。
(cssでレイアウトの位置替えは楽勝でできるので)

それでは。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む