- 投稿日:2020-12-07T23:56:31+09:00
見積書の日付を変更する
1.はじめに
営業視点だと、見積書の日付をちょこっと修正したいことがある。
週明けに提出する時とか、翌日に原紙を持参して打ち合わせしたい時とか。
でも見積書をシステム化してると、承認日は変更できないし、HTML上で日付のみ変えるにしても無制限だと困る。
だから、条件付きで日付を変更できるようにした。2.概要
できるようにしたこと
- カレンダー
<input type="date">
でHTML見積書の日付を変更。- 選択できる日付は、承認日〜承認日の最大3日後。
前提条件
- 見積書作成をSalesforceでシステム化している。
- 見積書レコードは、承認後はロックされる。
- 見積書はHTMLとして出力する。
3.ソースコード
HTML(Visualforce)
<input type="date" id="cal" onchange="changedate()" value="{!Quote__c.appdDateForCal__c}" min ="{!Quote__c.appdDateForCal__c}" max ="{!Quote__c.sbmtDateForCal__c}" > </input> <span id="date">{!Quote__c.appdDateForPrint__c}</span> <!-- appdDateForCal__c:承認日(YYYY-MM-DD) --> <!-- sbmtDateForCal__c:提出可能日(YYYY-MM-DD) --> <!-- appdDateForPrint__c:承認日(YYYY年M月D日) -->javascript
function changedate(){ let appdDate = "{!Quote__c.appdDate__c}"; let DateElmts = $("#cal").val().split("-"); //YYYY-MM-DDを[YYYY, MM, DD]に変換 if(appdDate != ""){ $("#date").text(DateElmts[0] + "年" + Number(DateElmts[1]) + "月" + Number(DateElmts[2]) + "日"); } //この条件分岐がないとmax/minが未設定扱いとなり日付を自在に変更できてしまう }Salesforce数式項目
//1.sbmtDate__c(承認日またはその最大3日後) IF( TODAY() <= appdDate__c + 3, appdDate__c, appdDate__c + 3 ) //2.sbmtDateForCal__c(上記をYYYY-MM-DDに変換) TEXT(YEAR(appdDate__c)) & "-" & RIGHT("0" & TEXT(MONTH(sbmtDate__c)), 2) & "-" & RIGHT("0" & TEXT(DAY(sbmtDate__c)), 2) /* ・appdDate__cは承認プロセスで自動入力させる日付型データ ・appdForCal__cも同様 ・コメントアウトは便宜的なもの */4.雑記
- カレンダー
<input type="date">
を<apex:outputPanel>
で囲い、表示条件を!ISBLANK(appdDate__c)
にしてもいいかも。<apex:outputPanel rendered="{!IF(!ISBLANK(Quote__c.appdDate__c), true, false)}"> ... </apex:outputPanel>
- 投稿日:2020-12-07T23:55:08+09:00
業務でチーム開発をしていて思うこと
チームで開発するのって難しい。。。
Webサービス開発を業務でやっていて大変に思うことが色々あります。
特にチーム開発で進めるときには細心の注意を払います。
(上手くいかないとお互いにストレス溜まりますよね)今後のためになんとなく現段階で気をつけようと思う点をメモしておきます。
言い方って大事
⇨ 何かお願いすることの連続
⇨ 教えてもらったり教えたり
⇨ 自分の知っている部分は他の人は知らなかったり知っていたり
↪︎誰が上で誰が下かがあまりないかもしれない
⇨ 開発している人は詳しいが、他の人はそのファイル回りをあまり見てないので詳しくない
⇨ 指示が不明確だとかなり困る
⇨ 横柄な態度はよくない
⇨ 指示なのかお願いなのか何を話したか曖昧になる
⇨ 言った言ってないの問題になる
↪︎メモとるの大事
⇨ 同じイメージをちゃんと共有できているか人によって開発スピードや順序が違う
⇨ 統一するべき?
⇨ ペースを合わせるのが大事だし大変どこまでテキトーにやるか、どこまで厳密にやるか
⇨ クオリティかスピードか
⇨ 質か量か
⇨ 難しい、日頃からすり合わせするべきかもしれない
⇨ 性格が出る、亀裂を生む可能性も今日の名言
お前のためにチームがあるんじゃねぇチームの為にお前がいるんだ!!
-安西先生(湘北高校バスケットボール部顧問)
- 投稿日:2020-12-07T23:05:20+09:00
CSSだけでクリックできるレビューの星(★★★★☆)を作る方法
どうも7noteです。レビューや口コミがとても重要な時代になってきましたね。
ホームページでもレビューを見て商品を購入するかどうか決める人がとても増えてきたと思います。
となると実装できないと困りますよね。そこでCSSだけでできるレビューの星を作って評価できるものを作っていきます。
データを記録させるためにはサーバーと連携させたり、javascriptの組み込みが必要になります。
ここでは見た目の動きについての作り方を解説します。見本
ソース
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のちょいテク詰め合わせ
- 投稿日:2020-12-07T22:43:49+09:00
【Javascript】英語の歌を上手に歌いたい
はじめに
これはクソアプリ2 Advent Calendar 2020の7日目の記事となります。
英語の歌を上手に歌いたいって思ったので、そのための準備をする簡易なアプリケーションです。
クソアプリということで、自分的に作り込んでない簡単なアプリケーションという意味合いとしています。今回はアプリよりもこんな方法もあるよって情報が広まれば、この記事としては成功です。
英語の歌
英語の勉強をしていて、そろそろシャドーイングってのをやらないとなと思って、とりあえずドラえもんの英語音声版を読んでいます。
漫画の「ドラゴン桜」と3巻でも、英語の勉強に「ビートルズを流し運動をしながら歌詞を口ずさむ」なんてやってるわけですよ。
ビートルズで英語を学ぶのは最高の学習法【ドラゴン桜勉強法考察】試したんですが、英語の歌ってネイティブっぽく歌うって難しいです。
あと、英語で歌えば歌が上手くなるみたい。喉発声法ってのポイント調査
るびが付いていれば、なんとか歌えるんじゃないかと思って調べると下記サイトを見つけました。
洋楽ふりがなドットコムこのサイトでは歌詞のカタカナ読みではなく、原曲と同じように英語っぽく歌える洋楽ふりがなになっています。
右クリック無効にされているとコピペが出来ないので、右クリック無効を解除するChrome拡張を入れました。
Pumpkin's Right Click EnableSmile
自分は、ナットキングコール "Smile”というゆったりとした曲が好きだったんで、先ずはこれを検索しました。
http://yogakufurigana.com/271英語の歌詞
https://www.komazawa-u.ac.jp/~kazov/Nis/smile.html【ナットキングコール】”Smile”- Nat King Cole 【lyrics 和訳】【名曲】【ジャズ】【洋楽1950年代】
https://www.youtube.com/watch?v=y5B2aX_28MoカーペンターズのTop of the Worldもお勧め。
洋楽で英語をマスター|カーペンターズのTop of the World - Youtube使い方
今回のデモサイト PCのChromeでしか確認してません。
http://yaju.sakura.ne.jp/demo/LyricsRuby.html入力するのは、曲名とURLと歌詞とるびになります。
るびとして洋楽ふりがな、URLにはYoutubeの動画のURLを指定します。
デモボタンをクリックすると、Smileの歌詞が3行だけセットされます。
出力ボタンをクリックすると、新しいタブに下記が出力されます。
このタブの内容を保存したい場合、Chromeの右クリックメニューの「名前を付けて保存」をクリックします。
※保存名には最初に曲名が指定されています。名前を付けて保存した場合の変更点
名前を付けて保存した場合、「動画を再生できません」のエラーとなるため下記の変更が必要となります。
曲名 Smileを例とします。
- 一緒にできる「Smile_files」フォルダを削除します。
- Smile.htmlのiframeのsrc属性の部分を変更します。「y5B2aX_28Mo」の部分は対象動画のものに変更してください。
- ローカル等のWebサーバー(IISやApacheなど)に保存します。
- Webサーバー経由でSmile.htmlにアクセスします。
<iframe width="336" height="189" src="./Smile_files/y5B2aX_28Mo.html" ↓ <iframe width="336" height="189" src="https://www.youtube.com/embed/y5B2aX_28Mo"ソースコード
LyricsRuby.html<!DOCTYPE html> <html lang = "ja"> <head> <meta charset = "utf-8"> <title>JavaScript</title> <style> input[type="text"] { width: 500px; } textarea { width: 500px; height:500px; } .demo { margin-left: 50px; } </style> </head> <body> <script> function output() { const title = document.getElementById("title").value; const url = document.getElementById("url").value; const lyrics = document.getElementById("lyrics").value; const ruby = document.getElementById("ruby").value; if(!title) { alert("曲名を入力してください"); return; }; if(!lyrics) { alert("歌詞を入力してください"); return; }; if(!ruby) { alert("るびを入力してください"); return; }; var date = new Date(); var time = ('0' + date.getHours()).slice(-2) + ('0' + date.getMinutes()).slice(-2) + ('0' + date.getSeconds()).slice(-2); newtab = window.open("", "NewTab_" + time); header(newtab, title); movie(newtab, url); song(newtab,lyrics, ruby); footer(newtab); }; function demo() { document.getElementById("title").value = "Smile"; document.getElementById("url").value = "https://www.youtube.com/watch?v=y5B2aX_28Mo"; document.getElementById("lyrics").value = "Smile though your heart is aching\nSmile even though it's breaking\nWhen there are clouds in the sky, you'll get by"; document.getElementById("ruby").value = "すまーぃぅ ぞうよぁはーでぃずえいきん\nすまーぃぅ いーぶんどーぅ いっつぶれいきん\nうぇんでぁらーくらーぅ(ず) いんだすかーぃ ゆーぅげっばーぃ"; } function header(newtab, title) { newtab.document.open(); newtab.document.location = "#"; newtab.document.write("<!DOCTYPE html>"); newtab.document.write("<html>"); newtab.document.writeln("<head>"); newtab.document.writeln("<title>" + title + "</title>"); newtab.document.writeln("<style>"); newtab.document.writeln("p { font-size: 120%; }"); newtab.document.writeln("</style>"); newtab.document.writeln("</head>"); newtab.document.writeln("<body>"); newtab.document.writeln("<h1>" + title + "</h1>"); } function movie(newtab, url) { if(!url) return; url = url.replace("watch?v=", "embed/"); newtab.document.write("<iframe width='336' height='189' "); newtab.document.write("src='" + url + "' frameborder='0' "); newtab.document.write("allow='autoplay; encrypted-media' allowfullscreen=''>"); newtab.document.write("</iframe>"); newtab.document.writeln(""); } function song(newtab, lyrics, ruby) { const lyricsList = lyrics.split('\n'); const rubyList = ruby.split('\n'); for(i=0; i<lyricsList.length; i++) { var str1 = "<ruby>" + lyricsList[i]; var str2 = "<rt>" + rubyList[i] + "</rt></ruby>"; newtab.document.writeln("<P>" + str1 + str2 + "</P>"); } } function footer(newtab) { newtab.document.writeln("</body>"); newtab.document.write("</html>"); newtab.document.close(); } </script> <input type="text" id="title" placeholder="曲名を入力してください"></input> <br> <input type="text" id="url" placeholder="YouTubeのURLを入力してください"></input> <br> <br> <textarea id="lyrics" placeholder="歌詞を入力を入力してください"></textarea> <textarea id="ruby" placeholder="るびを入力を入力してください"></textarea> <br> <input type="button" value="出 力" onclick="output()" /> <input type="button" value="デ モ" onclick="demo()" class="demo" /> </body> </html>技術的に調べたこと
名前を付けて保存が無効
Chromeで新しいタブを作成した際に、右クリックメニューの「名前を付けて保存」が無効状態で選択できませんでしたが、下記を追記することで解決しました。
newtab.document.location = "#";youtubeの接続が拒否されました。
埋め込み用にURLを変更する必要がありました。
YouTube動画埋め込み時に「www.youtube.com で接続が拒否されました。」が表示された際に確認することurl = url.replace("watch?v=", "embed/");最後に
これを作る上で調べ直したら、下記の2サイトが動画として英語っぽく歌えるるびが付いていました。
カラオケで英語の発音をきたえる英語発音表記システム「Nipponglish(ニッポングリッシュ)」が2017年10月に登場とのことで、カラオケって滅多にいかないし英語の歌を歌うこともなかったのでこんなのあるのは知りませんでした。
カラオケで87%が英語発音アップ!ビッグエコー運営の第一興商が発表これらのサイトにない英語歌詞があれば、このツールが役立つかも知れません。
英語の勉強
平日の会社帰りに30分〜1時間くらいシェアスペースで英語の勉強をしているのですがTOEICとか如何にも勉強系だと続かないので、自分はゲーム感覚で楽しめるものを選択しています。
iPhoneアプリ
ドラえもんの英語音声版の本
- 投稿日:2020-12-07T17:16:40+09:00
プログラミング用語辞典
インスタンス
インスタンスという言葉と一緒に
・クラス
・オブジェクト
の二つも出てくるそれぞれの意味は
クラス・・・設計図
インスタンス・・・設計図から実際に作ったもの
オブジェクト・・・クラスとかインスタンスをふんわりさせたものインスタンス化=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種類あり、「関数スコープ」「ブロックスコープ」と言うモノがある。
関数スコープ
関数スコープとは上にあげた物も関数スコープである。
let
やconst
は同じスコープ内に同じ名前の変数を二重に出来ないが、これは各スコープで同じ名前の変数は一つしか宣言できないからである。
js
let toaru;
let toaru;
//SyntaxErrorと表記される
だがスコープが異なれば同じ名前で変数を宣言できるブロックスコープ
ブロックスコープとは{~~~}で囲んだ範囲の事である
関数スコープと同じようにブロック内で宣言した変数はスコープ内でしか参照できない。if文やwhile文でもブロックスコープを作るが、これも同じように外側からは参照することは出来ないのである。
- 投稿日:2020-12-07T13:10:26+09:00
【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が適用されました。
参考
- 投稿日:2020-12-07T13:07:45+09:00
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.cssbody { 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に大きく影響が出ます。
ぜひ有効に活用してください。
- 投稿日:2020-12-07T11:41:04+09:00
レスポンシブ対応について
最初に
カレンダー企画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-07T11:12:49+09:00
知識ゼロでもHTML.CSSでアラートとボタンを作る方法
HTML.CSSのコピーアンドペースト手順の備忘録です。
大枠な流れ・Bootstrap使い方まとめ
- リンク先コピー:CSSのみ
- headerコピー:Components の Alerts
- ボタンコピー:Components の Buttons
- 全体の中央寄せ:Layout の container
- ボタン真ん中寄せ:Utilities の Flex
- スペースのバランスを整える:Utilities の Spacing「Exaples」
Step1 フォルダを作る
HTML.CSSを書いていくフォルダを作ります。
~$ mkdir testStep2 "html5boilerplate"フォルダをダウンロード&コピー
- html5boilerplate ダウンロード
- ダウンロードフォルダの中へ移動
- html5boilerplateフォルダを開く
- index.htmlのみコピー
![]()
- 最初に作ったtestフォルダの中に貼り付ける
Step3 HTML.CSS実行
- テストフォルダのindex.htmlをエディタで開く
<body></body>
で囲まれている中身を消す- Bootstrap検索
- ダウンロード又は下の方にある
CSSのみ
と書いてある所のコードを全部コピー![]()
index.htmlファイルを検索タブにドラック&ドロップしてタブに表示する(CSSのコードの結果が見れる)
Step4 headerをつける
- Bootstrapの
ドキュメント
をクリック- 左側メニューの
Components
をクリックAlerts
をクリックし好きなコードをコピー![]()
- index.htmlファイル
<body>
のなかに貼り付けStep5 ボタンをつける
- Buttonsをクリック
- 好きなボタンコードをコピー
![]()
- index.htmlファイル
<body>内の<div class="alert alert-info" role="alert">A simple info alert—check it out!</div>
の下あたりに貼り付け- 4つ同じコードを打つ
![]()
Step6 全体を中央に寄せる
- Layoutをクリックする
- コンテナーコピー
![]()
- 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 ボタンを真ん中に寄せる
例 <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>
- index.htmlタブをリロードして確認
Step 7 スペースのバランスを整える
- Spacingをクリック
- スクロールをし「Exaples」の
ml-1
をコピー- index.htmlファイルの
<Body>
内<button>
のclass内に入れていく![]()
- alartにも同様に入れていく
![]()
- index.htmlタブをリロードして確認(色は異なる場合があります)
![]()
完成です。参考になれば幸いです。
- 投稿日:2020-12-07T09:55:03+09:00
tableタグについて
tableタグとは?
HTMLでテーブル(表)を作成するために使用するタグ。実際にテーブルを作成する際は、以下のtableの子要素やブロック要素を併用して作成する。
tableの子要素 説明 thead テーブルの行(水平方向)をグループ化するタグ。このタグに囲まれた内容は表のヘッダ部分としてグループ化される。 tbody テーブルの行(水平方向)をグループ化するタグ。このタグに囲まれた内容は表のボディ部分としてグループ化される。 tfoot テーブルの行(水平方向)をグループ化するタグ。このタグに囲まれた内容は表のフッタ部分としてグループ化される。
ブロック要素 説明 tr テーブルの行部分(横方向)を指定するタグ。( table record ) th テーブルの見出しやタイトルとなるセルを指定するタグ。( table theme ) td テーブルのセル内容を作成を指定するタグ。( table date ) 記述例
<table class="teble"> <thead> <tr> <th class="header1">見出し1</th> <th class="header2">見出し2</th> </tr> </thead> <tbody> <tr> <td>内容1</td> <td>内容2</td> </tr> </tbody> <tfoot> <tr> <td>フッタ1</td> <td>フッタ2</td> </tr> </tfoot> </table>以下のようになる。
見出し1 見出し2 内容1 内容2 フッタ2 フッタ1
- 投稿日:2020-12-07T07:04:31+09:00
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
では!また次回の記事で!
- 投稿日:2020-12-07T06:33:17+09:00
走る!音が出る!HTMLでミニゲームをつくりました
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-change
やtransform
を使っていると、時々、想定外の事態に出くわします。
表示が欠けるとか、ボケるとか。重なりが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
- 投稿日:2020-12-07T00:59:32+09:00
マジミラ2020プロコンで使用したものまとめ1(背景)
概要
相生葵の Advent Calendar 2020 7日目の記事です。
初音ミク「マジカルミライ 2020」プログラミング・コンテストに参加したので使用したもののまとめ記事です。
目次
- 縦スプライト
- 黒幕
- 丸エフェクト
1. 背景のスプライト
こちらのページを参考にしています。
実際に使用したものが以下です。index.html<body> </body>index.cssbody{ 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)); /*ピンク色が最後で終わるグラデーション*/参考
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 繰り返す 時間以降は設定しなくてもデフォルト値が適用されます。
デフォルト以外を使用したい場合は、パラメータを設定します。まとめ
以上、背景で使用したものまとめでした。