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

CSSアニメーションと!importantの優先順位について

$
0
0

!importantが優先される

CSSアニメーションと!importantとでルールが衝突した場合、!importantが優先されます。

The origin of a declaration is based on where it comes from and its importance is whether or not it is declared !important (see below). The precedence of the various origins is, in descending order:
 

  1. Transition declarations [css-transitions-1]
  2. Important user agent declarations
  3. Important user declarations
  4. Important author declarations
  5. Animation declarations [css-animations-1]
  6. Normal author declarations
  7. Normal user declarations
  8. Normal user agent declarations

Declarations from origins earlier in this list win over declarations from later origins.
Origin and Importance | CSS Cascading and Inheritance Level 3

Chromeにはバグがある

優先順位について、Chromeにバグがあるようです。
https://jsfiddle.net/Lhankor_Mhy/xdgsp2ey/

<p>
test
</p><p>
test(!important規則があってもアニメーションする)
</p>
p{animation:test1.2seaseforwards2s;}@keyframestest{0%{color:#f00}100%{color:#000}}p{color:#00f;}p+p{color:#00f!important;}

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

$
0
0

100日チャレンジの180日目

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

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

180日目は

scrollのメモ

$
0
0

HTML

filename.html
<ahref="#"><buttonclass="scroll-top"id="js-button"><iclass="fa fa-chevron-up"aria-hidden="true"></i></button></a>

CSS

filename.css
/*scroll-top*/.scroll-top{position:fixed;right:20px;bottom:20px;z-index:100;background-color:#999;opacity:.8;width:50px;height:50px;border-radius:50%;border:none;color:#fff;}.scroll-top:hover{cursor:pointer;opacity:0.8;}

JavaScript

filename.js
$(function(){// #で始まるリンクをクリックしたら実行されます$('a[href^="#"]').click(function(){// スクロールの速度varspeed=500;// ミリ秒で記述varhref=$(this).attr("href");vartarget=$(href=="#"||href==""?'html':href);varposition=target.offset().top;$('body,html').animate({scrollTop:position},speed,'swing');returnfalse;});});

静的な Web サイトをどうやって速くするか

$
0
0

どうも、源武です。

N高等学校 Advent Calender 2019」の 11 日目を担当します。

今回は、静的な Web サイトの高速化についてのお話です。

静的な Web サイトって、なに?

あらゆるアクセスに対し、一意の内容の Web ページを配信する形式のサイトです。

要は、htmlファイルなどが、そのまま表示される形式です。

ランディングページや、ポートフォリオなどによく見られます。

静的であることのメリット・デメリット

ここまでを踏まえて、静的な Web サイトのメリットを考えましょう。

メリット

  • サーバーへの負担が少ない
  • サーバーやクライアントのことを考える必要がない
  • シンプル

デメリット

  • サーバーサイドでコンテンツを動的に変更できない

内容次第では、デメリットを気にする必要はないと思います。

速度の向上をどのように検討するか

私の Web サイト (genbu.studio) を構築する上で蓄積したノウハウをもとに、いくつかのパターンを列挙します。

1. サーバーを変更する

かなり効果が大きいです。

通信経路が長い(速度が遅い)遠い地域のサーバーや、貧弱なサーバーに設置すると、その分速度が大幅に低下します。

もちろん、キャッシュを使用するようにすれば、いくら遅くても2度目のアクセス以降から速度のロスを改善することができます。

日本をターゲットとするならば、国内・または近隣の国のサーバーで Web サイトをホストする方が、より高速に閲覧できるようになります。

私のサイトでは、アジアやアメリカ、ヨーロッパなどの各国にWebサイトを設置しています。

2. CDN を使用する

世界中の各地域に設置されている、CDN(Content Delivery Network)と呼ばれる高速なサーバーにWebサイトをキャッシュし、そちらからコンテンツを配信する方法があります。

これを用いることにより、速度の向上だけでなく、ホストの通信量の削減など、効率の良いコンテンツの配信が可能になります。

私のサイトでは、CSS や画像を CDN から配信しています。

3. コンテンツを minify する

HTML、CSS、JavaScriptのコードにインデントはありませんか?

コードのインデントなどは、ほとんどの場合では実際の閲覧に影響を及ぼしません。

データ量の削減につながるため、積極的に minify して、不要なインデント・改行を除去しましょう。

規模が大きくなればなるほど、その効果は大きくなります。

私のサイトでは、CSS を minify しています。

4. 独自ドメインを使用する

これは非常に効果が大きいようでした。

独自ドメインを使用した私のサイトと、同じ内容を Netlify 備え付けのサブドメインのサーバに展開した場合の速度を、実際に計測して比較してみました。

独自ドメインサブドメイン
1.07 秒1.48 秒

(BrowserStack, Windows 10, Chrome 78, JPN)

なんと、0.41 秒もの差がありました。

回線が非常に速い自宅で閲覧してみましたが、やはり独自ドメインを用いた方が高速な印象でした。

Netlify をお使いの方は、Netlify DNS を使用すればより効率的に独自ドメインを設定できます。

5. 阿部寛になる

イケメンになりたかった

まとめ

Web サイトの高速化は、UXをより良いものにする上でも非常に重要です。

特に静的サイトは高速化の余地がたくさんあるので、積極的に行なっていきましょう。

では、良い Web ライフを。

大人数の時使うオーバーレイのカスタムCSS

$
0
0

大人数の時使うオーバーレイのカスタムCSS

大規模コラボ用のカスタムCSSを作ってみました
手っ取り早くほしい人は一番下の方の使い方まで飛んでくださいね

元のCSSがあるのでURL貼りますね
順風満帆丸さんの萬巓堂本店の
「DISCORDで喋ってる人を分かり易くするカスタムCSS」http://manten-do.net/contents/dsk01
をヒントに作りました

Discord STREAMKIT Overlayを使ってカスタムCSSで変更を加えているんですね
なのでCSSを変えれば色々変えれるんです

実は 100人記念コラボしたときに自分含めて5人でお話ししたときに思ったのは
「IDと立ち絵登録面倒・・・」
凸に入る時に
「まって、今登録してるから」っていって待ってもらわなきゃいけなかったのでテンポが悪かったです
オーバレイの内容をCSSで変えて見やすくできる他の方法がないか考えてみます

まず目標を定めます

1 上のカスタムCSSみたいに光ってぴょこぴょこはそのまま
2 ID登録とかちょっと今回はやらないようにする
3 いつもの小さい縦じゃなくて大きく横に並べるようにする
4 名前もできれば表示

1ぴょこぴょこはそのまま

ぴょこぴょことかは前回買い換えたものがあるのでそれをそのままコピペします
どういう動きをするかというと
 しゃべると光る(正確には暗かったものが普通になる)しゃべると5秒間ぴょこぴょこ動く

2ID登録とか今回はやらない

今回は個人を特定することはしないのでユーザーID(#と数字のタグとは別)と画像を登録しないのでごっそり省きます

3大きく横に並べる

今回はアイコンを200×200で表示しますのでavatarの縦と横を変えます
線とか明るさは変えないです
そして!ここ重要!
リストの li.voice-state のCSSに display:inline-block; を追加します
これは簡単に言うと 「リストを縦から横に変える」 ってことです
やったよ・・・これで横に並ぶよ・・・

4名前を表示する

普通に表示させるとなんかごちゃごちゃしちゃいます
もう、大変だった
ずっとコレジャナイあれじゃないトライ&エラーです
分かったと思ったら違って違うと思ったら近づいて・・・目がしょぼしょぼするよ
面倒ですので結論と専門的なのは簡潔にだけ

できました

display: inline-block;で横に並べて
position: relativeで移動させて・・・出来た

つまり何がいいたいかというと
一日かけて恐ろしく大変で疲れた!!!!
その結晶をここに乗せます
もし名前を消したいときは
span.nameの{}の中身を
{display:none;}
に変えてくださいね
none;で消すって意味です

使い方

1 DiscordSTREAMKITOverlayのURLをOBSのブラウザに入れる
2 ここの下のカスタムCSSをOBSのブラウザのカスタムCSS の欄にコピー&ペーストで入れる
3 ブラウザの大きさは縦を200に設定する。横はどこまでもOK
4 参加者はアイコンを透過されているバストアップの立ち絵に変えてもらう
5 通話参加者は名前付きでぴょこぴょこできるよ!

というわけで完成したカスタムCSSです
大人数の雑談コラボや 大規模コラボゲーム配信などでどうぞ

/*アバターの大きさと明るさを決めるよ あと線を消してるよ*/
.avatar {
   height:200px !important;
  width:200px !important;
  border-radius:0 !important;
  filter: brightness(70%);
}

/*しゃべったときの状態の設定してるよ*/
.speaking {
  border-color:rgba(0,0,0,0) !important;
  position:relative;
  animation-name: speak-now;
  animation-duration: 5000ms;
  animation-fill-mode:forwards;
  filter: brightness(100%) ;
}
/*しゃべってるときのアニメーションの設定だよ*/
@keyframes speak-now {
  0% { bottom:0px; }
  5% { bottom:10px; }
  10% { bottom:0px; }
  15% { bottom:10px; }
  20% { bottom:0px; }
  25% { bottom:10px; }
  30% { bottom:0px; }
  35% { bottom:10px; }
  40% { bottom:0px; }
  45% { bottom:10px; }
  50% { bottom:0px; }
  55% { bottom:10px; }
  60% { bottom:0px; }
  65% { bottom:10px; }
  70% { bottom:0px; }
  75% { bottom:10px; }
  80% { bottom:0px; }
  85% { bottom:10px; }
  90% { bottom:0px; }
  95% { bottom:10px; }
  100% { bottom:0px; }
}

/* アバター及びネームタグ表示位置調整だよ*/
li.voice-state{
 position: static;
 display: inline-block;
 width: 200px;
 height: 200px;
}
.user{
 display: inline-block;
}
span.name{
 display: inline-block;
 position: relative;
 bottom: 70px;
}

/* 色々消すヤツ */
body { background-color: rgba(0, 0, 0, 0); overflow: hidden; }

/******カスタムCSS終わり******/```

立方体を描画してみよう!CSSだけで3D表現【フロントエンド】

$
0
0

はじめに

今回は,WebGL等を使わないで,単純な3DオブジェクトをCSSだけで描画する方法についてご紹介しようと思います.

CSSでの3D表現の特徴

確かに複雑なオブジェクトを作るのであれば,WebGLを使うのが好ましいでしょう.
しかし,CSSでやる良さもあるので,主なメリットとデメリットを以下に記述します.

  • CSSで3Dをやるメリット

    • 直感的に作れる.
    • イベントなどを設定しやすい.
    • 見た目をCSSで簡単に変更できる.
  • CSSで3Dをやるデメリット

    • 複雑な立体は作るのが大変.
    • 曲線のある立体は作るのが難しい.
    • 複雑なものを作ると時間が溶ける.

立方体のサンプル

See the Pen 3D Box by AO2324 (@AO2324) on CodePen.

円柱のサンプル

See the Pen 3D Cylinder by AO2324 (@AO2324) on CodePen.

イベントをつけた立体のサンプル

See the Pen 3D Box has event by AO2324 (@AO2324) on CodePen.

使用するCSSプロパティ

  • transform-style
  • transform
  • perspective

transform-style (描画形式)

子要素の変形を立体的に描画するか平面的に描画するかを指定します.

平面的(初期設定)
transform-style:flat;
立体的
transform-style:preserve-3d;

See the Pen 3D Box (Flat / 3d ) by AO2324 (@AO2324) on CodePen.

transform (要素の移動や回転)

transformプロパティを使用することで,要素を平行移動や回転,拡大など,変形させることができます.

指定方法は,transformの後にどう変形させるかを続けて記述します.
transform: 変形1 変形2;
変形の書き方
変形の種類+基本とする軸(値)

  • 変形の種類
    • translate : 平行移動
    • rotate : 回転
    • scale : 大きさ
    • skew : 傾斜
  • 基本とする軸
    • X : x軸
    • Y : y軸
    • Z : z軸
    • なし : ()の中にx軸とy軸をカンマ(,)で区切って表記
    • 3d : ()の中にx軸とy軸とz軸をカンマ(,)で区切って表記
サンプル
transform:translateX(50px)translateY(100px)rotateZ(90deg);/* x軸方向に50px,y軸方向に100px平行移動したのち,z軸を中心に90度回転 */transform:translate(50vw,50vh);/* x軸方向に50vw,y軸方向に50vh平行移動 */

perspective (遠近感)

3Dのオブジェクトに遠近感をもたせます.
数値が小さいほど魚眼レンズのようになり,大きければ大きいほど全ての幅が均等になります.
スクリーンショット 2019-12-10 23.35.30.png
perspective:200px;
スクリーンショット 2019-12-10 23.20.10.png
perspective:10000px;
数値が小さい(200px)と上のような見え方に,数値が大きい(10000px)と下のような見え方になります.
個人的には 800px ~ 1000px くらいがちょうどいい気がします.
スクリーンショット 2019-12-10 23.20.28.png
perspective:1000px;

実際に立方体を作ってみよう!

Step1

まず,HTMLで立体の面となる要素を定義してあげます.

html
<divid="scene"><divclass="boxBase"><divclass="top"></div><divclass="bottom"></div><divclass="front"></div><divclass="back"></div><divclass="left"></div><divclass="right"></div></div></div>

ここで,sceneは3Dオブジェクトを表示する場所,boxBaseは立方体,その中の要素は立方体のそれぞれの面を表しています.
これをCSSのtransformを使って変形することで,ペーパークラフトのように立体を作ることができます.

Step2

何も指定しないと平面的に描画してしまうので,transform-style:preserve-3d;をboxBaseに設定して,子要素を立体的に表示するようにしてあげましょう.
ここで,一辺の長さも決めてしまいましょう.

css(1/4)
.boxBase{transform-style:preserve-3d;width:200px;height:200px;}

Step3

立方体の面となる要素の設定を行い,ついでに色や縁にもスタイルを適応させましょう.

css(2/4)
.boxBase>div{position:absolute;width:100%;height:100%;background-color:rgba(100,100,100,0.7);/* 面の色 */border:0.5pxsolidblack;/* 縁 */}

Step4

それぞれの面を変形して,立方体を組み立てていきましょう.

css(3/4)
.boxBase>.top{transform:translateY(100px)rotateX(-90deg);}.boxBase>.bottom{transform:translateY(-100px)rotateX(90deg);}.boxBase>.front{transform:translateZ(100px);}.boxBase>.back{transform:translateZ(-100px)rotateX(180deg);}.boxBase>.left{transform:translateX(-100px)rotateY(-90deg);}.boxBase>.right{transform:translateX(100px)rotateY(90deg);}

Step5

perspectiveで遠近感を調整しましょう.

css(4/4)
#scene{perspective:1000px;}

立方体の完成

立方体
<divid="scene"><divclass="boxBase"><divclass="top"></div><divclass="bottom"></div><divclass="front"></div><divclass="back"></div><divclass="left"></div><divclass="right"></div></div></div><style>#scene{perspective:1000px;}.boxBase{transform-style:preserve-3d;width:200px;height:200px;}.boxBase>div{position:absolute;background-color:rgba(100,100,100,0.7);border:0.5pxsolidblack;width:100%;height:100%;}.boxBase>.top{transform:translateY(100px)rotateX(-90deg);}.boxBase>.bottom{transform:translateY(-100px)rotateX(90deg);}.boxBase>.front{transform:translateZ(100px);}.boxBase>.back{transform:translateZ(-100px)rotateX(180deg);}.boxBase>.left{transform:translateX(-100px)rotateY(-90deg);}.boxBase>.right{transform:translateX(100px)rotateY(90deg);}</style>

アニメーションをつけてあげると立体になっている事がよくわかるので,ぜひ試してみてください.

立方体のアニメーション
#scene{perspective:1000px;width:200px;height:200px;position:relative;top:50vh;left:50vw;transform:translateX(-50%)translateY(-50%);}.boxBase{transform-style:preserve-3d;width:200px;height:200px;animation:turnAround30slinear0sinfinitenormalnonerunning;}@keyframesturnAround{0%{transform:rotateX(0deg)rotateY(0deg);}100%{transform:rotateX(360deg)rotateY(360deg);}}

See the Pen 3D Box by AO2324 (@AO2324) on CodePen.

最後に

今回はCSSでの3D表現のざっくりとした説明と,簡単な立体の作り方を書かせていただきました.
これだけだと,実際のWebページやアプリ開発でどのように活かせるかわからないかもしれません.
ですが,発想次第ではいろいろな事ができるので,ぜひCSSでもっともっと遊んでみてください.
スクリーンショット 2019-12-11 1.34.08.png
↑人型の立体物 ↓Webサイトのナビゲーション
スクリーンショット 2019-12-11 1.32.15.png

参考

CSS3D!まるでWebGLのような3Dを超気軽に実装するCSS技 (中矢 雄介/Creator)

縦にたたむtransitionを頑張って再利用しやすい形で作る

$
0
0

tl;dr

これを作る
Dec-09-2019 04-21-00.gif

  • UIアニメーションはユーザに対して付属的な情報を効果的に伝えることができるので、ビシバシ使っていきたい
  • 特に頑張ってプレーンなCSSでアプリケーションを作っている人は、transitionを作るのが結構辛かったりする
  • transitionを気合い入れて再利用しやすい形で作ると、使うときの心理的障壁を下げることができて、アプリケーションの品質向上に寄与する
  • アニメーションによってはDOMのラッパーが必要な場合とかもあるけど、最悪VNode触ることでシンプルな使い勝手を確保できる

前提

この記事では縦に折りたたむtransitionをプレーンなCSS(ここではCSSプリプロセッサを使うかどうかは問わず、CSSフレームワークやUIライブラリをベースに構築しない方針を指します)で実装することを通して、再利用しやすいtransitionコンポーネントの作り方について述べていきます。

toB向け管理画面やMVPを作る場合はCSSフレームワークやUIライブラリを主軸にして、CSSをなるべく自分で書かないでアプリケーションを作っていく選択肢もあります。その場合はよく使われるtransitionはあらかじめ用意されていたり、コンポーネントに実装されていたりすると思います。
しかし、

  • アプリケーションのあしらいやビジュアル・ブランディングについての軸がある
  • 少しリッチめなユーザインタラクションを作っている・作る予定がある
  • 細かいインタラクションを調整する必要がある
  • 特定のCSSライブラリにフル依存することで学習コストが増す

などプレーンCSSでイチから作る選択肢を選ぶ状況などもあり、その場合は自らの手でtransitionを実装していく必要があります。
とはいえ、プレーンCSSではない技術スタックであっても、必要に応じて独自でtransitionを用意することはありえますし、再利用しやすいtransitionコンポーネントをきちんと作れることはどの環境でも有用なのではないかと思います。

UIアニメーションを実装することで提供できる価値

Apple Human Interface Guidelines先輩は

Beautiful, subtle animation throughout iOS builds a visual sense of connection between people and content onscreen. When used appropriately, animation can convey status, provide feedback, enhance the sense of direct manipulation, and help users visualize the results of their actions.

Animation - Visual Design - iOS - Human Interface Guidelines - Apple Developer

(意訳)

iOSのすみずみにある美しく控えめなアニメーションは画面上のコンテンツとユーザの間に視覚的な繋がりを構築するんやで。適切にアニメーションを使うと 状態を伝えたりフィードバックを与えたりインタラクティブUIの操作感覚を強化したりユーザインタラクションの結果を伝えるための補助ができるんやなあ。

とおっしゃっていますし、Material Design先輩はMotionのUsageに関する項

  • コンテンツの階層関係の説明
  • ユーザ操作のフィードバックやユーザ操作に対する状態の表示
  • 操作方法を示し、ユーザの学習を助ける

など、使いみちについてかなり具体的に例示しています。

実際、小難しい話をこねくり回す必要もなく、UIアニメーションを利用することで様々な情報を付加してユーザがアプリケーションを利用する手助けになることはとても理解しやすい話です。適切なUIアニメーションが適切に情報を画面表示することができれば、 ユーザの離脱を防いだり、アプリケーションに対するポジティブな感情を醸成して継続率を上げたりと、密かに数字につながっていたりということもあると思います。

それ以外にも特定のイージングやdurationを頻繁に利用することでアプリケーションのVisual Identityを構築する手助けをできれば、認知を強めたり、競合アプリケーションとの差別化を行う一助になるかもしれません。

UIアニメーションを実装するのは辛いし面倒くさい

とはいえUI実装者の皆様は思うところがあるのではないかと存じますが、UIアニメーションを作り込むのは非常に気だるい作業になることが多いと思います。

CSS TransitionやCSS Animationを利用して実装する際は、この書き方で動くだろう!と思って動かしたら、アニメーションが効いてなかったり、行きの動作はするが戻りの動作がしなかったり(なんか思たんとちゃう…)といった気持ちになる経験はありませんか?
そういったツラミな状態になったとしても、CSSではデバッグをするのも難しく、一度悩み始めると無限に時間が溶ける原因になりがちです。(console.logするとなぜか動くのに、消すと動かなくなったりすると終業意欲がMAXになったりしますよね)

また、せっかくUIアニメーションをコンポーネント志向にパッケージングしたのに、使う際に特定のスタイルを付与する必要があったり、逆に特定のスタイルをつけてはならない、などの使い方の制約があると、忘れたり知らなかったりしたときにドツボにハマりがちです。

UIアニメーションを実装するときは再利用性が重要

こうしてUIアニメーション実装に対する心理的障壁が高くなると、

  • 部分的にUIからアニメーションが消失していく
  • 機能実装の見積もりや実績が膨らんでしまい、逆にアプリケーションのイテレーション計画を阻害する

など、UIアニメーションを実装するメリットを打ち消して有り余るデメリットが発生します。

UIアニメーションを実装する際に(おもたんとちゃう…)という気持ちになる状況は開発ツール側の進化がないと当分解決出来ないと思うので(いい方法あるよという方コメントで教えて下さい!)一旦あきらめるとして、いざ作ったアニメーションを再度使うときに(使い方わからん…)となるのは時間の無駄ですし、逆に再利用しやすいUIアニメーションのコンポーネントを用意できれば、効率的に各所でUIアニメーションを実装できますし、効率よく価値を提供することに繋がります。

なので、UIアニメーションを実装するときは実装されたアニメーションがイケてるかだけではなく、再利用するときに悩まずに使えるか・想像した通りに使えるかも重視すべきです。
Vueでは公式に <transition><transition-group>が提供されており、これらを使ってうまくパッケージングすることでこれを実現することができます。
次の項から具体的にtransitionコンポーネントを作っていきます。

本題

よくペラペラ説教をするおじさんみたいなことばかり喋ってしまってごめんなさい。作ります。

縦にたたむとはどういうことか

そもそもcss transitionで縦にたたむ動作をするためには、たたむ対象となるコンポーネントのstyleに

.target{overflow:hidden;transition:height;}

を当てることでできそうだというのはすぐに思い浮かぶと思います1

しかし、たたむ対象のコンポーネントのoverflowを直接いじると、overflowを自身で設定しているコンポーネントで表示は壊れることが容易に想像できますし、heightをいじるとなれば、レイアウトを高さに対する相対指定で行っていたり、縦flex・gridを利用している場合に崩れるでしょう。

transitionを作ることに関しては、なるべく使うときに考えたり悩んだりしないで使えるものを作るという上記の指針があるので、今回は下図のように overflow: hidden; transition: height;だけの表示領域を区切るためだけのラッパーを用意し、縦にたたむスタイルを実装していきます。

IMG_5D9143A31CDA-1.jpeg

どういうものを作るか

Vueのtransitionはslotの中に入るコンポーネントが v-if or v-showが切り替わったときにtransitionが発生するというインターフェースになっています。今回作るtransitionもこのインターフェースに沿った形で、

<template><vertical-collapse-transition><pv-if="show">lorem ipsum...</p></vertical-collapse-transition></template><!-- OR --><template><vertical-collapse-transition><pv-show="show">lorem ipsum...</p></vertical-collapse-transition></template>

上記のような使い方ができるものを作ります。

内部的なラッパーをどうするか

疑似コードですが、

<template><transitionname="vertical-collapse"><divstyle="overflow: hidden; transition: height;"><slot/></div></transition></template>

のようなtransitionコンポーネントを作ってしまうと、 slotv-ifv-showを変更しても、 transitionから見て子コンポーネントの表示が変わっていないように見えるので、transitionが発火しません。

こういうときどうすればよいかというと、VNodeを直接触って slotの表示状態をラッパーにプロキシしてあげればよいです。

実際に表示状態で取りうるパターンとしては4種類あり、

  1. v-if="true"
  2. v-if="false"
  3. v-show="true"
  4. v-show="false"

のどれかになります。
これらの場合にどういったVNodeがslotsに入ってくるかの明確な仕様はドキュメントに特に明記されてなかったので、ぼんやりしたイメージはありましたが実際に console.logして中身を見ていきました。

1. v-if="true"

v-ifはそもそもVNodeを仮想DOMツリーに入れるか入れないかを制御するディレクティブなので、 v-if="true"の場合はシンプルにslotのVNodeが入ってきます。

{render(h){constchild=this.$slots.default&&this.$slots.default[0];console.log(child);// VNode { tag: "div", ...}}}

2. v-if="false"

一を聞いて十を知る皆さんであれば、仮想DOMツリーに入れないので undefinedとか null的なやつが来るのではないかと想像がついていると思います。正解です。

{render(h){constchild=this.$slots.default&&this.$slots.default[0];console.log(child);// undefined}}

ですが、template compiler次第なのか、下記のようなパターンもありました。2

{render(h){constchild=this.$slots.default&&this.$slots.default[0];console.log(child);// VNode { tag: undefined, ...}}}

3. v-show="true"

ディレクティブに関する情報 vnode.data.directivesに入ってきます。

{render(h){constchild=this.$slots.default&&this.$slots.default[0];console.log(child);// VNode { tag: "div", data: { directives: [{//         name: 'show',//         value: true,//         ...//       }] }, ...}}}

4. v-show="false"

もちろん3と同じ構造で、valueが falseになっているだけです。

{render(h){constchild=this.$slots.default&&this.$slots.default[0];console.log(child);// VNode { tag: "div", data: { directives: [{//         name: 'show',//         value: false,//         ...//       }] }, ...}}}

条件分岐

以上のVNodeの状態をそれぞれ区別して条件分岐しつつ、適切なラッパーを返してあげるとなると、下記のような感じになります。

{render(h){// transitionはもともとdefault slotの一個目の要素しか見ないインターフェースなので、ほかは捨ててよいconstchild=this.$slots.default&&this.$slots.default[0];// TODO: あとで中を実装するconstgenerateTransitionComponent=(children)=>h('transition',{},children);constisEmpty=!child||child.tag===undefined;if(isEmpty){// --------------// v-if="false"// --------------returngenerateTransitionComponent([]);// transitionの中を空で返す}constvShowDirective=child.data.directives&&child.data.directives.find(directive=>directive.name==='show');constisHidden=vShowDirective&&!vShowDirective.value;if(isHidden){// --------------// v-show="false"// --------------child.data.directives=[// 子のv-showディレクティブを消す...child.data.directives.filter(directive=>directive.name!=='show'),];returngenerateTransitionComponent([h('div',// h('div') でラッパーを作る{// ラッパーにv-showディレクティブを移植するdirectives:[{name:'show',value:false,},],},[child]),]);}if(vShowDirective&&!isHidden){// --------------// v-show="true"// --------------returngenerateTransitionComponent([h('div',{// ラッパーにv-show="true"をプロキシするdirectives:[{name:'show',value:true,},],},[child]),]);}// --------------// v-if="true"// --------------// 特に何もせず、ラッパーに子をそのまま入れて返すreturngenerateTransitionComponent([h('div',{},[child])]);}}

縦にたたむ

jsを使ってtransitionを作る場合は、cssのトランジションクラス(v-enterなどのスタイルを定義するやり方)を利用するよりも、jsフックを使ったほうが圧倒的に自由度が高くておすすめです。とくに、使いやすいtransitionを作るには timing functiondurationもpropsで渡せたほうが汎用的でベターですが、その場合はjsフックだとなおさら書きやすいです。

transitionおよびjsフックの詳しい仕様はドキュメントを参照してください。
(宣伝:Vue.jsのドキュメントは有志のメンバーがとてもマメに翻訳を更新しているのでぜひ公式ドキュメントを読んでください!)
https://jp.vuejs.org/v2/guide/transitions.html

方針としては

  • 非表示 → 表示
    • beforeEnterフックでラッパーのheight0にする
    • enterフックでラッパーのheightを子のscrollHeightにする
  • 表示 → 非表示
    • beforeLeaveフックでラッパーのheightを子のscrollHeightにする
    • enterフックでラッパーのheight0にする

というシンプルなものです。

完成形

実際に上記を実装するとこんな感じになるかと思います。

components/transitions/VerticalCollapseTransition.vue
<script>importVue,{VNode}from'vue';exportdefault{props:{duration:{type:Number,default:100,},easing:{type:String,default:'ease-out',},},render(h){// transitionはもともとdefault slotの一個目の要素しか見ないインターフェースなので、ほかは捨ててよいconstchild=this.$slots.default&&this.$slots.default[0];// transition要素を作る関数constgenerateTransitionComponent=childrenOfTransition=>h('transition',{on:{beforeEnter(el){el.style.height='0';},enter(el){el.style.height=`${el.scrollHeight}px`;},beforeLeave(el){el.style.height=`${el.scrollHeight}px`;},leave(el){// このif文がないと初回の非表示にtransitionがかからない(謎)// タイミング問題だと思われるif(el.scrollHeight!==0){el.style.height='0';}},},},childrenOfTransition);constwrapperData={// ラッパーに常時ついているスタイルstyle:{overflow:'hidden',transition:`height ${this.easing}${this.duration}ms`,},};constisEmpty=!child||child.tag===undefined;if(isEmpty){// --------------// v-if="false"// --------------returngenerateTransitionComponent([]);// transitionの中を空で返す}constvShowDirective=child.data.directives&&child.data.directives.find(directive=>directive.name==='show');constisHidden=vShowDirective&&!vShowDirective.value;if(isHidden){// --------------// v-show="false"// --------------child.data.directives=[// 子のv-showディレクティブを消す...child.data.directives.filter(directive=>directive.name!=='show'),];returngenerateTransitionComponent([h('div',// h('div') でラッパーを作る{...wrapperData,// ラッパーにv-showディレクティブを移植するdirectives:[{name:'show',value:false,},],},[child]),]);}if(vShowDirective&&!isHidden){// --------------// v-show="true"// --------------returngenerateTransitionComponent([h('div',{...wrapperData,// ラッパーにv-show="true"をプロキシするdirectives:[{name:'show',value:true,},],},[child]),]);}// --------------// v-if="true"// --------------// 特に何もせず、ラッパーに子をそのまま入れて返すreturngenerateTransitionComponent([h('div',wrapperData,[child])]);},};</script>

エッジケースの処理はまだ足りていないかもしれませんが、自分のプロジェクトではこの実装が活躍してくれています。

まとめ

今回は弊リポジトリの中でも一番複雑なトランジションコンポーネントを開陳してみました。他にもフェードしたりスライドしたりと多種多様なコンポーネントがありますが、どれももっとシンプルな実装になっています。

皆様もトランジションコンポーネントを美しく共通化して、きれいなソースコードのまま、素敵なアニメーションのアプリケーションを実装していけることをお祈り申し上げます。

ぼやき

transition-groupのコンポーネント化はマジでつらい


  1. css単体ではなくてjsのパワーを存分に使えるので max-heightをtransitionさせるハックなどはする必要がない。最高。 

  2. vueのソースを少し追ってみたのですが、v-ifをどこでハンドリングしているかわからなかったので、有識者の方いらっしゃったらコメントで教えて下さい!  

CSSでパタパタ動くランキングボードを作った

$
0
0

はじめに

12月になると流行語大賞をはじめとする、様々な〇〇大賞が発表されます。
今回は、そんなときに使われるランキングボードをCSSで作りました。

CSSで作ったらこうなった

オレンジの「PUSH ME」ボタンを押すとランキングがパタパタと動き出します。
今年の流行語大賞をランキングボードに出してみます。

See the Pen CSS Ranking Board by saka212 (@saka212) on CodePen.

ソース

カスタマイズに必要な部分のHTMLとCSSを載せます。
全体はちょっと長いのでcodepenで。
https://codepen.io/saka212/pen/PowZppx

ランキングが動き出す仕組み

css
input[type="checkbox"]{display:none;}label{display:inline-block;box-sizing:border-box;width:100px;height:100px;margin-right:5px;line-height:100px;background:#ffa500;border:solid1px#ffa500;border-radius:4px;border-bottom:4pxsolid#d37800;text-shadow:1px1px1pxrgba(0,0,0,0.2);color:#fff;text-align:center;font-weight:bold;vertical-align:top;cursor:pointer;}.btn:checked+label+.frame.flip:nth-of-type(1){/* animation: keyframe名 周期 開始タイミング 回数 アニメショーン終了後のスタイル指定 */animation:frame-rotatevar(--duration)calc(var(--delay)*0)var(--count)forwards;}.btn:checked+label+.frame.flip:nth-of-type(2){animation:frame-rotatevar(--duration)calc(var(--delay)*1)var(--count)forwards;}.btn:checked+label+.frame.flip:nth-of-type(3){animation:frame-rotatevar(--duration)calc(var(--delay)*2)var(--count)forwards;}.btn:checked+label+.frame.flip:last-child{display:none;}

チェックボックスを隠し、ボタン化したラベルを押すことで、ランキングがパタパタと動きます。
動きの流れを簡単に説明すると...

1.チェックボックスとラベルタグを関連付ける。
  ↓
2.関連付けることで、ボタン化したラベルをクリックするとチェックが入る。
  ↓
3.チェックされたときに適用されるCSSに、animationプロパティを書いておくことでアニメが動く。

表示したいパタパタの数の合わせて、
.btn:checked + label + .frame .flip:nth-of-type(1) {...}
の部分は増やしておきます。animationプロパティの値は、CSSカスタムプロパティで設定すると便利です。

ランキング内容を変えて遊びたいとき

html
<divclass="row">  <inputtype="radio"id="btn1"class="btn">  <labelfor="btn1">PUSH ME</label>  <divclass="frame">    <divclass="flip"data-txt="ONE TEAM"data-txt-next="令和"></div>    <divclass="flip"data-txt="令和"data-txt-next="タピる"></div>    <!-- ↓↓ この 「data-txt-next」にパタパタが止まった止まったときに表示したいテキスト -->    <divclass="flip"data-txt="タピる"data-txt-next="ONE TEAM"></div>    <!-- ↓↓ この「data-txt」にボタンを押す前に表示するテキスト -->    <divclass="flip open"data-txt="大賞"></div>  </div></div>

ランキングひとつ分のhtmlです。
3つのパタパタを表示しているので、このhtml部分を3つ用意しています。
ボタンを押す前に表示するテキストを、
<div class="flip open" data-txt="..."></div>data-txt="..."
に書きます。
パタパタが止まったときに表示したいテキストは、そのひとつ上のdivのdata-txt="..."に書きます。

感想

rotateを使ったアニメーションはパズルですね。
よくよく整理しながら進めていかないと、何を作っているかわからなくなります。
リアリティーを出せるように、いろいろなやり方をためしているうちに、自分の頭の中もパタパタしてきたので「それっぽさ」が出る方向に転換しました。
もっとシンプルな作り方もあるかもしれません。

おわりに

「2019年ティーンが選ぶトレンドランキング」は、ほとんどわかりませんでした...。


レスポンシブルサイト作成の手順と注意点

$
0
0

はじめに

ここ最近にPCとモバイルのどちらにも対応できるレスポンシブルサイトについて学んだので、それについてここで復習がてらにアウトプットしようと思います。

レスポンシブルサイトについて

レスポンシブルサイトとは、上で軽く触れた様にPC対応とモバイル対応の両方ができるサイトです。
具体的に言うと、メディアクエリを使って各端末ごとのCSSを記載していき、CSSを切り替えることで様々な端末絵の対応が可能になります。
しかし、レスポンシブルサイトにはメリットもありデメリットもあります。それを軽くまとめます。

メリット
PC対応とモバイル対応が両方できるため、サイトが一つだけですみ管理が簡単。

デメリット
HTML構造を同じにしないといけないため、レイアウトに制限あり。
必要な技術力が専用サイトよりも高い。

作成の流れ

1、スマホ版のレイアウトとPC版レイアウトのデザイン案を作成する。
この際に、レイアウトを先に考えるのではなく、必要コンテンツを先に出して、それを元にレイアウトを考える。

2、デザインにセクションやHTML要素を割り当てていきHTML構造を作成する。

3、各要素にクラス名などのセレクタ名をつけていく。
この際、セレクタ名は複数開発に仕様が出ない様に一目でわかるものにする。

4、CSSを割り当てていく。

親要素の背景透過を子要素に適用したくない

$
0
0

備忘.

親要素のstyleで'opacity: 0.8;'とかを設定すると,その子要素にも自動的に同じだけの透過が効いてしまうので

opacityではなく'rgba(r, g, b, 0.8)'のようなを設定してやるとよい

aタグをクリック後にボタンのカラーを変更しない処理

$
0
0

いきなり結論。

// aタグのtwitter-share-buttonクラスが付与されているタグ
a.twitter-share-button:visited {
    background-color: #6dc8fc !important;
}

「important!」 はあってもなくても問題ないと思います。
私の環境では必要だったので上記では記述されています。

画像をドラッグ&ドロップで登録してプレビュー表示

$
0
0

備忘録

input:fileの画像登録時にドラッグ&ドッロップでinputに値を渡し、プレビューを出す記述。
(通常登録も対応)

JSライブラリ依存せずに使用(してるはず)。

CODEPEN

See the Pen 画像をドラッグ&ドロップで登録してプレビュー表示 by manabu tanaka (@tonkatsu) on CodePen.

記述

html ドラッグ&ドロップするエリア
<formmethod="post"enctype="multipart/form-data"><divid="dragDropArea"><divclass="drag-drop-inside"><pclass="drag-drop-info">ここにファイルをドロップ</p><p>または</p><pclass="drag-drop-buttons"><inputid="fileInput"type="file"accept="image/*"value="ファイルを選択"name="photo"onChange="photoPreview(event)"></p><divid="previewArea"></div></div></div><buttontype="submit"name="submit"value="登録">登録<button></form>
css 簡単に整形
#dragDropArea{background-color:#f4f4f4;margin:10px;padding:10px;border:#ddddashed5px;min-height:200px;text-align:center;}#dragDropAreap{color:#999;font-weight:bold;font-size:14px;font-size:1.4em;}#dragDropArea.drag-drop-buttons{margin-top:20px;font-size:12px;font-size:1.2em;}.drag-drop-buttonsinput{margin:auto;}
JS ドラドロイベントでごにょごにょ&選択時にごにょごにょ
varfileArea=document.getElementById('dragDropArea');varfileInput=document.getElementById('fileInput');fileArea.addEventListener('dragover',function(evt){evt.preventDefault();fileArea.classList.add('dragover');});fileArea.addEventListener('dragleave',function(evt){evt.preventDefault();fileArea.classList.remove('dragover');});fileArea.addEventListener('drop',function(evt){evt.preventDefault();fileArea.classList.remove('dragenter');varfiles=evt.dataTransfer.files;console.log("DRAG & DROP");console.table(files);fileInput.files=files;photoPreview('onChenge',files[0]);});functionphotoPreview(event,f=null){varfile=f;if(file===null){file=event.target.files[0];}varreader=newFileReader();varpreview=document.getElementById("previewArea");varpreviewImage=document.getElementById("previewImage");if(previewImage!=null){preview.removeChild(previewImage);}reader.onload=function(event){varimg=document.createElement("img");img.setAttribute("src",reader.result);img.setAttribute("id","previewImage");preview.appendChild(img);};reader.readAsDataURL(file);}

参考サイト

https://www.kabanoki.net/1552/
http://koheik.hatenablog.com/entry/2016/07/08/152936
https://qiita.com/sanapon1020/items/77d37fe1fd6f87740e1b

電車の発車案内板をHTMLとCSSだけで作ってみる~試行錯誤編その2~

$
0
0

この記事は
電車の発車案内板をHTMLとCSSだけで作ってみる~試行錯誤編その2~
の継続記事です。

前回の反省点

・ドットの精度が低かった
・もう少しドットだけでフォントを生成する手法を勉強したほうがいい

ということでした。

ドットの研究

dot.jpg

Photoshopで、700%まで拡大し、見てみると、画像のふちに原色より薄い領域があることに気が付きました。

やってみる

今回は「品」だけで。

CSS(Scss)

style2.scss
$pixel:2px;$pixel2:2px;.guide{width:400px;height:50px;background:#000;&-inner{width:280px;height:40px;display:flex;}&-text-char1{width:40px;height:40px;display:grid;grid-template-columns:1frrepeat(16,$pixel)1fr;grid-template-rows:repeat(25,$pixel2);grid-template-areas:"... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...""... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...""... ... ... w01 g01 g01 g01 g01 g01 g01 g01 g01 g01 w02 w02 ... ... ...""... ... ... w03 g02 g02 w04 w04 w04 w04 w04 g03 g03 w05 w05 ... ... ...""... ... ... w06 g04 g04 w07 ... ... ... ... w08 g05 g05 w09 ... ... ...""... ... ... w10 g06 g06 w11 ... ... ... ... w12 g07 g07 w13 ... ... ...""... ... ... w14 g08 g08 w15 ... ... ... ... w16 g09 g09 w17 ... ... ...""... ... ... w18 g10 g10 w19 ... ... ... ... w20 g11 g11 w21 ... ... ...""... ... ... w22 g12 g12 w24 ... ... ... ... w25 g13 g13 w26 ... ... ...""... ... ... w27 g14 g14 g14 g14 g14 g14 g14 g14 g14 g14 w28 ... ... ...""... ... ... w29 g15 g15 g15 g15 g15 g15 g15 g15 g15 g15 w30 ... ... ...""... ... ... ... w31 w31 w31 w31 w31 w31 w31 w31 w31 w31 ... ... ... ...""... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...""... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...""... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...""... ... w32 g16 g16 g16 g16 ... ... ... w33 g17 g17 g17 g17 w34 ... ...""... ... w35 g18 w36 w36 g19 ... ... ... w37 g20 w38 w38 g21 w39 ... ...""... ... w40 g22 ... ... g23 ... ... ... w41 g24 ... ... g25 w42 ... ...""... ... w43 g26 ... ... g27 ... ... ... w44 g28 ... ... g29 w45 ... ...""... ... w46 g30 ... ... g31 ... ... ... w47 g32 ... ... g33 w48 ... ...""... ... w49 g34 ... ... g35 ... ... ... w50 g36 ... ... g37 w51 ... ...""... ... w52 g38 g38 g38 g38 ... ... ... w53 g39 g39 g39 g39 w54 ... ...""... ... w55 g40 g40 g40 g40 ... ... ... w56 g41 g41 g41 g41 w57 ... ...""... ... w58 g42 ... ... g43 ... ... ... w59 g44 ... ... g45 w60 ... ...";}@for$ifrom1through67{.char1-guide__dotg#{$i}{@if$i<10{grid-area:g0#{$i};}@else{grid-area:g#{$i};}background-color:#08eb0e;}.char1-guide__dotw#{$i}{@if$i<10{grid-area:w0#{$i};}@else{grid-area:w#{$i};}background-color:#d8fad8;}}}

薄い領域をw~にし、2色構成で、作ってみました。(並び作るの大変でした・・)

HTML(ejs)

dotsample.ejs
<html>
    <head>
        <link rel="stylesheet" href="./css/style2.css"> 
    </head>
    <body>

        <div class="guide">
            <div class="guide-inner">
                <div class="guide-text-char1">
                    <% pix1 = 1; %>
                    <% pix2 = 1; %>
                     <% for(i=0; i<=45; i++){ %>
                     <% for(j=1; j<=25; j++){ %>
                          <div class="char1-guide__dotg<%= pix1 %>"></div>
                       <% } pix1++; }  %>
                      <% for(k=0; k<=60; k++){ %>
                       <% for(l=1; l<=25; l++){ %>
                          <div class="char1-guide__dotw<%= pix2 %>"></div>
                       <% } pix2++; }  %>
                </div>
            </div>
        </div>
    </body>
</html>


2種類あるので、変数やfor文もその数増えます。

See the Pen sina by Saito Keita (@saito-keita) on CodePen.

やっぱり、精度が低いです・・

反省点

・ドットの文字の原理をもう少し勉強したほうがいい
・制度を上げる訓練をした方がいい

このあと

しばらく1週間置いて研究し続けてみたいと思います。

Bootstrap 4 のGRID SYSTEMを使ってWebサイトのコラムをレスポンシブ化する

$
0
0

BootStrapでコラムをレスポンシブデザインにする

HTML, CSSだけだとレスポンシブ化って難しいけど、Bootstrapを使用すると簡単!

まず【前提】WEBサイトの横幅は12個のBOXでできている

←------------------WEBサイトの横幅-----------------→

123456789101112

BootstrapのGrid SystemではWidthが12個のBOXで敷き詰められている。

PC,タブレット, スマホによって、その12個のBOXのうちの何個を使用するかをそれぞれ指定できる。説明難しいからとりあえずコード。

column-responsive.html
<divclass="container"><divclass="row"><divclass="col-lg-2 col-md-3 col-sm-12"style="background-color:red; border: 1px solid;">コンテンツ
    </div></div></div>

まずdiv class="row(行)"の中にcolumn(列) divを作成する。

ここでポイントが
①col-lg-2 (PC表示の時は12個のBOXのうち2個のスペース使いますよ)
②col-md-3 (タブレット表示の時は12個のBOXのうち3個のスペース使いますよ)
③col-sm-12 (スマホ表示の時は12個のBOXのうち12個のスペース使いますよ)

つまり
①2/12 = 6分割(6コラム)
②3/12 = 4分割(4コラム)
③12/12 = 1分割(1コラム)
となる。

ちなみにlgはPC、mdはタブレット、smはスマホの表示コラムってしたけど、厳密には横幅のPixcel数で区切っているみたい。

スクリーンショット 2019-12-11 17.50.47.png
(BootStrapのHPより抜粋)

レスポンシブ化、Bootstrapを使うとめちゃくちゃ簡単だからとてもおすすめ。
ちなみにコラムのレスポンシブ化するときは、基本的に最初にclass="container"のdivを使うから気をつけてね。

ちなみにこちらが公式ドキュメント
https://getbootstrap.com/docs/4.4/layout/grid/

HTML・CSSでよく使う記述

$
0
0

初めに

よく使う記述で、毎度調べたりするのが面倒なので、ここに書き溜めていきます。

HTML編

HTML空の記述

<htmllang="ja"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge"><title></title></head><body></body></html>

CSSの読み込み

<!-- cssの読み込み --><linkrel="stylesheet"href="./css/mobile.css"><!-- メディアクエリーで条件付きのcssの読み込み --><linkrel="stylesheet"href="./css/xxx.css"media="screen and (min-width: 640px)">

CSS編

メディアクエリ

@mediascreenand(min-width:640px){/* 横幅640pxより大きくなると適応 */}

親要素をはみ出す

.title{margin-left:calc(((100vw-100%)/2)*-1);margin-right:calc(((100vw-100%)/2)*-1);}

↓↓こういうやつ↓↓
download.png
対応ブラウザ https://caniuse.com/#search=calc
対応ブラウザ https://caniuse.com/#feat=viewport-units

以上


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

$
0
0

100日チャレンジの181日目

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

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

181日目は

Nuxt.jsで縦横見出しが固定されたタイムテーブルを作った話

$
0
0

はじめに

駒場祭という場所でウェブサイト制作担当をしていました。プログラミング・コーディング的なものは大学始めで、HTMLのHの字も分からないところからのスタートでした。(というのは流石に嘘で、中学くらいのときにJavaScriptを勉強しようとして挫折した経験があるので、HTMLのTの字くらいからのスタートだったかもしれません)

つくったもの

タイムテーブルページを作りました。学園祭といえばステージ!ステージといえばタイムテーブル!くらいのノリで重要なサイトですが、僕は初心者なりにめちゃくちゃ苦労しました。先日、「学園祭サイトになくてはならないタイムテーブルのつくりかた」という記事を読んで、「あ!!これ!!ぼくもやったやつ!!」という気分になったので、学園祭アドベントカレンダー、急遽参加します。

開発環境

良い記事を書くには開発環境の記述が不可欠らしいですが、正直どこを書けばいいのか分からない。

  • Nuxt.js(ver 2.0.0)
  • @nuxtjs/axios(ver 5.3.6)
  • pug
  • scss

できたもの

タイムテーブルページ写真
こんな感じになりました。別委員が作ったAPIからステージの遅れ時間を表示したり、現在公演中の企画に薄ーく色をつけたり(駒場祭期間のみの機能です)…、といろんな機能がついています。(写真はフォントが実際とちょっと違います)

どうやってつくったか

実装途中のタイムテーブルページ
できた!

とは言ってもこれは「サーバーからデータを引く」とか「コンポーネント」とかあんまり考えずに作った、HTMLとCSSだけの仮置きです。このころはまだ気持ちに余裕があるので、「200分遅れ」というふざけをしています。(遅れ時間表示の下にある「このステージを固定」の機能は、いわゆる「アイデアの段階ではあったけど、実装が全然間に合わなかったのでいつのまにか消滅した機能」ってやつです。)

肝心の横線が並んでいる部分は、全部divタグで作っていて、30分に対して、「幅79px+1pxの横線」の80pxが対応するような作りにしました。CSSで言うと下みたいな感じ。

30分に対応するdiv要素
.timelines{height:79px;border-top:1pxdashed$orange;border-bottom:1pxsolid$orange;margin-bottom:79px;}

このtimelinesクラスのdiv要素の集合体である、timebodyというdiv要素の上に、position: absolute;で公演情報を並べていく感じになりそうです。

各企画の情報をコンポーネント化する

企画情報のコンポーネント
これ。
当時「コンポーネント」という言葉を覚えたてくらいだった僕でも、流石にコンポーネント化したほうがいいことは分かります。

position: absolute;でこのコンポーネントを並べることを考えると、topheightが難関になってくる予感がします。

コンポーネントに渡すデータを決める

TimeTableBox.vue
<templatelang="pug">
    button.timetable-box(:style="boxStyle" :class="nowClass")
        .content-box
            .start-time {{kikaku.startTime.str}}
            .title(v-html="kikaku.title[l]" :style="kikaku.style[l]")
            .end-time {{kikaku.endTime.str}}</template><script>exportdefault{computed:{boxStyle(){letbox_height=(this.kikaku.endTime.h-this.kikaku.startTime.h)*160+(this.kikaku.endTime.m-this.kikaku.startTime.m)*160/60;letbox_top=(this.kikaku.startTime.h-8)*160+(this.kikaku.startTime.m-30)*160/60;return"top: "+box_top+"px;"+"height: "+box_height+"px;"+"left: "+this.box_left+"px;";},nowClass(){if(this.kikaku.startTime.str==this.nowData){return"now"}else{return""}},},props:{kikaku:Object,nowData:String}}</script><style>/* 省略 */</style>

とりあえずコンポーネントの構造はできました。
topheightは、開始時間と終了時間のデータから「30分:80px」の比例関係を使って、px数を計算するようにしました。
ここから引数kikakuに渡すオブジェクトを決めます。

kikakuに渡したオブジェクト
{"id":114,"title":{"ja":"特別講演会<br>「東大式・誤答の美学<br>〜クイズ王はまた<br>間違える〜」","en":"Special Lecture Izawa Takushi"},"startTime":{"h":14,"m":0,"str":"14:00"},"endTime":{"h":15,"m":30,"str":"15:30"},"style":{"ja":"font-size:10px; letter-spacing:-0.04em;","en":""}}

特に注目したいポイントは以下。

「公演中」機能をつける

やっぱり、「今やっている企画が何か」は一目で分かるようになっていてほしいものです。フロント的にはnowのクラスさえ付いちゃえば、あとはどうにでもデザインできますが、どのようにしてnowクラスをつけるかが難しいところでした。
今回は、「現在ステージで公演中の企画の基本情報」を教えてくれる委員会のAPIから引いてきたデータを利用して、「現代公演中の企画の開始時間の文字列」と「コンポーネントに渡されたデータのstartTimeの文字列」を比較し、nowクラスをつけました。
学園祭のステージって「同じサークルが毎日何度も出演する」みたいなこと多いので、企画IDみたいな情報で比較するよりも、開始時間or終了時間で比較した方が、より正確に判定できると思われます。

企画名を美しく配置する

講演会の企画などは、企画名が長くなってしまうものです。しかし、できることなら「企画名の改行」や「ちょうど収まる感じの文字サイズ、行高にしたい」というデザインのこだわりも捨て切れません。

  • v-html<br>を用いて、気持ちいい感じの改行にする。
  • コンポーネントに渡す企画データすべてにstyleのデータをつけて、手作業でfont-size, line-height, letter-spacingなどを調整できるようにする。

大量の企画を一つ一つチェックして文字配置を調整する手間が要りましたが、おかげでより綺麗なタイムテーブルページができました。(こういう面倒な作業に限って、同じウェブサイト担当の同期S君に丸投げしちゃいました。ごめんね!)

「ステージ名」と「時間の目盛」を固定する

僕個人が絶対つけたい機能でした。
詳しくはタイムテーブルページで縦横スクロールしてもらえると分かると思うのですが、縦横スクロールしたときにExcelみたいに見出しの行・列が固定されるような機能を実装しました。

overflow: scroll;で良さそうだが

「ステージ名」「時間の目盛」両方を固定するとなると、うまくいきません。ひとまず、overflow-y: scroll;で「時間の目盛」が固定されるようにしました。

ステージ名をどうやって固定する???

ページの上の方にいるとき

ページの上の方にいるとき

「ステージ名」が固定されたあと

「ステージ名」が固定されたあと
スクロール量で、キワまで行ったどうかを判定し、「ステージ名」のdiv要素が上まで行ったらposition: fixed;を使って、その場でdiv要素を固定することにしました。
同じタイミングで、「タイムテーブル本体(上画像青)」の方に「ステージ名(上画像赤)」と同じ高さのmargin-topを設定し、いかにも固定されているように見せる、という作戦です。

「ステージ名」の固定
<script>exportdefault{methods:{fixStageName:function(){if(window.pageYOffset>334){this.stage_fix="fixed"// 「ステージ名」のdiv要素に「fixed」というクラスをつけている。this.timeline_margin="margin-top: 80px;"// 「タイムテーブル本体」にmargin-topをつけている。if(window.pageYOffset>1850){this.stage_disappear="display: none;"// 結構下の方まで行ったら「ステージ名」のdiv要素を消しちゃう。}else{this.stage_disappear=""}}else{this.stage_fix=""this.timeline_margin=""}}},mounted(){window.addEventListener("scroll",this.fixStageName());}}</script>

レスポンシブデザインへ

スマホでもパソコンでもアクセスされる学園祭ホームページ、レスポンシブデザインであることが必須です。CSS部をレスポンシブにする分にはメディアクエリをどうにかすればいいので簡単ですが、Vue.jsを使ってバインディングしているstyle属性をレスポンシブにするのには骨が折れました。

window.matchmediaの利用

をしました。「.matchMedia()でJSでもメディアクエリを使って条件分岐する | SPYWEB」というサイトを参考にしました。

window.matchmedia()
data(){return{media_query:[false,false,false]}},beforeMount(){letmq_pc_list=window.matchMedia("(min-width: 961px)");letmq_tab_list=window.matchMedia("(min-width: 641px) and (max-width: 960px)");letmq_sp_list=window.matchMedia("(max-width: 640px)");this.media_query[0]=mq_pc_list.matches;this.media_query[1]=mq_tab_list.matches;this.media_query[2]=mq_sp_list.matches;window.matchMedia("(min-width: 961px)").addListener(mql=>{this.media_query[0]=mql.matches;});window.matchMedia("(min-width: 641px) and (max-width: 960px)").addListener(mql=>{this.media_query[1]=mql.matches;});window.matchMedia("(max-width: 640px)").addListener(mql=>{this.media_query[2]=mql.matches;});}

media_queryというデバイスの横幅のサイズを判定する配列を用意し、これを利用して「JavaScript版メディアクエリ」を実現しました。

これを利用して書き換えた先のfixStageNameメソッドが以下です。

fixStageNameメソッド
fixStageName:function(){if(this.media_query[0]){if(window.pageYOffset>334){this.stage_fix="fixed"// 「ステージ名」のdiv要素に「fixed」というクラスをつけている。this.timeline_padding="margin-top: 80px;"// 「タイムテーブル本体」にmargin-topをつけている。if(window.pageYOffset>1850){this.stage_disappear="display: none;"// 結構下の方まで行ったら「ステージ名」のdiv要素を消しちゃう。}else{this.stage_disappear=""}}else{this.stage_fix=""this.timeline_padding=""}}elseif(this.media_query[1]){if(window.pageYOffset>277){this.stage_fix="fixed"this.timeline_padding="margin-top: 80px;"// 「タイムテーブル本体」にmargin-topをつけている。if(window.pageYOffset>1793){this.stage_disappear="display: none;"this.stage_fix=""}else{this.stage_disappear=""}}else{this.stage_fix=""this.timeline_padding=""}}else{if(window.pageYOffset>234){this.stage_fix="fixed"this.timeline_padding="margin-top: 80px;"// 「タイムテーブル本体」にmargin-topをつけている。if(window.pageYOffset>1760){this.stage_disappear="display: none;"this.stage_fix=""}else{this.stage_disappear=""}}else{this.stage_fix=""this.timeline_padding=""}}}

matchmediaメソッドのおかげで、スマホやタブレットでも「ステージ名」が固定できるようになりました。

おわりに

ロックフェスティバルとかのタイムテーブルページ、案外画像ファイル埋め込みだったりPDFダウンロードだったりするんですよね。それでも青空の下の熱気溢れるステージを思い浮かべてCSSでちまちまタイムテーブルを実装するのが学園祭プログラマーの性なのかもしれません。(当日は大雨でした。)

年末まで毎日webサイトを作り続ける大学生 〜54日目 JavaScriptでじゃんけんゲームを作る〜

$
0
0

はじめに

こんにちは!@70days_jsです。

じゃんけんゲームを作りました。
例のごとく何も参考にせずに作りました。
ので、もっといいじゃんけんのアルゴリズムがあれば教えて欲しいです。

今日は54日目。(2019/12/11)
よろしくお願いします。

サイトURL

https://sin2cos21.github.io/day54.html

やったこと

こんな感じのじゃんけんゲームを作りました(gif)↓
test3.gif

勝ちは赤、
負けは青、
引き分けは黄色で表現しています。

ソース全文

html↓

<body><section><divid="jankenWord">勝負の刻!</div></section><section><divid="MyjankenImage"></div><divid="jankenImage"></div></section><section><divid="rock"class="jankenButton">グー</div><divid="scissors"class="jankenButton">チョキ</div><divid="paper"class="jankenButton">パー</div></section></body>

css↓

body{margin:0;display:flex;justify-content:center;align-items:center;flex-direction:column;}section{display:flex;}div{width:100px;height:100px;border:solid1pxblack;margin:50px0050px;display:flex;justify-content:center;align-items:center;}#jankenImage{background-size:cover;}#MyjankenImage{background-size:cover;}#jankenImage::before{content:"敵";margin-top:130px;}#MyjankenImage::before{content:"Me";margin-top:130px;}.jankenButton{border-radius:50%;cursor:pointer;}.click-none{pointer-events:none;}

JavaScript↓

letjanken={0:"rock",1:"paper",2:"scissors"};letimg={rock:"day54/rock.png",scissors:"day54/scissors.png",paper:"day54/paper.png"};letjankenImage=document.getElementById("jankenImage");letMyjankenImage=document.getElementById("MyjankenImage");letjankenWord=document.getElementById("jankenWord");letjankenButton=document.getElementsByClassName("jankenButton");for(vari=0;i<jankenButton.length;i++){letjankenChoice=jankenButton[i];jankenChoice.addEventListener("click",function(){letrandom=Math.floor(Math.random()*3);jankenImage.style.backgroundImage="url("+img[janken[random]]+")";MyjankenImage.style.backgroundImage="url("+img[jankenChoice.id]+")";judge(random,jankenChoice.id);});}//引数はtekidaは乱数、meは選択した要素のidfunctionjudge(tekida,me){letteki=janken[tekida];if(teki===me){jankenWord.innerHTML="ヒキワケ";jankenWord.style.backgroundColor="rgba(255,250,50, .5)";}elseif(me==="rock"&&teki==="scissors"){jankenWord.innerHTML="カチ";jankenWord.style.backgroundColor="rgba(255,50,50, .5)";}elseif(teki==="rock"&&me==="scissors"){jankenWord.innerHTML="マケ";jankenWord.style.backgroundColor="rgba(55,50,250, .5)";}elseif(me.length>teki.length){jankenWord.innerHTML="カチ";jankenWord.style.backgroundColor="rgba(255,50,50, .5)";}else{jankenWord.innerHTML="マケ";jankenWord.style.backgroundColor="rgba(55,50,250, .5)";}}

じゃんけんのアルゴリズム

重要なのはここですよね。

今回、僕はグーチョキパーをhashにしました。

let janken = {
0: "rock",
1: "paper",
2: "scissors"
};

その時、文字数に着目しました。
すると、rock以外は文字数が大きいほど強い法則を見つけました。
scissors > paper
paper > rock
なので、以下のように書きました。

if (teki === me) {
ヒキワケ
} else if (me === "rock" && teki === "scissors") {
カチ
}else if (teki === "rock" && me === "scissors") {
マケ
} else if (me.length > teki.length) {
カチ
} else {
マケ
}

文字数じゃなく、数字を使っても良かっても良かったかもしれませんね。
ただ、まあ、これでじゃんけんは一応成立します。

感想

ロジックを考えてちゃんと動いてくれると楽しかったりします。
ただ、あまり正確すぎたり最適を求めたりしすぎたりするのは自分の性に合わないということも分かりました。
これからもゆるく楽しく作れればいいな。

CSSでdashedとは違う破線をつくる方法

$
0
0

はじめに

いままで画像で対応していたことが実はCSSで再現できると知ったとき少しうれしいですよね。
今回は最近知ったことを1つ。

ボーダーの破線

普段、borderを破線にしたい場合、border-styleはdashedを指定するかと思います。

See the Pen vYEGbxv by Shinji (@Shinji_Shinji) on CodePen.

dashedは線の太さは指定できるけど、線の長さや線と線の間隔が設定できず
デザインによってはdashedが使えないということがありました。
その際、自分の場合はいままで画像で対応していました。

もし、
CSSで線の長さや間隔が自由に設定できたらいいですね。

解決方法

グラデーションを使って破線を作ります。

例えば、
線の幅が「5px」、間隔が「5px」の破線を作る場合

See the Pen dyPMmyx by Shinji (@Shinji_Shinji) on CodePen.

これで完成です。

参考
dottedでもdashedでもない!CSSで好きな間隔の点線(破線)を作る方法

CSSは神なのでゲームも作れる

$
0
0

はじめに

この記事はN高等学校 Advent Calendar 2019の12日目の記事です。
昨日の記事は同じキャンパスの先輩であるGenbu氏の静的な Web サイトをどうやって速くするかです。合わせて是非。
因みに明日の記事も同じキャンパスの狂人Nemesis氏のflutter触ったです。こちらも合わせてどうぞ。

こんにちは。N高等学校1年生のはとと申します。プログラミング歴は一年にも満たない若輩者ではありますが、ここまでの学習の成果としてアドベントカレンダーを書かせていただきます。よろしくお願いいたします。

作ったもの

See the Pen GRgJjZL by HATO (@hatopoppo) on CodePen.

タイピングゲームです。HTMLとCSSだけで実装しました。JavaScriptは使ってません。
inputの自動選択は、私が知る限りHTMLとCSSではどうにもならないので一々クリックしないといけない仕様となっております。
本来こんな事CSSでやるべきじゃないので当然の如く脳筋クソコードです。それにあいまって元々のクソコード力も重なりクソの二段構えコードとなっております。

若干ゃ解説

このコードの鍵や、苦労した点等の解説を少しだけします。

仕組み

まずこのタイピングゲームで鍵になるのはここ

CSSisGOD.html
<inputtype="text"class="A1"pattern="java"required>

inputタグの中の

CSSisGOD.html
pattern="java"

ここ。

このpatternという所でinputに入れられたテキストの正誤判定をしています。
本来の用途としては、

sample.html
pattern="^[0-9A-Za-z]+$"

等で受け付ける文字の種類を限定するのですが1、今回はここに文字列javaを入れることにより、この文字列以外は受け付けないという形に出来ました。

そして、文字列が正しいと判断されると擬似要素 :valid が、
正しくないと判断されると擬似要素 :invalid が与えられる為、CSSで

CSSisGOD.css
.A1:valid~.A2:invalid~.A3:invalid~.A4:invalid~.A5:invalid~.A6:invalid~.Q2{opacity:1;}

のように書いてやって、A1(最初の問題のinput)が正しければ次の問題を表示という形に出来ました。
因みに得点の部分もこういう感じで切り替えています。本来は別の方法を使いたかったのですが後述の理由により断念しました。

苦労した点

今回苦労したのは、inputの切り替えと得点の兼ね合いです。
現在正解時のinputの交代は、

CSSisGOD.css
input[pattern]:valid{display:none;}

このように、validの時に要素を消すという処理で行なっているのですが、このような形にすると、元々得点の部分で行なっていた、要素のカウントを出来るコード

sample.css
input[pattern]:valid{counter-increment:count;

が動かなくなり、原因と改善案の模索で半日ほど潰していました。
理由は恐らく、要素そのものを消している為、カウント対象の要素がなかった事だと思います。
実際、opacityを0にする方式にしてみるとしっかりと得点のカウントがされました。
しかし、opacityを切り替える場合では、inputが常に存在している状態になります。
その状態だと問題を解いた後も、見えないinputがまだ選択状態、即ち入力待ちの状態になってしまう為、そこから次のinputの選択をせずに入力を行うと

一問前のinputに入力
    ↓
patternの正誤判定が働き一問前の問題とinputがまた表示

というストレスフルの状態になった為、やむなくinputは消滅させ得点は正誤判定で切り替えるシステムとしました

終わりに

脳筋実装ではありますが、CSSでもゲームを作る事ができました。
CSSの限界はもっと上にあると思うので、今後もCSSの限界に挑み続けようと思います。
最後に、このゲームを作っている途中にクラスメートに言われた言葉を金言として書き、終わろうと思います。
「CSSはゲームを作るものじゃない」
「時間の無駄」
「JavaScript使え」
「CSS中毒」

ご覧いただきありがとうございました。

追記

すでにCSSで僕なんかよりも完成度高くタイピングゲームを作り、分かりやすく解説もしていらっしゃる先駆者の方がおりましたので、畏敬の念を込めてリンクを貼らせていただきます。
codegrid


  1. この場合は、半角英数字のみという制限をかけている。詳しくは他記事参照。 

Viewing all 8501 articles
Browse latest View live