Quantcast
Channel: CSSタグが付けられた新着記事 - Qiita
Viewing all 8546 articles
Browse latest View live

ブログ始めました

$
0
0

プログラミングスクールに通う

TECH::EXPERTを受講し、時の流れに身を任せていたら1週間がいとも簡単に過ぎ去りました。
ブログを書くのも10年以上ぶりでお目汚し感が物凄いですがご容赦ください。

Qiitaに投稿

ガイドラインに目を通すと価値のある役に立つ記事を書けとのことですがとりあえず最初は無視することに決めました。
「質」よりも「継続」をスローガンにして、できたら誰かの役に立てる記事を投稿していきたいとおもいます。

アウトプット

ツイッターとブログをとりあえず感は否めないものの開設しました。
身の回りの「友人」や「知人」または「過去の自分」の助けになれるような内容を意識してアウトプットに努めていきたいです
そして何よりもコミュニケーション力の最大化が求められている……!!

最後に

もっと凝った編集もできるはずなのでしょうけどこれが精一杯です
1週間に二回は更新したい!!


一筋の光が差し込むWebサイト

$
0
0

ゆるWeb勉強会@札幌 Advent Calendar 2019 16日目の記事です。

前置き

皆様おはようございます。DE-TEIUです。
本日の記事はCSSとJavaScriptを使った小ネタの紹介です。
ソースコードも公開しておりますので、何かの参考になれば幸いです。

成果物

Webサイトが停電によって真っ暗になってしまい、内容が読めなくなった事はあるでしょうか。
今回は、そんな時にWebサイトを光らせて内容を表示させる方法をご紹介します。

サーチライト
【使い方】タップしたところが光ります。暗闇に隠された何かを見つけましょう。

一筋の光が

差し込みましたね。

解説

概要

Webサイトのメインコンテンツの上に真っ黒なオーバーレイを重ねてます。
また、オーバーレイは縦3*横3の計9個の要素で構成されています。
その要素の内、中央の要素の背景色だけを画面クリック時に切り替えられるようにします。

図にするとこんな感じです。
Untitled Diagram.png

後は、現在クリックされている位置に合わせてオーバーレイを移動させます。

基本的な仕組みは以上です。
(ロジック自体はjQueryで適当に実装してます。)

実装の要点

Android Chromeでの画面更新を阻止

AndroidのGoogle Chromeには、画面の上の方から下の方にドラッグすると画面のリフレッシュを行うPull-to-Refresh機能が搭載されています。
便利な機能ではありますが、今回のWebアプリでは画面をドラッグしてライトを動かすという仕様上、このリフレッシュ機能が暴発してしまう事があります。切りましょう。

cssでbodyに以下の設定をするだけで良いです。

index.scss
body{overscroll-behavior-y:none;}

overscroll-behaviorプロパティとは、画面内のスクロール可能な領域が末端に来た時の画面の振る舞いを指定するものです。
今回はoverscroll-behavior-yにnoneを設定したため、縦スクロールが画面端に来ても何も処理を起こさないようにしています。

参考:
Android ChromeでPull-to-RefreshをCSSで無効化する。

iOS Safariでのバウンススクロールを阻止

iOS Safariには、画面内のスクロール可能な領域が末端に来た時、そのままスクロールし続けると画面外へスクロールし、指を離すと画面内に戻るという機能が搭載されています。
この機能がONになっていると画面ドラッグ時にライトと一緒に表示させたいコンテンツまでスクロールされてしまいます。切りましょう。

iOSのSafariでは前述のoverscroll-behaviorが未対応なので、JavaScriptでなんとかしましょう。

index.js
$(()=>{document.addEventListener('touchmove',function(event){event.preventDefault();},{passive:false});}

これでスクロール時に発生するデフォルトのイベントを阻止できます。

参考:
2019年、JavaScriptでのスクロール一時禁止はこれだ!(スマートフォン)

CSSのグラデーションでライトっぽい表現

まずはradial-gradient関数で円形の透過グラデーションを作ります。

index.scss
$light-gradient:radial-gradient(ellipseatcenter,rgba(0,0,0,0)0%,rgba(0,0,0,0.3)40%,rgba(0,0,0,1)70%,rgba(0,0,0,1)100%);

そしてmask-imageプロパティを使うと、要素内に円形の透過グラデーションをマスクレイヤーとして設定できます。
(Chromeではベンダープレフィックスが必要)

index.scss
#light-space{&.on{mask-image:$light-gradient;-webkit-mask-image:$light-gradient;}}

おわりに

こういうちょっとしたネタを形にするだけでも、作ってる過程でそこそこ知見を得られたりするので皆さんもやっていきましょう。

ソースコード

GitHubに公開中です。

Node.jsとSocket.ioで簡易的なチャットを実現させる

$
0
0

簡易的なWebチャットを実装する

はじめに

この記事はSLP KBIT Advent Calender 2019の16日目の記事である。
Socket.ioを用いた開発をまだやったことがなかったので今回はそういった開発を行っていきたいと思います。
かなり前に作ったので実装過程をかなり忘れてしまいました…。

環境

・Windows 10(64bit)
・Node.js : v10.15.3

準備

この開発ではNode.js、Socket.ioの他に、ローカルサーバーを簡単に立ち上げれるExpressを使うのでもしインストールしていない人は以下のコマンドでインストールしてみましょう。

$ npm install express

実装

サーバをたてる準備

create.js
varexpress=require('express');//expressを使用varapp=express();varhttp=require('http').Server(app);constpath=require('path');constio=require('socket.io')(http);constPORT=process.env.PORT||4649;app.get('/',function(req,res){res.sendFile(__dirname+'/chat_room/chat.html');//chat.htmlへ移動});io.on('connection',function(socket){// メッセージ送信処理socket.on('chat message',function(msg,user){//io.emit('chat message', msg);io.emit('chat message',{userName:user,message:msg});});});http.listen(PORT,function(){console.log('server listening. Port(・v・)/:'+PORT);//PORT番ポートに接続});app.use(express.static(path.join(__dirname,'chat_room')));//chat_roomディレクトリを公開

ちなみにchat_roomディレクトリにチャットを表示するhtmlとcssファイルを用意しているので最後の行で

app.use(express.static(path.join(__dirname, 'chat_room')));

と記述しています。(これがないとchat_roomの中にあるcssを読み込んでくれなかった)

HTMLとCSS

チャットを表示するためのHTMLとCSSを作ります

chat.html
<!DOCTYPE html><html><head><metahttp-equiv="content-type"content="text/html charset=UTF-8"><title>Socketテスト</title><linkrel="stylesheet"type="text/css"href="./style.css"/></head><body><h1>チャットテスト(・v・)/</h1><!-- メッセージ入力欄 --><formaction="#"id="chatForm"><inputid="u"autocomplete="off"placeholder="ユーザ名"/><inputid="m"autocomplete="off"placeholder="テキスト"/><button>Send</button></form><hr><!-- メッセージの表示 --><ulid="messages"></ul><!--必須モジュール2つ--><script src="/socket.io/socket.io.js"></script><script src="https://code.jquery.com/jquery-1.11.1.js"></script><script>varsocket=io();//var userName = 'test';$(function(){varuserName='';// メッセージを送信する$('form').submit(function(){socket.emit('chat message',$('#m').val(),$('#u').val());$('#m').val('');returnfalse;});// 受信したメッセージを表示// ul id ="messages"の部分socket.on('chat message',function(data){varchat=data.userName+""+data.message+"";if((data.message!='')&&(data.userName!='')){//空じゃない場合$('#messages').append($('<p id="chat">').text(chat));//ユーザとテキストは前提条件}});});</script></body></html>
style.css
#messages{padding:0.5em1em;margin:2em0;border:solid3px#9facd4;}#chat{margin-top:1px;padding:2px;background-color:rgb(218,246,255);color:#292929;}

実際に使ってみる

nodeコマンドでサーバを立ち上げます。
ad1.png

ローカルホストで指定したポートにアクセスすると以下のような画面が出ます。

ad2.png

ユーザ名とテキストを入力してsendを押すと
ad3.png

ad4.png
きちんとセリフっぽくなりましたね!

ウインドウを2つ以上開いた場合

・送信前
ad5.png
・送信後
ad6.png
片方が送信すると両方のウインドウに反映されましたね。
これでローカル環境でチャットが出来そうです。

おわりに

チャット機能の実装だけを目標にしていたので見た目がシンプルになってしまいましたが最後まできちんとやりきることができてよかったです。アイコンを実装したりもう少し見た目をよくすると本格的なチャットが楽しめるかなと思います。

内容に応じてサイズが可変する を素敵に実装する

$
0
0

概要

内容に応じてサイズが可変する textareaを、できるだけ手間をかけず、スマートな実装を試みます。
しかも、ネイティブのフォームが持っている利点をそのまま活かして、堅牢でアクセシブルな設計を目指します。

標準 textareaの難点

HTML の textarea要素は基本的に高さが固定されていて使い勝手が悪いです。3行分くらいしか領域がなくて、長い文章を打つのがとにかく苦痛なんていうこともザラです。

最近のブラウザ実装では、多少気を利かせてくれているのか、テキストエリアの領域をドラッグで拡大・縮小できます。

textarea をマウスドラッグでリサイズしているところの動画。

ただ私は思うのです。めんどくさいし、最初っから、入力するテキスト量に応じて自動的に伸び縮みしてくれればいいのに……と。スクロールバーなんて、1ページにひとつあればじゅうぶんなんですよ。

実装方法

難しいことはありませんが、HTML と CSS、JS が協調して動作します。

HTML

<labelfor="FlexTextarea">伸縮するテキストエリア</label><divclass="FlexTextarea"><divclass="FlexTextarea__dummy"aria-hidden="true"></div><textareaid="FlexTextarea"class="FlexTextarea__textarea"></textarea></div>

CSS

.FlexTextarea{position:relative;font-size:1rem;line-height:1.8;}.FlexTextarea__dummy{overflow:hidden;visibility:hidden;box-sizing:border-box;padding:5px15px;min-height:120px;white-space:pre-wrap;word-wrap:break-word;overflow-wrap:break-word;border:1pxsolid;}.FlexTextarea__textarea{position:absolute;top:0;left:0;display:block;overflow:hidden;box-sizing:border-box;padding:5px15px;width:100%;height:100%;background-color:transparent;border:1pxsolid#b6c3c6;border-radius:4px;color:inherit;font:inherit;letter-spacing:inherit;resize:none;}.FlexTextarea__textarea:focus{box-shadow:0004pxrgba(35,167,195,0.3);outline:0;}

JavaScript

functionflexTextarea(el){constdummy=el.querySelector('.FlexTextarea__dummy')el.querySelector('.FlexTextarea__textarea').addEventListener('input',e=>{dummy.textContent=e.target.value+'\u200b'})}document.querySelectorAll('.FlexTextarea').forEach(flexTextarea)

コードの大半は CSS ですし、JavaScript もなんかしてる? ってくらい簡素なものです。ですがしっかり動きます。

動作サンプル

サイズ可変の textarea の動作サンプル(CodePen)

解説

コードこそ簡素なものですが、とりあげて解説したい箇所はたくさんあります。

動作原理の概要

ざっくりと説明します。textarea要素は親要素の大きさに追随して大きさが決まるようになっています。position: absoluteで絶対配置をし、同時に widthheight100%を指定することで、親要素の大きさと同一にしています。

.Textarea__textarea{position:absolute;top:0;left:0;width:100%;height:100%;}

そして親要素がテキストの量に応じた大きさになるように、入力されたテキストを .Textarea__dummyにそのまま流し込みます。

textarea.addEventListener('input',e=>{dummy.textContent=e.target.value})

すると、高さを指定していない .Textarea__dummyはテキスト量に応じて自動的に大きさが決まり、連動して textareaの大きさも決まるという按配です。

textarea がテキスト内容に応じて拡大している様子。

原理解説用の 3D 表示の FlexTextarea

基本はこれだけです。続いて、細かい部分を解説していきます。

最小・最大の高さを指定する

何もテキストが入力されていないときの最小の高さと、たくさん入力されたときの最大の高さを設定できます。設定しないことも可能で、min-heightを指定しなければ1行分の高さになり、max-heightを指定しなければ、内容に応じてどこまででも大きくなります。個人的には min-heightの指定のみで良いだろうと思います。

.FlexTextarea__dummy{min-height:120px;max-height:480px;}

入力された改行を改行として表示する

テキストエリアの値をそのまま .Textarea__dummyに流し込むと、改行が半角スペースに変化してしまいます。これは DOM において、改行文字はホワイトスペースとして等価に扱われてしまうためです。とても有名な話ですが、HTML で改行をするには <br>タグを使う必要があります。

ですが、改行文字を改行として反映するための CSS プロパティがあります。

.FlexTextarea__dummy{white-space:pre-wrap;}

こうしておけば、次のような単純なテキスト代入でも、改行文字が改行として表示されるようになります。

textarea.addEventListener('input',e=>{dummy.textContent=e.target.value})

「改行文字を <br>に文字列置換して、innerHTMLに代入する」ようなアプローチは、セキュリティ脆弱性を孕みやすいので、ぜったいにやめてね!!

textareadummyの細かな挙動を統一する

ここが最も難所です。

テキストエリアに設定される大きさは、完全に dummyの表示に依存しています。そのため、「何文字で改行するか」「改行ごとにどれくらい大きさが変わるか」「禁則処理ルール」などの点を同一にしていかなくてはいけません。条件を同一にするためのファクターは以下のとおりです。

  1. フォント
  2. 文字サイズ
  3. 行の高さ
  4. 文字間隔
  5. 罫線の幅
  6. padding の大きさ
  7. 改行できない文字が連続したときの取扱い

これらを解決するために、以下のコードが必要になります。

.FlexTextarea__dummy{box-sizing:border-box;padding:5px15px;white-space:pre-wrap;word-wrap:break-word;overflow-wrap:break-word;border:1pxsolid;}.FlexTextarea__textarea{box-sizing:border-box;padding:5px15px;border:1pxsolid#b6c3c6;font:inherit;letter-spacing:inherit;}

テキストエリアにはたいてい、borderpaddingの設定が必要になるでしょう。それにあわせて dummyにも同じ大きさで borderpaddingを指定しています。また、ボックスの大きさ計算の基準をそろえるために box-sizing: border-boxも必要です。

文字周りをそろえるために、font: inherit, letter-spacing: inheritを指定します。これで、フォント、ウエイト、行の高さが揃います。letter-spacing: inheritも必要です。

テキストエリアは「aaaaaaaaaaaa……」と入力されたとしても、矩形を突き出ず、必ず改行される仕様になっています。dummyもそれにあわせるため、 overflow-wrapと、後方互換のための word-wrapプロパティも設定します。

手動リサイズは行わないようにする

テキストエリアが自動的にリサイズされるようになったので、右下に表示されているリサイズハンドルは不要になりました。

.FlexTextarea__textarea{resize:none;}

フォーカスリングを表示する

伸縮可能かどうかに関係はありませんが、テキストエリアがフォーカスを受け取ったときに、ちゃんとそれとわかるインジケーターを表示してあげましょう。たいていはデフォルトの outlineで良いでしょう。余裕があれば独自のインジケーターを表示させるとカッコいいでしょう。

.FlexTextarea__textarea:focus{box-shadow:0004pxrgba(35,167,195,0.3);outline:none;/* box-shadow を代わりに使っているから outline は不要 */}

スクリーンリーダー等の支援技術に配慮する

.FlexTextarea__dummyには、テキストエリアに入力された文字がそのまま流し込まれています。同要素には visibility: hiddenプロパティが設定されているとはいえ、念のため HTML にも、aria-hidden属性を使って、この要素がコンテンツとは無関係であることを示しておきましょう。

<divclass="FlexTextarea__dummy"aria-hidden="true"></div>

完成

このようにして出来上がった FlexTextarea コンポーネントを皆さんお使いください。

1111memo

$
0
0

HTML

<!DOCTYPE html><htmllang="en"><head><linkrel="stylesheet"href="css.css"> <metacharset="UTF-8"> <title>Document</title></head><body><div><buttonid="DigitalCLOCK"class="clock-btn"onload="showTime()"></div><pclass="mb1"></p><textareaclass="ta"name="textarea1"id=""cols="30"rows="30"></textarea><textareaname="textarea2"id=""cols="30"rows="30"></textarea><textareaname="textarea3"id=""cols="30"rows="30"></textarea><!-- ライブラリの読み込み --><script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script><!-- JavaScriptを記述する --><script>// 時刻表示// 今日の日付情報を取得し変数dTodayに格納するfunctionshowTime(){vardate=newDate();vary=date.getFullYear();varmo=date.getMonth()+1;vard=date.getDate();varh=date.getHours();varm=date.getMinutes();vars=date.getSeconds();if(h==0){h=12;}h=(h<10)?"0"+h:h;m=(m<10)?"0"+m:m;s=(s<10)?"0"+s:s;vartime=y+""+mo+""+d+""+h+""+m+""+s+"";document.getElementById("DigitalCLOCK").innerText=time;document.getElementById("DigitalCLOCK").textContent=time;setTimeout(showTime,1000);}showTime();// ClipBoard.js関連varclipboard=newClipboardJS('.clock-btn',{target:function(){returndocument.querySelector('div');}});clipboard.on('success',function(e){console.log(e);});clipboard.on('error',function(e){console.log(e);});varclipboard=newClipboardJS('.btn',{target:function(){returndocument.querySelector('div');}});clipboard.on('success',function(e){console.log(e);});clipboard.on('error',function(e){console.log(e);});</script></body></html>

CSS

.clock-btn{width:500px;text-align:center;display:block;padding:15px;text-decoration:none;color:#fff;background-color:grey;border:none;font-family:游ゴシック;font-size:30px;border-radius:5px;margin:0auto;transition:1s;}.clock-btn:hover{transition:0.5s;background-color:black;width:500px;}.mb1{padding:10px;}.ta{border:none;}.ta:hover{transition:10s;border:1pxsolidblack;}

3000人に聞いた、2019年最先端のフロントエンド開発者が使ってるツールはこれだ

$
0
0

Ashley Nolanは、CSSとJavaScriptの機能やフレームワークをどれだけ使っているかというアンケートを毎年行っています。

以下では2019年版である、The Front-End Tooling Survey 2019というアンケート結果の概要を紹介してみます。

回答者数が昨年から4割も減ってるのだが一体何があったのだろう。

The Front-End Tooling Survey 2019 - Results

3005人の開発者が、27の質問に回答してくれました。
私の家族に女の子が増えたので集計結果を出すのが遅れました。ごめんね!

昨年からの一年で変わったところを見ることで、みんなの考えやトレンドが他者と共有されているか確認することができます。
これらの結果から、フロントエンドツール全体の知識レベルや使用傾向を分析するのにも役立ちます。

Quick Thanks

手伝ってくれたWes Bosと、理解のある雇用者Just Eatのおかげで、今年もこの結果を提供することができました。

The responses

今年は3005件の回答がありました。
昨年の5461件より少しばかり減少しています。

このアンケートへの導線は、Twitter、Reddit、HackerNews、DesignerNews、Echo.js、LinkedIn、Frontendfrontに投稿されました。

General Front-end Experience

フロントエンドエンジニアの経験はどのくらいか。

01.jpg

59.1%、ほぼ6割が5年以上です。
昨年は54.18%だったので経験の長いエンジニアが増えています。
2年未満という開発者はわずか12.31%でした。

もっとも、新人はこのようなアンケートに答える割合が低いからとも考えられます。

CSS Knowledge Levels

CSSのレベルはどれくらいか。

02.jpg

回答者の90.75%が自分は中級以上であり、さらに過半数の63.63%が自分は上級以上であると評価しています。

まあ、あくまで自称ですからね。

CSS Processor Usage

CSSプロセッサを使っているか。

03.jpg

昨年までは択一だったものが今年は複数選択可になっているため、単純比較はできません。

相変わらずSassの圧勝ですが、増加割合はPostCSSのほうが多くなっています。
これはPostCSSの人気が急上昇したというより、元々2番目に選ぶツールだったと考える方が適切でしょう。

CSS Processor Experience

CSSプロセッサをどれだけ使っているか。

04.jpg

この質問への回答は前回とほぼ変わっていません。
Sassがお気に入りの開発者は開発者の3/4を超えているのに対し、使ったことのない開発者は僅か7.15%です。

またPostCSSも使用者が増えましたが、LessとStylusは漸減しています。
ほぼSassに集約されたと言っていいでしょう。

CSS Framework Usage

最もよく使っているCSSフレームワークをひとつだけ選択。

05.jpg

最も人気のあるフレームワークは、35.07%の『フレームワークを使ってない』でした。
第二位はBootstrapの27.95%ですが、昨年から7%も減少しています。

昨年と同じく、初心者ほどBootstrapを使いたがり(43.17%)、上級者は使わなくなる(23.69%)傾向がありました。
またカスタムフレームワークは初心者の8.63%、上級者の20.35%が使っているという回答になりました。

全体的にはCSSフレームワーク使用者数は昨年より僅かに減少しています。

CSS Naming Schemes

CSS命名規則を使っているか。

06.jpg

昨年より3.15%増え、2016年の調査開始以来初めて過半数がCSS命名規則を使っていると回答しました。
CSS命名規則を聞いたことのない開発者は1割を切る少数派です。

CSS Linting

CSS Linterを使っているか。

07.jpg

こちらも2016年の調査開始以来使用者数は増えつつあり、今年ちょうど5割を超えました。
初心者でLinterを使っているのはわずか28.42%である一方、上級者は60.15%となっていて、はっきりとした傾向が見られます。

CSS Tool Experience

Autoprefixer・Modernizr・Stylelintの各CSSツールを使ったことがあるか。

08.jpg

3ツールのうちではAutoprefixerが最もよく使われていて、割合は50.52%です。
Stylelintは認知度が急激に増加し、+5.16%の23.03%が常用しています。
Modernizrはこの中で唯一、使用者の割合が低下しています。

CSS Methodologies and Naming Scheme Experience

CSS設計手法を使っているか。

09.jpg

知名度はBEMが圧倒的で、常用している人も昨年から5.37%も増加した43.53%になりました。
最も上げ幅の大きかったのはCSS-in-JSで、7.60%増加の27.52%です。
調査項目のSMACSS、OOCSS、Atomic Design、ITCSSの全てにおいて、一度でも使ったことのある人は増加しています。

少なくともひとつを使ったことがある人は昨年の66.49%から70.75%に増加、少なくともひとつを快適に使っている人も昨年の34.90%から42.26%へと増加し、CSS設計手法は広く普及しつつあります。

CSS Tool Usage

何のCSS設計手法・CSSツールを使っているか。
ただしCSS-in-JSについては次で個別に質問しているので外している。

10.jpg

最もよく使われているのは56.84%のAutoprefixerで、次いで45.52%のBEM、27.49%のStylelintが続いています。
Modernizrは8.24%減という大幅な減少で終わりつつあります。

CSS-in-JS Usage

CSS-in-JSを使っているか。

今年新設された質問で、最近注目を浴びつつあるCSS-in-JSについての質問です。

11.jpg

半数弱の44.69%が何らかのCSS-in-JSツールを使っていると答えました。
最も人気のあるツールは27.02%のStyled Componentsで、その後は9.72%のCSS Modules (9.72%、3.96%のEmotionと微々たるものです。

予想通り、CSSのスキルが高いほどCSS-in-JSツールを使う割合も増え、上級者のCSS-in-JSツール使用率は51.38%でした。

CSS Feature Usage

CSS機能を使っているか。

新しめのCSS機能を知っているか、どれだけの人が使っているかを訊ねました。

12.jpg

Flexboxは12.81%増の80.40%と、ほとんどの開発者が常用しています。
CSS Gridは10.11%増の28.59%、CSSカスタムプロパティも8.86%増の27.89%と、使用者は順調に増加中です。

Flexboxを一度でも使ったことのある開発者は97.07%で、ほぼ全ての開発者が使用しています。
昨年は93.53%でした。

CSS GridとCSSカスタムプロパティは広まりつつあるものの、まだ伸びる余地があります。

今年初めてCSS Houdiniについて質問しましたが、聞いたことすらない開発者が過半数の51.75%、使ったことのある人はわずか1.79%でした。
この機能はまだ最先端であるため、ほとんどの開発者が使ったことがなくても驚くことではありません。
まあ最新とか言っても、CSS Houdiniは少なくとも3年以上前からあるんですけどね。

JavaScript Knowledge Levels

ここからはJavaScriptとそのエコシステムについての質問です。

最初の質問は、JavaScriptのレベルはどのくらいか。

13.jpg

ほとんど(88.98%)が自分は中級以上であり、過半数(59.0%)が上級者かエキスパートであると回答しました。

これが業界全体のレベルを表していると考えるのは誤りです。
このような調査に回答する人のレベルを表していると考えるのが適切でしょう。

Task Runners

タスクランナーは何を使っているか。

14.jpg

NPM Scriptsの一強です。
昨年より16.44%も増加し、半数を超えたどころか2/3近い64.33%を占めました。

それ以外のほぼ全ては、タスクランナーを使用しなかった人も含めて減少しています。
GulpもGruntも終わりつつあり、ワークフローはNPM Scriptsにほぼ統一されました。

Knowledge of JavaScript Libraries and Frameworks

JavaScriptライブラリ/フレームワークをどれだけ使っているか。

15.jpg

衰えつつあるとはいえ、jQueryの普及率は75.37%で他を圧倒しています。

7.92%増加して48.35%のReact、6.12%増加して23.19%のVueは使用者数が最も増えたツールです。

今年は質問にD3.jsを追加しましたが、よく使っているのは7.29%でした。

全回答者のうち78.54%が、少なくともひとつのJavaScriptフレームワーク(jQuery・Underscore・Lodash・D3.js以外の選択肢)を使って満足したと回答しました。
これは昨年から8.04%、2016年からは28%も増加しています。
フロントエンドエンジニアは少なくともひとつのJavaScriptフレームワークを知っておくことが重要でしょう。

Most frequently used JS frameworks/libraries

最もよく使っているJavaScriptライブラリ/フレームワークは何か。

16.jpg

2015年の調査開始以来初めて、jQueryがトップの座から転落しました。

Reactが4.82%増加して過半数の52.21%になったいっぽう、jQueryは-14.24%の激減で36.81%になりました。

Most essential JS framework/library

あなたにとって最も重要なフレームワークは何か。

17.jpg

Reactが4.31%増加の32.78%、Vueが3.22%増加の13.44%、Angular2+は2.30%増加の8.49%です。
jQueryはここでも減少し、-8.59%減の11.15%になりました。

そして21.66%は、必ずしもフレームワークやライブラリが必須ではないと答えました。

経験レベルで分けてみると、開発経験2年以下の開発者は45.14%がReactが最も重要と答えたのに対し、10年以上の開発者は24.02%となっています。

この3問からわかることは、JavaScriptフレームワークで最も使われているものはReactであり、ますます伸びているということです。
Vueは2番手につけていますが、まだまだReactの後塵を拝しています。
jQueryは減少しつつあるものの、依然としてLodashなど他のライブラリより上位につけています。

JavaScript Module Bundler Usage

JavaScriptモジュールバンドラを使っているか。

18.jpg

Webpackがシェアをますます伸ばし、7.66%増加の73.34%と完全に一強です。
近い将来、他のツールが台頭してくる兆候は全くありません。

モジュールバンドラを使っていない開発者は5.91%減の14.64%であり、ほとんどの回答者がモジュールバンドラを使っているという結果になりました。

JavaScript Module Bundlers & Task Runners experience

JavaScriptモジュールバンドラとタスクランナーの使用経験はどれくらいか。

19.jpg

前問と同じ傾向で、64.39%がNPM Scriptsを使いやすいと感じていて、52.38%のWebpackや45.79%のGulpより高くなっています。

JavaScript Transpilers

JavaScriptトランスパイラを使っているか。

20.jpg

ほとんどの回答者がトランスパイラを使っています。

ご想像のとおり、開発経験の少ない開発者ほどトランスパイラの使用割合が低くなります。
といっても2年未満の開発者でも70%が使っています。

JavaScript Extension Languages

JavaScript拡張言語を使っているか。

21.jpg

TypeScriptが引き続き順調で、10.03%増の31.91%が手放せないと答えています。
他の言語はほとんど変動せず低調なままです。

JavaScript Linting

JavaScriptのLinterを使っているか。

22.jpg

ESLintが15.39%も伸ばして76.7%と完全に一択になりました。
理由のひとつとして、ESLintでTypeScriptをサポートするという決定が影響していることは間違いないでしょう。

Linterを使っていない人は3.42%減の11.98%で、既にほとんどの回答者がLinterを使用しています。

JavaScript Testing

JavaScriptのテストツールを使っているか。
昨年までの一択から複数選択制に変更したため、昨年からの変動は参考値となります。

23.jpg

一位はJestの44.86%ですが、26.12%のMocha、19.49%のJasmine、18.64%のEnzymeあたりと決定的なまでの差は開いていません。

ひとつ以上のテストツールを使っている開発者は64.36%となり、昨年より7.98%増加しています。
JavaScriptのテストツールもようやく一般的になりつつある状況です。

Performance Tools & Features

パフォーマンスツールや機能を使っているか。
今年追加された質問で、複数選択制です。

24.jpg

最も人気のあるツールはLighthouseで、52.11%が使用していました。

2番目は、リストアップされたツールの何れも使用していない、というものでした。

24.29%がWebPageTest、23.13%がService Workersを使っています。

AMPを使っているのはわずか5.79%で、Googleのごり押しにも関わらずほとんど普及していません。

Accessibility Tools

アクセシビリティのテストを使っているか。
今年追加された質問で、複数選択制です。

25.jpg

驚いたことに、回答者の63.13%が何もしていませんでした。

わずか22.20%がColor Contrast Checkerを、15.44%がスクリーンリーダ対応を行ったということでした。

これは非常に残念な結果です。

JavaScript Package Managers

パッケージマネージャを使っているか。

26.jpg

NPMがわずかに増加し、65.39%となりました。
逆にyarnはわずかに減少し、29.78%です。

開発者の多くはNPMで満足しているようで、このカテゴリの数値はほぼ変動していません。

Miscellaneous Tools

その他ツールの満足度について。

27.jpg

開発者のほとんど(87.02%)がNPMを快適だと感じています。
50.62%のYarn、49.62%のBabelもよく知られているツールであり、使用者もわずかに増えています。

最も目立ったのはPrettierの増加で、16.53%も増えた40.43%になっています。

Summary

多くの分野では、使用するツールがひとつに集約されつつあります。
LintingのESLint、タスクスケジューラのNPM Scripts、モジュールバンドラのWebpackなどです。

フレームワークとしてはReactが頂点となり、その首をVueが狙っています。
少なくない開発者(21.66%)がまだフレームワークが必要ないと感じていて、実際ネイティブJavaScriptの機能も年々向上しています。

今年最も衝撃的だった結果はアクセシビリティで、2/3もの開発者が何も対応していません。
業界全体として、この問題に対応していかなければならないでしょう。
道義的にという理由だけではなく、対応しないと訴えられるという法律的観点からも必要です。

全体として、フロントエンドツールの成熟度が高まるにつれ、ツールの集約が進むように思われます。
数年前はそうでもなかったですが、今や多くのカテゴリには明確なリーダーが存在します。
全てのレベルの開発者が、新しいツールを使い始めるときに何を選ばなければならないか調査にかける時間を節約し、適切な方法を選べる可能性が増します。
これはよいことでしょう。

コメント欄 & Reddit

「プログラミングテクノロジーの傾向を見るのに役立つ記事!」
「問2と問13はダンニング・クルーガー効果が入ってるね。間違いない。」
「来年の調査でGatsbyとSvelte/Sapperが出てくることを楽しみにしてる。後者は何年後かにReactを打ち倒すぞ、間違いない。」「来年追加しとく。」
「次回は『このツール嫌い』の選択肢も入れてくれ」「考えとく。」
「来年はサーバサイドも入れよう。うちは以前PHPだったけど今やTSとNodeになった。」
「63%もアクセシビリティのテストをしてないなんて信じられない。どうせ本当に必要ではない最新のJSツールをいじくるのに忙しいからだろう。」
「CSSカスタムプロパティはCSS変数って覚えてる人も多いから気付いてなかったのかも」「『CSSカスタムプロパティ(CSS変数)』ってアンケートしたのでそれはない。」
「Vueはいいぞ。使ったことがなければ使ってみてくれ。」
「VueがAngularの2倍使われてる?信じられない!」
「Angularってまだ息してたのか。みんなReactに行ったと思ってた。」「ReactからAngularに移行したわ。」「Vueに行った。」
「Bowerどこ?」「どうぞ。」
「Ramdaは?」「あとRxJSも!」

感想

一刻も早くReactが滅びますように。

昨年の調査からはjQueryが落ちたくらいで、あまり目立って変わったものはありません。
燦然と輝く新たなツールが颯爽と現れてシェアをかっ攫っていってはまた次のツールに追い越されるという、これまでのJavaScriptの歴史によくあった現象は影を潜め、昨年までにシェアを取っていたツールが順当に寡占化を進める結果となっています。

混迷期をようやく通り過ぎて落ち着いてきたかんじなので、これまで「うわ、面倒臭そう…」とか思って遠巻きにしていた人たちもそろそろ手を出すチャンスだと思います。
まあ開発環境作るだけでこれなので、やっぱり「うわ、面倒臭そう…」なんですけどね。

ちなみに2014年のトレンドはYeoman・Grunt・Jenkinsだったようです。
どれも既に終わってますね。
2019年の最先端であるこのアンケート結果も、5年後には選択肢にすら挙がらなくなっているかもしれません。

きちんと更新されるブラウザキャッシュの活用方法

$
0
0

この記事は 弁護士ドットコム Advent Calendar 2019 16日目の記事です。

はじめに

弁護士ドットコムというサービスのUXエンジニアをやっている白井と申します。

日々ユーザー体験を向上させるべく様々な開発を行っていますが、Webサイトの速度改善もその1つです。

今回はその一環で実施した、ブラウザキャッシュ戦略の再設計の事例についてご紹介します。

対象リソース

この記事では、以下のようなリポジトリで管理されているリソースを主な対象としています。

  • JavaScript, JSX, TypeScriptなどのスクリプト類
  • CSSなどのスタイルシート類
  • 画像やフォント類

これらは頻繁に変更される傾向にあるため、ブラウザキャッシュを適切に制御する必要があります。

注意深く設計しないと、以下のような問題が発生する場合があります。

ブラウザキャッシュのよくある問題

問題① ユーザーのブラウザに古いキャッシュが残ったままになる

内容に変更があったにも関わらず、ユーザーのブラウザにキャッシュが残ってしまい、古いコンテンツが表示されてしまったり、ページ自体が正常に表示されなくなってしまったりするケースです。

また、自分の開発環境では正常に表示されてしまい、なかなか問題に気づけなかった…といった方も多いのではないでしょうか。

問題② ブラウザキャッシュが全く活用できていない

逆に、適切な設定をしていなかったために、ブラウザキャッシュを全く活用できていないケースも多いと思います。

リソースに変更がないにも関わらず毎回リソースを取得させてしまうのは、ユーザーにとってもWebサーバにとっても好ましい状況ではありません。

この記事では、これらの問題が発生しないブラウザキャッシュ制御方法について検討していきたいと思います。

今回の改善結果

弊社では Speed Indexのユーザによる実測値をサイト表示速度のKPIとして採用しています。

Speed Index はWebページのファーストビューが表示されるまでにかかる時間を計測したものです。

対応を行った結果、 Speed Index に約14%の改善が見られました。

本記事でご紹介する手法によってどのくらいサイト表示速度が改善されるかは、以下のようなWebサイトの特性に依存するため、一概には言えません。

  • リソースのサイズや分割単位
  • ユーザーの新規・再訪問比率

しかし、何らかの改善のヒントになれば幸いです。

そもそもブラウザキャッシュとは?

ご存知のとおり、Webページを閲覧する際にはHTML文書だけではなく、JavaScript・CSS・画像といった様々なリソースをサーバからネットワーク経由で取得する必要があります。

一度訪れたサイトであれば、リソースの大半は前回の訪問時から変更されていないことが多いと考えられます。その場合、変更されたリソースに限って取得すればWebサイトをより高速に表示することができます。

そのために、前回取得したリソースをブラウザ側で記憶しておく仕組みがブラウザキャッシュです。

ブラウザキャッシュを制御する仕組み

サーバ側からブラウザキャッシュを制御するためには、以下の2つの仕組みが必要です。

  1. ブラウザにリソースをキャッシュさせる仕組み
  2. ブラウザにキャッシュを破棄させる仕組み

①ブラウザにリソースをキャッシュさせる仕組み

特定のHTTPヘッダを付与することで、ブラウザに対し、そのリソースがキャッシュ可能か指示することができます。

代表的なHTTPヘッダには以下があります。

  • Cache-Control
  • Expires
  • Age
  • ETag
  • Last-Modified

詳しくは RFC 7234を参照してください。

これらの指示内容に応じて、ブラウザはリソースをキャッシュして良いか判断します。そして、次の訪問時には以下のいずれかの挙動を示します。

  1. ブラウザキャッシュを使用し、リソースの再取得を行わない
    • リクエスト自体が発生しませんので、最も高速な挙動となります。
  2. リソースに更新があるかどうか確認するリクエストを送り、更新がなければキャッシュを使用する
    • If-Modified-SinceIf-None-Matchヘッダ付きのリクエストを送信します。
    • Webサーバはそれらの内容から更新の有無を判断し、更新がある場合のみ最新のリソースを含めたレスポンスを返します。更新がなければ、 304 Not Modifiedレスポンスを返します。
  3. ブラウザキャッシュを使用せず、必ずリソースを再取得する
    • リクエストとリソースの取得が必ず発生しますので、最も遅い挙動です。

Webサイトの表示を高速化するには 1. を目指すことになりますが、この場合 リクエスト自体が発生しないため、リソースに更新が発生してもブラウザが再取得してくれないという問題が生じます。

それを解消するのが次に紹介する仕組みです。

②ブラウザにキャッシュを破棄させる仕組み

リソースが更新された場合に最新のものを取得してもらうためには、キャッシュ破棄 (Cache Busting)と呼ばれる手法が必要です。

ブラウザキャッシュの制御においては、リソースを参照する側のURLを変更するという手法が一般的です。

たとえば、HTML内からCSSファイルへの参照があった場合、そのURLを最新のものに更新します。 (当然、HTMLの方はキャッシュさせないようにしておきます)

以下の例ではURLの一部が version=1から version=2に変わっているため、ブラウザに新しいリソースとして認識され、再度取得されるようになります。

<linkrel="stylesheet"href="/css/main.css?version=1">

<linkrel="stylesheet"href="/css/main.css?version=2">

この手法には、細かく分けると以下のような種類があります。

  1. 実際に新しいファイルを作る
    • リソースが更新される度に新しいファイル名 (URL) を発行する
  2. ファイルは同じだが、名前の一部を変える
    • ファイル名の一部に何らかのパラメータを付与する

この「何らかのパラメータ」は通称キャッシュバスター (Cache Buster) とも呼ばれます。

キャッシュバスターを付与する方法には、以下のバリエーションがあります。

キャッシュバスターの「生成元」によるバリエーション

何に基づいてキャッシュバスターを生成するかについてはいくつかの方法があり、それぞれメリット・デメリットがあります。

キャッシュバスターの生成元キャッシュ破棄のタイミングキャッシュが破棄される単位説明
・ビルド日時
・デプロイ日時
デプロイ時全てのリソースWebサーバが複数台ある場合に同一時刻になるように注意が必要
・アプリケーションのバージョン番号
・GitのコミットID
デプロイ時全てのリソース
・ファイルの更新日時リソース更新時該当リソースのみデプロイ時のファイルコピーで更新日時が書き換わらないように注意が必要
・ファイル内容のハッシュ値リソース更新時該当リソースのみファイルごとにハッシュ値の計算が必要

詳しくは後述しますが、弊社では 「ファイル内容のハッシュ値」と「GitのコミットID」を併用する方式を採用しました。

キャッシュバスターを付与する「場所」によるバリエーション

一方、キャッシュバスターをリソースURLのどこに付与するかについても、以下の方法があります。

ファイル名に付与する
例: image-4d300b8f57bff89d8f4f87b5cc1b3de5.png

クエリパラメータとして付与する

例: image.png?version=4d300b8f57bff89d8f4f87b5cc1b3de5

ファイル名を毎回変更する場合、ローカル開発時に不要なファイルが蓄積していく原因にもなりますので、Webpack処理の際に先にクリーンナップ処理を行うなどの配慮が必要になります。

なお、デプロイごとにキャッシュバスターを更新する場合は、まとめてディレクトリ名に付与してしまうという方法もあります。

弁護士ドットコムにおけるブラウザキャッシュ戦略

一般論が続いたため、そろそろお腹いっぱいかと思いますが、ようやく本題です!

以前はどうだったか?

改善前の弁護士ドットコムでは、以下のような設計になっていました。

ブラウザにリソースをキャッシュさせる仕組み

HTTPヘッダ (Expiresヘッダ) により、1年間のキャッシュを許可していました。

ブラウザにキャッシュを破棄させる仕組み

ファイル名にデプロイ日時ベースのキャッシュバスターをクエリパラメータとして付与していました。

  • CSSファイルから url()関数で読み込まれる画像のURLについては、デプロイ時のWebpack処理によってタイムスタンプが付与されていました
  • その他のJavaScript・CSS・一部の画像については、PHPの関数により、デプロイ日時のタイムスタンプが付与されていました

しかし、この設計・実装には以下の課題がありました。

課題① デプロイの度にブラウザキャッシュが破棄されてしまう

弊社ではデプロイが完全に自動化されているため、エンジニア・デザイナーを問わず、Slackから簡単にデプロイを行うことができます。
そのため、デプロイは1日に数回、多い時には数十回行われる場合があります。

しかし、上記の設計における大きな課題の1つが、デプロイする度にすべてのリソースのブラウザキャッシュが破棄されてしまうということでした。

大半のリソースには変更がないにもかかわらず、その度にブラウザキャッシュが破棄されてしまっていました。

課題② サーバごとにキャッシュバスターが異なる

なんと、デプロイ日時ベースのキャッシュバスターを付けていたはずが、サーバごとにキャッシュバスターの値が異なっていました。

現在のデプロイ方式では、複数台あるアプリケーションサーバ間で、デプロイ日時に数秒〜数十秒のズレが生じてしまっています。

デプロイ日時ベースのキャッシュバスターを各サーバで生成していたため、このような現象が起こってしまっていました。

その結果、せっかくブラウザキャッシュを持っていても、前回と異なるサーバにアクセスするだけでキャッシュが破棄されてしまうという問題が起こっていました。

ついやってしまいがちかとは思いますが、なかなか気付きにくいタイプの問題かと思います。

どのように改善したか?

この仕組みを以下のように改善しました。

ブラウザにリソースをキャッシュさせる仕組み

従来通り、HTTPヘッダにより1年間のキャッシュを許可する設定を踏襲しました。

ブラウザにキャッシュを破棄させる仕組み

以下のロジックを採用しました。隙を生じぬ二段構え。

① 特定のリソースにはコンテンツベースのキャッシュバスターを付与する

具体的には、Webpackで処理する以下のリソースが対象となります。

  • JavaScriptファイル (.js, .jsx)
  • CSSファイル (.css, .scss)
  • CSSファイルの url()関数で読み込まれる画像やフォント類 (.png, .woff2など)

これらのリソースの内容をハッシュ化した文字列をキャッシュバスターとして付与します。

こうすることで、内容が変更されるまでブラウザキャッシュが破棄されないようになります。

② それ以外のリソースにはコミットIDをキャッシュバスターとして付与する

一方、全てのリソースをWebpackで処理するわけではありません。例えば、以下のようなリソースは対象外です。

  • (CSSから読み込まれない) 画像やフォント類
  • Webpackを通さない、生のJavaScriptやCSSファイル類

これらについては、デプロイ時のGitのコミットIDをキャッシュバスターとして付与します。

デプロイごとにブラウザキャッシュが破棄されてしまいますが、重要なリソースの多くは何らかの形でWebpackで処理されていたため、問題なしと判断しました。

ようやく実装編

さて、前述の仕組みをどのように実装していったのか見ていきましょう。

RailsLaravelなどのフレームワークでは似たような仕組みが用意されていますが、諸事情により、今回は自前で仕組みを作っていきました。

概要

今回の実装は大きく2つの部分から構成されています。

① Webpackで manifest.jsonファイルを出力する

manifest.jsonとは、サーバ内のリソースのパスと、キャッシュバスター付きのリソースのパスの対応関係を記述したJSONファイルです。

{"/js/lawyer/mypage/pc.bundle.js":"/js/lawyer/mypage/pc.bundle.js?b91d959a9994e8c2fc51"}

上記の例では、 ?b91d959a9994e8c2fc51というクエリパラメータが付与されています。

② PHPコードから manifest.jsonを読み込む

一方、HTML出力時にはPHPの関数で manifest.jsonファイルを読み込み、キャッシュバスター付きのURLに変換を行います。

<script src="<?= asset('/js/lawyer/mypage/pc.bundle.js') ?>"></script>

<script src="/js/lawyer/mypage/pc.bundle.js?b91d959a9994e8c2fc51"></script>

指定されたパスが manifest.jsonに存在しない場合は、一律でGitのコミットIDを付与します。

なお、PHPに限らず、任意のサーバサイド言語で同様の仕組みが実現可能かと思います。

Webpack側の実装

CSSの manifest.jsonファイルを出力するためのWebpack設定は以下のようなイメージになりました。 (一部パスなどを変更しているため、そのままでは動作しません)

importMiniCssExtractPluginfrom'mini-css-extract-plugin';importManifestPluginfrom'webpack-manifest-plugin';exportconstconfig={entry:entries.css,output:{path:'../service/css'},module:{rules:[{test:/\.scss$/,use:[MiniCssExtractPlugin.loader,{loader:'css-loader',options:{sourceMap:true,},},{loader:'postcss-loader',options:{sourceMap:true,},},{loader:'resolve-url-loader',options:{sourceMap:true,root:'../service',},},{loader:'sass-loader',options:{sourceMap:true,outputStyle:'compressed',},},],},{test:/\.(gif|png|jpe?g|eot|wof|woff|woff2|ttf|svg)$/,use:[{loader:'file-loader',options:{name:'[path][name].[ext]?[hash]',// CSSのurl()で読み込まれる画像やフォント類にコンテンツのハッシュ値を付与するcontext:'../service',publicPath:'/',emitFile:false,},},],},],},plugins:[// manifest.json ファイルを出力するnewManifestPlugin({basePath:'/css/',publicPath:'/css/',filter:fileDesc=>fileDesc.isInitial,}),newMiniCssExtractPlugin({filename:'[name].css?[contenthash]',// 出力されるCSSのファイル名にコンテンツのハッシュ値を付与する}),],};

これは以下のような処理を行なっています。

  1. CSSから読み込まれている画像やフォント類のハッシュ値を計算する
    • resolve-url-loaderを使い、CSSの url()関数で参照されている画像やフォント類をWebpackで処理できるようにします。
    • それらの画像やフォント類を file-loaderで処理し、名前にコンテンツのハッシュ値を付与します。
      • このプラグインは本来、ファイルシステム上にファイルを出力するプラグインですが、 emitFile: falseを指定し、出力を行わない設定にしています。
      • このようにすることで、ファイル名の変更のみを行うことができます。
  2. CSSファイルのハッシュ値を計算する
    • mini-css-extract-pluginを使ってCSSを個別のファイルとして出力します。
      • その際、 filenameオプションに [contenthash]を指定し、ファイル名にコンテンツのハッシュ値を付与します。
  3. manifest.jsonファイルを出力する

ここで、1.の処理を忘れないようにご注意ください。CSSから読み込まれる画像やフォント類にも適切にキャッシュバスターを付与する必要があります。

これを忘れてしまうと、以下のような問題が発生します。

  • キャッシュバスターを付与していなかったために、それらの画像やフォント類はブラウザキャッシュが破棄されないままになってしまった
  • タイムスタンプベースのキャッシュバスターを付与していたため、CSSファイルの内容が毎回変わってしまい、コンテンツのハッシュ値が変わってしまった

PHP側の実装

そろそろ読むのも疲れてきたでしょうから省略します。 (書くのも疲れてきました)

manifest.jsonファイルを読み、指定されたパスに対応するものがあればそれを返し、なければGitのコミットIDを付与するだけです。

まとめ

今回は弁護士ドットコムにおけるブラウザキャッシュ活用の改善事例についてご紹介しました。

  • ブラウザキャッシュを活用すると、2回目以降のサイト表示速度を高速化できる
  • それには以下の2つの仕組みが必要である
    • ブラウザにリソースをキャッシュさせる仕組み
    • ブラウザにキャッシュを破棄させる仕組み
  • リソースのコンテンツのハッシュ値をキャッシュバスターとして付与すると、最も長い期間ブラウザキャッシュを保持させることができる

よい良いユーザー体験を提供しようとする、全てのエンジニアの皆様の参考になれば幸いです。

おまけ:設計時にほかに検討したこと

Q. ローカルでの開発時はどうするの?

DIを使ってキャッシュバスターを生成するクラスを差し替え、メソッドが呼び出された時刻のタイムスタンプを返すようにしています。

こうすることで、常にキャッシュが破棄される状態になります。

Q. CDNを導入すれば良いのでは?

CDNで高速化されるのは1回目の読み込みであり、今回の改善対象は2回目以降の読み込みです。補完的な関係にあると考えています。

Q. Webpackのハッシュアルゴリズムに依存してしまうと、将来ほかのツールに移行できないのでは?

ファイルコンテンツベースのハッシュアルゴリズムであれば何でも良い (新旧のツールで同じハッシュ値になる必要がない) ため、問題ないと考えました。

もし、乗り換え先のツールにそういった機能がない場合でも、性能の劣化を許容すれば良さそうです。

Q. 各ファイルの更新時刻をWebサーバ (Apache/Nginx) に認識させて、 Last-Modifiedヘッダを出力すると簡単なのでは?

それが実現できればこんなに複雑な仕組みは必要なくなるため、実装上はシンプルになります。

しかし、以下の理由から今回考えた方式を採用しました。

If-Modified-Sinceヘッダ付きリクエストの発生

Last-Modifiedヘッダを出力させた場合、ブラウザはキャッシュが最新か確認するために If-Modified-Sinceヘッダ付きのリクエストを送信します。

(更新がない場合はレスポンスのボディが含まれないとはいえ) 通信が発生するよりはしない方が高速にブラウジングできるはずです。

デプロイ処理の運用コストの問題

デプロイ時のファイルコピーにより、ファイルの更新時刻が変わってしまうことはよくあります。
また、複数台のAppサーバ間で同一ファイルの更新時刻がズレてしまうとさらにカオスになります。 (弊社で実際にやらかしてしまっていたのは前述のとおりです。。)

これらの問題が発生しないようなデプロイ処理を作り込むことは可能ですが、アプリケーション開発者が普段意識しないレイヤですので、運用の中で意図せず壊れてしまう可能性があります。

CSSでベルトスクロールゲームを作った。

$
0
0

この記事は CSS Advent Calendar 2019 16日目の記事です。

概要

CSSで昔懐かしのベルトスクロールゲームを作りました。
PC、画面サイズ大きめで遊んでください><
https://codepen.io/Rin_T_T/pen/MWYjJKv?editors=1100

本記事はあくまで作った物の簡単な説明のため、コード,制作過程を一部省略しています。

設計書

必須機能をまとめます。

衝突判定: hoverで判定
ゲーム画面のスクロール: transitionで実装
スタート: hoverで判定
スコア用のアイテムを設置: checkboxで実装
キャラクターの移動: カーソルを画像に置き換える。

早速作っていきます。

最初にスクロールエリアと衝突オブジェクトを作成します。

index.pug
    .gameArea_inner
      .playArea.-test2
        - for(var i = 1; i <= 50; i++)
          .item.-object(class='-o' + i)

衝突判定を実装します。
.item.-objectをhoverすると兄弟要素のFailが最前面に表示するようにします。
※faile要素を最前面に維持するため、.faile要素自体にもhover状態を指定しておきます。
ひとまず判定が通ることが確認できれば良いでしょう。

index.pug
    .gameArea_inner
      .playArea.-test2
        - for(var i = 1; i <= 50; i++)
          .item.-object(class='-o' + i)
        + .faile
           .faile_inner
             .faile_contents   
style.scss
.faile{position:absolute;width:100%;height:100%;z-index:-100;//初期状態では非表示display:none;//初期状態では非表示&:hover{opacity:1;z-index:100;display:block;}&_inner{display:flex;}&_contents{text-align:center;background-image:url('https://portfolio.littledemon.pw/cssadvent/failed.png');background-size:386px160px;width:100%;height:100vh;&>*{margin-top:15px;}}}.item{width:30px;height:30px;background-color:#9c290d;position:absolute;z-index:100;opacity:1;&.-object{//衝突オブジェクトクラスposition:absolute; &:hover{&~.faile{z-index:100;opacity:1;display:block;}}}}

ゲーム画面のスクロールを実装します。

.gameArea_innerのサイズをプレイエリアとし最大値を560vwに設定します。
animationを指定し、スクロールスピードを設定します。

ついでにスタート/リスタート用のanimationも作っておきます。

style.scss
.gameArea{&_inner{width:560vw;display:flex;position:relative;z-index:0;left:0;animation:areaScroll70slinearforwards;}}@keyframesareaScroll{0%{position:relative;left:0vw;}100%{position:relative;left:-560vw;}}@keyframesareaScrollBack{0%{position:relative;}100%{position:relative;left:0vw;}}

スクロールを制御できるスタート/リスタートを作成します。

CSSは常に親から子へもしくは兄弟要素のみ指定できます。
スタートはスクロールしている要素と同じレイヤーに配置します。
hoverで先ほど作成したareaScrollBackを発火できるよう設定します。

index.pug
+ .wrap
+  .gameArea
+   .startArea
+     .startChecker
+     .startLine 
+       .startLine_text START 
    .gameArea_inner
      .playArea
        .item.-object.-ribbon.-top
        .item.-object.-ribbon.-bottom
        - for(var i = 1; i <= 50; i++)
          .item.-object(class='-o' + i)

style.scss
.startArea{display:flex;z-index:10;&:hover+.gameArea_inner{// 兄弟要素を指定し、animationを発火する。animation:areaScrollBack150seaseforwards;}}.startChecker{width:160px;background:linear-gradient(45deg,black25%,transparent25%,transparent75%,black75%),linear-gradient(45deg,black25%,transparent25%,transparent75%,black75%);background-color:white;background-size:40px40px;background-position:00,20px20px;}.startLine{background-color:white;height:100%;padding:015px;&_text{writing-mode:vertical-rl;height:100%;text-align:center;}}.gameArea{width:100%;height:100vh;background-color:white;overflow:hidden;display:flex;&_inner{~~~}}

スコア用のアイテムを作成します。

index.pug
 .wrap
  .gameArea
   .startArea
     .startChecker
     .startLine 
       .startLine_text START 
    .gameArea_inner
      .playArea
        .item.-object.-ribbon.-top
        .item.-object.-ribbon.-bottom
        - for(var i = 1; i <= 50; i++)
          .item.-object(class='-o' + i)
+    - for(var i = 1; i <= 24; i++)
+         label.present
+           input(type="checkbox")
+           .present_item(class='-p' + i)

style.scss
.present{&_item{position:absolute;width:40px;height:40px;display:block;background-size:30px30px;transition:left7s;&.-p1{//ゲームエリア上の位置(checkboxがfalseの時)top:45vh;left:20.5vw;}}&input{display:none;&:checked{&+.present_item{pointer-events:none;transition:left4s;&.-p1{//アイテムを取得した後の位置(checkboxがtrueの時)top:60vh;left:585vw;}}}}}

scssが少し複雑ですね、メインのデザインは.present_itemに担当してもらいます。
input要素は非表示にしておき、checkBoxがtureの時、後述するゴールエリアに遷移するように設定します。

キャラクターの移動

Cursolを画像に変更します。
利用できるサイズは最大128*128のようですが、
ブラウザの互換性から考えると32*32が推奨のようです。
※今回は少し小さかったため、64pxにしました。

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Basic_User_Interface/Using_URL_values_for_the_cursor_property

以上で完成です。

反省点。

遊んでいただけると気づくかもしれませんが、hover要素はカーソルの移動時に現在位置を再取得します。強制スクロールというゲーム上、衝突オブジェクトの手前で停止するとhoverの判定されず貫通します。
とりあえずゴールを見たいという人はカーソル放置でゴールまでたどり着きます(笑)

スコア用のカーソル衝突オブジェクトはもっとCSS芸的なもので、頑張りたかった...時間が足りませんでした...

chrome PCのみの対応です。


半々人前のフロントエンドエンジニアが成長するために必要だと感じたこと

$
0
0

はじめに

 私は今年の4月から初めてMacを使い始めてプログラミングというものに触れ、今日まで約8ヶ月間勉強をしてきました。
今は、フロントエンドの分野で月に約3本のWeb案件に取り組んでいます。(まだ独り立ちはできていませんが...)
今まで勉強してきて、効率的に勉強を進め最も近道に成長をしていくためにはどうしたらいいのか、自分なりの考えをまとめ、初心を忘れないようにするためにも記事にしたいと思います。

今までやってきたこと

  •  4~6月前半...pythonチュートリアルを基本1人で学び、わからないところは教えてもらう
  •  6月前半~9月中旬...初めての案件に参加(バックエンド)
  •  9月中旬~10月中旬...フロントエンドの勉強
  •  ~現在...フロントエンドの案件でアウトプット&お勉強

できるようになったこと(まだまだ未熟ですが...)

  1. Git、Github
  2. html、css
  3. Wordpress
  4. Figma
  5. インフラ、セキュリティ周り(ほんーの少し)

実践してみてよかったこと

  • pythonチュートリアル→なんとなーくプログラミングの様子がわかり、操作に慣れた
  • スキルが全くないにも関わらず案件に参加→急成長できた
  • 自分で調べてもわからなかったらためらわずに質問する

習うより慣れろ!

案件に初めて参加させていただいた時にはpythonしか知らず(それもほんの少し)、いきなりDjangoやDocker、Git、Gihubなどを使い、最初はわからないことが多すぎてパンクしそうでした。任せていただいた仕事も私1人の力で終わらせることはできず、いつも先輩に教えてもらってばっかりでした。でも、思ったのは...習うより慣れろ!
自分で勉強を進めるだけでは世界が狭く、モチベーションを保つのも大変です。案件に参加することで先輩方のコードを見ながら勉強することができました。
仕事を振ってもらう→調べる→少しは自己解決→わからないところを教えてもらう→理解する→成長→新たな仕事を振ってもらう、という好循環が生まれ、短期間(?)でいろいろなことができるようになりました。

教えてもらう立場

教えてもらうには受け身ではなく、積極的に動き、教えてもらえるような人になるということが大切だなと思いました。
自己解決できない時には、
1. 現状報告
2. コードを見せる
3. 何をどう改善したいのか
4. 試してみたこと
これらを報告することは絶対必要です。見た目だけ見せられてもどういうコード書いているのか、考えてもらう手間が発生してしまいます。教えてもらうために開発や休憩する時間を奪って教えてもらうので、少しでも手間を減らさなければいけないと感じました。

これから

将来的には、フロントエンドの分野だけではなく、バックエンドの分野も理解できるようになりたいです。
まだまだ勉強しなければならないことはたくさんありますが、少しずつ自分の理解していることをアウトプットする機会が増えていきます。
今まで親切に教えてくださったように自分も教えられるようになりたいです。

次世代Webスタイリングを追う ーScroll Snap, :focus-within, @media (prefers-*), :is()

$
0
0

こちらは「DeNA Advent Calendar 2019」 の16日目です。
今年は「20新卒のAdvent Calendar」もありますので、よかったら合わせてご覧ください。

概要

Chrome Dev Summit 2019にて、「 Next-generation web styling」というセッションがありました。このセッションでは、CSSのセレクタやプロパティをはじめとするWebスタイリングにまつわる多くの新しいTipsが紹介されています。

セッション内の全てのTipsを手を動かしながら確認したかったんですが、色々と間に合いませんでした。なので、現時点で僕が理解できた(であろう)内容を記載したいと思います。

本記事で紹介するTips

本記事では、セッションの前半で紹介されているこちらのTipsを紹介します。

記載しているサンプルとソースコードは、こちらで公開しています。よかったらご覧ください(随時更新予定)。

それでは見ていきましょう/

CSSプロパティ: Scroll Snap

トップバッターは Scroll Snap。新しいCSSのプロパティです。

このプロパティを指定することで、スマホのホーム画面のスクロールのように、スクロールした後に特定の要素にsnapするようになります。CSSだけでこれを表現できるのは嬉しいですね。

サンプル

水平軸のスクロールで、snap対象の要素を中央に表示させるようにしています(サンプルのURL)。

scroll-snap

HTML
<divclass="container"><imgsrc="1.png"width="300"height="300"><imgsrc="2.png"width="300"height="300"><imgsrc="3.png"width="300"height="300"><imgsrc="4.png"width="300"height="300"><imgsrc="5.png"width="300"height="300"></div>
CSS
.container{display:flex;height:300px;overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:xmandatory;}.containerimg{scroll-snap-align:center;}

scroll-snap-type: スクロール方向を指定。今回は水平軸(x)でしたが、垂直軸(Y)や両軸(both)などもあります。実際の挙動はこちらで確認ください。

scroll-snap-align: snap対象の要素をどの位置に表示するかを指定。今回は中央(center)でしたが、先頭(start)や後尾(end)にもすることができます。

overscroll-behavior: Scroll Snapと合わせたい代物です。これは、スクロールの連鎖を抑制します。何が嬉しいのかというと、Scroll Snapのエリアをスマホで左にスクロールし続けた場合、ユーザーの意図とは反してブラウザバッグする可能性があります。そんな事故防止に効果的です。

ブラウザの対応状況

CSS property: scroll-snap-typeCSS property: scroll-snap-align
scroll-snap-type
caniuse.com
scroll-snap-align
caniuse.com

参考資料

CSSセレクタ: :focus-within

次にCSSセレクタに新しく追加された :focus-withinです。これは、その要素自体がフォーカスされているか、または、その要素の子孫がフォーカスされているかを表現することができます。

サンプル

入力欄がフォーカスされた時に親要素の背景色と、その子要素の画像をアニメーションさせています(サンプルのURL)。Chrome DevToolsでも:focus-withinに切り替えることができます。

:focus-within

HTML
<divclass="container"><imgsrc="1.png"width="50"height="50"><inputtype="text"value=""placeholder="Please focus..."></div>
CSS
.container{display:flex;justify-content:center;align-items:center;}.containerimg{transition:all.5s;opacity:.25;}.container:focus-within{background-color:#2c8898;}.container:focus-withinimg{opacity:1;transform:rotate(360deg);}

ブラウザの対応状況

CSS selector: :focus-within
:focus-within
caniuse.com

参考資料

メディアクエリ: @media (prefers-reduced-motion)

激しいアニメーションが苦手な方に向けて、「視差効果を減らす」という設定メニューがあります(初耳だった)。iPhoneはこちらのサポートページの通り、設定メニューがありました。

そしてWebコンテンツも、prefers-reduced-motionを使うことで、視差効果を減らしたいユーザー向けにCSSを記述できるようになりました。

サンプル

意味もなく激しく動くアニメーションを、視差効果を減らしたいユーザーには静止して表示します(サンプルのURL)。

なお、Chrome DevToolsのシミュレーターは、Chrome 79で対応ということです。それまではCanaryで開発すると良いと思います。

prefers-reduced-motion

HTML
<divclass="container"><divclass="motion"><imgsrc="1.png"width="50"height="50"><imgsrc="2.png"width="50"height="50"><imgsrc="3.png"width="50"height="50"><imgsrc="2.png"width="50"height="50"><imgsrc="1.png"width="50"height="50"></div></div>
CSS
.container{position:relative;height:300px;}.motion{position:absolute;top:0;right:0;left:0;display:flex;justify-content:space-around;animation:MoveUpDown1slinearinfinite;}@media(prefers-reduced-motion:reduce){.motion{top:50%;transform:translateY(-50%);animation:none;}}@keyframesMoveUpDown{0%,100%{top:0;}50%{top:250px;}}

.motionで上下にアニメーションを行なっており、@media (prefers-reduced-motion: reduce)を使って、アニメーションを打ち消しています。

ブラウザの対応状況

一生懸命がんばって作ったアニメーションが...という心の悲鳴が聞こえてきそうですが、ブラウザは大方対応できているようです。

prefers-reduced-motion media query
prefers-reduced-motion
caniuse.com

参考資料

メディアクエリ: @media (prefers-color-scheme)

macOS Mojaveにて、OSレベルで外観をdarkモードに変更できるようになりました(僕はまだdarkモードに慣れずにいます)。

そしてWebコンテンツも、prefers-color-schemeというメディアクエリを用いて、light/darkごとにCSSで表現できるようになりました。

サンプル

背景色と文字色をモードごとに切り替えるサンプルです(サンプルのURL)。prefers-reduced-motionと同じく、Chrome DevToolsのシミュレーターはChrome 79で対応ということです。

prefers-color-scheme

HTML
<divclass="container"><p>Hello</p><p>你好</p><p>こんにちは</p><p>안녕하세요</p><p>السلام عليكم</p></div>
CSS
:root{--theme-font:#0f0f0f;--theme-background:#ffffff;}@media(prefers-color-scheme:dark){:root{--theme-font:#ffffff;--theme-background:#0f0f0f;}}.container{background-color:var(--theme-background);color:var(--theme-font);}

CSSのカスタムプロパティを使い、初期値のlightモード用のカラーコードをdarkモード時にはdarkモード用のカラーコードに上書きするようにしています。そのため、背景色と文字色はカスタムプロパティを参照すればlight/darkそれぞれのモード用に色を切り替えることができます。

ブラウザの対応状況

ブラウザもだいぶ対応されてきている状況です。iOSアプリでは、darkモード対応を推奨しているので、WebViewの開発担当者をはじめ、darkモードの対応が今後増えてくると思います。

prefers-color-scheme media query
prefers-color-scheme
caniuse.com

参考資料

CSSセレクタ: :is()

最後に、新しくCSSに追加されそうな :is()セレクタについてです。このセレクタを使うと、CSSを少しだけシンプルにすることができます。

サンプル

:is()のサンプルです(サンプルのURL)。例えば、記事の見出し(h1 ~ h6)のスタイルを統一したいときに、これまで下記のようにCSSを書いてきたかと思います。

CSS
article>h1,article>h2,article>h3,article>h4,article>h5,article>h6{color:#0f4c81;}

これに :is()を使うと、以下のようにシンプルに記載することができます。

CSS
article>:is(h1,h2,h3,h4,h5,h6){color:#0f4c81;}

ブラウザの対応状況

使う場面けっこうありそうですが、ブラウザの対応状況は待ちの状況。期待して待っていましょう/

CSS selector: :is()
:is()
caniuse.com

参考資料

おわりに

Next-generation web stylingのセッションでは、他にも多くの新しいWebスタイリングのTipsが紹介されています。Gap / Logical Properties / Subgrid / Paint API / Houdini ...。

お腹いっぱいになりそうですが、サービスの使い心地を少しでも高めるためにもぜひキャッチアップしていきたいと考えています。また、試作品ができてきたら公開していきます。

以上、ご覧くださりありがとうございました。

@storybook/htmlがあまりにも使いづらいのでpugを使って上手く利用する方法を考えてみた

$
0
0

storybook@htmlを利用する場合ってvueとか使わないと思うのですが、実制作で素のHTMLで書くケースなんてほぼ無いですよね。pugで書かれる事が多いかと思います。
そんな中でstorybookを利用したい場合【コード補完の利かない環境でHTMLを書く】か【ビルドしてコピペ】、しかも書いたところでpugとして再利用できる内容ではないのです。
下手したら仕事が増えるだけでハッキリ言って使えません。検索にも殆ど引っかからない状態です。
かと言ってstorybookを使うためにnuxtを利用するってのも違うと思うので、上手く使う方法を考えてみました。

前準備

今回説明する内容はpugの利用方法を説明するだけなので、特別な環境は作りません。
サンプルを作るのが面倒なので初期インストール+bootstrapのcssを使用して進めます。

npx -p @storybook/cli sb init --type html

インストールしたら、とりあえずこんな感じの構成で準備します。

├── .storybook
│   ├── config.js
│   └── preview-head.html
└── stories
    └── sample
        ├── button.pug
        └── sample.stories.js
storybook/preview-head.html
<linkrel="stylesheet"href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"crossorigin="anonymous">
stories/sample/button.pug
button.btn.btn-primary(type="button")Primarybutton.btn.btn-secondary(type="button")Secondarybutton.btn.btn-success(type="button")Successbutton.btn.btn-danger(type="button")Dangerbutton.btn.btn-warning(type="button")Warningbutton.btn.btn-info(type="button")Infobutton.btn.btn-light(type="button")Lightbutton.btn.btn-dark(type="button")Dark

stories.jsでpugから直接表示する方法

pugを読み込めるようにするには、以下を追加インストールします。

npm i pug pug-plain-loader html-loader -D

pugを読み込んで表示するだけだったら実は簡単だったりします。
pug-plain-loaderhtml-loaderを経由したものを出力するだけです。

import{storiesOf}from'@storybook/html';importPug2HtmlBtnfrom'!html-loader!pug-plain-loader!./button.pug'storiesOf('sample',module).add('pug2html',()=>Pug2HtmlBtn)//htmlタグに変換されたものを表示するだけ

st01.jpg

Pugのソース表示&コピーが出来るようにする

pugからhtmlを表示できたとしても、ページ上でpugのソース表示やコピーが出来ないと微妙なのでコピー出来るようにします。
コードのコピーに関しては、storybookのaddonsにある【Copy code block】を使います。

npm i -D @pickra/copy-code-block

pugソースの表示方法は、html-loaderのみで読み込むことでpugのソースとして読み込むことができます。

stories/sample/sample.stories.js
import{storiesOf}from'@storybook/html';importcopyCodeBlockfrom'@pickra/copy-code-block';importPug2HtmlBtnfrom'!html-loader!pug-plain-loader!./button.pug'importPugSrcBtnfrom'!html-loader!./button.pug'storiesOf('sample',module).add('pug2html',()=>Pug2HtmlBtn)//htmlタグに変換されたものを表示するだけ.add('pug&html',()=>Pug2HtmlBtn+copyCodeBlock(PugSrcBtn))

st02.jpg

サンプルは載せませんが下のイメージのようにpugのレイアウトやmixinなども反映することができます。
st03.jpg

さらに使いやすくする方法を考える

nuxt開発者レベルであれば、このくらいのことが出来ていれば十分活用できると思うのですが、マークアップの人がメインで使うことを考えるとまだちょっと複雑な感じです。
あとimportの記述が多くなってしまう点も問題としてあります。
その辺りを改善していきます。

importを動的化する

require.contextとかで一気に読み込んでも良いのですが、HTMLとpugは基本セットで使用するのでcopyCodeBlockをラッパー化してその中で読み込むようにします。

.storybook/config.js
import{configure}from'@storybook/html';importcopyCodeBlockfrom'@pickra/copy-code-block';// 指定したパスのhtml表示とpugソースのコピーボタンを表示するglobal.code=(path,title=false)=>{constpug=require(`!html-loader!../stories/${path}.pug`);consthtml=require(`!html-loader!pug-plain-loader!../stories/${path}.pug`);return`
  <div class="container mt-4">
    ${title?'<h1 class="mb-5">'+title+'</h1>':''}${html}<div class="mt-5">
    ${copyCodeBlock(pug)}</div>
  </div>`}// automatically import all files ending in *.stories.jsconfigure(require.context('../stories',true,/\.stories\.js$/),module);

sample.stories.jsに記述していたような内容をconfig.jsに記述し、global関数として登録することで全てのstories.jsから使用することができます。

storiesOf('ボタン',module).add('ノーマルボタン',()=>code('sample/button','ノーマルボタン'))

こんな感じのシンプルな記述を追加をするだけで【見出し+HTML+pug 】の表示が出来るようになります。
st04.jpg

マークダウンも同じような考え方で作れます

今までの内容にマークダウンでの記述を追加すれば、編集のしやすいページが出来ると思います。
マークダウンも今回のpugと同じ考え方でhtml-loadermarkdown-loaderで読み込むだけで同様の処理が出来ます。

const md2html = !html-loader!markdown-loader!パス名

メソッドを同じように作成すればより記述を少なくして、自由度の高いページ作成が出来ると思います。
参考にしつつ色々お試しください。

cssうんちに魂宿してみた

$
0
0

💩おさらい💩

前回cssでうんちを書いたぞ!
cssだけでうんち書いてみた
今日はこれをよりうんちらしくするべく、魂を宿してみたい!

💩cssでぷる〜んていうアニメーション💩

キーフレームを頑張って打て、scale()を変えればできる!

@keyframesjelly{0%{transform:scale(1,1);}8%{transform:scale(1,1);}10%{transform:scale(1.1,0.9);}13%{transform:scale(0.9,1.1);}17.5%{transform:scale(1,1);}19%{transform:scale(1,1);}58%{transform:scale(1,1);}60%{transform:scale(1.1,0.9);}63%{transform:scale(0.9,1.1);}67.5%{transform:scale(1,1);}69%{transform:scale(1,1);}100%{transform:scale(1,1);}}

💩宿した💩

See the Pen unchi-soul by petapetapeta (@petapetapeta) on CodePen.

マウスを乗せるとぷる〜んってなるようになりました!
可愛いですね💩

インタラクティブなLINE風チャット小説

$
0
0

インタラクティブなLINE風チャット小説を作ってみよう

チャット小説というものが段々浸透してきているみたいなので、
LINE風チャット小説サイトを作っていきます。
HTML CSS JavaScript phpで書いていきます。

フロント部分

まず、フロント部分をHTML CSS Javascriptで作っていきます。

chat.html
<htmllang="ja"><head><metacharset="UTF-8"/><title>インタラクティブなLINE風チャット小説</title><linkrel='stylesheet'href='style.css'type='text/css'media='all'/><metaname="viewport"content="width=350"><metaname="viewport"content="width=device-width,initial-scale=1.0,minimum-scale=1.0"></head><body><!-- ▼LINE風ここから --><divclass="line__container"><!-- タイトル --><divclass="line__title">犯人探し
    </div><!-- ▼会話エリア scrollを外すと高さ固定解除 --><divclass="line__contents scroll"><!-- 自分の吹き出し --><divclass="line__right"><divclass="text">犯人はこの中にいる!</div><spanclass="date">既読 2<br>0:30</span></div><!-- 自分のスタンプ --><divclass="line__right"><divclass="stamp"><imgsrc="./stamp/job_tantei_man.png"/></div><spanclass="date">既読 2<br>0:30</span></div><!-- 相手の吹き出し --><divclass="line__left"><figure><imgsrc="./icon/doctor.jpg"/></figure><divclass="line__left-text"><divclass="name">医者</div><divclass="text">私はやっていないぞ!</div><spanclass="date">0:32</span></div></div><!-- 相手のスタンプ --><divclass="line__left"><figure><imgsrc="icon/doctor.jpg"/></figure><divclass="line__left-text"><divclass="name">医者</div></div><divclass="stamp"><imgsrc="./stamp/dog2_angry.png"/></div><spanclass="date">0:32</span></div><!-- 相手の吹き出し --><divclass="line__left"><figure><imgsrc="icon/macho.jpg"/></figure><divclass="line__left-text"><divclass="name">マッチョ</div><divclass="text">疑われて悲しです・・・</div><spanclass="date">0:33</span></div></div><!-- 相手のスタンプ --><divclass="line__left"><figure><imgsrc="icon/macho.jpg"/></figure><divclass="stamp"><imgsrc="./stamp/cat3_cry.png"/></div><spanclass="date">0:33</span></div></div><!-- ▲会話エリア ここまで --><formaction = "chat.html"method = "get"><divclass="bms_send"><inputtype = "text"class="bms_send_message"name ="answer"><inputtype = "submit"value ="送信"class="bms_send_btn"></div></form></div><!-- ▲LINE風ここまで --></body></html>
style.css
body{padding:1em0;background-color:#000000;}.line__container{padding:0;background:#7494c0;overflow:hidden;max-width:400px;margin:-10pxauto;font-size:80%;}/* タイトル部分 */.line__container.line__title{background:#273246;padding:10px;text-align:center;font-size:150%;color:#ffffff;}/* 会話部分 */.line__container.line__contents{padding:10px;overflow:hidden;line-height:135%;}.line__container.scroll{height:80%;overflow-y:scroll;}/* スタンプ画像最大幅 */.line__container.line__left.stampimg{max-width:120px;margin:30px15px5px60px;}/* 相手の会話 */.line__container.line__left{position:relative;display:block;margin:5px0;max-width:80%;float:left;margin-right:15px;clear:both;}/* アイコン画像 */.line__container.line__leftfigure{width:50px;position:absolute;top:15px;left:0;padding:0;margin:0;}/* 正方形を用意 */.line__container.line__leftfigureimg{border-radius:50%;width:50px;height:50px;}.line__container.line__left.line__left-text{margin-left:70px;padding:10px000;}.line__container.line__left.line__left-text.name{font-size:80%;color:#ffffff;}/* コメントエリア */.line__container.line__left.text{margin:0;position:relative;padding:10px;border-radius:20px;background-color:#ffffff;}/* 吹き出し */.line__container.line__left.text::after{content:'';position:absolute;display:block;width:0;height:0;left:-10px;top:10px;border-right:20pxsolid#ffffff;border-top:10pxsolidtransparent;border-bottom:10pxsolidtransparent;}/* 相手のの時間エリア */.line__container.line__left.date{content:'';position:absolute;display:block;width:100px;text-align:right;right:-30px;bottom:0px;font-size:80%;color:#ffffff;}/* 自分の会話 */.line__container.line__right{position:relative;display:block;margin:5px0;max-width:75%;float:right;margin-right:15px;clear:both;}/* コメントエリア */.line__container.line__right.text{padding:10px;border-radius:20px;background-color:#8de055;margin:0;margin-left:80px;}/* 吹き出し */.line__container.line__right.text::after{content:'';position:absolute;display:block;width:0;height:0;right:-10px;top:10px;border-left:20pxsolid#8de055;border-top:10pxsolidtransparent;border-bottom:10pxsolidtransparent;}/* 自分がスタンプを送る時 */.line__container.line__right.stamp{position:relative;margin-left:80px;}/* 自分がスタンプを送る時 */.line__container.line__right.stampimg{max-width:100px;}/* 自分の既読エリア */.line__container.line__right.date{content:'';position:absolute;display:block;width:100px;text-align:right;left:-30px;bottom:0px;font-size:80%;color:#ffffff;}/* テキストエリア、送信ボタン */.bms_send{background-color:#eee;border-right:1pxsolid#ddd;border-left:1pxsolid#ddd;border-bottom:0pxsolid#ddd;height:48px;padding:5px;max-width:400px;margin:20pxauto-13px;}.bms_send_message{width:calc(100%-75px);/*常に送信ボタンの横幅を引いたサイズに動的に計算*/line-height:16px;height:48px;padding:14px6px0px6px;/*文字がテキストエリアの中心になる様に隙間調整*/border:1pxsolid#ccc;border-radius:4px;/*角丸*/text-align:left;/*文字を左寄せ*/box-shadow:2px2px4px0pxrgba(0,0,0,0.2)inset;/*内側に影を入れてテキストエリアらしくした*/box-sizing:border-box;/*paddingとborderの要素の高さと幅の影響をなくす(要素に高さと幅を含める)*/font-size:16px;}.bms_send_btn{width:72px;height:48px;font-size:17px;line-height:3em;float:right;/*bms_sendに対して右寄せ*/color:#fff;font-weight:bold;background:#bcbcbc;text-align:center;/*文字をボタン中央に表示*/border:1pxsolid#bbb;border-radius:4px;/*角丸*/box-sizing:border-box;/*paddingとborderの要素の高さと幅の影響をなくす(要素に高さと幅を含める)*/text-decoration:none;}.bms_send_btn:hover{background:#13178E;/*マウスポインタを当てた時にアクティブな色になる*/cursor:pointer;/*マウスポインタを当てた時に、カーソルが指の形になる*/}.button{width:70px;height:48px;border-radius:5%;/* 角丸       */font-size:15pt;/* 文字サイズ */text-align:center;/* 文字位置   */cursor:pointer;/* カーソル   */font-weight:bold;padding:12px10px;/* 余白       */background:#bcbcbc;/* 背景色     */color:#ffffff;/* 文字色     */line-height:1.5em;/* 1行の高さ  */transition:.3s;/* なめらか変化 */border:1pxsolid#bbb;}.button:hover{background:#13178E;/* 文字色     */}

これで、一応LINE風チャット小説っぽくなりました。

スクリーンショット 2019-12-16 15.06.32.png

アイコンやスタンプ類は、お好みでカスタマイズしてください。
メッセージの送信時間や既読数も適宜変更してください。

ただ読むだけの小説ならこれで終わりです。

インタラクティブ機能

ここからは、読者の送信内容によって内容が変わるようなものにしていきましょう。
今回は、犯人の名前を入力することで物語が進ものもを作っていきます。

part1.html
<htmllang="ja"><head><metacharset="UTF-8"/><title>インタラクティブなLINE風チャット小説</title><linkrel='stylesheet'href='style.css'type='text/css'media='all'/><metaname="viewport"content="width=350"><metaname="viewport"content="width=device-width,initial-scale=1.0,minimum-scale=1.0"><script src="https://code.jquery.com/jquery-1.11.3.min.js"></script></head><body><!-- ▼LINE風ここから --><divclass="line__container"><!-- タイトル --><divclass="line__title">犯人探し
    </div><!-- ▼会話エリア scrollを外すと高さ固定解除 --><divclass="line__contents scroll"><!-- 自分の吹き出し --><divclass="line__right"><divclass="text">犯人はこの中にいる!</div><spanclass="date">既読 2<br>0:30</span></div><!-- 自分のスタンプ --><divclass="line__right"><divclass="stamp"><imgsrc="./stamp/job_tantei_man.png"/></div><spanclass="date">既読 2<br>0:30</span></div><!-- 相手の吹き出し --><divclass="line__left"><figure><imgsrc="./icon/doctor.jpg"/></figure><divclass="line__left-text"><divclass="name">医者</div><divclass="text">私はやっていないぞ!</div><spanclass="date">0:32</span></div></div><!-- 相手のスタンプ --><divclass="line__left"><figure><imgsrc="icon/doctor.jpg"/></figure><divclass="line__left-text"><divclass="name">医者</div></div><divclass="stamp"><imgsrc="./stamp/dog2_angry.png"/></div><spanclass="date">0:32</span></div><!-- 相手の吹き出し --><divclass="line__left"><figure><imgsrc="icon/macho.jpg"/></figure><divclass="line__left-text"><divclass="name">マッチョ</div><divclass="text">疑われて悲しです・・・</div><spanclass="date">0:33</span></div></div><!-- 相手のスタンプ --><divclass="line__left"><figure><imgsrc="icon/macho.jpg"/></figure><divclass="stamp"><imgsrc="./stamp/cat3_cry.png"/></div><spanclass="date">0:33</span></div><!-- 自分の吹き出し --><divclass="line__right"><divclass="text">犯人は....</div><spanclass="date">既読 2<br>0:35</span></div><?php
      $comment = htmlspecialchars($_GET['answer']);
      if($comment==""){

      }
      elseif($comment=="医者"){
        http_response_code( 302 ) ;
        header( "Location: ./part2.html") ;
        exit ;
      }
      else{
        echo "
        <!-- 自分の吹き出し -->
        <div class=\"line__right\">
          <div class=\"text\">$comment<br>では無いな。。</div>
          <span class=\"date\"><br>0:35</span>
        </div>
        ";
      }
    ?></div><!-- ▲会話エリア ここまで --><formaction = "part1.html"method = "get"><divclass="bms_send"><inputtype = "text"class="bms_send_message"name ="answer"><inputtype = "submit"value ="送信"class="bms_send_btn"></div></form></div><!-- ▲LINE風ここまで --></body></html>
part2.html
<htmllang="ja"><head><metacharset="UTF-8"/><title>インタラクティブなLINE風チャット小説</title><linkrel='stylesheet'href='style.css'type='text/css'media='all'/><metaname="viewport"content="width=350"><metaname="viewport"content="width=device-width,initial-scale=1.0,minimum-scale=1.0"></head><body><!-- ▼LINE風ここから --><divclass="line__container"><!-- タイトル --><divclass="line__title">犯人探し
    </div><!-- ▼会話エリア scrollを外すと高さ固定解除 --><divclass="line__contents scroll"><!-- 自分の吹き出し --><divclass="line__right"><divclass="text">犯人はこの中にいる!</div><spanclass="date">既読 2<br>0:30</span></div><!-- 自分のスタンプ --><divclass="line__right"><divclass="stamp"><imgsrc="./stamp/job_tantei_man.png"/></div><spanclass="date">既読 2<br>0:30</span></div><!-- 相手の吹き出し --><divclass="line__left"><figure><imgsrc="./icon/doctor.jpg"/></figure><divclass="line__left-text"><divclass="name">医者</div><divclass="text">私はやっていないぞ!</div><spanclass="date">0:32</span></div></div><!-- 相手のスタンプ --><divclass="line__left"><figure><imgsrc="icon/doctor.jpg"/></figure><divclass="stamp"><imgsrc="./stamp/dog2_angry.png"/></div><spanclass="date">0:32</span></div><!-- 相手の吹き出し --><divclass="line__left"><figure><imgsrc="icon/macho.jpg"/></figure><divclass="line__left-text"><divclass="name">マッチョ</div><divclass="text">疑われて悲しです・・・</div><spanclass="date">0:33</span></div></div><!-- 相手のスタンプ --><divclass="line__left"><figure><imgsrc="icon/macho.jpg"/></figure><divclass="stamp"><imgsrc="./stamp/cat3_cry.png"/></div><spanclass="date">0:33</span></div><!-- 自分の吹き出し --><divclass="line__right"><divclass="text">犯人は....</div><spanclass="date">既読 2<br>0:35</span></div><!-- 自分の吹き出し --><divclass="line__right"><divclass="text">医者だ!</div><spanclass="date">既読 1<br>0:36</span></div><!-- 相手の吹き出し --><divclass="line__left"><figure><imgsrc="./icon/doctor.jpg"/></figure><divclass="line__left-text"><divclass="name">医者</div><divclass="text">私がやりました。。。</div><spanclass="date">0:36</span></div></div></div><!-- ▲会話エリア ここまで --><formaction = "commons_nazo1.html"method = "get"><divclass="bms_send"><inputtype = "text"class="bms_send_message"name ="answer"><inputtype = "submit"value ="送信"class="bms_send_btn"></div></form></div><!-- ▲LINE風ここまで --></body></html>

これでpatr1.htmlのメッセージボックスに「医者」と入力すると、patr2.htmlに遷移します。

「医者」以外の文字を入力をすると、それは無いな。。と出ます。
こちらは、別ページにジャンプさせずに、同ページで処理します。

HTML内でPHPスクリプトを実行できない場合は、以下のリンク等を参考にしてください。
【PHP入門】HTMLでPHPスプリクトを実行しよう!

これでインタラクティブなLINE風チャット小説の完成です!

参考URL:
ゼロから作る、簡単WebチャットUIの作り方
HTMLとCSSでLINE風チャット画面(会話方式)を記事に表示する方法
別のページへジャンプするphpの関数

rails swiper(画像のスライド)

$
0
0

よくWebサイトでみる画像のスライダー機能を実装する

完成品

Image from Gyazo

環境

ruby:2.5.1
rails:2.5.3
DB:mysql(Sequel Pro)
ブラウザ:Google
OS:Mac(10.14.6)

Github

https://github.com/tana1818/frutweet2

スライダーとは

スライダー(カルーセルともいう)とは、Webサイトのスライドショーを表す用語。
よく使われるのはWebサイトのメインビジュアルで、小スペースで企業が提供したい情報を組み込むことができるので、多くのサイトで実装されている。
イメージこんな⬇︎
https://shop.spiegelau.co.jp/
https://www.matchnews.com/
https://reclasy.jp/

今回はswiperと言うプラグインのを使ってスライダー機能を実装しようと思います。

swiperとは
jQueryの読み込みが不要の為、動作が軽く
特徴としてオプション等の機能が豊富なことが挙げられます
Swiper公式のAPI解説(英語)
サンプル付き!簡単にスライドを作れるライブラリSwiper.js超解説(基礎編)

作成手順

以前、作成したナビバーのコードを元にスライダー機能のコードを追記して実装していく。

 

Githubからコードをダウンロード

下記のURLからコードをダウンロード
https://github.com/tana1818/frutweet
3.png

ダウンロードが完了したらファイル名を変更(なんでも良い)

任意のディレクトリにファイルを配置したらファイル名変更
記事では「frutweet2」で書きます
 

gemのインストール、DBの作成、マイグレーション実行、サーバー起動

frutweet2
bundleinstall#gemのインストールrakedb:create#DB作成rakedb:migrate#マイグレーションファイルを実行するrailss#サーバーの起動

http://localhost:3000/
を検索

下図のように表示されたらOK
4.png

コード編集(ビュー)

①タイトルバーの名前変更+CDNの記載追記

views/fruits/application.html.haml
!!!
%html%head%meta{:content=>"text/html; charset=UTF-8","http-equiv"=>"Content-Type"}/
    %title Frutweet
    =csrf_meta_tags=csp_meta_tag=stylesheet_link_tag'application',media: 'all','data-turbolinks-track':'reload'=javascript_include_tag'application','data-turbolinks-track':'reload'=stylesheet_link_tag"https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.5.0/css/swiper.min.css"=javascript_include_tag"https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.5.0/js/swiper.min.js"%body=yield

補足としてSwiperを使用するにあたって主に2つの方法があります。
1つはapplication.htmlにCDNの読み込み先を記述するか
もう1つは必要なファイルをダウンロードして特定のディレクトリに配置してそれを読み込みます。

今回は前者のCDNで読み込む方式で記述していきます。
因みに今回はバージョン4で書いています。最新はバージョン5になりますので是非試してみてください。

参考
「Swiper」の使い方とオプションを使ってカスタマイズする方法
CDNってそもそも何?なんかサーバの負荷が下がるって聞いたんだけど!〜Web制作/運営の幅が広がるCDNを知ろう第1回〜
【Rails5】「Swiper」を使ってスライダー、カルーセルを作る方法

 
②indexのページ編集

views/fruits/index.html.haml
%nav.navbar.navbar-expand-sm.sticky-top.navbar-dark{style: "background-color: #222222"}%a.navbar-brand{href: root_path} Furtweet
  %button.navbar-toggler{"aria-controls":"navbar","aria-expanded":"false","aria-label":"Toggle navigation","data-target":"#navbar","data-toggle":"collapse",type: "button"}%span.navbar-toggler-icon#navbar.collapse.navbar-collapse.justify-content-end%ul.navbar-nav.mr-auto%li.nav-item.dropdown%a#dropdown.nav-link.dropdown-toggle{"aria-expanded":"false","aria-haspopup":"true","data-toggle":"dropdown",href: "#"}ドロップダウン
        .dropdown-menu{"aria-labelledby":"dropdown"}%a.dropdown-item{href: "#"}リンク1
          %a.dropdown-item{href: "#"}リンク2
          %a.dropdown-item{href: "#"}リンク3
    %form.form-inline.my-2.my-md-0%input.form-control.mr-sm-1{placeholder: "Search",type: "search"}%button.btn.btn-outline-warning.mt-2.my-sm-0{type: "submit"} Search

/以下 swiperのコード/
.swiper-container.swiper-wrapper.swiper-slide=image_tag"fruit_sample1.jpg".swiper-slide=image_tag"fruit_sample2.jpg".swiper-slide=image_tag"fruit_sample3.jpg".swiper-pagination.swiper-pagination-white.swiper-button-prev.swiper-button-white.swiper-button-next.swiper-button-white:javascript
varswiper=newSwiper('.swiper-container',{navigation:{prevEl:'.swiper-button-prev',//左側にあるこれ「<」nextEl:'.swiper-button-next',//右側にあるこれ「>」},loop:true,//画像のループの有無speed:1000,//スライドするスピードpagination:{//ページネーションの種類el:'.swiper-pagination',type:'bullets',clickable:true,//クリック判定の有無},autoplay:{//自動スライドdelay:3000,//3秒ごとにdisableOnInteraction:true},});

 
補足
1行目の部分でナビバーの色を変えています。お好みで編集してください

SwiperのJavaScriptですが、記述が少ないのでhtmlのファイルに記述しました。JavaScriptのフォルダに記述しても良かったんですが記述少ないし、ファイル探すの面倒いし、見やすいし同じでいっかとゆるく考えながら書きました。開発の規模によっては愚行かもしれないで調べてみてください、、

Swiperのオプションはコードのコメントに記述してあるので見てください
参考にしたオプション一覧が載ってるサイト⬇︎
「Swiper」の使い方とオプションを使ってカスタマイズする方法

 

scssファイル編集

①application.scssにfruits.scssファイルを読み込ませる

assets/stylesheets/application.scss
@import"bootstrap";@import"fruits";

②fruits.scssファイル名変更+記述

fruits.scss ➡︎ _fruits.scssにファイル名変更

assets/stylesheets/_fruits.scss
.swiper-slide{width:100%;max-height:500px;min-height:250px;}.swiper-slideimg{width:100%;min-height:250px;object-fit:cover;}

補足
レスポンシブ対応を配慮したかったのでmax-heightmin-heightと使用しました。
widthに対してmax-heightは「〜〜px以上大きくしない」で
min-heightは「〜〜px以下小さくしない」という制限をかけるようなプロパティです。

上記の記述でいうと
max-height: 500px;は500px以上要素を大きくしない
min-height: 250px;は200px以下要素を小さくしない
という意味

width: 100%;にすることによって横幅一杯に画像を見せることができます。

最後の行のobject-fit: cover;は勝手に画像の中央でトリミングしてくれるプロパティです。
まあ効いてるかちょっとわかんないけどw

以上で実装は終了
再度サーバーを立ち上げ直して

auto_update
railss

うまく実装してできていれば上記のGIFのように画像のスライダー機能が実装できるはず

 
エキスパートの時、どう実装するか全然わからなかったのに意外と簡単に実装できてびっくりしました。
あの時の自分はどう検索していたのか、、
憧れのスライダー機能が実装できてまた少し成長できたように思いますw

ただ今の状態だと画像の大きさを一律にしないと高さが違うまま画像がスライドしてしまうことと
スライドの途中で画面の大きさを変えるとスライドが止まってしまうという仕様になっています、、、
ちょい頑張ったのですが、わからなかったので一旦あげようと思います。わかったら書きます。
ブラウザによって見え方も違うと思うので、指摘事項あれば教えて頂けると嬉しいです。
それでは

参考文献

Swiper公式のAPI解説(英語)
サンプル付き!簡単にスライドを作れるライブラリSwiper.js超解説(基礎編)
脱jQuery!?、Swiper使ってみた。
便利すぎ!jQuery不要のスライダー「Swiper.js」で色々と遊んでみよう
【Rails5】「Swiper」を使ってスライダー、カルーセルを作る方法
CDNってそもそも何?なんかサーバの負荷が下がるって聞いたんだけど!〜Web制作/運営の幅が広がるCDNを知ろう第1回〜
【実例12パターン】画像スライダーはSwiper使っておけば間違いない!実用的な使い方を紹介
「Swiper」の使い方とオプションを使ってカスタマイズする方法
CSSのwidth(幅)とheight(高さ)の指定方法をマスターしよう
1行追加でOK!CSSだけで画像をトリミングできる「object-fit」プロパティー

はてなブログもやっているので是非!
https://tanagram18.hatenablog.com/

SVGでお手軽にリッチなアニメーションをつけよう

$
0
0

今までSVGを使ったことがなかったので、お勉強がてらまとめてみました。
SVGについて順序立てて説明していくので、不要な方は飛ばしてください…!

SVGとはなんぞや?

画像を扱う形式としてよく知られる、JPEGやPNGとの違いはなんでしょう?
それは、ビットマップ画像ベクター画像かの違いです。
(※便宜上、ここではドットとピクセルは同義のものとして扱わせていただきます。)

ビットマップ画像について

ビットマップ画像が、JPEGやPNGに値します。
ピクセルを用いた画像形式で、ピクセルの色などを配列情報として扱います。
1ピクセルずつ色情報を持たせることができ複雑な表現が可能なため、写真などのデータはこの形式で扱われます。

よって、ご想像のとおり、ビットマップ画像は拡大するとこうなります。
img_01.jpg

ラスター画像

ビットマップ画像はラスター画像とも呼ばれますが、狭義の意味では違いがあり
ビットマップがピクセルの集合体を画像として表現するのに対し
ラスターはピクセルが横線状に並んだもの(ラスター線)を平行に並べた面を画像として表現します。

あくまでピクセルの集まりという意味合いで、ビットマップとラスターは同義の言葉として扱われています。

ビットマップ:呼称の由来

元々は、モノクロ2値(白黒など)の画像情報を格納した初期のフレームバッファとその内容である画像情報をビットマップと呼んだ。モノクロ2値の画像はピクセルあたり1ビットの情報量で記録されており、ディスプレイのピクセル配置とメモリのビット配置が1対1で対応(マッピング)することから「ビットマップ」と呼んだのである。
(引用元:Wikipedia

ピクセルを集合として扱う画像をざっくりと「ビットマップ」と広くいっていますが
狭義の技術用語としては「ピクセルマップ」「ピクスマップ(pixmap)」と呼ぶこともあるそうです。

ベクター画像について

ベクター画像が、SVGに値します。
ビットマップ画像がピクセル単位で情報をもつのに対し
ベクター画像は点の座標や線、面や色などの図形を表す数値情報の集合で、図形同士の関係は数学的方程式として表されるため
拡大・拡小での劣化はありません。
単純な図形で構成されている場合は、ビットマップ画像よりもデータサイズは小さくなります。
img_02.jpg

以下の画像は、SVGをズームしたものです。
img_03.jpg

また、vectorという表記ゆえに、ベクトル画像などとも呼ばれます。

SVGを埋め込んでみる

さっそくSVGを埋め込んでみます。

動きをつけない場合

単純にアニメーションをつけずにJPEGやPNGの代わりとして使う場合は、imgタグを使うのが一番簡単です。
もしくは、CSSのbackground-imageプロパティで指定することもできます。

<p>imgタグを使う場合</p><imgsrc="./01.svg"alt="">

img_04.jpg

<head><style>.svg{background:url(./01.svg)no-repeat0/100%;margin:0auto;width:140px;height:55px;}</style></head><body><p>backgroundプロパティを使う場合</p><divclass="svg"></div></body>

img_05.jpg

動きをつける場合

次に、SVGアニメーションをつけたい場合です。(この記事のメインになります!)
CSSアニメーションを使う方法と、JavaScriptを使う方法があります。

まず共通するのは、SVGをエディタで開いてコードをまるっとコピペすることです。
これは後ほどアニメーションさせるときにコードを載せるのでそちらをご覧ください。

① CSSアニメーションを使用する。
まず、お手軽にできるのがCSSアニメーションを使った方法です。
SVGのプロパティは色々あるので、検索してみてください!

コードやアニメーションなどの内容は、以下のコードペンをご覧ください。
https://codepen.io/chelcat3/pen/JjoRgJj

CSSアニメーションを使う方法はかなりお手軽でよいのですが、残念ながら現状IEには対応しておりません。

② JavaScriptでアニメーションさせる。
次はJavaScriptでアニメーションさせる方法です。
JavaScriptでイチから書こうと思うとかなり大変なので、ライブラリを使うことをおすすめします。

今回はvivus.jsを使ってみます。

こちらも、コードやアニメーションなどの内容は、以下のコードペンをご覧ください。
https://codepen.io/chelcat3/pen/eYmBOxB

調べてみると、IE11は対応していると多く見かけましたが、自分の環境だとfill-opacityが上手くアニメーションされていませんでした。

まとめ

まだまだ勉強途中なので、これからもがんばります。


HTML・CSS復習編 clearfixについて

$
0
0

はじめに

現在自分はHTML・CSSモダンコーディングという技術書を使ってHTML・CSSの復習をしております。
この記事はその復習のアウトプットです。

clearfixとは

clearfixというのは、floatプロパティを使用した場合、親要素に高さが反映されずに潰れてしまう現象を改善するための技術です。

clearfixクラスを親要素に追加してあげることで、親要素ないの最後にclearプロパティが適応してあるdiv要素が追加されます。それによりfloatの効果が打ち消されて親要素が潰れる現象を防ぎます。

実際にclearfixクラスを記述してみると....

.clearfix::after{display:block;clear:both;}<divclass='main clearfix'></div>

擬似クラスafterを使うことで、このクラスを追加した要素ないの最後にdiv要素を追加します。

stylusで乱数を生成する方法と、特定の範囲内で値を返す関数

$
0
0

タイトルの通りだけど、なかなか見つけられなかったので、メモ

stylusで乱数を生成

これは簡単で、

  transform translateX(unit(floor(math(0, 'random') * 3), "px"))

例えば上のように書くと、
translateXの値が0~2pxの間で出力される。
ちなみにmath(0, 'random')で0~1未満の数値がランダムで生成されて(JSのMath.random()と同じ)
それに3を掛けたものを stylusのfloor()関数で小数点以下切り捨てている感じ。
最後にunit関数でpxを後ろに繋げている。ちなみに+ 'px'とかやってもエラーになる。

特定の範囲内で値を返す関数の作り方

上が分かれば、こんな物が作れる

random(min, max)
  return floor(math(0, 'random') * (max - min + 1) + min)

これで、3~10の間で乱数を生成したければ、

.test
  $x = unit(random(3,10), 'px')
  transform translateX($x)

とすれば、
コンパイルされて

.testtransform:translateX(4px);

となる。

参考:https://github.com/stylus/stylus/issues/876

初心者によるプログラミング学習ログ 185日目

$
0
0

100日チャレンジの185日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。

100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。

185日目は

SVGで一筆書き

$
0
0

会社のアドベント用です

最近アイコンなどでよくみかけるSVG画像。

ラスターのpng/gifと違い、ベクターなのでどんな拡大縮小にも耐えることができます。Retina対応が当たり前な今どきのWEB製作時に、1つのファイルのみで対応できるので大変ありがたいです。

※写真等色と形が複雑なものは引き続きjpg(最近はWebPという選択肢も)が優秀です。

各要素のお役目をメモっておきました。svgアニメをかけるとき理解しておくと便利&イラレ等編集ソフトを使わずエディター内で画像編集を終わらせることができて結構便利。

軽量化

SVGOMG – SVGO’s Missing GUI 

Svgの時点でよほどのことがない限りpngより軽くなっているとは思うのですが、上記ツールで不要箇所を削除できます。
ちなみに手動でするときはタグに下記4点があればOKだと思っています。

viewBox=
xmlns="http://www.w3.org/2000/svg”
width=
height=

たまにwidth/heightについて案内していないパターンをみかけますがIEで豆粒みたいになるので、もしそんなときはエディターでsvgを開いて確認してみてください。

扱える図形

tag図形のタイプ
pathパス。基本的に一筆書きアニメはこのpathに開始と終了を設定して再生するかんじ。
rect四角
circle
ellipse楕円
line直線
polygon多角形
polyline折れ線
text基本icon等では使わない。これが含まれていたらむしろテキストのアウトラインをしていないので要確認
image基本icon等では使わない。

svg一筆書きアニメーション

色々あるアニメーションのなかでも、svgならでは!の「一筆書きアニメ」のサンプルをつくってみました。簡単なのに動きが大きいので見栄えがする感じでウケが良い気がします。

See the Pen Qiita_1216 by ayayayayayayayayayayayayayayayayayayayayayayayayayayaya (@ayaaaa) on CodePen.

忘年会で力尽きたので

stroke-width 線の太さ
stroke-dashoffset 位置の指定
stroke-dasharray 線の間隔
├●   ●   ● ←離れてる
└●●●●●●●●●●●●● ←とおくからみたら線に見える
//明日詳しく書くかも・・・おやすみなさい

最後に

明日は黙して結果を出す!みんなから頼りにされるプラコレのすごい人その1オッスンさんです!LINEのAPIからaws周りやスクレイピングイロハまで豊富な知識で助けてくれるエンジニアさんです。

プラコレではフロントを触りたいエンジニアも!もっと違うことをやりたいエンジニアも!デザイナーも!募集しています!!
プラコレはプラコレWedding以外にもウェディングのメディアDressyやfarnyなども運営しています。ぜひ見ていってもらえると嬉しいです!

初投稿

$
0
0

ProgateでHTML&CSS学習コース 初級編を修了。

目標はサッカーのサポーター同士のマッチングサービスの制作(一緒に試合に行く友達を見つける)。

Qiitaは勉強記録、備忘録として使っていこうと思います。

Viewing all 8546 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>