20191223のHTMLに関する記事は13件です。

[ライブコーディング]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で続きを読む

pythonでスクレイピングした表をcsvに保存する方法

この記事について

研究でWebページ上にある表をスクレイピングする必要があったので,そのときに使用したpythonのプログラムを紹介します.ちなみに,自分はスクレイピング歴0だったので色々調べながら作っていったのですが,Webページ上の表をHTML化した後にHTMLの表部分をcsv化する方法まで説明しているものがほとんどなかったのでこの記事を書きました.

はじめに

スクレイピングについての注意点は,下記URLから御覧ください
https://qiita.com/Azunyan1111/items/b161b998790b1db2ff7a

Pythonでスクレイピング

プログラム全体はこちらに置いてあります.

import

import csv
import urllib
from bs4 import BeautifulSoup

importしたライブラリの説明
・csvは,Pythonの標準ライブラリで今回はCSVファイルの書き込みで使用
・urllibは,web上のデータ(HTML)にアクセス&取得するために使用
・BeautifulSoupは,HTMLから狙ったデータを抽出するために使用

HTMLを取得

image.png

url = "https://en.wikipedia.org/wiki/List_of_cities_in_Japan"
html = urllib.request.urlopen(url)
soup = BeautifulSoup(html, 'html.parser')
# HTMLから表(tableタグ)の部分を全て取得する
table = soup.find_all("table")

今回は,日本の都市についてまとめてあるwikipediaの表をスクレイピングしてみます.

プログラムのurllib.request.urlopenは,指定したurlのHTMLを取得します.その後,Beautiful Soupを使って扱いやすいように整形し,更に,HTMLから表がある部分(table タグで囲われてある部分)を全てsoup.find_all("table")で取得すれば準備完了です.

取得したいTABLEタグの名前を調べる

image.png

chromeブラウザを使用している方は,F12(macだとcommand+option+I)を押すことで開発者ツール(スクリーンショットの黒い画面)に入ることができます.その後,ElementsからHTMLのソースコードを見ることができるので,その中からスクレイピングしたいtableタグを探します.今回は,青く選択されている表を取得してみたいと思います.実はこれは単純に,全てのtableタグからclassNameが"wikitable"のものを選択することで取得できます.

for tab in table:
    table_className = tab.get("class")
    print(table_className)
    if table_className[0] == "wikitable":
        break

# break文がないときの出力結果
# ['vertical-navbox', 'nowraplinks', 'hlist']
# ['wikitable'] <- ここで,break文を使って抜ける
# ['wikitable', 'sortable']
# ['wikitable', 'sortable']
# ['wikitable']
# ['nowraplinks', 'mw-collapsible', 'autocollapse', 'navbox-inner']

・table_className[0]としているのは,classNameの先頭にwikitableが来ているためです.
・また,今回の場合,HTML上で他にもwikitableと同じ名前の表が複数存在していますが,今回欲しい表は常に一番目のwikitableなので初めてif文を通過した後,すぐにbreak文を使ってループを抜ける処理を入れてます.

目的の表を取り出せたら,CSVに変換して保存する

最後に,上のプログラムにCSV保存機能をつけます.

for tab in table:
    table_className = tab.get("class")
    if table_className[0] == "wikitable":
        # CSV保存部分
        with open("test.csv", "w", encoding='utf-8') as file:
            writer = csv.writer(file)
            rows = tab.find_all("tr")
            for row in rows:
                csvRow = []
                for cell in row.findAll(['td', 'th']):
                    csvRow.append(cell.get_text())
                writer.writerow(csvRow)
        break

CSV保存機能の部分は,tableタグを行方向("tr")に抜き出して更に列方向("td", "th")に取り出し,list形式にappendしてCSVで保存するというものです(tableタグまで抜き出せたら,ここはコピペで使用していいと思う).

確認のため,pandasを使ってCSVを表示してみる

import pandas as pd
pd.read_csv("test.csv")

image.png

無事,csv保存したものがpandasによって表示することができました!

まとめ

スクレイピングしたいサイトにもよりますが,大体この流れで表をCSVで取得できると思います!ここまでご覧頂きありがとうございました〜!

参考文献

https://qiita.com/Azunyan1111/items/b161b998790b1db2ff7a

  • このエントリーをはてなブックマークに追加
  • 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で続きを読む

正しいマークアップとはなんなのか?

はじめに

本文章は、来年からマークアップを教えることになるMoが、「なぜ正しくマークアップする必要があるのか?」「そもそも正しいマークアップとは何を基準としているのか?」ということを再考するために書いたものです。

マークアップとは?

マークアップとは端的に言えば、テキストやその構造を与えることをいう。広く使われているマークアップ言語はHTMLとXMLで、web開発をしているとどちらも目にかかるものである。

例えば、HTMLでは以下のようにマークアップする。

<!DOCTYPE HTML>
<html>
 <head>
  <title>My Awsome Page</title>
 </head>
 <body>
  <h1>Hello World!</h1>
  <p>I am Mo! I like Dogs!</p>
  <h2>Where I live</h2>
  <p>I live in a small city in Tokyo!</p>
 </body>
</html>

これはブラウザで以下のように表示される。
スクリーンショット 2019-12-23 11.44.58.png

<head>タグの中の<title>タグ内の記述がページタイトルになっていたり、<body>タグの<h>タグ内の記述が強調されていたりと、なんとなく文書構造が出来上がっていることがわかる。

なぜマークアップが必要なのか?

先の例を見ると、果たしてこの手続きが必要なのかという疑問にぶつかる。

headタグ内の記述はブラウザの表示の都合上必要であるにせよ、<body>タグ内の記述に関して、いちいちdivタグを使うのか、pタグを使うのか...,spanタグを使うのか,hタグを使うのか...,と頭を悩ませる必要はあるのだろうか?

特に、太字にしたり行間を整えたりなんていう視覚上の効果は、cssでスタイルを当てる方が幅も効くし、質も上がるし、そんなに難しい作業ではない。では、マークアップする意味はなんなのだろうか?

以下文章では、HTMLの開発コミュニティの一つであるWHATWGが作成したHTML standard のみに準じて、HTMLの仕様を見ることでマークアップの必要性を考えることとする。

セマンティクス(Semantics)

マークアップの必要性はセマンティクスを与えられるとい点にある。

Because HTML conveys meaning, rather than presentation, the same page can also be used by a small browser on a mobile phone, without any change to the page. Instead of headings being in large letters as on the desktop, for example, the browser on the mobile phone might use the same size text for the whole page, but with the headings in bold....

HTMLは見栄えよりもむしろ意味を伝えるため、ページを一切変更することなく、同じページが携帯電話上のスモールブラウザーによっても使用できる。たとえば、デスクトップと同様に大きな文字である見出しの代わりに、携帯電話のブラウザーは、ページ全体で同じサイズのテキストを使用するが、太字の見出しを持つかもしれない。

ここで語られているのは、HTMLの要素や属性に備わっているセマンティクスによって生み出される、文章の構成の共通性である。
ある一つのWebページを作成しても、利用するユーザーがどのようなインターフェースを通じてそのページの情報を得ているかはわからない。たとえPCやタブレット用にスタイルが当てられていたとしても、ガラケーやスマートフォンでページを見ている人にはそのスタイルは伝わらない。ましてや、もし目の不自由なユーザーがいたらスタイルなんてわかるはずもない。HTMLはこうしたことを解決するために、文章に最低限の意味を持たせてくれるらしい。

nav要素

実際にセマンティクスがどう機能していくのかを見ていくためにnav要素の例を見てみる。
続く(後日作成します)...

まとめ

HTML Standard で割と難しい話が展開してるのでゆっくりと全文を読んで仕様をもっと理解していきたいと思いました。とりあえず何が言いたかったというと、HTMLは仕様を活かせるようにマークアップすればいいわけだということです。仕様を理解してもいないのにそれらしいようにマークアップするのは無駄にコストのかかることだし、教える側になった時に、仕様を教えずに、これはこういう決まりだからと教えるのも無駄なので、それを意識できればいいなと思いました。

  • このエントリーをはてなブックマークに追加
  • 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で続きを読む

標的型攻撃メール耐性訓練をハックしたい

標的型攻撃メール

怖いですよね、標的型攻撃メール。
本当に標的にされたら引っかかっちゃいそうなメールがきっと届くのでしょう。
ただ、普通の会社員はホンモノの攻撃メールを受け取るよりも「標的型攻撃メール耐性訓練」として届く怪しげメールのほうが馴染みがあるかと思います。

そんな標的型攻撃メール耐性訓練メールをハックしたい (が、本当にやると怒られが発生するし、倫理的にNGなのでやらないのだけれど話題としては好きなので吐き出しておきたい) という記事が本記事です。

ハックする とは

  • 一人の訓練対象者として自分以外の耐性訓練の結果に影響を与える。

これを目標にします。
なんでもありだと思考実験としても面白くないので、実現可能そうで :police_car: 案件にならないように検討します。
善良なエンジニアとして、:police_car:のお世話になってはいけません。

なので、脆弱性のあるインフラで動いているケースは配慮せず、その領域に対するアタックするようなアプローチは考えません。
計測サイトのサービスをダウンさせるとか、DNSポイゾニング的なことは考えない

標的型メール耐性訓練システム

だいたいこういう構造になると思います。

スクリーンショット 2019-12-22 23.45.39.png

訓練の結果に影響を与えるにはこのプロセスに干渉する必要がありそうです。
そもそも論として、ブラウザで知らないページを開くだけなら私は危険ではないと考えていますが記事の目的には影響しないのでおいておきましょう。

システムに対する影響の与え方

先程の絵に現れた3つのプロセスそれぞれにどういうふうに影響を与えられるか考えてみます。

(1)メールの配信

メールの特徴を利用するフィルタ

訓練対象者に訓練メールを受信しない/させないというアプローチがあります。

当然ですが訓練メールは、訓練のために自組織から送られるものです。
そのため、組織が運用しているメールフィルターには引っかかりません。
なので、メール自体を拒否するのは原則困難です。

ただ、何度も訓練が実施されていてパターン化しているなら可能性はあります。
メールの内容は変わっていても、「配信方法、配信タイミング、メールの配送経路、ヘッダのパターン」など毎回同じ方法で配信している可能性はあるでしょう。
これを他の訓練者に啓蒙することで、訓練結果に影響を与えることはできそうです。

(2)メールのリンクを踏む

「リンクを踏む」に対するのアプローチですが、自分以外の他人について影響を与える手段を検討します。
自分が踏んで結果に影響があるのは当たり前なので。

ソーシャルエンジニアリング

この工程は人間の操作なので、ソーシャルエンジニアリング的な方法。
嘘情報(例えば、あのメールのリンク先重要なこと書いてあったよ。とか)を同僚に流すことで、訓練メールのリンク先を積極的に踏ませに行くというソーシャルなアプローチは考えつきます、が、普通に恨まれる。
ただし、:police_car:は恐らく避けられるのでレギュレーション的にはセーフ

(3)ユニークなHTTPリクエスト

訓練対象者ごとにユニークなURLが届き、そのURLにアクセスしたことを計測されるモデルだとして、計測結果に影響を与える方法を検討します

アクセスを捏造する

他の訓練対象者のアクセスを捏造することができれば、目標は満たせるでしょう。

他の訓練者に送付されたユニークなキーを生成できるかどうかが鍵となりそうです。
こういったユニークなキーとして以下のようなものが使われていると思われます。。

  1. 類推不能なIDを付与する場合(UUID/乱数など)

    • 類推不能なユニークキーに使っている場合は、手のうちようがない。
    • 適当にユニークキーを生成してアクセスするのが関の山。
  2. 類推可能なIDを付与する場合(連番/社員IDなど)

    • 他の訓練対象者に配布されたURLが推測できるので、かんたんに訓練の結果を蹂躙できる。
    • 社員IDのような情報を元にユニークキーを作っていれば特定の訓練者を特定してアクセスすることも可能
    • 計測用サイトに対してiframe等で類推したURLへアクセスを行うページを設置&流布することで結果に影響を与えることはできる。
  3. IPアドレスや事前に埋め込まれたクライアント証明書など変更が困難なもの

    • 変更困難なキーを使われる場合はソーシャルエンジニアリング的な手法になり、アクセスを捏造するためにメールのリンクを踏ませる手段になるので (2)メールのリンクの手法と同じような作戦になりそう。

ユニークなキーがどのように生成されているかにかかっていますが、キーが類推可能だと耐性訓練の計測をパーにすることはできそう。

まとめ

こんな事ばかり考えて生きていますが、頭の体操としてセキュリティを考えている程度です。

メリークリスマス! :christmas_tree:

  • このエントリーをはてなブックマークに追加
  • 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で続きを読む

A-Frame v1.0 で クリスマスアニメーション

はじめに

A-Frame v1.0 が今週リリースされましたね!
HTMLだけで簡単にWebVRができちゃうっていうあれです。

AFrameについての記事はたくさんありますが、バージョンアップで動かない書き方が多いように思います。
なので、ほぼ公式サイト( A-Frame 1.0 Document )を見て作りました。

デモ
https://naughty-nobel-fdd6bb.netlify.com/

HTMLベース

A-Frame 読み込み
地面と背景とライトを配置

<html>
  <head>
    <meta charset="utf-8">
    <title>A-Frame X'mas</title>
    <meta name="description" content="X'mas - A-Frame">
    <script src="https://aframe.io/releases/1.0.0/aframe.min.js"></script>
  </head>
  <body>
    <a-scene id="scene" cursor="rayOrigin:mouse">

      <a-entity id="ground"
                geometry="primitive: circle; radius: 5;"
                rotation="-90 0 0"
                material="shader: flat; color: #ECECEC"></a-entity>

      <a-sky color="#00111a"></a-sky>
      <a-light type="directional" color="#fff" intensity="0.2" position="-1 2 1"></a-light>
      <a-light type="ambient" color="#fff"></a-light>
    </a-scene>
  </body>
</html>

ツリーを作る

木はコーン型を3つ重ねる
a-cone

<a-entity id="tree">
  <a-cone color="#004d40" radius-bottom="0.5" radius-top="0.01" position="0 1.8 -3" height="0.8"></a-cone>
  <a-cone color="#004d40" radius-bottom="0.6" radius-top="0.05" position="0 1.4 -3" height="0.9"></a-cone>
  <a-cone color="#004d40" radius-bottom="0.65" radius-top="0.05" position="0 1 -3" height="1"></a-cone>
  <a-cylinder color="#663300" height="0.3" radius="0.1" position="0 0.3 -3"></a-cylinder>
</a-entity>

星はBlenderで作成して配置
a-obj-model

<a-assets>
  <a-asset-item id="star-obj" src="./assets/obj/star.obj"></a-asset-item>
  <a-asset-item id="star-mtl" src="./assets/obj/star.mtl"></a-asset-item>
</a-assets>

<a-obj-model scale="0.4 0.4 0.4" position="0 2.3 -3" src="#star-obj" mtl="#star-mtl"></a-obj-model>

image.png

雪を降らす

パーティクルコンポーネント

<html>
  <head>
    <script src="https://aframe.io/releases/1.0.0/aframe.min.js"></script>
    <script src="https://unpkg.com/aframe-particle-system-component@1.0.9/dist/aframe-particle-system-component.min.js"></script>
  </head>
  <body>
    <a-scene>
      <a-entity particle-system="preset: snow" position="0 0 -10"></a-entity>
    </a-scene>
  </body>
</html>

image.png

文字を表示する

a-text

<a-text position="-0.8 2.8 -3" color="red" value="Merry Christmas!"></a-text>

image.png

星をクリックで回転させる

Animation

animationコンポーネントで 位置や回転、マテリアルを変えるアニメーションを設定できる(property)
複数アニメーションを作る場合は「__(アンダーバー2つ)」をつなげて指定する(animation_star_rotation)
startEventsを使うことで、クリックやマウスオーバー時に実行させることができる(startEvents:click)

<a-obj-model scale="0.4 0.4 0.4" position="0 2.3 -3" src="#star-obj" mtl="#star-mtl"
  animation__star_rotation="property: rotation; startEvents:click; from: 0 0 0; to: 0 360 0; loop:5;"></a-obj-model>

ツリーをクリックで雪を降らす

さっきは星をクリックして星のアニメーションでしたが、
別オブジェクトのアニメーションを実行する場合は、javascriptで操作できます。

snow を visible=false にしておき、tree のclick-treeでイベントが発生するように設定する。

<a-entity id="snow" visible="false">
  <a-text position="-0.8 2.8 -3" color="red" value="Merry Christmas!"></a-text>
  <a-entity particle-system="preset:snow;" position="0 0 -4"></a-entity>
</a-entity>

<a-entity click-tree>
  <a-cone color="#004d40" radius-bottom="0.5" radius-top="0.01" position="0 1.8 -3" height="0.8"></a-cone>
  <a-cone color="#004d40" radius-bottom="0.6" radius-top="0.05" position="0 1.4 -3" height="0.9"></a-cone>
  <a-cone color="#004d40" radius-bottom="0.65" radius-top="0.05" position="0 1 -3" height="1"></a-cone>
  <a-cylinder color="#663300" height="0.3" radius="0.1" position="0 0.3 -3"></a-cylinder>
</a-entity>
AFRAME.registerComponent('click-tree', {
  init: function () {
    this.el.addEventListener('mousedown', function () {
      document.getElementById('snow').setAttribute('visible', 'true');
    });
  }
});

完成

あとはサンタとか家とか置いて、アニメーションつければ完成!

https://naughty-nobel-fdd6bb.netlify.com/

3f7ms-9444r.gif

ほとんどBlenderの力な気もするけど、気軽にWEBVRができるのでA-Frameは楽しい!

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