20201207のCSSに関する記事は12件です。

CSSだけでクリックできるレビューの星(★★★★☆)を作る方法

どうも7noteです。レビューや口コミがとても重要な時代になってきましたね。

ホームページでもレビューを見て商品を購入するかどうか決める人がとても増えてきたと思います。
となると実装できないと困りますよね。

そこでCSSだけでできるレビューの星を作って評価できるものを作っていきます。

データを記録させるためにはサーバーと連携させたり、javascriptの組み込みが必要になります。
ここでは見た目の動きについての作り方を解説します。

見本

sample.gif

ソース

index.html
<div class="review">
  <p>レビュー</p>
  <div class="stars">
    <span>
      <input id="review01" type="radio" name="review"><label for="review01"></label>
      <input id="review02" type="radio" name="review"><label for="review02"></label>
      <input id="review03" type="radio" name="review"><label for="review03"></label>
      <input id="review04" type="radio" name="review"><label for="review04"></label>
      <input id="review05" type="radio" name="review"><label for="review05"></label>
    </span>
  </div>
</div>

style.css
.stars span{
  display: flex;               /* 要素をフレックスボックスにする */
  flex-direction: row-reverse; /* 星を逆順に並べる */
  justify-content: flex-end;   /* 逆順なので、左寄せにする */
}

.stars input[type='radio']{
  display: none;               /* デフォルトのラジオボタンを非表示にする */
}

.stars label{
  color: #D2D2D2;              /* 未選択の星をグレー色に指定 */
  font-size: 30px;             /* 星の大きさを30pxに指定 */
  padding: 0 5px;              /* 左右の余白を5pxに指定 */
  cursor: pointer;             /* カーソルが上に乗ったときに指の形にする */
}

.stars label:hover,
.stars label:hover ~ label,
.stars input[type='radio']:checked ~ label{
  color: #F8C601;              /* 選択された星以降をすべて黄色にする */
}

解説

ラジオボタンで星を用意して、選択された星以降の星に色がつくようなCSSをかいていきます。

まず、予め星を逆順に並べますflex-direction: row-reverse;)。そのうえで、input[type='radio']:checked ~ labelの指定をすることでラジオのチェックがついた星以降の星に色が付きます。
逆順に並んでいるので、見た目的には左から順番に色がついているような状態になります。

おそまつ!

~ Qiitaで毎日投稿中!! ~
【初心者向け】HTML・CSSのちょいテク詰め合わせ

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

notion.soの横幅が狭い気がするのでなんとかした

notion.soを使い始めてみたけれど,横幅が900pxより大きくならず,表とかが見にくいので1200pxまで引き伸ばしてみた.アップデートが活発なのですぐ使えなくなりそうだけど.

.notion-frame > .notion-scroller > div:first-child > div,
.notion-page-content{
    width: 1200px !important;
    max-width: 100% !important;
    padding-left: 0px !important;
    padding-right: 0px !important;
}
.notion-page-content > div.notion-selectable.notion-collection_view-block {
    width: 1200px !important;
}
.notion-page-content > div.notion-selectable.notion-collection_view-block > div,
.notion-page-content > div.notion-selectable.notion-collection_view-block > div > div.notion-table-view{
    padding-left: 0px !important;
    padding-right: 0px !important;
}

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

flexboxで作るレスポンシブ対応のwebサイトレイアウト

初めに

こんにちは!Ateam Hikkoshi samurai Inc.× Ateam Connect Inc. Advent Calendar 2020の7日目は、最近コーディングをやる機会に恵まれているデザイナーの@futa_326がお送りします。

本記事は、CSSの中でも基礎に当たりますが、
レスポンシブ対応サイトをコーディングする機会があったので、
flexboxの復習のためにも、コードを書き記します
(2次利用などはご自由に)。

全体のレイアウト

全体のレイアウトは一般的に多い、下記の形を再現いたします。

2020-12-06_222054.png

コードとしては下記になります。

html
<body>
    <header>
        ヘッダー
    </header>
    <div class="flexbox">
        <div class="main">メインカラム</div>
        <div class="side">サイドカラム</div>
    </div>
    <footer>
        フッター
    </footer>
</body>
css
@charset "utf-8";

html {
    font-size: 62.5%;
}

body {
  margin: 0; /* 既存CSSのリセット */
  background-color: #f3f3f3;
  min-height: 100vh;
  font-size: 1.6rem;
  display: flex;
  flex-flow: column;
}

header {
  color: #fff;
  background-color: #000;
  padding: 10px;
  height: 50px;
}

.flexbox {
  display: flex;
  justify-content: center;
  margin: 10px;
}

.main {
  flex: 1 1 800px; /*メインカラムの基本幅は800pxで伸びるし、縮む*/
  padding: 10px;
  background-color: #fff;
  border: 2px solid #D3D3D3;
  border-radius: 6px;
}

.side {
  flex: 0 0 400px; /*サイドカラムの基本幅は400pxで伸びないし、縮まない*/
  padding: 10px;
  margin-left: 10px; /*メインカラムとの間の余白*/
  background-color: #fff;
  border: 2px solid #D3D3D3;
  border-radius: 6px;
}

footer {
  color: #fff;
  background-color: #000;
  padding: 10px;
  text-align: center;
  margin-top: auto; /*footerを下に固定する*/
}

/*ディスプレイ幅1340px以下は下記が適用*/
@media screen and (max-width: 1340px) {
    .flexbox {
      display: block;
      margin: 0;
    }
    .main {
      box-shadow: 0 5px 5px hsl(0 0% 0% / 5%);
      margin: 10px 5px;
    }
    .side {
      margin: 10px 5px;
      box-shadow: 0 5px 5px hsl(0 0% 0% / 5%);
    }
  }

メディアスクリーンにて幅狭サイズになると、
displayをblockにすることでflexを解除しています
(この方法がメジャーなのかわかっておりませんが)。

結果としては下記のようになります。
名称未設定-2.png

今、幅広時では常に左右のmarginは10pxづつとなっていますが、
mainクラスのflex: 1 1 800px;をflex: 0 0 800px;とすることで、
伸縮をしなくなり、左右にウインドウに応じた余白を出すこともできます。

image.png

4カラムレイアウト

次にメインカラムの中に、ブログなどでよくある、
サムネイルとテキストのリンクが
4カラムで構成されているレイアウトを組みます。
ただし、ウインドウの幅が狭い(1340px以下)場合には1カラムになるようにします。

image.png

コードとしては下記になります。

html
<div class="main">
  <div class="thumbnail-wrapper">
    <div class="thumbnail"><a href="hogehoge"><img src="hoge.jpg">画像サンプル</a></div>
    <div class="thumbnail"><a href="hogehoge"><img src="hoge.jpg">画像サンプル</a></div>
    <div class="thumbnail"><a href="hogehoge"><img src="hoge.jpg">画像サンプル</a></div>
    <div class="thumbnail"><a href="hogehoge"><img src="hoge.jpg">画像サンプル</a></div>
    <div class="thumbnail"><a href="hogehoge"><img src="hoge.jpg">画像サンプル</a></div>
    <div class="thumbnail"><a href="hogehoge"><img src="hoge.jpg">画像サンプル</a></div>
    <div class="thumbnail"><a href="hogehoge"><img src="hoge.jpg">画像サンプル</a></div>
    <div class="thumbnail"><a href="hogehoge"><img src="hoge.jpg">画像サンプル</a></div>
  </div>
</div>
css
.thumbnail-wrapper {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
}
.thumbnail {
  max-width: 23.5%;
  margin: 6px 5px;
  text-align: center;
  background-color: #ffe3d2;
}
.thumbnail a {
  color: #8a3e1a;
}
.thumbnail img {
  width: 100%;
  height: auto;
}

/*ディスプレイ幅1340px以下は下記が適用*/
@media screen and (max-width: 1340px) {
    .thumbnail-wrapper {
      display: block;
    }
    .thumbnail {
      max-width: 100%;
      margin-bottom: 20px;
    }
}

ウインドウサイズの伸縮があっても、画像もそれに伴って大きくなるようにしてあります。

結果としては下記のようになります。

4カラム

image.png

1カラム

image.png

最後に

まだまだflexboxを用いたレスポンシブ対応で、
学びたいことが数多くありますが、本記事はここで一旦終わりとさせて頂きます。
また、他レイアウトなども随時UPしていきたいと思います。

以上、
Ateam Hikkoshi samurai Inc.× Ateam Connect Inc. Advent Calendar 2020の7日目でした。

明日は、
同じ学校を卒業した@J0UJ1が記事を投稿して下さるので、
楽しみにお待ち下さい!

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

【Rails】チャットの吹き出しの向きを変える方法

本記事は 駆け出しエンジニアの第一歩!AdventCalendar2020 7日目の記事です。

作りたいもの

下図のように、自分のメッセージと自分以外のメッセージでユーザーアイコンの位置や吹き出しの向きが変わるチャット機能を作ります。

image.png

前提条件

  • チャット機能自体はできている
  • bootstrap4がインストールされている

やることまとめ

  1. 自分用の吹き出しと相手用の吹き出しのCSSクラスを用意する
  2. Viewファイルにて、メッセージごとに自分or自分以外のメッセージかを判定する
  3. それぞれの判定ごとに適用するCSSクラスを変える

1.自分用の吹き出しと相手用の吹き出しのCSSクラスを用意する

吹き出しの枠をデザインするCSSクラスを作成します。
自分のメッセージの場合、吹き出しの尻尾が左側に来るように、
自分以外のメッセージの場合、吹き出しの尻尾が右側に来るようにします。

// 自分のメッセージの吹き出し
.says {
  float: left;
  position: relative;
  width: calc(100% - 56px);
  padding: 16px;
  background: #ffffff;
  border-radius: 15px;
  line-height: 1.5;
  word-break: break-all;
}
.says:after {
  content: "";
  display: inline-block;
  position: absolute;
  top: 3px;
  left: -19px;
  border: 8px solid transparent;
  border-right: 18px solid #ffffff;
  -webkit-transform: rotate(35deg);
  transform: rotate(35deg);
}

// 自分以外のメッセージの吹き出し
.other-user-says {
  float: right;
  position: relative;
  width: calc(100% - 56px);
  padding: 16px;
  background: #ffffff;
  border-radius: 15px;
  line-height: 1.5;
  word-break: break-all;
}
.other-user-says:after {
  content: "";
  display: inline-block;
  position: absolute;
  top: 3px;
  right: -19px;
  border: 8px solid transparent;
  border-right: 18px solid #ffffff;
  -webkit-transform: rotate(145deg);
  transform: rotate(145deg);
}

2.Viewファイルにて、メッセージごとに自分or自分以外のメッセージかを判定する

each文でmessageを全て読み込んだ後、メッセージごとに紐づいているユーザーが自分か自分以外かをif文で判定します。

この後、判定結果に応じて、適用する吹き出しのCSSクラスを変えていきます。

message.html.erb
<% messages.each do |m| %>
    <!-- 自分のメッセージの場合 -->
    <% if m.user == current_user %>

    <!-- 自分以外のメッセージの場合 -->
    <% else %>

    <% end %>
<% end %>

3. それぞれの判定ごとに適用するCSSクラスを変える

自分のメッセージの場合、自分用の吹き出しのCSSクラスを適用し、
自分以外のメッセージの場合、自分以外用の吹き出しのCSSクラスを適用するようにします。

message.html.erb
<% messages.each do |m| %>
  <!-- 自分のメッセージの場合 -->
  <% if m.user == current_user %>
    <tr class="row justify-content-center">
      <!-- アイコンを左側に表示する -->
      <td class="col-2">
        <%= link_to attachment_image_tag(m.user, :image, :fill, 80, 80, fallback: "noimage.png", size:'80x80', class:"profile-image align-top"), user_path(m.user) %>
      </td>
      <!-- メッセージを右側に表示する -->
      <td class="col-10">
        <%= m.user.display_name %> <br>
        <!-- 自分用の吹き出しCSSクラスを適用する -->
        <div class="says">
          <p><%= safe_join(m.content.split("\n"),tag(:br)) %></p>
          <span><%= l m.created_at %></span>
        </div>
      </td>
    </tr>

  <!-- 自分以外のメッセージの場合 -->
  <% else %>
    <tr class="row justify-content-center">
      <!-- メッセージを左側に表示する -->
      <td class="col-10">
        <div class="col-11 float-right">
          <%= m.user.display_name %> <br>
        </div>
        <!-- 自分以外用の吹き出しCSSクラスを適用する -->
        <div class="other-user-says">
          <p><%= safe_join(m.content.split("\n"),tag(:br)) %></p>
          <span><%= l m.created_at %></span>
        </div>
      </td>
      <!-- アイコンを右側に表示する -->
      <td class="col-2">
        <%= link_to attachment_image_tag(m.user, :image, :fill, 80, 80, fallback: "noimage.png", size:'80x80', class:"profile-image align-top"), user_path(m.user) %>
      </td>
    </tr>
  <% end %>
<% end %>

終わりに

今回は私個人のアプリで実際に実装したものを題材にしたので、人によってはビューの書き方は異なってくると思います!

ご参考になれば幸いです…!

参考サイト

RailsでDM(ダイレクトメッセージ)を送れるようにしよう

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

プログラミング用語辞典

インスタンス

インスタンスという言葉と一緒に
・クラス
・オブジェクト
の二つも出てくる

それぞれの意味は
クラス・・・設計図
インスタンス・・・設計図から実際に作ったもの
オブジェクト・・・クラスとかインスタンスをふんわりさせたもの

インスタンス化=new演算子で「newする」とも言う
インスタンス化=実際にものをつくる

メソッド

メソッドとはプロパティに入っている関数の事である

var sumple {
  プロパティ : ,
  プロパティ : 関数  これがメソッド

スコープとは

スコープとは、変数の名前や関数などを参照できる範囲のこと
JavaScriptでは、変数や関数などにアクセスできる範囲が決まっている

function fn() {
    const x = 1;
    // fn関数のスコープ内から`x`は参照できる
    console.log(x); // => 1
}
fn();
// fn関数のスコープ外から`x`は参照できないためエラー
console.log(x); // => ReferenceError: x is not defined

仮引数も同じで関数の中では参照可能だが関数の外からは参照できない。

このスコープという言葉は
・グローバルスコープ
・ローカルスコープ
という形でよく見かける

これらの意味は
グローバルスコープは名前の通り最も外側にあるスコープである
だから「JavaScriptのどこからでもアクセスできて全部の範囲が有効」
グローバルスコープで定義した変数はグローバル変数と呼ばれる

ローカルスコープは更に2種類あり、「関数スコープ」「ブロックスコープ」と言うモノがある。

関数スコープ

関数スコープとは上にあげた物も関数スコープである。
letconstは同じスコープ内に同じ名前の変数を二重に出来ないが、これは各スコープで同じ名前の変数は一つしか宣言できないからである。
js
let toaru;
let toaru;
//SyntaxErrorと表記される

だがスコープが異なれば同じ名前で変数を宣言できる

ブロックスコープ

ブロックスコープとは{~~~}で囲んだ範囲の事である
関数スコープと同じようにブロック内で宣言した変数はスコープ内でしか参照できない。

if文やwhile文でもブロックスコープを作るが、これも同じように外側からは参照することは出来ないのである。

https://jsprimer.net/basic/function-scope/

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

【Ruby】Railsを利用して画像にリンクを貼りたい【link_to】

背景

Bootstrapを用いてリンクを装飾しようと意図したとき、適用されずあれ?ってなったので備忘録です

結論

「link_toメソッド内にclassを指定するオプションが存在した」

事象

そもそもなぜこの問題が起こったかですがa要素にclassを適用して、a要素の中にlink_toメソッドを記載していました。(以下のコード)

問題のコード

index.hetml
<a class="nav-link" href=""> 
 <%= link_to('tweetApp',"/") %> 
</a> 

実際に作成されたHTMLを見ると、、、

index.html
<a class="nav-link" href=""></a> 
<a href="/">tweetApp</a> 

あれ?a要素の中にlink_toメソッドを記載したはずなのに飛び出てますね

ここでおかしいと思い調べたところどうやらlink_toメソッド内にclassを指定してあげることでlink_toから生成されるa要素にcssが適用されるみたいです。

解決方法

書式
link_to(リンクテキスト, パス [, オプション, HTML属性 or イベント属性])

実際のコード(今回はtext-lihgtを使用しています)

index.html
<%= link_to("tweetApp","/",class: "text-light"%>

生成されたhtml

index.html
<li class="nav-item">
 <a class="nav-link" href="">tweetApp</a>
</li>

無事a要素にcssが適用されました。

参考

Railsドキュメント:ビューについて

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

CSS:基本編

CSSについて考える

まずは自己紹介

私は、およそ6年間のプログラマ経験のほとんどをフロントエンド開発をしてきました。
そのなかで得た知識をアウトプットしようと思います。

CSSとは

はじめに

スタイルシート用の言語で、HTMLとセットで記述する。
レイアウトや色付け、サイズの変更はもちろん、
アニメーションだってできちゃうすごいやつです。
JavaScriptにしかできなかったこともCSS3になってからはかなりできることが増えました。
今回は基本的な書き方や、お作法などを記載していきます。

基本的な書き方

CSSは3種類の記載方法が存在しています。
1. HTMLタグに直接書く
2. HTML上のstyleタグに書く
3. .css拡張子ファイルに書く

HTMLタグに直接書く

<p style="background: red;">

上記のような記載の仕方が可能です。
基本的に後述する2種類の記載方法より優先的に適用されます。
それゆえに、適用範囲(スコープ)は限定されてしまい、
何度も同じ記載をしなければいけないことや、
2や3の記載方法で書いたスタイルが適用されないなど、
あまりオススメできる記載方法ではありません。

HTML上のstyleタグに書く

<style>
  body {
    background-color: #00ff00;
  }
</style>

上記のような記載でheadタグ内に書くのが一般的かと思います。
3の記述方法よりも優先的に適用されます。
対象の画面を適応対象とし、画面単位に設定可能です。
ただし、昨今のSPAにおけるスタイルとしては弱点が多くあります。
上記のようにbodyタグに書きたい場合、画面が入れ子になっている構造(レンダリング)
だと意図しないタグに影響範囲が出てしまったりするので、十分に考慮が必要です。
またhead内に記載すると可読性の低下に繋がりやすく、
必要なHTML情報へ到達するのに、随分とスクロールしてあげねばなりません。
こちらも場合にもよりますが、基本的にオススメしていません。

.css拡張子ファイルに書く

style.css
body {
  background-color: #00ff00;
}

最後に3の記載方法ですが、他の記法よりも優先度が低く、適用範囲が外部ファイルとして読み込んだHTMLに適用されます。
これは2の特性と同じで、周囲のHTMLに影響が出るので十分に考慮が必要です。
また、外部ファイル取り込み時に
<style src="./style.css">など記載が必要で、より下にあるCSSファイルが有効となります。
今回、この記法をオススメしたいと思いますが、利点として

  • 可読性が良い
  • HTMLとの明示的な役割分担ができる
  • 何度も同じ記載をしなくて良い

などが挙げられます。
外部ファイルにすることで、再利用なども可能になるので是非とも対応いただきたいと思います。

セレクタについて

CSSは基本的にセレクタと呼ばれる適用箇所を限定する記載方法を用いて書いていきます。
例外的に*がありますが、これは全てのノード(要素)へ作用します。
非常に有効的ではありますが同時に予期せぬ影響範囲となるので使用は控えた方が無難でしょう。
ここでは一般的なセレクタの記載方法を書いていきます。

ノードセレクタ

body {
  height: 100%;
}
p {
  margin: 0;
  padding: 0;
}

HTMLタグに直接作用するセレクタです。
当てたい要素の基本設定を作るのに主に用いられます。
影響範囲は大きく、細やかな設定をするのに向きません。
逆に大まかに設定したい時や、サイト全体的に統一したい時など、メインのHTMLに持っていくことで、スタイルを合わせることが可能です。

IDセレクタ

#content-main {
  height: 100%;
}
div#content-main {
  width: 100%;
}

#と記載することでノード属性のid=""を取得可能です。

これらはHTMLタグ内の<main id="content-main">などのID属性を見て適用されます。

また上記タグだと最初に記載したスタイルは適用されますが、後に書いた物は適用されません。
これはIDセレクタの前にノードセレクタがついており、ノードとIDが一致していないためです。
これを解消するには、HTML側のノードを<div id="content-main">とする必要があります。

クラスセレクタ

.btn {
  height: 100%;
}
.btn.primary {
  background: blue;
}

.と記載することでノード属性のclass=""を取得可能です。

これらはHTMLタグ内の<button class="btn">などのclass属性を見て適用されます。
後に記述したように連結して書くことも可能であり、<button class="btn primary">としたノードに適用されます。

属性値セレクタ

[name="text"] {
  border: 1px solid black;
}

属性値セレクタとしては[ ]の中に書くようになります。
こちらについては属性値であれば使用可能です。

正規表現を利用する

属性値セレクタでは正規表現として*^$が利用可能です。

[name*="txt"] {
  border: 1px solid black;
}
[name^="txt"] {
  border: 1px solid red;
}
[name$="Box"] {
  border: 1px solid blue;
}
<input type="text" name="txtName" />
<input type="checkbox" name="checkBox" />

*は中間一致
^は前方一致
$は後方一致
として利用可能です。

階層セレクタ

table thead tr th {
  text-align: left;
}

半角スペースでノードを分けることで階層構造のノードに適用可能です。
これはセレクタ全般に適用可能です。

ちょっとした小技

隣接ノードの取得

+セレクタを使うと隣のノードに対応できます。

span + span {
  font-size: 18pt;
}

>セレクタを使うと直下のノードに対応できます。

div > div {
  display: none;
}

擬似クラス

よく使うのを書いておきます。
:hover マウスが乗った時のスタイル
:active クリックやドラッグしている時のスタイル
:focus ノードにファーカスが当たっている時のスタイル
:visited 遷移後のリンクとかのスタイル
:first-child 出現する最初のノードのスタイル
:last-child 出現する最後のノードのスタイル
:nth-child() 指定したn番目のノードのスタイル
:nth-child(odd) 奇数
:nth-child(even) 偶数
など

詳しくはこちら

おわりに

JSを覚えたりするとついJSでやってしまいますが、
CSSでできることはCSSでやってあげることで、
処理が早くなったりとUI/UXに大きく影響が出ます。
ぜひ有効に活用してください。

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

レスポンシブ対応について

最初に

カレンダー企画2020の7日目
プログラミングの勉強を始めて3ヶ月程経ったので学んだことのメモをアウトプットとして記事に残します。
これからプログラミングの世界に入る人の手助けになれたら嬉しい限りです。
間違っていたり、言葉が違っていたり、表現が誤解されるような言葉があったら教えて下さい^^
言葉を長々と読みづらかったら申し訳ありません。少しずつなれてがんばります。

レスポンシブ対応って何?

PCやタブレット、スマホそれぞれ画面の大きさが違いますよね?それに合わせてレイアウトを変える対応のことをレスポンシブ対応と言います。
こんな経験は無いですか?
PCで見ているときとスマホで見ている時で表示のされ方が違うサイトとか(それがレスポンシブ対応していると言います)

レスポンシブ対応の準備

レイアウトを変える前に行う設定みたいのがあります!
それはHTMLファイルにviewportというものを記述します。

qiita_post.html
<meta name="viewport" content="width=devise-width,initial-scale=1.0">

width=devise-width

widthでコンテンツ表示領域を指定、devise-widthでデバイスの横幅が自動的に適応される設定

initial-scale=1.0

initial-scaleで表示倍率を指定、1.0は等倍という意味

これが終わったら

レスポンシブ対応用に変更する

最も基本的なやり方の「メディアクエリ」を紹介します
いじるところはCSSファイルです

qiita_post.css
.box1 {
  width: auto;
  height: 100px;
  background-color: red;
}

.box2 {
  width: auto;
  height: 50px;
  background-color: blue;
}
/* 画面が1024px以下の際に適応 */
@media screen and (max-width: 1024px) {
  .box1 {
    width: auto;
    height: 100px;
    background-color: green;
  }

  .box2 {
    width: auto;
    height: 50px;
    background-color: gold;
  }
}
/* 画面が768px以下の際に適応 */
@media screen and (max-width: 768px) {
  .box1 {
    width: auto;
    height: 100px;
    background-color: aqua;
  }

  .box2 {
    width: auto;
    height: 50px;
    background-color: slateblue;
  }
}
/* 画面が425px以下の際に適応 */
@media screen and (max-width: 425px) {
  .box1 {
    width: auto;
    height: 100px;
    background-color: hotpink;
  }

  .box2 {
    width: auto;
    height: 50px;
    background-color: black;
  }
}


上記のを見て少しわかったかもしれないですが、
@media screen and (max-width: 〇〇px)これを記述した中に書かれたCSSは対象の大きさになったらこれを適応するといった意味になります。

スクリーンショット 2020-12-06 23.42.48.png
スクリーンショット 2020-12-06 23.43.04.png
スクリーンショット 2020-12-06 23.43.11.png
スクリーンショット 2020-12-06 23.43.17.png

他にもいろいろ出来るので試して見るといいと思います!

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

知識ゼロでもHTML.CSSでアラートとボタンを作る方法

HTML.CSSのコピーアンドペースト手順の備忘録です。

大枠な流れ・Bootstrap使い方まとめ

  1. リンク先コピー:CSSのみ
  2. headerコピー:Components の Alerts
  3. ボタンコピー:Components の Buttons
  4. 全体の中央寄せ:Layout の container
  5. ボタン真ん中寄せ:Utilities の Flex
  6. スペースのバランスを整える:Utilities の Spacing「Exaples」

Step1 フォルダを作る

HTML.CSSを書いていくフォルダを作ります。

~$ mkdir test

Step2 "html5boilerplate"フォルダをダウンロード&コピー

  1. html5boilerplate ダウンロード
  2. ダウンロードフォルダの中へ移動
  3. html5boilerplateフォルダを開く
  4. index.htmlのみコピースクリーンショット 2020-12-07 1.31.04.png
  5. 最初に作ったtestフォルダの中に貼り付ける

Step3 HTML.CSS実行

  1. テストフォルダのindex.htmlをエディタで開く
  2. <body></body>で囲まれている中身を消す
  3. Bootstrap検索
  4. ダウンロード又は下の方にあるCSSのみと書いてある所のコードを全部コピースクリーンショット 2020-12-07 9.01.02.png
  5. <head></head>の19行目あたりに貼り付けスクリーンショット 2020-12-07 11.17.31.png

  6. index.htmlファイルを検索タブにドラック&ドロップしてタブに表示する(CSSのコードの結果が見れる)

Step4 headerをつける

  1. Bootstrapドキュメントをクリック
  2. 左側メニューのComponentsをクリック
  3. Alertsをクリックし好きなコードをコピースクリーンショット 2020-12-07 9.23.46.png
  4. index.htmlファイル<body>のなかに貼り付け

Step5 ボタンをつける

  1. Buttonsをクリック
  2. 好きなボタンコードをコピースクリーンショット 2020-12-07 9.30.32.png
  3. index.htmlファイル<body>内の<div class="alert alert-info" role="alert">A simple info alert—check it out!</div>の下あたりに貼り付け
  4. 4つ同じコードを打つスクリーンショット 2020-12-07 9.45.28.png

Step6 全体を中央に寄せる

  1. Layoutをクリックする
  2. コンテナーコピースクリーンショット 2020-12-07 9.57.11.png
  3. index.htmlファイルの<Body>タグすぐ下に貼り付けて、</div>の部分のみ</Body>のすぐ上に移動させる
例 
<body>
 <div class="container">
    <!-- Content here -->

    <div class="mt-3 alert alert-info" role="alert">
    A simple info alert—check it out!
    </div>
      <button type="button" class="ml-1 btn btn-secondary">Secondary</button>
      <button type="button" class="ml-1 btn btn-secondary">Secondary</button>
      <button type="button" class="ml-1 btn btn-secondary">Secondary</button>
      <button type="button" class="ml-1 btn btn-secondary">Secondary</button>

 </div>
</body>

Step7 ボタンを真ん中に寄せる

  1. UtilitiesFlexをクリック
  2. 好きなレイアウトのコードをコピースクリーンショット 2020-12-07 10.12.57.png
  3. index.htmlファイルの<Body>buttonのすぐ上に貼り付け
例
<div class="d-flex justify-content-center">
      <button type="button" class="ml-1 btn btn-secondary">Secondary</button>
      <button type="button" class="ml-1 btn btn-secondary">Secondary</button>
      <button type="button" class="ml-1 btn btn-secondary">Secondary</button>
      <button type="button" class="ml-1 btn btn-secondary">Secondary</button>
</div>
  1. index.htmlタブをリロードして確認

Step 7 スペースのバランスを整える

  1. Spacingをクリック
  2. スクロールをし「Exaples」のml-1をコピー
  3. index.htmlファイルの<Body><button>のclass内に入れていくスクリーンショット 2020-12-07 10.33.26.png
  4. alartにも同様に入れていくスクリーンショット 2020-12-07 10.36.11.png
  5. index.htmlタブをリロードして確認(色は異なる場合があります)スクリーンショット 2020-12-07 10.38.11.png

完成です。参考になれば幸いです。

参考にした動画:【JavaScript超入門講座】わずか50分で知識ゼロから基礎をマスター!

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

Vue.jsの基礎をまなぼう!

あいさつ

初めての人は初めまして!知っている人はこんにちは!
中学生バックエンドPGのAtieです!
今回はVue.jsについて学んできたのでアウトプットします!
「え?バックエンドがVue.jsをなんで勉強してるの?」
これはフレームワークに慣れておくためとSPAを開発したかったからです
実際Vue.jsを学んだ感想は少し難しかったです
しかしコードがシンプルだったので読みやすく書きやすかったです
では!

環境構築

まずはVue.jsの環境構築をしていきます
まずは必要なファイルを作っていきます

  • main.js
  • index.html
  • style.css

この3つのファイルを作ってください

次にVue.jsを使えるようにします

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

Vue.jsを使うには以下の1行を追加するだけで使えるようになります
便利で簡単ですね!

index.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

この行をhtmlのbodyタグの最後の行に入れます

双方向データバインディング

Vue.jsには「双方向データバインディング」ができるという特徴があります
双方データバインディングですがデータバインディングとはUIとデータを結びつけるという意味で双方向というのはdataを更新すればUIが更新されて逆にUIが更新されればdataが更新されるという意味です
たとえばTwitterを例に挙げてみましょう
Twitterのハートの部分を押すと色がピンクになりハートの数が表示されるようになれます
これはUI(ハートの部分)が更新されることでデータ(ハートの数)が更新されています
この双方向データバインディングができることでSPAが実現可能です

では実際にしてみましょう
まずはVue.jsで制御する要素を作ります

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div id="main">
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

divタグにmainというidを付けました
今回はこのmainという要素の中をVue.jsで制御していきます
次にjsを書いていきます
まずは即時関数でエラーチェックをしてjsからmainを使えるようにします

mian.js
(function () {
    'use strict';
    const vm = new Vue({
        el: '#main'
    })
})();

UIに結び付くモデルはよくView Modelと言われているので略してvmとしました
そしてnewでVueオブジェクトを作成します
どの領域かを結びつけるかをelementsの略であるelで指定します
cssのように#をつけてidを指定します
これでjsから扱えるようになりました
ではこのモデルにdataを保持してもらいます
dataのなかにnameというキーでAtieという値を保持してもらいます

main.js
(function () {
    'use strict';
    const vm = new Vue({
        el: '#main',
        data: {
            comment: "name",
        }
    })
})();

では表示させてみます
表示するには二重波カッコで囲う必要があります

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div id="main">
            <p>{{ name }}</p>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

これで値が表示されてます

次はUIからデータに変更できるようにしてみます
inputたぐにv-model="name"とすることでnameと入力フォームが結びつくようになります
即時に変更されていることがわかります

この二重波カッコですがjsの式をそのまま書くことができます
たとえば入力された文字を大文字にするためにname.toUpperCaseと書くことができます

ToDoListアプリを作る

ではVue.jsの基礎を抑えたのでToDoListアプリを作っていきます
まずはToDoListを保存する配列を作ります
todosというキーで配列を保存します

mian.js
(function () {
    'use strict';
    const vm = new Vue({
        el: '#main',
        data: {
            todos: [
                'todo 1',
                'todo 2',
                'todo 3'
            ],
        }
    })
})();

次にhtmlに反映させますがただ単にliタグの中身を二重波カッコで囲てしまうと追加や削除するときにリロードを挟んでしまうので挟まないようにliタグを配列のループでその数だけ表示するようにします
そのためにはv-forでv-for="todo in todos"とします
こうすることでtodoにtodosの値が一つずつ入っていきその分ループします
そしてliタグの中身をtodoの値を表示するようにすればループされながら配列の中身が一つずつ表示されるようになります
これで表示する仕組みが整いました

ちなみにですがv-のように始まる特殊な属性をディレクティブと呼びます

次にToDoの追加をできるようにします
そのためにformを追加します

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div id="main">
            <ul>
                <li v-for="todo in todos">{{ todo }}</li>
            </ul>
            <form>
                <input type="text" v-model="newTodo">
                <input type="submit" value="Add Todo">
            </form>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

v-modelを使って結び付けていきます
submitされた時のイベントを設定していきます
イベントを設定するにはv-onとする必要があります
v-onはよく使うので@と略することができます

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div id="main">
            <ul>
                <li v-for="todo in todos">{{ todo }}</li>
            </ul>
            <form v-on:submit="addTodo">
                <input type="text" v-model="newTodo">
                <input type="submit" value="Add Todo">
            </form>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

ではsubmitされた時のイベントを設定していきます
methodsというキーにメソッドを設定していきます
data内のデータにはthisでアクセスすることができます

main.js
(function () {
    'use strict';
    const vm = new Vue({
        el: '#main',
        data: {
            newItem: '',
            todos: [
                'todo 1',
                'todo 2',
                'todo 3'
            ],
            methods: {
                addTodo: function() {
                    this.todos.push(this.newItem);
                },
            },
        },
    })
})();

このように書くことができます
しかしこのままではformがsubmitされてページが移行してしまうのでうまくいいきません
これを防ぐためには@submit.preventとすることで防ぐことができます

これでうまくいきます

追加した後にフォームに値が残ってしまうのでnewItemを空にしておきます

main.js
(function () {
    'use strict';
    const vm = new Vue({
        el: '#main',
        data: {
            newItem: '',
            todos: [
                'todo 1',
                'todo 2',
                'todo 3'
            ],
            methods: {
                addTodo: function() {
                    this.todos.push(this.newItem);
                    this.newItem = '';
                },
            },
        },
    })
})();

ToDoの削除

次にToDoの削除をできるようにしていきます
原理としてはtodosの配列から削除すればいいので簡単です

spanでxを作っていきます

この場合todosの何番目を削除すればいいのかわからないのでこのようにしてindexに数字が入るようにします

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div id="main">
            <ul>
                <li v-for="(todo, index) in todos">{{ todo }} <span @click="deletItem">[X]</span></li>
            </ul>
            <form @submit.prevent="addTodo">
                <input type="text" v-model="newTodo">
                <input type="submit" value="Add Todo">
            </form>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

では削除するメソッドを作っていきます
といっても簡単ですspliceでindex番目から1番目を消すだけです
簡単簡単♪

main.js
(function () {
    'use strict';
    const vm = new Vue({
        el: '#main',
        data: {
            newItem: '',
            todos: [
                'todo 1',
                'todo 2',
                'todo 3'
            ],
            methods: {
                addTodo: function() {
                    this.todos.push(this.newItem);
                    this.newItem = '';
                },
                deletItem: function(index) {
                    this.todos.splice(index, 1);
                    this.newItem = '';
                },
            },
        },
    })
})();

これで削除の仕組みができました

完了状態を管理する

では次に完了状態を管理できるようにします
完了状態を管理するためにtodosをオブジェクトにしてtitleとisDoneで管理します

main.js
(function () {
    'use strict';
    const vm = new Vue({
        el: '#main',
        data: {
            newItem: '',
            todos: [{
                title: 'task 1',
                isDone: false
            }, {
                title: 'task 2',
                isDone: false
            }, {
                title: 'task 3',
                isDone: true
            }],
            methods: {
                addTodo: function() {
                    this.todos.push(Item);
                    this.newItem = '';
                },
                deletItem: function(index) {
                    this.todos.splice(index, 1);
                    this.newItem = '';
                },
            },
        },
    })
})();

ただこのままでは表示がおかしくなるのでhtmlも変えます

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div id="main">
            <ul>
                <li v-for="(todo, index) in todos">{{ todo.title }} <span @click="deletItem">[X]</span></li>
            </ul>
            <form @submit.prevent="addTodo">
                <input type="text" v-model="newTodo">
                <input type="submit" value="Add Todo">
            </form>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

次に完了状態を可視化していきます

まずは以下のようにhtmlを変えます

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div id="main">
            <ul>
                <li v-for="(todo, index) in todos">{{ todo.title }} <span @click="deletItem">[X]</span></li>
            </ul>
            <form @submit.prevent="addTodo">
                <input type="checkbox" v-model="todo.isDone">
                <input type="text" v-model="newTodo">
                <input type="submit" value="Add Todo">
            </form>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

次にisDoneの状態によってチェックの表示状態を変えたいのですが...
便利なことにisDoneがtrueの時にチェックがついてくれます

チェックがついた項目はdoneというclassをつけてあげてあげます
データにおうじてclassを付け替えるにはv-bind:classをつけます
v-bindもv-onと同じくよく使われるので:で略してつけることができます

main.js
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div id="main">
            <ul>
                <input type="checkbox" v-model="todo.isDone">
                <span :class="{done :todo.isDone}"
                <li v-for="(todo, index) in todos">{{ todo.title }}<span @click="deletItem">[X]</span>
                </li>
            </ul>
            <form @submit.prevent="addTodo">
                <input type="text" v-model="newTodo">
                <input type="submit" value="Add Todo">
            </form>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

次にToDoListがなかったらなにか表示させてみましょう
そのためには条件分岐をする必要があるのですが条件分岐をするにはv-ifというディレクティブがあります
今回はToDoListがなかったら「No todolist」と表示させます
通常ならいつもどおりliのところに入れて条件分岐させるのですがv-ifとv-forではv-forのほうが優先されてしまうのでulのほうに書きます

main.js
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/styles.css">
    </head>
    <body>
        <div id="app" class="container">
            <h1>My Todos</h1>
            <ul v-if="todos.length">
                <li v-for="(todo, index) in todos">
                <input type="checkbox" v-model="todo.isDone">
                <span :class="{done: todo.isDone}">{{ todo.title }}</span>
                <span @click="deleteItem(index)" class="command">[x]</span>
                </li>
            </ul>
            <ul v-else>
                <p>No todolist</p>
            </ul>
            <form @submit.prevent="addItem">
                <input type="text" v-model="newItem">
                <input type="submit" value="Add">
            </form>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

todos.lengthがtrueだったらv-ifの処理が実行されてv-elseの場合はメッセージを表示するようになっています
v-ifを使わないで書く書き方がありますがそっちのほうがすっきりしているしているのでそちらを使います
v-showというディレクティブです
v-showはv-ifと同じように条件を入れます

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/styles.css">
    </head>
    <body>
        <div id="app" class="container">
            <h1>My Todos</h1>
            <ul>
                <li v-for="(todo, index) in todos">
                <input type="checkbox" v-model="todo.isDone">
                <span :class="{done: todo.isDone}">{{ todo.title }}</span>
                <span @click="deleteItem(index)" class="command">[x]</span>
                </li>
                <li v-show="!todos.length">No todos</li>
            </ul>
            <form @submit.prevent="addItem">
                <input type="text" v-model="newItem">
                <input type="submit" value="Add">
            </form>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

今回はtodos.lengthがfalseの時に実行してほしいので!を使ってnot論理回路にしました

ToDoListの数を表示する

次にToDoListの数と終わった数を表示させます
Vue.jsは算術プロパティを使うことができるので使っていきます

computedというキーを使ってデータから動的にプロパティを計算してくれる算術プロパティがあるので使っていきます
remainingとしてあげてデータから自動的にremainingを算出してプロパティにしてあげます
今回はisDoneがfalseの項目を調べたいのでjsのfilterという命令を使ってみます
filter関数を引数に取るのでfunction(todo)としつつtodoのisDoneがfalse
つまり残っているタスクをreturnすればそれをフィルターしてitemにまだ終わっていないタスクを入れていけばわかります
今回まだ終わってないタスクの件数を調べたいのでlengthを返してあげればremainingにはisDoneがfalseの件数はいってくるはずです

main.js
(function() {
    'use strict';
    var vm = new Vue({
        el: '#app',
        data: {
            newItem: '',
            todos: [{
                title: 'task 1',
                isDone: false
            }, {
                title: 'task 2',
                isDone: false
            }, {
                title: 'task 3',
                isDone: true
            }]
        },
        methods: {
            addItem: function() {
                var item = {
                    title: this.newItem,
                    isDone: false
                };
                this.todos.push(item);
                this.newItem = '';
            },
            deleteItem: function(index) {
                this.todos.splice(index, 1);
            },
            computed: {
                remaining: function() {
                    const items = this.todos.filter(function() {
                        return !todo.isDone;
                    });
                    return items.length;
                }
            }
        }
    });
})();

少しややこしいですがよく見るとただitemsにtodo.isDoneがfalseのを入れていてその下でitemsの数を返しています

main.js
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/styles.css">
    </head>
    <body>
        <div id="app" class="container">
            <h1>
                My Todos
                <span class="info">({{ remaining }} / {{ todos.length }})</span>
            </h1>
            <ul>
                <li v-for="(todo, index) in todos">
                <input type="checkbox" v-model="todo.isDone">
                <span :class="{done: todo.isDone}">{{ todo.title }}</span>
                <span @click="deleteItem(index)" class="command">[x]</span>
                </li>
                <li v-show="!todos.length">No todos</li>
            </ul>
            <form @submit.prevent="addItem">
                <input type="text" v-model="newItem">
                <input type="submit" value="Add">
            </form>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

あとはspanタグで表示できるようにしておきました

完了したタスクの一括削除

次に完了したタスクの一括削除をしていきます
purgeというボタンをつけて@clickでpurgeという処理をさせます

purgeというメソッドを作っていきます
まずthis.todosにremainingを割り当てることで終わった数ではなくて終わった配列そのものを返すようにします
remainingを表示するところも直す必要があります

main.js
(function() {
    'use strict';
    var vm = new Vue({
        el: '#app',
        data: {
            newItem: '',
            todos: [{
                title: 'task 1',
                isDone: false
            }, {
                title: 'task 2',
                isDone: false
            }, {
                title: 'task 3',
                isDone: true
            }]
        },
        methods: {
            addItem: function() {
                var item = {
                    title: this.newItem,
                    isDone: false
                };
                this.todos.push(item);
                this.newItem = '';
            },
            deleteItem: function(index) {
                this.todos.splice(index, 1);
            },
            computed: {
                remaining: function() {
                    return this.todos.filter(function(todo) {
                        return !todo.isDone;
                    })
                }
            },
            purge: function() {
                if (!confirm('delet finished?')) {
                    return;
                }
                this.todos = this.remaining;
            }
        }
    });
})();
index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>My Vue App</title>
        <link rel="stylesheet" href="css/styles.css">
    </head>
    <body>
        <div id="app" class="container">
            <h1>
                My Todos
                <span class="info">({{ remaining.length }} / {{ todos.length }})</span>
            </h1>
            <ul>
                <li v-for="(todo, index) in todos">
                <input type="checkbox" v-model="todo.isDone">
                <span :class="{done: todo.isDone}">{{ todo.title }}</span>
                <span @click="deleteItem(index)" class="command">[x]</span>
                </li>
                <li v-show="!todos.length">No todos</li>
            </ul>
            <form @submit.prevent="addItem">
                <input type="text" v-model="newItem">
                <input type="submit" value="Add">
            </form>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="js/main.js"></script>
    </body>
</html>

LocalStrageでデータの永続化

次にLocalStorageでデータの永続化をしてみます

データの保存はtodosに変更が加えられたときに保存の処理を実行したいのでdeep watcherを使っていきます
watchだけだと配列の中身の変更まで監視してくれないのでdeepオプションをオンにするためにdeep: trueとする必要があります

main.js
(function() {
    'use strict';
    var vm = new Vue({
        el: '#app',
        data: {
            newItem: '',
            todos: [{
                title: 'task 1',
                isDone: false
            }, {
                title: 'task 2',
                isDone: false
            }, {
                title: 'task 3',
                isDone: true
            }]
        },
        watch: {
            todos: {
                handler: function() {
                    localStorage.setItem('todos', JSON.stringify(this.todos));
                }
            },
            deep: true
        },
        methods: {
            addItem: function() {
                var item = {
                    title: this.newItem,
                    isDone: false
                };
                this.todos.push(item);
                this.newItem = '';
            },
            deleteItem: function(index) {
                this.todos.splice(index, 1);
            },
            computed: {
                remaining: function() {
                    return this.todos.filter(function(todo) {
                        return !todo.isDone;
                    })
                }
            },
            purge: function() {
                if (!confirm('delet finished?')) {
                    return;
                }
                this.todos = this.remaining;
            }
        }
    });
})();

データが保存できたので取り出してみます
this.todosに対してjsonデータをparseしつつlocalStrageからtodoのキーでデータをgetItemすればいいです
ついでにthis.todosの配列を空にしておきます

main.js
(function() {
    'use strict';
    var vm = new Vue({
        el: '#app',
        data: {
            newItem: '',
            todos: []
        },
        watch: {
            todos: {
                handler: function() {
                    localStorage.setItem('todos', JSON.stringify(this.todos));
                }
            },
            deep: true
        },
        methods: {
            addItem: function() {
                var item = {
                    title: this.newItem,
                    isDone: false
                };
                this.todos.push(item);
                this.newItem = '';
            },
            deleteItem: function(index) {
                this.todos.splice(index, 1);
            },
            mounted: function() {
                this.todos = JSON.parse(localStorage.getItem('todos')) || [];
            },
            computed: {
                remaining: function() {
                    return this.todos.filter(function(todo) {
                        return !todo.isDone;
                    })
                }
            },
            purge: function() {
                if (!confirm('delet finished?')) {
                    return;
                }
                this.todos = this.remaining;
            }
        }
    });
})();

もし何も保存されていなければエラーになるのでそれを防ぐためになかった場合は空の配列を返すようにしました

これで一通りのVue.jsの基礎をまなべました

最後に

Vue.jsの基礎をこの記事にギュッと詰め込みました!
コンポーネントはまた別の記事で解説します
最後まで読んでいただきありがとうございました!
Twitterしています!→AtieのTwitter
では!また次回の記事で!

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

走る!音が出る!HTMLでミニゲームをつくりました

FIRE RUNNER SAN
会社非公式な感じの、シンプルなランゲームを作りました。

https://hasegawa-campfire.github.io/fire-runner-san/
こちらから遊べます。
PC推奨です。IEは死にました。すこし音も出ます。

実際に開くと、なんだか下のほうがゴチャゴチャしていますね。
ここ数日、色々と後付けをしてこうなりました。
ランキングとか。リプレイとか。SNSシェアとか。効果音とか。
ああ、付けてみたいなと思ってしまったのですから、これは仕方のないことです。

その辺り、思うところや紆余曲折などイロイロありますが。
モロモロは note にしたためて、こちらは技術的な記録を残します。

※note
会社でランゲームをつくりました

技術概要

HTML5 でゲーム作りと言えば、やはり WebGL でしょう。
描画が高速で、表現力も高い。ライブラリやフレームワークも充実しています。

しかし今回は WebGL どころか canvas ですらなく、本当に HTML で構成されています。
ライブラリも特に使っていません。(Firebase を除く)

概略
<div class="app">
  <div class="world" style="transform: translate(32px, 256px);">
    <svg class="player" style="transform: translate(0px, 0px);">...</svg>
    <svg class="item" style="left: 200px; top: 0px;">...</svg>
    ...
  </div>
  <div class="status show">...</div>
  ...
</div>

div を連ねて、 positoin で配置して、 border background などで彩る。
addEventListener を仕掛けて、transform などで動かす。

Webフロントとしては、いつもの見慣れた HTML + CSS + JS です。

つよいところ

  • とにかく書き慣れていること
  • 複雑な仕組みが要らないこと
  • 簡単なアニメーションが、本当に簡単なこと

あと今回に関して言えば、Webフロントエンジニアの発表物として、
HTML と CSS を活用したほうが紹介しやすい部分も多いだろう、
という目論見もありました。
当初は。

つらいところ

  • とにかく遅いこと
  • ゲームとしての表現力は弱いこと
  • 表示の見え方についても動作環境に振り回されること

あと今回に関して言えば、スクリーンショットが取れないこと。
SNSシェアに使いたかったんです。
しかし自前で実現しようと思ったら、ものすごく頑張る必要があります。
html2canvas でもダメだったので、諦めました。

ゲーム表示対応

実は、見慣れた内容になりすぎて、改めて紹介できそうな部分がとても少なくなりました。
とはいえ、
さすがに普段作っているWebページでは使わないようなものも、いくつかあります。

描画速度の改善

HTMLの要素をゴリゴリ動かすので描画が遅くなりがちです。特にスマホ。
完成してから、初めて手元のスマホから見て頭を抱えたりもしましたが、もう遅い。

というわけで描画改善ですが、ただおもむろに will-change をつけるだけです。
簡単ですね。
これは予めどう動くのか宣言しておくことで、
その要素についてGPUを確保するなど最適化をしてくれるそうです。

ただし、乱用するとGPUやメモリを圧迫して逆に遅くなるとも言います。
あと意図しない表示バグを引き起こしたりもします。

.hoge {
  will-change: transform, opacity;
}

とはいえ効果は絶大。ガタガタしていたゲームが、突然ヌルヌルになるほど。

スマホのスペックの目安がわからなくて、どの程度まで動けばいいのか判断に困るのですが、
とりあえず手元の Rakuten Mini で動けばだいたい動くのでは?
という調整で作っています。

※ただしゲーム的にはPC推奨。スマホでのクリアは相当困難です。

GPUで困った時のおまじない

will-changetransform を使っていると、時々、想定外の事態に出くわします。
表示が欠けるとか、ボケるとか。重なりが z-index を無視するとか。

そんな時は backface-visibility: hidden を書くと解決する、こともあります。

.hoge {
  backface-visibility: hidden;
}

まるで魔法のようですが、私には解決する理由がわからないので、だいたい魔法です。
意味合いとしては裏面を表示しないようにする、つまりカリングだと思うんですが。
どうしてそれで直るんでしょうね。

右クリックメニューや文字選択の禁止

Webコンテンツを守る不毛な対策。ではありません。
スマホでの長押し操作は、文字の選択やメニューの表示でキャンセルされてしまうため、
それを回避します。

addEventListener('contextmenu', (e) => {
  e.preventDefault()
})
html {
  user-select: none;
}

スマホのビューポート

だいたい定型文で済まされる viewport の定義。
しかしスマホやゲームなら、ちょっと立ち止まってみる価値があるかもしれません。

width に数値を入れると、画面はその幅で表示してくれます。可能な限り。
もうスマホの画面に合わせようと、スクリプトで頑張らなくてもいいんですね。
画面が合わせます。

ダブルタップや、ピンチアウトでの画面の拡大。
それを禁止するのは良くないことだと言われますが、ゲームでは知ったことではありません。

<meta name="viewport" content="width=640,user-scalable=no">

Pull to Refreshの抑制

画面を下に引っ張って、ページを読み直す。
これもゲームでは操作の間違いになりますから、動かないようにします。

body {
  overscroll-behavior-y: none;
}

キーボードでのスクロールの抑制

当然ですが、スペースキーや矢印キーを押すと、ページがスクロールします。
これもゲームでは嬉しくないのでストップ。
なのですが、テキストの入力も出来なくなってしまいますので、
実際にはそういったものを除外するような記述が必要になります。

addEventListener('keydown', (e) => {
  if (e.key === ' ') {
    e.preventDefault()
  }
})

ところで keydown ってキャンセルしても keyup が動くんですね。
keypress は動かなくなりますけど。

音周り

JavaScriptにおいて、音の再生はとても簡単です。
<audio> を作って play() するだけ。ありがたい。

const audio = new Audio('hoge.mp3')
audio.play()

しかしこれではスマホで遅延が起きます。
読み込みので話ではありません。1度再生をした音でも毎回再生で遅延します。

とはいえ、遅延くらい別にいいのでは。PC推奨ですしね。と思っていたのですが。

Safari対応

実はこのままだとSafariでほとんど音が鳴りません。

Chromeでも何か操作をするまで再生できないというセキュリティ仕様がありますが、
Safariは更に厳格で、毎回、ユーザー操作のタイミングでしか再生できないようです。

当然のように回避策はあって、初回のユーザー操作時に再生して、すぐに一時停止して、
あとは好きなタイミングで再開すれば良いとかなんとか。なるほど。

しかし1度再生し終えたら、また再生するには同じことが必要になりそうなので、
効果音として使うのは難しそうでした。
(動きを見ないで次に進んだため、試していません。違っていたらすみません)

Web Audio API

そこで Web Audio API
こちらにも似たセキュリティ仕様はありますが、大本の AudioContext をなんとかすれば、
あとはそれぞれの音を好きなタイミングで鳴らせます。スマホでの遅延もなくなります。
素敵ですね!

今回はこんな感じのものを書いて AudioContext を取りました。

const audioContextPromise = new Promise(resolve => {
  for (const type of ['touchend', 'mouseup', 'keyup']) {
    addEventListener(type, resolve)
  }
}).then(async () => {
  const ctx = new AudioContext()
  await ctx.resume()
  return ctx
})

※参考
Web Audio APIの闇

Firebase

今回、気まぐれにランキングやアカウントの紐付けを実装したくなったので、
初めて Firebase を利用してみました。

これは、なんと言いますか、
とても簡単に使えすぎて知見としてはあまり残せそうにないのが難ですね。
ありがたいことです。

しいて言うと、
Firebase Authentication がユーザー名を持ってくれるので便利だとか、
ランキングは Firestore から1度読んだら、あとは放置しても自動更新されて楽だとか、
そんな感じです。

※参考
0から始める Firestore + Firebase Authentication
Firestore Security Rules の書き方と守るべき原則

おわりに

Webフロントはたのしいですね!
Webフロントはたのしいですよ!

※ FIRE RUNNER SAN リポジトリ
https://github.com/hasegawa-campfire/fire-runner-san

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

マジミラ2020プロコンで使用したものまとめ1(背景)

概要

相生葵の Advent Calendar 2020 7日目の記事です。

初音ミク「マジカルミライ 2020」プログラミング・コンテストに参加したので使用したもののまとめ記事です。

再生はこちらでできます。
コードはこちらにあります。

目次

  1. 縦スプライト
  2. 黒幕
  3. 丸エフェクト

1. 背景のスプライト

こちらのページを参考にしています。
実際に使用したものが以下です。

index.html
<body>

</body>
index.css
body{
    background-color: #ff007f;
    background-image: -webkit-gradient(linear, 0 0, 100% 0, color-stop(.5, #ffffff), color-stop(.5, transparent), to(transparent));
    -webkit-background-size: 50px;
}

background-color

「background-color」は背景色の指定です。
ここでは、派手なピンク色(#ff007f)を指定しています。

background-size

-webkit-background-size」は背景イメージの大きさ指定です。
background-sizeを指定するとデフォルトで背景に敷き詰めが発生するらしいです。
ここでは50pxを指定しているので、画面には「50px × 50px」の正方形が敷き詰められています。

background-image

background-image」は背景にイメージを指定するためのパラメータです。

ここでは背景を指定せずに、-webkit-gradient(linear-gradient)という線形グラデーションの指定を使用しています。

ちなみに、「-webkit-gradient」は旧式の書き方で今は「linear-gradient」が一般的らしいです。

通常は「from」で開始色を指定しますが、私が書いたものには「from」にあたるパラメータがありません。
おそらく「color-stop」が「from」の代わりになっているのではないかと思います。
少し動かして見た感じ、開始位置から1つ目の「color-stop」で指定した位置まで指定した色になっているようです。

まとめると以下のようになります。

background-image: -webkit-gradient(linear
, 0 0                         /*開始地点の指定 0, 0 なので 背景イメージ領域の左下?*/ 
, 100% 0                      /*終了地点の指定 100%, 0 なので 背景イメージ領域の右下?*/
, color-stop(.5, #ffffff)     /*開始地点から50%の位置までを白色にする*/
, color-stop(.5, transparent) /*開始地点から50%の位置以降をピンク色にする*/
, to(transparent));           /*ピンク色が最後で終わるグラデーション*/

参考

S3 グラデーション(gradient)の指定方法

2. 黒幕

実際に使用したものは以下です。

index.html
<body>
  <div id="background-effect"></div>
</body>
index.css
#background-effect{
    background-color: #000000;
    opacity: 0.5;
    position: fixed;
    z-index: -1;
    width: 100%;
    height: 120%;
    top: -20%;
}

単純に背景全体に黒色をかぶせています。

各パラメータは以下のように設定しています。

パラメータ 設定値 意図
background-color #000000(黒) 色の指定
opacity 0.5 透過度
position fixed 位置の指定
他の要素の位置によって位置が変化しないように絶対値を指定
z-index -1 背景以外に黒幕がかからないようにマイナス値を指定
width 100% 背景全体に黒幕をかけたかったので100%を指定
height 120% 背景全体に黒幕をかけたかったので120%を指定
top  -20% 黒幕がかからない部分があったので少し調節

3. 丸エフェクト

実際に使用したものは以下です。

index.html
<body>
  <div id="background-object-effect" class="display-none">
    <div id="effect-1"></div>
    <div id="effect-2"></div>
    <div id="effect-3"></div>
  </div>
</body>
index.css
.display-none{
    display: none;
}

#background-object-effect > div{
    color:#bce2e8;
    position: fixed;
    z-index: -1;
}

#effect-1{
    animation: effect1 3s ease-out forwards infinite;
}

#effect-2{
    animation: effect2 2s ease-out forwards infinite;
}

#effect-3{
    animation: effect3 1s ease-out forwards infinite;
}

@keyframes effect1 {
    0% {
        font-size: 300px;
        left: -20%;
        top: -20%;
        opacity: 0.7;
    }
    100% {
        opacity: 0;
        left: 50%;
        top:50%;
        font-size: 0px;
    }
}

@keyframes effect2 {
    0% {
        font-size: 200px;
        left: 0%;
        top: 20%;
        opacity: 0.7;
    }
    100% {
        opacity: 0;
        left: 50%;
        top:50%;
        font-size: 0px;
    }
}

@keyframes effect3 {
    0% {
        font-size: 100px;
        left: 80%;
        top: 10%;
        opacity: 0.7;
    }
    100% {
        opacity: 0;
        left: 50%;
        top:50%;
        font-size: 0px;
    }
}

html内に記述した「●」を動かしています。
htmlの上から見ていきます。

background-object-effect

index.html
<div id="background-object-effect" class="display-none">
  <!-- エフェクト要素 -->
</div>
index.css
.display-none{
    display: none;
}

ここでは、「background-object-effect」というid要素をもったdiv要素を定義しています。

曲が始まるまでは、エフェクトを画面に表示させたくなかったので「display: none」で画面に表示させないようになっています。

曲の読み込みが終わったらjs側で、以下のように「display-none」クラスを取り除いています。

js
$("#background-object-effect").addClass("display-none");

曲が停止されたら表示を消すようにjs側で、「display-none」クラスを再度付与しています。

js
$("#background-object-effect").addClass("display-none");

effect-1~3共通要素

index.html
<div id="background-object-effect" class="display-none">
  <div id="effect-1"></div>
  <div id="effect-2"></div>
  <div id="effect-3"></div>
</div>
index.css
#background-object-effect > div{
    color:#bce2e8;
    position: fixed;
    z-index: -1;
}

cssの「#background-object-effect > div」は「#background-object-effect」直下の「div要素」という意味です。

「background-object-effect」以下に「effect-1, effect-2, effect-3」のdiv要素があるので、このcss要素が「effect-1, effect-2, effect-3」の全てのdiv要素に適用されています。

各パラメータについては以下です。

パラメータ 設定値 意図
color #bce2e8 文字色(●)の指定
position fixed 位置の指定
他の要素の位置によって位置が変化しないように絶対値を指定
z-index -1 背景以外に●がかからないようにマイナス値を指定

effect-1~3個別要素

index.html
<div id="background-object-effect" class="display-none">
  <div id="effect-1"></div>
  <div id="effect-2"></div>
  <div id="effect-3"></div>
</div>
index.css
#effect-1{
    animation: effect1 3s ease-out forwards infinite;
}

@keyframes effect1 {
    0% {
        font-size: 300px;
        left: -20%;
        top: -20%;
        opacity: 0.7;
    }
    100% {
        opacity: 0;
        left: 50%;
        top:50%;
        font-size: 0px;
    }
}

effect-1, 2, 3はパラメータの値を変えているだけなので、effect-1だけ見ていきます。
ここでは、cssアニメーションを使用して、動きの設定をしています。

アニメーションの設定

「@ keyframes」の部分がアニメーションの設定です。

「effect1」はアニメーションの名前です、呼び出し時にこの名前を指定します。

「0%」が開始時点、「100%」が終了時点の要素の状態です。
間の「1%~100%」については指定しなくてもいい感じに調整してくれます。(指定することも可能です)

各パラメータについては以下のように設定しました。

パラメータ 0% 100% 意図
font-size 300px 0px 文字をだんだん小さく
left -20% 50% 位置の指定
左の画面外20%の位置から画面中央の横幅に移動する
top -20% 50% 位置の指定
画面の上から20%の位置から画面中央の高さに移動する
opacity 0.7 0 透明度をだんだん大きく

アニメーションの呼び出し

以下の部分でアニメーションの呼び出しを行っています。

#effect-1{
    animation: effect1 3s ease-out forwards infinite;
}

以下のように各パラメータが対応しています。

パラメータ 設定値 意図
アニメーション名 effect1
アニメーション時間 3s
イージング ease-out
アニメーション終了時の状態を終了後も適用するか forwards 適用する
繰り返すか infinite 繰り返す

時間以降は設定しなくてもデフォルト値が適用されます。
デフォルト以外を使用したい場合は、パラメータを設定します。

まとめ

以上、背景で使用したものまとめでした。

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