20191211のCSSに関する記事は16件です。

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

はじめに

いままで画像で対応していたことが実は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で好きな間隔の点線(破線)を作る方法

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

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

はじめに

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

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

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

サイトURL

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

やったこと

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

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

ソース全文

html↓

  <body>
    <section>
      <div id="jankenWord">勝負の刻!</div>
    </section>
    <section>
      <div id="MyjankenImage"></div>
      <div id="jankenImage"></div>
    </section>
    <section>
      <div id="rock" class="jankenButton">グー</div>
      <div id="scissors" class="jankenButton">チョキ</div>
      <div id="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: solid 1px black;
  margin: 50px 0 0 50px;
  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↓

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

let img = {
  rock: "day54/rock.png",
  scissors: "day54/scissors.png",
  paper: "day54/paper.png"
};

let jankenImage = document.getElementById("jankenImage");
let MyjankenImage = document.getElementById("MyjankenImage");
let jankenWord = document.getElementById("jankenWord");
let jankenButton = document.getElementsByClassName("jankenButton");

for (var i = 0; i < jankenButton.length; i++) {
  let jankenChoice = jankenButton[i];
  jankenChoice.addEventListener("click", function() {
    let random = 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は選択した要素のid
function judge(tekida, me) {
  let teki = janken[tekida];
  if (teki === me) {
    jankenWord.innerHTML = "ヒキワケ";
    jankenWord.style.backgroundColor = "rgba(255,250,50, .5)";
  } else if (me === "rock" && teki === "scissors") {
    jankenWord.innerHTML = "カチ";
    jankenWord.style.backgroundColor = "rgba(255,50,50, .5)";
  } else if (teki === "rock" && me === "scissors") {
    jankenWord.innerHTML = "マケ";
    jankenWord.style.backgroundColor = "rgba(55,50,250, .5)";
  } else if (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 {
マケ
}

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

感想

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

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

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

100日チャレンジの181日目

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

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

181日目は

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

HTML・CSSでよく使う記述

初めに

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

HTML編

HTML空の記述

<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
</head>
<body>

</body>
</html>

CSSの読み込み

<!-- cssの読み込み -->
<link rel="stylesheet" href="./css/mobile.css">

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

CSS編

メディアクエリ

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

親要素をはみ出す

.body {
  width: 640px;
  margin: 0 auto;
}
.title {
  margin-left: calc(((100vw - 100%) / 2) * -1);
  margin-right: calc(((100vw - 100%) / 2) * -1);
  background: #ccc;
}
.title span {
  width: 640px;
}

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

以上

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

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

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

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

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

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

1 2 3 4 5 6 7 8 9 10 11 12

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

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

column-responsive.html
<div class="container">
  <div class="row">
    <div class="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/

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

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

この記事は
電車の発車案内板を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:1fr repeat(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 $i from 1 through 67 { 
  .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文もその数増えます。

※サーバーに負荷がかかりそうなので一旦削除しました。別な方法考えます。

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

反省点

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

このあと

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

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

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

備忘録

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

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

CODEPEN

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

記述

html ドラッグ&ドロップするエリア
<form method="post" enctype="multipart/form-data">
    <div id="dragDropArea">
        <div class="drag-drop-inside">
            <p class="drag-drop-info">ここにファイルをドロップ</p>
            <p>または</p>
            <p class="drag-drop-buttons">
                <input id="fileInput" type="file" accept="image/*" value="ファイルを選択" name="photo" onChange="photoPreview(event)">
            </p>
            <div id="previewArea"></div>
        </div>
    </div>
    <button type="submit" name="submit" value="登録">登録<button>
</form>
css 簡単に整形
#dragDropArea{
  background-color: #f4f4f4;
  margin: 10px;
  padding: 10px;
  border: #ddd dashed 5px;
  min-height: 200px;
  text-align: center;
}
#dragDropArea p{
    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-buttons input{
    margin: auto;
}

JS ドラドロイベントでごにょごにょ&選択時にごにょごにょ
var fileArea = document.getElementById('dragDropArea');
var fileInput = 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');
    var files = evt.dataTransfer.files;
    console.log("DRAG & DROP");
    console.table(files);
    fileInput.files = files;
    photoPreview('onChenge',files[0]);
});
function photoPreview(event, f = null) {
  var file = f;
  if(file === null){
      file = event.target.files[0];
  }
  var reader = new FileReader();
  var preview = document.getElementById("previewArea");
  var previewImage = document.getElementById("previewImage");

  if(previewImage != null) {
    preview.removeChild(previewImage);
  }
  reader.onload = function(event) {
    var img = 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

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

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

いきなり結論。

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

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

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

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

備忘.

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

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

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

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

はじめに

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

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

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

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

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

作成の流れ

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

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

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

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

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

2019年 スマホブラウザ対応で苦戦したこと

私は普段業務で、Webページの保守・運用を主にしております。
特にスマホブラウザメインのサイトを扱っているため、スマホでの表示崩れや仕様に対応しなければならないことが多いです。

そのなかでも、今年1年の間で直すのにてこずったスマートフォン向けの対応について、今回は備忘録もかねて紹介してみようと思います。

1. iOS編

1-1. 横画面表示に切り替えると、文字の大きさがバラバラになる

文字サイズがバラバラになってしまったコード

まずは、実際に横画面にして崩れてしまっていた箇所を、最小限のコード量で再現してみます。

<p class="line-height-big">てすと</p>
<p class="line-height-big">てすてす<span class="red">てすと</span></p>
<style>
 *{
   line-height: 1;
 }

 .line-height-big {
   line-height: 1.2;
 }

 .red {
   color: #ff0000;
 }
 </style>
  • まずすべての要素(*セレクタ)に line-height:1を指定。
  • 異なる値のline-heightを指定したクラス(今回は.line-height-big)を作成。
  • pタグを2つ用意し、片方に.line-height-bigを指定
  • .line-height-bigで囲った要素の子要素にただのテキストとspanで囲ったテキストを配置

スクリーンショット 2019-12-09 11.47.07.png
スクリーンショット 2019-12-09 13.41.58.png

横画面表示にすると、なぜか.line-height-bigの中のspanの中の文字だけ小さくなってしまいました。
ちなみにclass="red" はみやすくするためにつけたので、文字を赤くしなくても同じことが起こります。

原因

ブラウザを横画面にした時に、文字サイズを調整する機能がうまく動作せずにサイズがバラバラになってしまいます。
下記のコードのように-webkit-text-size-adjust: 100%; を指定すれば、横画面にしても文字サイズが変わらず、大きさがバラバラになることもありません。

body {
  -webkit-text-size-adjust: 100%;
}

スクリーンショット 2019-12-09 13.16.09.png
スクリーンショット 2019-12-09 13.16.26.png

だけど、横画面にした時、文字が大きくなってほしい

しかし今回は、横画面にした時に文字サイズが大きくなって欲しかったため、-webkit-text-size-adjust: 100%;は使わずに対応するために挙動について検証してみました。

 パターン1
<p class="line-height-big">てすと</p>
<p class="line-height-big">てすてす<span class="red">てすと</span></p>
<style>
 *{
   line-height: 1;
 }

 .line-height-big {
   line-height: 1.2;
 }

 .red {
   color: #ff0000;
 }
 </style>

スクリーンショット 2019-12-09 13.55.20.png
line-heightの値が異なる要素だけ小さくなってしまっているようでした。

 パターン2
<p>てすと</p>
<p class="line-height-big">てすてす<span class="red">てすと</span></p>
<style>
 *{
   line-height: 1;
 }

 .line-height-big {
   line-height: 1.2;
 }

 .red {
   color: #ff0000;
 }
 </style>

スクリーンショット 2019-12-09 13.46.06.png
この場合も同様、文字が小さくなってしまっている箇所はline-heightの値が他と異なっています。

解決方法

ということで、line-heightの値が一定になるように修正します。

 <p class="line-height-big">てすと</p>
 <p class="line-height-big">てすてす<span class="red line-height-big">てすと</span></p>
 <style>
 *{
   line-height: 1;
 }

 .line-height-big {
   line-height: 1.2;
 }

 .red {
   color: #ff0000;
 }
 </style>

上記のコードのようにline-heightの値を揃えれば、文字の大きさが一定になりました。

スクリーンショット 2019-12-09 11.58.54.png

でも、下記のようにpタグが1つだけの場合だと

<p class="line-height-big">てすてす<span class="red">てすと</span></p>
<style>
 *{
   line-height: 1;
 }

 .line-height-big {
   line-height: 1.2;
 }

 .red {
   color: #ff0000;
 }
 </style>

スクリーンショット 2019-12-09 14.44.39.png

line-height の値が異なるものが混じっていますが、文字サイズはバラバラになりません。

line-heightの値が異なる要素が3つ以上ある場合、バラバラになってしまう?ように思えますが
どの文字が小さくなってしまうのかなどは、規則性がわからずじまいでした、、、

しかし今回のように、同じブロックの中に、さまざまなline-heightが混在しないようにすれば、他の原因で文字サイズがバラバラになってしまう事象には遭遇していません。

詳しい仕様などがわかる方がもしいらっしゃれば、コメントでぜひ教えていただけると助かります?

1-2. 入力フォームにカーソルを合わせたときにズームしないでほしい

iOS端末では、input要素のフォントサイズが16px以下だと、入力フォームびカーソルを合わせたときに拡大表示される機能があります。

textarea {
  font-size: 10px;
  width: 60%;
  height: 50px;
  margin: 0 20%;
  padding: 10px;
  resize: none;
}

font-sizeを16px以上にすれば拡大されないようになります。

textarea {
  font-size: 16px;
  width: 60%;
  height: 50px;
  margin: 0 20%;
  padding: 10px;
  resize: none;
}

font-size 16px以下で、拡大しないように実装したい場合は、scaleを利用します。

textarea {
  /* scale(0.5)で半分の大きさになってしまうので、全て2倍の値にする */
  font-size: 20px;

  width: 120%;
  height: 100px;
  margin: 0 40%;
  padding: 20px;

  resize: none;
  transform: scale(.5);
  transform-origin: left top;
}

scale によって文字以外も縮小されてしまい、見た目が少し変わってしまうので、縮小率に合わせて大きい値を指定するか、入力フォームをdiv などで括って、そこにcssを当てるなどで対応できると思います。

こちらの方法使用する場合は、標準の機能を動作させないようにすることになるため、フォントサイズが小さくても操作性に問題がないか、要検証です?

2. Android編

2-1. Android4以下の一部ブラウザでFlexが崩れる

これは単純にブラウザが対応していないだけでした?
ベンダープレフィックス付与すれば、Android4以下でもFlexを使用できます。

Flexコンテナ

.flex {
  display: -webkit-box; /* Android用 */
  display: flex;
}

Flexカラム

.flx-column{
    -webkit-box-flex: 1; /* Android用 */
    flex: 1 1 0;
}

justify-contentプロパティ

.flex-start{
    -webkit-box-pack: start; /* Android用 */
    justify-content: flex-start;
}

.flex-end{
    -webkit-box-pack: end; /* Android用 */
    justify-content: flex-end;
}

.flex-center{
    -webkit-box-pack: center; /* Android用 */
    justify-content: center;
}

.flex-space-between{
    -webkit-box-pack: justify; /* Android用 */
    justify-content: space-between;
}

align-itemsプロパティ

.flex-start{
    -webkit-box-align: start; /* Android用 */
    align-items: flex-start;
}

.flex-end{
    -webkit-box-align: end; /* Android用 */
    align-items: flex-end;
}

.flex-center{
    -webkit-box-align: center; /* Android用 */
    align-items: flex-center;
}

.flex-baseline{
    -webkit-box-align: baseline; /* Android用 */
    align-items: baseline;
}

.flex-stretch{
    -webkit-box-align: stretch; /* Android用 */
    align-items: stretch;
}

align-content は使えません、、、?

制作のときは、開発者ツールを使ってAndroid用のベンダープレフィックスが付いているcssのみを有効にして表示確認をすれば、Flexが未対応の実機でも崩れず表示できました。

まとめ

以上、対応に手こずったスマホブラウザ対応についてまとめてみました。
同様の現象で困っている人のヒントになれば幸いです。。。?‍♂️

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

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

はじめに

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

CSSで作ったらこうなった

オレンジの「PUSH」ボタンを押すとランキングがパタパタと動き出します。
今年の流行語大賞をランキングボードに出してみます。
(※ スマホでは上手く表示されないかもしれません。。)

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: 80px;
  height: 50px;
  margin-right: 5px;
  line-height: 50px;
  background: #ffa500;
  border: solid 1px #ffa500;
  border-radius: 4px;
  border-bottom: 4px solid #d37800;
  text-shadow: 1px 1px 1px rgba(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-rotate var(--duration) calc(var(--delay) * 0) var(--count) forwards;
}
.btn:checked + label + .frame .flip:nth-of-type(2) {
  animation: frame-rotate var(--duration) calc(var(--delay) * 1) var(--count) forwards;
}
.btn:checked + label + .frame .flip:nth-of-type(3) {
  animation: frame-rotate var(--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
<div class="row">
  <input type="radio" id="btn1" class="btn">
  <label for="btn1">PUSH ME</label>
  <div class="frame">
    <div class="flip" data-txt="ONE TEAM" data-txt-next="令和"></div>
    <div class="flip" data-txt="令和" data-txt-next="タピる"></div>
    <!-- ↓↓ この 「data-txt-next」にパタパタが止まった止まったときに表示したいテキスト -->
    <div class="flip" data-txt="タピる" data-txt-next="ONE TEAM"></div>
    <!-- ↓↓ この「data-txt」にボタンを押す前に表示するテキスト -->
    <div class="flip open" data-txt="大賞"></div>
  </div>
</div>

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

感想

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

おわりに

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

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

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

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>
    <p v-if="show">lorem ipsum...</p>
  </vertical-collapse-transition>
</template>

<!-- OR -->

<template>
  <vertical-collapse-transition>
    <p v-show="show">lorem ipsum...</p>
  </vertical-collapse-transition>
</template>

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

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

疑似コードですが、

<template>
  <transition name="vertical-collapse">
    <div style="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) {
    const child = this.$slots.default && this.$slots.default[0];

    console.log(child);
    // VNode { tag: "div", ...}
  }
}

2. v-if="false"

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

{
  render(h) {
    const child = this.$slots.default && this.$slots.default[0];

    console.log(child);
    // undefined
  }
}

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

{
  render(h) {
    const child = this.$slots.default && this.$slots.default[0];

    console.log(child);
    // VNode { tag: undefined, ...}
  }
}

2019/12/12 追記
空白しかないtext nodeの可能性が高いです。

3. v-show="true"

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

{
  render(h) {
    const child = 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) {
    const child = 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の一個目の要素しか見ないインターフェースなので、ほかは捨ててよい
    const child = this.$slots.default && this.$slots.default[0];

    // TODO: あとで中を実装する
    const generateTransitionComponent = (children) => h('transition', {}, children);

    const isEmpty = !child || child.tag === undefined;

    if (isEmpty) {
      // --------------
      // v-if="false"
      // --------------

      return generateTransitionComponent([]); // transitionの中を空で返す
    }

    const vShowDirective =
      child.data.directives &&
      child.data.directives.find(directive => directive.name === 'show');
    const isHidden = vShowDirective && !vShowDirective.value;

    if (isHidden) {
      // --------------
      // v-show="false"
      // --------------
      child.data.directives = [
        // 子のv-showディレクティブを消す
        ...child.data.directives.filter(
          directive => directive.name !== 'show'
        ),
      ];

      return generateTransitionComponent([
        h(
          'div', // h('div') でラッパーを作る
          {
            // ラッパーにv-showディレクティブを移植する
            directives: [
              {
                name: 'show',
                value: false,
              },
            ],
          },
          [child]
        ),
      ]);
    }

    if (vShowDirective && !isHidden) {
      // --------------
      // v-show="true"
      // --------------

      return generateTransitionComponent([
        h(
          'div',
          {
            // ラッパーにv-show="true"をプロキシする
            directives: [
              {
                name: 'show',
                value: true,
              },
            ],
          },
          [child]
        ),
      ]);
    }

    // --------------
    // v-if="true"
    // --------------
    // 特に何もせず、ラッパーに子をそのまま入れて返す
    return generateTransitionComponent([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>
import Vue, { VNode } from 'vue';

export default {
  props: {
    duration: {
      type: Number,
      default: 100,
    },
    easing: {
      type: String,
      default: 'ease-out',
    },
  },
  render(h) {
    // transitionはもともとdefault slotの一個目の要素しか見ないインターフェースなので、ほかは捨ててよい
    const child = this.$slots.default && this.$slots.default[0];

    // transition要素を作る関数
    const generateTransitionComponent = 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
      );

    const wrapperData = {
      // ラッパーに常時ついているスタイル
      style: {
        overflow: 'hidden',
        transition: `height ${this.easing} ${this.duration}ms`,
      },
    };

    const isEmpty = !child || child.tag === undefined;

    if (isEmpty) {
      // --------------
      // v-if="false"
      // --------------

      return generateTransitionComponent([]); // transitionの中を空で返す
    }

    const vShowDirective =
      child.data.directives &&
      child.data.directives.find(directive => directive.name === 'show');
    const isHidden = vShowDirective && !vShowDirective.value;

    if (isHidden) {
      // --------------
      // v-show="false"
      // --------------
      child.data.directives = [
        // 子のv-showディレクティブを消す
        ...child.data.directives.filter(
          directive => directive.name !== 'show'
        ),
      ];

      return generateTransitionComponent([
        h(
          'div', // h('div') でラッパーを作る
          {
            ...wrapperData,
            // ラッパーにv-showディレクティブを移植する
            directives: [
              {
                name: 'show',
                value: false,
              },
            ],
          },
          [child]
        ),
      ]);
    }

    if (vShowDirective && !isHidden) {
      // --------------
      // v-show="true"
      // --------------

      return generateTransitionComponent([
        h(
          'div',
          {
            ...wrapperData,
            // ラッパーにv-show="true"をプロキシする
            directives: [
              {
                name: 'show',
                value: true,
              },
            ],
          },
          [child]
        ),
      ]);
    }

    // --------------
    // v-if="true"
    // --------------
    // 特に何もせず、ラッパーに子をそのまま入れて返す
    return generateTransitionComponent([h('div', wrapperData, [child])]);
  },
};
</script>

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

まとめ

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

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

ぼやき

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


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

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

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

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

はじめに

今回は,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
<div id="scene">
  <div class="boxBase">
    <div class="top"></div>
    <div class="bottom"></div>
    <div class="front"></div>
    <div class="back"></div>
    <div class="left"></div>
    <div class="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.5px solid black; /* 縁 */
}

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;
}

立方体の完成

立方体(htmlファイル)
<div id="scene">
  <div class="boxBase">
    <div class="top"></div>
    <div class="bottom"></div>
    <div class="front"></div>
    <div class="back"></div>
    <div class="left"></div>
    <div class="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.5px solid black;
  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: turnAround 30s linear 0s infinite normal none running;
}
@keyframes turnAround {
  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)

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

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

大人数の時使うオーバーレイのカスタム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終わり******/```
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

大人数の配信時使うDiscordStreamKitOverlayのカスタムCSS

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

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

元の記事

あるCSSがあってそれを使っていました
順風満帆丸さんの萬巓堂本店の
「DISCORDで喋ってる人を分かり易くするカスタムCSS」
http://manten-do.net/contents/dsk01
です。
Discord STREAMKIT Overlayを使ってカスタムCSSで変更を加えているんですね
なのでCSSを変えれば色々変えれるんです

経緯

実は、自分のチャンネルで100人記念で配信した時に自分含めて5人でお話しする機会がありました。その時に思ったのは
「IDと立ち絵登録面倒・・・」
凸に入る時に
「まって、今登録してるから」っていって待ってもらわなきゃいけなかったのでテンポが悪かったです

上のCSSは
・出演者が決まっている
・画面のいろんなところに配置する必要がある
・立ち絵全部見せたい
時に真価を発揮するものだと思うのです

なので今回の雑談みたいに
・通話チャンネルに出たり入ったりする
・別にバラバラじゃなくて横並びでいい
・顔だけ出しておけばいい
っていうときは使い勝手が悪いわけです

なので
カスタム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終わり******/

19/12/11追記
「配信の時自分はFacerigとかで動いてるからオーバーレイに自分が居たら困る・・・」
と言われたのです
ID使うから最初のコンセプトとは外れますが
もし自分のことを消したい方は

/* 選択したユーザの画像を非表示にする */
li.voice-state[data-reactid$="ここにじぶんのID"] {
 display:none;
}

を先頭に追加してください
そして「ここにじぶんのID」のところをDiscrdの自分のIDにしてください

ちなみに
IDの出し方は
歯車マークの「ユーザー設定」→「テーマ」→「詳細設定」にある「開発者モード」をオンにしてIDを取得したいユーザーを右クリック
「IDをコピー」でIDとなる数字の羅列をコピーできます
(技術者さんは当たり前にわかるかもですが 私の場合、技術者さん以外も見る可能性がありますのでご容赦ください)

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