20191223のCSSに関する記事は17件です。

[ライブコーディング]JavaScriptでモンテカルロ法シミュレータを作る

モンテカルロ法とは

ルーレットの必勝法とか言われる掛け方の法則。
実際にカジノで実践するには紙とペンが必要になるので、それをWebアプリ化したかった

言い訳

事前準備のない、ガチのライブコーディングをやりたかった。

そして、
フレームワークとか、IDEだとか、ネーミングとか
一切気にせず
ただただ頭に浮かんだコードを書いて、htmlとjscssで楽しく遊ぶことを意識した

結果

とんでもないコードができあがった。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>モンテカルロ法シミュレーター</title>
    <link rel="stylesheet" href="index.css">
  </head>
  <body>
    <h1>モンテカルロ法シミュレーター</h1>

    <div>
      ゲーム数: <span id="js-game"></span>
    </div>

    <div>
      数列: <span id="js-sequence"></span>
    </div>

    <div>
      bet: <span id="js-bet"></span>
    </div>

    <div>
      status: <span id="js-status"></span>
    </div>

    <div>
      ベット総額: <span id="js-total-bet"></span>
    </div>

    <div class="controller">
      <button id="js-win">勝った</button>
      <button id="js-lose">負けた</button>
    </div>
    <script src="index.js"></script>
  </body>
</html>

index.js
import $ from 'jquery';

// ゲームの回数
let game = 1;

// 数列
let sequence = [];

// ベット数
let bet = 0;

let status = 'はじまり'

const getSequence = (game) => {
    if (game === 1) {
        return [1, 2, 3];
    } else {
        return sequence;
    }
}

const getBet = () => {
    const first = sequence[0];
    const last = sequence[sequence.length -1];

    return first + last;
}

// 勝ったときの処理
$(document).on('click', '#js-win', () => {
    game++;
    sequence.pop();
    sequence.shift();

    // 終了かどうか
    if (sequence.length <= 1) {
        status = 'おわり'
    }

    // ゲーム数表示
    $('#js-game').html(game);

    // 数列表示
    sequence = getSequence(game);
    $('#js-sequence').html(sequence.join(','));

    // ベット表示
    bet = getBet();
    $('#js-bet').html(bet);

    // ステータス表示
    $('#js-status').html(status);


})

// 負けたときの処理
$(document).on('click', '#js-lose', () => {
    game++;
    sequence.push(bet);

    // ゲーム数表示
    $('#js-game').html(game);

    // 数列表示
    sequence = getSequence(game);
    $('#js-sequence').html(sequence.join(','));

    // ベット表示
    bet = getBet();
    $('#js-bet').html(bet);

    // ステータス表示
    $('#js-status').html(status);
})

// ゲーム数表示
$('#js-game').html(game);

// 数列表示
sequence = getSequence(game);
$('#js-sequence').html(sequence.join(','));

// ベット表示
bet = getBet();
$('#js-bet').html(bet);

// ステータス表示
$('#js-status').html(status);

感想

忘れかけていた何かを思い出した。

ライブコーディングの様子:
https://youtu.be/HJR_iUAMLWA

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

JSでDVDのロゴが動くやつ作ってみた

JSでDVDのロゴが動くやつを作りたくなったので作ってみました。

ezgif.com-video-to-gif.gif

デモ

dvd.pug
#container
  #box(style="width: 100px; height: 100px; background: red;") DVD
button#play-button 再生
dvd.scss
* {
  box-sizing: border-box;
}

body {
  margin: 0;
}

#container {
  width: 500px;
  height: 500px;
  background-color: black;

  #box {
    font-size: 44px;
    transition: transform 1s linear 0s;
  }
}
dvd.ts
// 色の文字列を格納した配列
const colors = ["red", "blue", "yellow", "green"];

// boxのHTML要素
const box = document.getElementById("box");
// boxのwidth(数値)
const boxWidth: number = Number(box.style.width.replace("px", ""));

// containerのHTML要素
const container = document.getElementById("container");

// HTML要素の現在位置を取得する
function getCurrentPosition(element: HTMLElement) {
  const elementRect = element.getBoundingClientRect();
  return {
    top: elementRect.top,
    right: elementRect.right,
    bottom: elementRect.bottom,
    left: elementRect.left
  };
}

// boxの初期位置
const boxInitialPosition = getCurrentPosition(box);
// boxの現在位置
let boxCurrentPosition = boxInitialPosition;

// 現在位置の方向
enum Direction {
  Top,
  Right,
  Bottom,
  Left
}

// containerの位置
const containerPosition = getCurrentPosition(container);

// boxの左上の頂点が移動できるx座標の最小値
const minimumX = containerPosition.left;
// x座標の最大値
const maximumX = containerPosition.right - boxWidth;
// y座標の最小値
const minimumY = containerPosition.top;
// y座標の最大値
const maximumY = containerPosition.bottom - boxWidth;

// x座標の可動域(range of motion)の距離
const romX = maximumX - minimumX;
// y座標の可動域の長さ
const romY = maximumY - minimumY;

// boxを指定した絶対位置に移動させる
function changeTranslate(x: number, y: number): void {
  box.style.transform = `translate(${x - boxInitialPosition.left}px, ${y -
    boxInitialPosition.top}px)`;
}

// boxを現在位置から指定した距離だけ移動させる
function changeTranslateFromCurrent(x: number, y: number): void {
  box.style.transform = `translate(${getCurrentPosition(box).left +
    x -
    boxInitialPosition.left}px, ${getCurrentPosition(box).top +
    y -
    boxInitialPosition.top}px)`;
}

// 配列の要素をランダムに取り出す
function getRandomFromArr<T extends any[]>(arr: T): T[number] {
  return arr[Math.floor(Math.random() * arr.length)];
}

// ピタゴラスの定理
function pythagorasTheorem(x: number, y: number) {
  return Math.sqrt(x ** 2 + y ** 2);
}

// boxの移動する速さ[px/s]
const boxSpeedPerSecond = 200;

// 再生中かのフラグ
let isPlaying = false;

// boxに付与する、transitionendイベント用のイベントハンドラ
function onTransitionEnd() {
  // boxの背景色を現在の色以外のランダムな色に変える
  const filteredColors = colors.filter(
    color => color !== box.style.backgroundColor
  );
  box.style.backgroundColor = getRandomFromArr(filteredColors);

  // 前回の位置
  const boxPrevPosition = boxCurrentPosition;
  // 現在位置
  boxCurrentPosition = getCurrentPosition(box);
  // 現在位置が上辺、右辺、下辺、左辺のどの方向にあるか
  const direction: Direction = (() => {
    switch (boxCurrentPosition.top) {
      case minimumY:
        return Direction.Top;
      case maximumY:
        return Direction.Bottom;
    }
    switch (boxCurrentPosition.left) {
      case minimumX:
        return Direction.Left;
      case maximumX:
        return Direction.Right;
    }
  })();
  // 前回の位置と現在位置の間の、x軸方向の距離とy軸方向の距離
  const prevToCurrentLength = {
    x: Math.abs(boxCurrentPosition.left - boxPrevPosition.left),
    y: Math.abs(boxCurrentPosition.top - boxPrevPosition.top)
  };
  // yに対するxの割合
  const XYrate = prevToCurrentLength.x / prevToCurrentLength.y;

  // 現在位置の方向によって場合分け
  if ([Direction.Top, Direction.Bottom].includes(direction)) {
    // 現在位置に対して向かい側に跳ね返るか
    const isBounceOppositeSide = (() => {
      if (boxCurrentPosition.left > boxPrevPosition.left) {
        // 左から右に移動した場合
        return boxCurrentPosition.left + romY * XYrate < maximumX;
      } else {
        // 右から左に移動した場合
        return minimumX < boxCurrentPosition.left - romY * XYrate;
      }
    })();

    // 現在位置と次回の位置の間の、x軸方向の距離とy軸方向の距離
    const currentToNextLength = (() => {
      if (isBounceOppositeSide) {
        const y = romY;
        return {
          x: y * XYrate,
          y
        };
      } else {
        const x =
          boxCurrentPosition.left > boxPrevPosition.left
            ? maximumX - boxCurrentPosition.left
            : boxCurrentPosition.left - minimumX;
        return {
          x,
          y: x / XYrate
        };
      }
    })();

    // 移動する時間を調整
    const currentToNextRLength = pythagorasTheorem(
      currentToNextLength.x,
      currentToNextLength.y
    );
    box.style.transitionDuration = `${currentToNextRLength /
      boxSpeedPerSecond}s`;

    // 現在位置から計算した距離だけ移動させる
    changeTranslateFromCurrent(
      (boxCurrentPosition.left > boxPrevPosition.left ? 1 : -1) *
        currentToNextLength.x,
      (direction === Direction.Top ? 1 : -1) * currentToNextLength.y
    );
  } else {
    // 現在位置に対して向かい側に跳ね返るか
    const isBounceOppositeSide = (() => {
      if (boxCurrentPosition.top > boxPrevPosition.top) {
        // 上から下に移動した場合
        return boxCurrentPosition.top + romX / XYrate < maximumY;
      } else {
        // 下から上に移動した場合
        return minimumY < boxCurrentPosition.top - romX / XYrate;
      }
    })();

    // 現在位置と次回の位置の間の、x軸方向の距離とy軸方向の距離
    const currentToNextLength = (() => {
      if (isBounceOppositeSide) {
        const x = romX;
        return {
          x,
          y: x / XYrate
        };
      } else {
        const y =
          boxCurrentPosition.top > boxPrevPosition.top
            ? maximumY - boxCurrentPosition.top
            : boxCurrentPosition.top - minimumX;
        return {
          x: y * XYrate,
          y
        };
      }
    })();

    // 移動する時間を調整
    const currentToNextRLength = pythagorasTheorem(
      currentToNextLength.x,
      currentToNextLength.y
    );
    box.style.transitionDuration = `${currentToNextRLength /
      boxSpeedPerSecond}s`;

    // 現在位置から計算した距離だけ移動させる
    changeTranslateFromCurrent(
      (direction === Direction.Left ? 1 : -1) * currentToNextLength.x,
      (boxCurrentPosition.top > boxPrevPosition.top ? 1 : -1) *
        currentToNextLength.y
    );
  }
}

// onTransitionEndイベントハンドラを付与
box.addEventListener("transitionend", onTransitionEnd);

const playButton = document.getElementById("play-button");
playButton.addEventListener("click", function() {
  if (!isPlaying) {
    // 再生中の状態にする
    isPlaying = true;
    // ボタンのテキストを「リセット」にする
    playButton.innerText = "リセット";
    // onTransitionEndイベントハンドラを付与
    box.addEventListener("transitionend", onTransitionEnd);

    // x座標の最小値超から最大値未満までの数字の連番が格納された配列
    const randomXArr = (() => {
      let arr: number[] = [];
      for (let i = minimumX + 1; i < maximumX; i++) {
        arr.push(i);
      }
      return arr;
    })();
    // y座標の最小値超から最大値未満までの数字の連番が格納された配列
    const randomYArr = (() => {
      let arr: number[] = [];
      for (let i = minimumY + 1; i < maximumY; i++) {
        arr.push(i);
      }
      return arr;
    })();

    const random = Math.random();
    // 右辺に移動させるか、下辺に移動させるかをランダムに決める
    if (random < 0.5) {
      // x座標の最小値超から最大値未満までの数字をランダムに取り出す
      const randomY = getRandomFromArr(randomYArr);
      // 斜辺の長さを求める
      const rLength = pythagorasTheorem(maximumX, randomY);
      // 移動する時間を調整
      box.style.transitionDuration = `${rLength / boxSpeedPerSecond}s`;
      // 右辺のランダムな位置に移動させる
      changeTranslate(maximumX, randomY);
    } else {
      // y座標の最小値超から最大値未満までの数字をランダムに取り出す
      const randomX = getRandomFromArr(randomXArr);
      // 斜辺の長さを求める
      const rLength = pythagorasTheorem(randomX, maximumY);
      // 移動する時間を調整
      box.style.transitionDuration = `${rLength / boxSpeedPerSecond}s`;
      // 下辺のランダムな位置に移動させる
      changeTranslate(randomX, maximumY);
    }
  } else {
    // 停止中の状態にする
    isPlaying = false;
    // ボタンのテキストを「再生」にする
    playButton.innerText = "再生";
    // 色を赤に戻す
    box.style.background = "red";
    // onTransitionEndイベントハンドラを削除
    box.removeEventListener("transitionend", onTransitionEnd);
    // トランジションをOFFにする
    box.style.transitionDuration = "0s";
    // 左上に移動させる
    changeTranslate(minimumX, minimumY);
    // 現在位置をリセット
    boxCurrentPosition = getCurrentPosition(box);
  }
});

ずっと見てられますね。

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

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

100日チャレンジの192日目

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

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

192日目は

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

disabled にしたボタンの hover スタイルが消えなくて困った時の対処法

修正前のコード

disabled="disabled" にしても hover のスタイルが適用されてしまう...

See the Pen xxbdWvX by Shiori SHONO (@sshono1210) on CodePen.

修正後のコード

「〜はスタイルを除外したい」というものを指定できる :not() を使う

See the Pen qBEmoxm by Shiori SHONO (@sshono1210) on CodePen.

参考

https://qiita.com/noraworld/items/74ab6d7f6ace6e7ec3c4

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

Chrome拡張機能開発の公式チュートリアルを解説+補足 前編

はじめに

GoogleのChrome拡張の公式が出してるチュートリアルを解説します。
僕なりに分かりやすくなるように、公式の流れに沿って書こうと思います。
ただの翻訳といえば否定できませんが、それだけだとつまらないので、ところどころ補足欲しいなぁ〜て自分が感じたところを付け足していきます。
僕は英語が嫌いなので、僕と同じように英語で書いてあるサイト読むのだりぃ〜って人の役に立てばと思います。

説明に入る前に、僕が以前書いたこの記事

を読んでいただけると少しは理解しやすくなるかと思います。
分かりにくかったら、その記事に載せてる参考文献が分かりやすいので、そちらを見てください。

要約すると、Chrome拡張でやりたいことを実現するには

  • Background
  • Contents Script
  • Popup

の3つの世界を使い分ける必要があるってことです。
理解していただけたら、次に進みましょう。

チュートリアル

さっそく補足ですが、まずは色々なファイルを入れる用のフォルダを作っておきましょう。
場所はどこでも良いです。名前も何でも良いです。
僕は試しに、デスクトップに「testChromeExtension」というフォルダを作りました。
フォルダ作成
ここに、これから作成するファイルや、アイコンの画像などを入れていきます。
それでは作成していきます。

1. マニフェストファイルの作成

「Manifest = 公約」という意味から分かるように、決まりごとを書いていくファイルです。
これがないと拡張機能が作れません。
例えば、

  • この拡張機能の名前は何なのか
  • マニフェストのバージョンは何を使うのか
  • アイコンの画像はどれか
  • PopupのHTMLファイルはどれを使うのか

などをJSONファイルに書いていきます。
ファイル名は「manifest.json」にしましょう。
公式に書いてあるのはこれです↓

manifest.json
{
    "name": "Getting Started Example",
    "version": "1.0",
    "description": "Build an Extension!",
    "manifest_version": 2
}

とりあえずこれだけです。
簡単に補足すると、

  • name → 拡張機能の名前(自分で決める)
  • version → 拡張機能のバージョン(自分で決める)
  • description → 拡張機能の説明(自分で決める)
  • manifest_version → マニフェストのバージョン(これは固定、現在*投稿日現在*は2を書く)

という意味になります。
自分用に書き換えると、こうなります↓

manifest.json
{
    "name": "Test Chrome-Extension",
    "version": "1.0",
    "description": "これはChrome拡張機能のテストです。",
    "manifest_version": 2
}

2. アップロード

マニフェストファイルを作り終えたら、最初に作成したフォルダに保存しましょう。
マニフェストファイルを保存

早速これをアップロードしていきます。
ローカルで実行するだけですので、実際に拡張機能のストアに公開するわけではありません。
chrome://extensions/ を開きましょう。
アップロード手順

(0. まず、⓪のデベロッパーツールをオンにして、「パッケージ化されていない拡張機能を読み込む」、「拡張機能をパッケージ化」、「更新」の3つのボタンを表示させましょう。)
 1. ①の「パッケージ化されていない拡張機能を読み込む」というボタンを押しましょう。
  英語だと、「LOAD UNPAKED」と書いてあるかと思います。
 2. フォルダを選べるようになるので、最初に作成してマニフェストファイルを入れたフォルダ(②)を選択します。
 3. 選択!(③)

アップロード完了
すると、マニフェストファイルに書き込んだ名前・バージョン・説明・自動生成されたIDが表示されてるかと思います。
アイコンはまだ設定していないので、デフォルトのままですね。
表示されていたら成功です。
表示されていなかったら、ファイル名「manifest.json」やJSONに記入ミスが無いか、manifett_version はちゃんと 2 になっているかを確認してください。

以上がアップロードの手順です。
これからファイルを更新したら、右下のラジオボタンの隣にある更新マークを押せば、更新できます。
反映されない場合は、一旦削除してから再びアップロードするなどを試してみてください。

3. Backgroundファイルの作成

まずは、マニフェストファイルに、「Backgroundのコードはこのファイルに書くよ!」ということ教えてあげる必要があるます。
公式と同じように追加します。

manifest.json
{
    "name": "Test Chrome-Extension",
    "version": "1.0",
    "description": "これはChrome拡張機能のテストです。",

    "permissions": ["storage"],
    "background": {
      "scripts": ["background.js"],
      "persistent": false
    },

    "manifest_version": 2
}

BackgroundファイルはJavaScriptで記述します。
ファイル名は何でも良いですが、無難に「background.js」とでもしておきましょう。

それぞれの説明をします。

  • permission → 使うAPI等を記述します。今回はstrage APIを使用します(詳細は後述)。
  • background → Backgroundに関しての色々を記述します。
    • script → Backgroundファイルのファイル名を記述します。
    • persistent → 永続的に動くか否か。falseにすることで、backgroundファイルが呼ばれた(通信する時)のみ動きます。永続的に動いているとメモリが無駄に消費されてしまうので、falseにすることをお勧めします。

では、実際にBackgroundファイルを作成していきます。

background.js
chrome.runtime.onInstalled.addListener(function() {
    chrome.storage.sync.set({color: '#3aa757'}, function() {
        console.log("The color is green.");
    });
});

ここで使用するstorage APIは、名前の通りデータを保存してくれるストレージのAPIです。
今後書くContents Scriptファイルに書くことで、わざわざBackgroundにデータを移さなくても、データを保持してくれるみたいです。
ただ、容量がそんなに大きく無いのと、セキュリティがガバガバなので個人情報などは扱わない方が良い、というのが注意点です。

  • chrome.runtime.onInstalled.addListener(function() {}); → 拡張機能がインストールされたときや更新された時に関数内を実行する。
    • chrome.storage.sync.set({color: '#3aa757'}, function() {}); → 先ほど許可したstorage APIの命令です。colorというKeyに#3aa757というValueをセットしています。
      • console.log("The color is green."); → コンソールに文字を出力します。デバッグなどに使えます。

とりあえずこれらをコピペして、フォルダに保存してください。
スクリーンショット 2019-12-23 16.28.19.png
ファイルが保存できたら、chrome://extensions/ で拡張機能を更新しましょう。

すると、「バックグラウンド ページ」というリンクができているかと思います。
拡張機能更新
そのリンクを押すと、Backgroundのデベロッパーツールが表示されます。
background.jsに書いたコンソール出力は、ここで確認することができます。
Background_コンソール
これでBackgroundでChrome拡張を開発することはできました。
ただ、まだ見た目には何も変化がないですが、重要な準備段階です。
後日、残りのチュートリアルの解説をしていきます

おわりに

今回はここまでです。
あと中編と後編を書く予定です。
書けたらリンクを貼ります。

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

CSS擬似クラスnth-of-typeが予想以上に使える件

nth-of-typeとは

擬似クラスnyh-of-typeは、同じ階層にある同じ要素で指定した順番の要素をセレクトすることができる擬似クラスです。
nth-of-type(odd)で奇数番目の要素、nth-of-type(even)で偶数番目の要素、nth-of-type(3n)で3の倍数の要素、など様々な順番でのセレクトが可能です。

class-name:nth-of-type(odd) {
}

class-name:nth-of-type(even) {
}

class-name:nth-of-type(3n) {
}

これが可能なことで、デザインにもすごい幅が生まれます。
例えば、奇数番目のbackground-colorはblackで、偶数番目のbackground-colorはwhiteとすることで、簡単にシマウマ模様が作れたりなどデザイン性は無限にありそうです。

ぜひ擬似クラスを使用したことのない方は、使用してみてはいかがでしょう。

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

HTML テーブルの空セルに斜線を引く方法

テーブルの空セルに斜線を引く方法を紹介します。

まずはテーブルを作成します。

index.html
    <table>
        <tr>
            <th></th>
            <th>タイトル1</th>
            <th>タイトル2</th>
            <th>タイトル3</th>
        </tr>
        <tr>
            <td>1</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>2</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>3</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </table>
style.css
/* table */
table {
    border-collapse: collapse;
    margin: 20px;
}
table th,
table td {
    width: 50px;
    height: 50px;
    text-align: center;
    border: 1px solid black;
}

スクリーンショット 2019-12-23 16.03.22.png
このようなテーブルが作成されます。
それでは、この空のセルに斜線を書いてみましょう。

右上がりの斜線

右上がりの斜線の書き方です。
斜線を入れたいセルにクラスを追加してください

index.html
    <tr>
        <th class="right_up_border"></th> <!-- 斜線を入れたいセルにクラスを追加する -->
        <th>タイトル1</th>
        <th>タイトル2</th>
        <th>タイトル3</th>
    </tr>

そしたらCSSを書いていきます。

style.css
/* 右上がりの斜線 */
.right_up_border {
    background-image: linear-gradient(-45deg, /*角度*/
                     transparent 49%,
                     black 49%, /*斜線の色*/
                     black 51%, /*斜線の色*/
                     transparent 51%, 
                     transparent); 
}

スクリーンショット 2019-12-23 16.05.29.png
これで斜線を引くことができました!!
簡単です!

右下がりの斜線

次は右下がりの斜線です。これもやり方は同じです

index.html
    <tr>
        <th class="right_up_border"></th> <!-- 斜線を入れたいセルにクラスを追加する -->
        <th>タイトル1</th>
        <th>タイトル2</th>
        <th>タイトル3</th>
    </tr>
style.css
/* 右上がりの斜線 */
.right_up_border {
    background-image: linear-gradient(45deg, /*角度*/
                     transparent 49%,
                     black 49%, /*斜線の色*/
                     black 51%, /*斜線の色*/
                     transparent 51%, 
                     transparent); 
}

スクリーンショット 2019-12-23 16.15.45.png
できました!!
簡単ですね!

角度の求め方はこちらを参照してください。

底辺と高さから角度と斜辺を計算

まとめ

こんな感じでテーブルの空セルに斜線を引くことができました。
でも多分他にもやり方はあると思うので、アドバイス有りましたらよろしくお願いします。

参考リンク

CSSで斜線を引く方法
底辺と高さから角度と斜辺を計算

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

ナンの実りもないクソアプリを作った

ナンを咲かせる木を作った

題して「枯れ木にナンを咲かせましょう」

See the Pen 枯れ木にナンを咲かせましょう by Thugumi Ishimaru (@thugumi-ishimaru) on CodePen.

「ナンを咲かせる」ボタンを押したら木にナンが生るクソアプリを作りました。
20個までナンを咲かせることができます。

遊び方

  • 1. 「ナンを咲かせる」ボタンを押す
  • 2. ナンが咲く

以上!!!!!!!!!!!!とっても簡単です!!!!!!!!!!!誰でも遊べる!!!!!!!!!!
そしてクソアプリ!!!!!もちろんなんの役にも立たない!!!!!!!

アプリもクソなのですが、ソースもクソなので何がクソなのかをこちらに解説していこうと思います。
このアプリを反面教師にしていただけたらと思います。

クソアプリのここがクソ

クソ部分その1:何の得もない

この木にナンができたからといって、「......で?」で終わると思うんです。
そう、本当にナンの役にも立ちません。

クソアプリなんだからいいですよね。。。クソなんだから。。。

クソ部分その2:scssが長すぎる

ソースをみてみましょう。

index.html
<div class="nan"></div>
style.scss
.nan {
  width: 400px;
  height: 300px;
  position: absolute;
  top: -100px;
  &_item {
    position: absolute;
    z-index: 1;
    &:nth-child(1) {
      left: 0;
      top: 0;
    }
    &:nth-child(2) {
      left: 100px;
      top: 0;
    }
    &:nth-child(3) {
      left: 200px;
      top: 0;
    }
    &:nth-child(4) {
      left: 300px;
      top: 0;
    }
    &:nth-child(5) {
      left: 0;
      top: 100px;
    }
    &:nth-child(6) {
      left: 100px;
      top: 100px;
    }
    &:nth-child(7) {
      left: 200px;
      top: 100px;
    }
(中略)
    &:nth-child(20) {
      left: 200px;
      top: 100px;
    }

scssファイル読みづら!!!!!!!!
20個以上増やしたくなった時にまたscssに増やさんといけんとか頭痛くない?!
しかもいちいちズラすの?!?!?!?!??!
間に.nan_itemじゃないもの入ってきたら死ぬよ?!?!?!??!

と、色々出てくるんです。
じゃあどうすればいいか、、、今回はscssのみで解決できる方法をお伝えしようかと思います。

解決方法手順その1: ルールを明確にする

間に.nan_itemじゃないもの入ってきたら死ぬよ?!?!?!??!

なぜ死ぬか、nth-child(n)を使っているからです。
と思うのですが、その前にルールを明確にしていないのですね。
ではルールを明確にしてあげましょう。

ということで「.nan以下には.nan_itemしか入れちゃ嫌!!!」とルール化しちゃいましょう。コードで語るのです。
<ul>タグの配下には<li>のみ記述が許可されているのでそのルールを利用します。

  • div.nanul.nanに変更
  • div.nan_itemli.nanに変更
index.html
<ul class="nan"></ul>
script.js
$('.nan').append('<li class="nan_item"><img src="https://illust-shokudo.com/assets/item/other_00001.png" alt=""</li>');

これで、.nan.nan_itemの間に部外者を入れられなくなりました。

解決方法手順その2: for文でnth-child(n)を生成

20個以上増やしたくなった時にまたscssに増やさんといけんとか頭痛くない?!

大変面倒ですよね。次にこれを解決しましょう。
scssではfor文が使えるので、そちらを使用します。

style.scss
.nan {
  width: 400px;
  height: 300px;
  position: absolute;
  top: -100px;
  &_item {
    position: absolute;
    z-index: 1;
    @for $i from 1 through 20 {
      &:nth-child(#{$i}) {
       top: 0;
       left: 0;
      }
    }
  }
}

これでnth-child地獄は脱出しました。
しかし、このままでは配置位置が全て同じになってしまう。。。じゃあどうしよう。。。

次の手順をみていきましょう。

解決方法手順その3: 絶対配置の配置位置をランダムにする

このままでは配置位置が全て同じになってしまう。。。

以下の記事にもあるようにscssではrandom()が使用できます。
これによって、ランダムの数値を当てることができるようになります。
今回はナンの位置を数値で指定していますよね。こちらを使ってみましょう。
https://qiita.com/tonkotsuboy_com/items/909b4073459ecaf7a435

style.scss
.nan {
  width: 400px;
  height: 300px;
  position: absolute;
  top: -100px;
  &_item {
    position: absolute;
    z-index: 1;
    @for $i from 1 through 20 {
      &:nth-child(#{$i}) {
        left: random(300) + px;
        top: random(300) + px;
      }
    }
  }
}

これで全てのナンをバラバラの位置に表示することができます。

このリファクタを行ったナンの木がこちら

See the Pen 枯れ木にナンを咲かせましょう(リファクタバージョン) by Thugumi Ishimaru (@thugumi-ishimaru) on CodePen.

この実装のメリット

  • scssのみで実装可能
  • scssのソースがスッキリして読みやすくなる

この実装のデメリット

  • 数値はrandom()なので毎回うまく配置されるとは限らない。前提が「この位置に絶対配置するのである」の要件にはそぐわない。

まとめ

クソアプリと共にクソースコードを紹介しました!
クソコードを修正するのに、以下を明確にしています。

  • 枯れ木にナンを咲かせるクソアプリを作った

  • 何がクソか

    • 要素が増えたときに死ぬ
    • 長い
    • 読みづらい
  • scssで以下を修正してクソから少し脱却する

    • ルールを明確にする
    • for文を使用する
    • random()を使用する

枯れ木にナンを咲かせましょう!!!!!!!

おまけ

.nanって命名にしてますが、JavaScriptではNaNという予約語が存在するので、
こんがらがらないように気をつけましょう。
れむさんありとうございます!
https://www.javadrive.jp/javascript/ini/index5.html

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

【CSS】<img>を上下左右中央で、エリアいっぱいに配置する

やりたいのはこれ!

上下左右中央配置 (3).png

作りたいのは、よくあるサムネイル付き記事の 一覧ページ↑↑。
(ほんでもって、ホバーしたら画像がふわっと拡大表示なんかしたりね、するよね。ね。)(←後述します)

今回は、cssだけでごにょごにょがんばります。

実装

3カラムとか作る前に、一旦width100%でコンポーネントを作ります。
widthが100% (3).png

index.html
<article class="articleWrap">
  <a class="thumbnailWrap" href="#">
    <img class="thumbnail" alt="画像のalt" src="画像のパス" />
  </a>
  <h3 class="title"><a href="#">記事タイトル</a></h3>
  <p class="text">どんな画像を入れてもピッタ。。。</p>
</article>
style.scss
a:hover{
  cursor:pointer;
}
.articleWrap {
  width: 100%;
  background-color:#eee;
}
.thumbnailWrap {
  overflow: hidden;
  position: relative;
  display:block;

  height: 200px;// ←画像の高さを設定します。
  /* (※ @mediaで、ウィンドウ幅ごとにheightを書き分けている↓↓のは、リキッドデザインに対応するためです。
  この一手間で、ウィンドウ幅が伸び縮みしてもきれいに表示させることが出来ます!)*/
  @media (min-width: 641px) and (max-width: 1000px) {
    height: 18vw;
  }
  @media (max-width: 640px) {
    height: 48vw;
  }
}

$times: 5; //何倍
.thumbnail {
  min-height: calc(100% * #{$times});//←ここがポイント!
  min-width: calc(100% * #{$times});//←ここがポイント!
  transform: translate(-50%, -50%) scale(calc(1 / #{$times}));//←←scale(1/5) = scale(0.2)ここがポイント!
  position: absolute;
  top: 50%;
  left: 50%;
}

↓何してる??

<img class="thumbnail">5倍して、5で割ります
からのー、
translateとpositionで中央配置!
すると、ほーらできあがり\(^o^)/
($timesは、5にしています。いろんな画像をはめてみたところ、5で問題なく表示できました。)

ホバーしたらふわっと拡大表示させちゃう?

せっかくなので、ユーザーがリンク領域だと認識できるように、
ホバーしたらアニメーションをさせてみましょう!

htmlに、classを1つ、zoomAnimationを新たに付加します。
場所は、thumbnailのところです。

index.html
<article class="articleWrap">
  <a class="thumbnailWrap" href="#">
    <img class="thumbnail zoomAnimation" alt="画像のalt" src="画像のパス" />
  </a>
  <h3 class="title"><a href="#">記事タイトル</a></h3>
  <p class="text">どんな画像を入れてもピッタ。。。</p>
</article>

scssには、以下のstyleを追記します。↓

style.scss
// 記事をホバーしたら。。
.zoomAnimation {
  transition: all 0.5s ease;
  transform-origin: center;
  &:hover{
    transform: translate(-50%, -50%) scale(calc(1 / #{$times} + 0.02));//←0.02足す
  }
}

scaleを0.2から、ホバーしたら0.22にして、少しだけふわっと大きくなるようにしました。
ってことで、これでコンポーネントは完成!

See the Pen どんな画像も中央でぴったりフィット by sayaka kiyose (@sayaka_kiyose) on CodePen.

3カラムにして見た目を整えよう!

widthが100% (2).png

前に書いた記事内の、3カラムレイアウト<li class="col3ListItem">の中に、
<article>タグごと放り込むだけ!おわり!

See the Pen 【3カラム】どんな画像も中央でぴったりフィット by sayaka kiyose (@sayaka_kiyose) on CodePen.

background-imageじゃだめなの?

いざCMS化して、方向もサイズもバラバラな画像たちが
imgタグに流し込まれたら、崩れてしまった。。。
だったらいっそのこと、
imgタグなんかやめちゃって、background-imageからのbackground-size:coverにしちゃって。。。↓
名称未設定ファイル (1).png

↑↑こんな風に出来たらなんて楽なんだろう。

でも、CSSはあくまで装飾。
コンテンツによっては、<img>タグとして画像を表示させたいときもありますよね。
見た目だけOKではなく、
セマンティックなマークアップをしていきましょう。!!





長くなりましたが終わり!

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

治安の良いCSSを目指して 〜 平和な世界のために僕たちができること 〜

はじめに

業務でCSSを書くようになってから、いくつかの月日が流れました。
CSSを学び始めた当初は、要素をキレイに横並びにすることすら手こずっていましたが、最近は随分スムーズにデザイン通りのスタイルを書くことができるようになりました。

今日に至るまで、過去の自分が書いたCSSへの後悔の念で眠れない日々や、原因のよくわからない表示崩れの悪夢にうなされる夜もありました。1
これからCSSを学ぶ人、CSSにはあまり詳しくないけどたまに書くよという人にそんな思いをして欲しくない。できたらCSSのことを好きになって欲しい。

そんな思いで自分がスタイルを書く時・レビューをする時に気をつけていることを(自戒も込めて)まとめまてみました。

? 良いスタイルってなんだろう?

スタイルを書く時に大切だと考えていることは3点あります。

  • 開発効率
  • デザイン再現性
  • パフォーマンス

開発効率

色々な記事や本でも引用されているCSS Architectureという記事によると、CSS設計のゴールには4つのポイントがあります。

  • Predictable(予測しやすい)
  • Reusable(再利用しやすい)
  • Maintainable(保守しやすい)
  • Scalable(拡張しやすい)

Predictable(予測しやすい)

予測しやすいスタイルとは、ルールが期待どおりの振る舞いをするスタイルのことです。
ルールを追加や更新した場合に意図しない箇所へ影響を与えないこと、つまり影響範囲が明確であることを指します。

Reusable(再利用しやすい)

再利用しやすいスタイルとは、同じようなデザインのUIが登場した時に再コーディングせず、既存のパーツを使いまわせるスタイルのことです。
抽象的で機能が分離している必要があり、そのようなスタイルは制作・運用を効率的にします。
逆に再利用しにくいスタイルの例をあげると、モジュール自体に他のモジュールに影響を与えるmarginがついていたり、クラス名にh1とついていて、同じ見た目のh2が登場した時に使いまわせないスタイルが挙げられます。

Maintainable(保守しやすい)

保守しやすいスタイルとは、新しいコンポーネントの追加・更新・再配置する場合に既存のスタイルをリファクタリングする必要がないスタイルのことです。

Scalable(拡張しやすい)

拡張しやすいスタイルとは、単純に個々のCSSのルールが増える場合だけでなく、サイト・サービス自体が拡大して、複雑になってきても耐えうることを指します。
サイト・サービスが拡大してチームに新しいメンバーが増えた場合の教育コストが低いことも含まれます。

Scalable(拡張しやすい)スタイルにするためには...

「Predictable(予測しやすい)」、「Reusable(再利用しやすい)」、「Maintainable(保守しやすい)」を守ると同時に、コンポーネント設計により関心の分離・カプセル化を行うことが重要だと考えています。
そのためには、styled-componentsEmotionなどのCSS in JSを使用してコンポーネント思考でスタイルを書くこと、BEM、OOCSS、SMACSS、FLOCSSといったCSS設計手法を取り入れる事が大切です。

デザイン再現性

デザイン通りの表示になるように、きちんとスタイルが当てられているかを指します。
「デザイン通りの表示」とは、細かくみていくと多くの確認箇所があります。
例えば、下記の場合にも表示が崩れずに、デザインの意図がきちんと反映されているが重要です。

  • すべての対応ブラウザで表示した時
  • ウィンドウサイズを変えた時
  • ウィンドウの縦長/横長表示を変えた時
  • 動的に取得する箇所に様々なパターンのデータが入ってきた時
  • ブラウザの機能でテキストサイズを大きくした時
  • ページを拡大・縮小表示した時

どこまで対応するかの線引きはプロジェクトによって様々かと思います。プロジェクト内で他のメンバーやクライアントと対応範囲の合意を取る事でスムーズに案件を進める事ができます。

パフォーマンス

DOMを必要以上に深くしない

スタイルの計算では、マッチングするセレクタを総当たりで探します。
DOMが多くなれば、それだけ計算コストがかかります。
なるべくシンプルなDOM構造で書くことを意識しましょう。

セレクタを最適化する

ブラウザはセレクタを右から左へ照合する

.box p {...} 

というセレクタの指定があった場合、ブラウザはページ内のすべてのpタグを探し、次にclass="box"をもつ要素を探します。
そのため、キーセレクタ(一番右のセレクタ)とマッチする要素・子孫セレクタが少ないほどパフォーマンスが良いセレクタだと言えます。

要素にはなるべくユニークなクラスを与え、隣接セレクタ(A + B)や子セレクタ(A > B)、全称セレクタ(*)の乱用をしないよう心がけましょう。

ただし、近年のブラウザは以前より処理速度が改善しているため、神経質になりすぎず、開発効率も考慮してセレクタを最適化すると良いでしょう。

不要なスタイルがない

スタイルは必要な箇所に必要なだけ書くことが大切です。
下記を意識することでCSSのサイズが肥大化しないようにしましょう。

  • 使用していないスタイルは削除する
  • ベンダープレフィックスは必要十分なだけ
  • なるべくシンプルなスタイルを書く

レンダリングの負荷が考慮されている

ブラウザがレンダリングする際に要素の位置関係と大きさをを計算するレイアウトの処理後にしてから、ペイントの処理が行われます。
frame-full.jpg

引用: レンダリング パフォーマンス | Web | Google Developers

アニメーションをつける際は、なるべく処理順がフローの後のものを動かす方がレンダリングの負荷は軽くなります。
例えば、要素の位置を動かす時に、topleftの値をいじるより、transform: translate();を使用する方がパフォーマンス向上には有利です。
各CSSプロパティがどの段階で処理されるのか知りたい場合は
CSS Triggersが詳しいです。
また、will-changeプロパティを使用すると、要素にどのような変更を加えるかを前もってブラウザに知らせることができます。適切に使用することでパフォーマンスの改善が期待できます。
参考: MDN Web Docs | will-change

? スタイルアンチパターン

自分がコードレビューをするときに「mustで直してね」とコメントを書きがちな具体事例を紹介します。
もちろん、最適なスタイルの書き方はプロジェクトの性質によって変わってくる部分もあると思います。
プロジェクトのコーディング規約がない場合やこれから作るという場合の参考になれば幸いです。

!importantを使用している

!importantを指定すると優先度が最強になってしまいます?
!importantを指定したスタイルを上書こうとすると、セレクタで詳細度を高めた上で!importantをぶつけるという、血を血で洗う戦いが始まってしまいます。
!importantは切り札にして諸刃の剣...、これが繰り返されると地獄を見る羽目になるのでやめましょう。

ただ、既存のソースをいじれない場合や、外部ライブラリのスタイルを上書きしたいなど、どうしても!important使わなければならないという場面に出くわすこともあるかもしれません。

その場合は、後世のために「なぜ!importantなしではダメだったのか」をコメントに残すことをオススメします。

idにスタイルを当てている

idは1つのページで1度しか使用できません。
そのためidセレクタを使用すると、再利用することができなくなってしまいます。
また、idセレクタはクラスセレクタより詳細度が高いです。詳細度が複雑になると詳細度の管理コストが発生します。
結果として、予測しづらく、再利用しにくいCSSになってしまいます。

不要なスタイルが存在する

不要なスタイルはCSSのサイズを無意味に増やしてしまうし、見通しも悪くなります。
また、スタイルが継承されることで、その子孫要素にも不要なスタイルの影響が出てしまいます。
それにより、打ち消しのスタイルが増えたり、依存関係がわかりにくくなってしまいます。
不要なスタイルが混入する原因としては、「デザインデータのCSSからコピペしてきた」「色々試してた結果残ったままになっていた」というものが経験上多いです。
期待通りの表示になった後、不要なスタイルが紛れていないか再度確認すると良いでしょう。

valueが0の場合に単位が指定されている

marginpaddingborderなどを打ち消す時に0を指定することがありますがCSSの仕様でvalueが0の時は単位がoptionalになります。
とても細かいのですが、不要なものは書かないのが原則なので0の時は単位を書かないようにしましょう。2

line-heightに単位が指定されている

line-heightには単位を使用せず相対値で書く方がよいとされています。
理由は2点あります。
1つ目の理由は、スタイルは親から子に継承されるので単位ありの絶対値で指定すると、子要素で打ち消すスタイルを書かなくてはならない場合があります。
デザインでは「1.2」や「1.5」など、行間が統一されて決められていることが多いため、相対値で指定する方が打ち消さずに済むことが多いです。

2つ目の理由はブラウザの機能で文字を大きくした時に、line-heightの高さより文字の大きさが大きくなって、表示が崩れることがあるためです。

marginが相殺されている

marginには相殺の性質があります。
相殺が起きている要素間に別の要素が増えた場合や要素を再配置・再利用する場合にうまくいかないことが多いです。
再利用性・メンテナンス性が低くなってしまう原因になるため、相殺が起きるスタイルはあまり推奨しません。
マージンの相殺 | MDN

? 気をつけたいスタイル

「絶対だめ」というわけでもないですが、開発効率やデザイン再現性観点で注意したいスタイル指定についてまとめました。

heightが決め打ちで指定されている

要素のheightを決め打ちで指定する場合、動的なテキストが長くなる場合がないか・画面サイズが変わって中に入るテキストが折り返す場合がないかに注意しましょう。
また、ブラウザ側でテキストサイズを大きくした時に表示崩れが起きないかも確認すると良いでしょう。

marginの値がマイナス

値がマイナス値のmarginはネガティブマージンやマイナスマージンと呼びます。
多用するとmarginの計算が複雑になり、デザイン崩れの原因になりやすいです。
ネガティブマージンを使わなくても実現する方法がないか考えて、ここぞという時以外にはなるべく使用しないのが吉です。

Flexbox or Grid Layout以外での横並び・上下中央寄せ

一行の横並びはFlexbox、複数行の横並びはGrid Layoutで書くと、行数やDOMの深さが少なく済むことが多いです。
また、Flexbox or Grid Layoutを使うことでレスポンシブ対応もしやすくなります。

position: absolute;を指定している要素の直前の親にposition: relative;の指定がない

position: absolute;はpositionプロパティのstatic以外の値が指定されている親要素を基準に位置が決まります。基準としたい要素にposition: relative;を指定して、その直下にposition: absolute;要素を置くことで依存関係がわかりやすくなり、「あれ?absoluteなアイコンが、気がついたらいないぞ?」ということを防止できます。

? オススメスタイルTips

marginの向きを揃える

上で述べたmarginの相殺を防止するために、marginの向きを揃えることはとても有効です。
margin-top/leftで揃えるかmargin-bottom/rightで揃えるかは、個人的にはプロジェクト内で統一されていればどちらでもいいかなと思います。

font-sizeの指定はrem

font-sizepxで指定すると、ブラウザの機能でテキストサイズを大きくしようとしても変更できないため、アクセシビリティを考慮するとイマイチです。

また、SPとPCで基準となるfont-sizeを変えたいという場合やSPの時だけ画面幅に応じてfont-sizeを変えたいという場合もあるでしょう。

htmlタグにベースのfont-sizeを指定して、各要素にはremで指定をするとfont-sizeを扱いやすく書くことができます。

html {
    font-size: 62.5%;
    @media screen and (max-width: 768px) {
      font-size: calc(100vw / 375 * 10);
    }
}

全要素にbox-sizing: border-box;を指定する

box-sizing: border-box;を指定すると、widthheightで指定する幅と高さにpaddingborderの値が含まれるようになります。
これを全要素に指定することで幅と高さの算出方法をすべてのスタイルで統一する事ができます。
また、デザインデータを作成する際に、ボーダーあり/なしのパターンで高さを揃えるために、要素の内側にボーダーをつける事が多いらしいです。
box-sizing: border-box;の指定によって、デザインデータからCSSにスタイルを起こしやすくすることもできますね。

ressなどbox-sizing: border-box;の指定が初めから含まれているリセットCSSを使うか、そうでない場合はベースのスタイルとして自分で追加しておくとベターです。

*,
::before,
::after {
  box-sizing: border-box;
}

便利なツールを利用する

人間は不完全な生き物なので、ツールでできることは任せてしまいましょう。
見落としを防ぐだけでなく、実装やレビューの工数を削減にも繋がります。

便利なツールたち

  • stylelintでルールを指定する
  • stylelint + Prettierでフォーマットする
  • Autoprefixerで自動的に過不足ないベンダープレフィックスをつける
  • reg-suitでビジュアルリグレッションテストを行い意図しない表示の変更を検知する

?‍♂️ 最後に

1つ1つは細かいことですが、その積み重ねが平和なCSSの世界を築いていくと信じています。
私もまだまだ勉強中なので頑張ります。
世界が平和でありますように?


  1. イメージです。 

  2. ただしflex-basisはIE11で単位が必須なため、flex-basis: 0%とかく必要がります 

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

先頭の文字だけ大文字にする

image.png

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

Cssのcontentの中に入れられるもののサイト。

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

Vw,vh,vmin,vmaxの使い方

image.png

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

Borderの長さを短くしたい

image.png

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

まとめて書くやり方css

image.png

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

Inline-blockとblockの違い

image.png

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

最近流行りの斜めデザインのレイアウトを組む時に気をつけたほうが良いこと

最近流行りの斜めデザインのレイアウトを組む時に気をつけたほうが良いこと

こんばんわ。寒いですね。
いいネタが思いつかなかったので、最近苦戦した斜めデザインのCSSについて書きます。
斜めのデザインをCSSのskewで再現しようとすると、特定のブラウザで挙動が不安定なので気をつけよう・・・という内容になります。

問題の内容

まずはこれを見てください。

See the Pen povPeoy by Tatataichichichi (@unt_taichi) on CodePen.

よくある背景画像が斜めにトリミングされたデザインのレイアウトですね。
では、次にこちらを見てください。

See the Pen gObWweb by Tatataichichichi (@unt_taichi) on CodePen.

斜めになっている方向が、上のものとは逆向きになっているだけなのですが。。。
実はこちら、Safariなどのwebkit系のブラウザでレンダリングのバグが発生します。
背景画像の斜めの要素の部分が、ページをスクロールしたりコンテンツの表示域を可変させると、チラついた状態になり正しくレンダリングされません。

このレンダリングのバグの発生条件は、transformskewで右側に傾斜をつけた要素に対して、overflow:hiddenのCSSが適用されると起こるようです。

原因?

で、おそらくの原因ですが、transformskewで角度をつけたことにより、スタックコンテキストが生成されz-indexがただしく適用されず、Safariなどのブラウザ上ではレンダリングのバグとして表示されてしまう・・・ように見えます。

スタックコンテキストとz-indexについては、@ykyk1218さんの記事で分かりやすく書かれています。

参考記事:z-indexが言うことを聞いてくれない場合はこの辺のことが原因だと思うよ
https://qiita.com/ykyk1218/items/155ec7a8552da9fb2d8e

解決方法・・・?

解決方法としては、transform:skewで斜めに角度をつけた要素に、更にtranslate3d(0,0,0)でスタックレベル(Z軸)の指定をしてください。
これでSafariでもチラつかなくなると思います。

See the Pen LYEyWmM by Tatataichichichi (@unt_taichi) on CodePen.

分からなかったこと

・・・skewプロパティを使うことによりスタックレベルがずれるのが原因として、どうして右側に角度がついた時にレンダリングがおかしくなるのだろう。。。もし詳しい方いらっしゃいましたら教えていただけると幸いです。

それにしても寒いですね。。。風邪をひかないように暖かくしてクリスマスを迎えましょう!

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