好きなだけかりんとうを食べる人生

フロントエンドとかデザインとかバックエンドとか浅く広く。社会人3年目のうぇぶでぃれくたーです。

transitionでheightをautoにしても動かない

困った経緯

インターン先で、ボックスを固定の高さに折りたたみ、クリックで元の高さに広げる処理を実装することになった。

これは仕様

リファレンスにtransitionでアニメーション可能なプロパティが掲載されている。

height, min-height, max-height : length, percentage, or calc

つまり数値や計算値のみ指定可能で、height: auto;の場合アニメーションさせることができない。 これはめんどくさい。

解決策

あらかじめ元の高さを取得しておけばいい。

/* ベンダープレフィックスは省略 */
#box {
    height: auto;
    transition: height 1s linear 0;
}
/* クロスブラウザは省略 */
var box = document.getElementById("box");
var bHeight = box.clientHeight;

(function init() {
    // 高さ100pxに固定
    box.style.height = "100px";
})();

// クリックしたら元の高さに広げる
addEventListener(box, function() {
    box.style.height = bHeight;
}, false);

これで多分いけるはず。 数値の設定や取得はJavascript、アニメーション部分はcss3に任せちゃうのがお手軽かなーと。 idじゃなくて複数存在するclassだったら配列にでも格納してイテレートすればいいとおもう。

【jQuery】on()メソッド内の関数に引数を渡したい

時って結構多い気がします。

function greet(phrase) {
    console.log(phrase);
}

$("hoge").on("click", greet("おはようございます"));

コードをこう書くと、 ページが読み込まれたタイミングでgreet()が実行される。 これを回避するためには、

1. 無名関数を噛ませる

var phrase = "おはようございます"
$("hoge").on("click", function() {
    console.log(phrase);
});

コレジャナイ感。

2. 第二引数に連想配列(map形式)を指定する

リファレンスには、

.on( events[, selector] [, data], handler(eventObject));

と書いてある。 つまり、

function greet(phrase) {
    console.log(phrase.data.voice);
}

$("hoge").on("click", {voice: "おはようございます"}, greet);

これで大丈夫。 data(連想配列名)を指定してあげることを忘れないように!

Javascriptでスクロール量を取得する時の話

今更だけど、自分はパララックスデザインが好きです。
好きなものが流行ってると見る機会が多くなってニヤニヤできるからとってもうれしい。

ちなみにパララックスを導入する時にはjQueryプラグインを使うとめちゃめちゃ簡単に導入できる。 skrollrとか。

でもやっぱり自分の手で実装したい感ある。
なんでもjQueryに頼るのはスマートじゃないし。

というわけでせこせこピュアJSでコード書いてみたら「スクロール量を取得」するところでつまづいたのでメモ。前置きながいね。

スクロール量を取得するには

スクロール量を取得するメソッドはいくつか存在する。

  • document.body.scrollTop
  • window.pageYOffset
  • window.scrollY
  • document.documentElement.scrollTop

それぞれブラウザやDOCTYPEでのモード指定(後方互換とかそういうの)対応状況が違うからめんどくさい! 例えばIE8だとwindowpageYoffsetはundefinedを返すし、firefoxだとdocument.body.scrollTopは返り値が0になる。 じゃあdocument.documentElement.scrollTopでいいじゃんって話になるけれど、webkit系のブラウザ(chrome,safari)の対応があやしい。

じゃあどうすればいいんだろう

function getPos() {
    var scroll = (window.pageYOffset !== undefined) ? window.pageYOffset : document.documentElement.scrollTop;
}

window.onscroll = function() {
    getPos();
}

とりあえずこれでブラウザ毎の対応はできてるはず。
DOCTYPE宣言忘れるようなドジっ子は知らないよ〜〜〜

あと位置情報系のメソッドいい加減おぼえたい。今度まとめるかな…

canvasで花火(らしきもの)

前回ホタルを描いてみたので、今回はその応用として花火です。

サンプル

サンプル
クリックしてみてね。

コード(Javascriptのみ)

// 定数
var HANABI_NUM = 75;
var SCREEN_WIDTH = window.innerWidth;
var SCREEN_HEIGHT = window.innerHeight;
var GRAVITY = 0.05;

// 変数
var cvs = null;
var ctx = null;
var mX = 0;
var mY = 0;
var timer = "";
var hanabiArr = [];

window.onload = function() {

  // 初期化
  init();

  // クリックイベントの登録
  bundleEvent();

};

// イベントリスナの登録
function bundleEvent() {

  addListener(cvs, 'click', getPosition);
  addListener(cvs, 'click', startHanabi);

};

// イベントリスナの削除
function removeEvent() {

  removeListener(cvs, 'click', getPosition);
  removeListener(cvs, 'click', startHanabi);

};

// 初期化
function init() {

  // canvasの初期化
  cvs = document.getElementById("canvas");
  cvs.width = SCREEN_WIDTH;
  cvs.height = SCREEN_HEIGHT;
  ctx = cvs.getContext("2d");

  // 背景の描画
  ctx.fillStyle = "rgb(0, 0, 0)";
  ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

};

// 座標の取得
function getPosition(e) {

  var rect = e.target.getBoundingClientRect();
  mX = e.clientX - rect.left;
  mY = e.clientY - rect.top;

};

// アニメーション
function startHanabi() {

  create(mX, mY);
  removeEvent();
  loop();

};

// クラス
var Hanabi = function(x, y) {
  this.x = x;
  this.y = y;
};

Hanabi.prototype = {

  // 座標
  x: 0,
  y: 0,

  // 速度
  vX: 0,
  vY: 0,

  // 大きさ
  size: null,

  // 色
  color: null,

  // 透明度
  alpha: 1.0,

  // 削除フラグ
  deleteflg: false,

  update: function() {

    this.vY += GRAVITY;
    this.x += this.vX;
    this.y += this.vY;
    this.size *= 0.98;
    this.alpha = Math.round((this.alpha - 0.01) * 100) / 100;
    if (this.x < 0 || this.x > SCREEN_WIDTH || this.y > SCREEN_HEIGHT) {
      this.deleteflg = true;
    }

  },

  draw: function() {

    ctx.fillStyle = this.color;
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false);
    ctx.fill();

  }

};

// インスタンスの設定、格納
function create(mX, mY) {

  for (var i = 0; i < HANABI_NUM; i++) {

    var hanabi = new Hanabi(mX, mY);
    hanabi.color = "rgba(255, 255, 255, " + hanabi.alpha + ")";
    hanabi.vX = Math.random() * 4 - 2;
    hanabi.vY = Math.random() * 4 - 2;
    hanabi.size = 5;
    hanabiArr.push(hanabi);

  }


};

// 花火の描画
function draw() {

  ctx.fillStyle = "rgb(0, 0, 0)";
  ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

  for (var i = 0; i < hanabiArr.length; i++) {
    hanabiArr[i].draw();
  }

};

// 位置の更新
function update() {

  var list = [];

  for (var i = 0; i < hanabiArr.length; i++) {

    hanabiArr[i].update();
    if (!hanabiArr[i].deleteflg) {
      list.push(hanabiArr[i]);
    }

  }

  if (list.length == 0) {
    clearTimeout(timer);
    bundleEvent();
  }

  hanabiArr = list;

};

function loop() {

  draw();
  update();
  timer = setTimeout(loop, 1000 / 60);

};

解説

canvasで何かを描画する時は

  1. オブジェクトを生成(パラメータを設定)
  2. オブジェクトの位置情報を更新
  3. 背景を描画
  4. オブジェクトを描画
  5. 2~4を繰り返す

ことが多いです(体感)。
他に方法あるのかな…気になる。

感想

まだ複数の花火を同時に描画したり、スワイプに対応したような処理ができていないのでその辺りが課題です。
あとはきれいなコード書けるようになりたいです。
もっとコードを書く機会を増やさないと…

canvasでホタルを描いてみた

canvas使ってる色々なサンプル眺めてたら作ってみたくなりました。
夜、明かりを消してディスプレイを見た時(目に良くないけど)、画面いっぱいにホタルが舞ってたらなんとなく癒やされるなーなんて。

ライブラリやフレームワークを使わず、純粋なJavascriptとhtmlのみで実装してみました(リセット用のCSSは使ってます)。
canvas使っているのでIE7,IE8とかは見れないと思います〜。アプデしてね。

サンプル

サンプル

コード

  <canvas id="canvas"></canvas>

// 定数の宣言
var FPS = 60;
var SCREEN_WIDTH = window.innerWidth;
var SCREEN_HEIGHT = window.innerHeight;
var HOTARU_NUM = 80;

// 変数の宣言
var hotaruArr = [];
var ctx;

window.onload = function() {
  init();
};

// 初期化
var init = function() {

  var cvs = document.getElementById('canvas');
  cvs.width = SCREEN_WIDTH;
  cvs.height = SCREEN_HEIGHT;
  ctx = cvs.getContext('2d');
  createHotaru();
  loop();

};

// インスタンスの生成
var createHotaru = function() {

  for (var i = 0; i < HOTARU_NUM; i++) {
    var hotaru = new Hotaru();
    hotaru.setConfig();
    hotaruArr.push(hotaru);
  };

};

// 各要素を描画
var draw = function() {

  ctx.fillStyle = 'rgb(0, 0, 0)';
  ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

  for (var i = 0; i < hotaruArr.length; i++) {
    hotaruArr[i].drawHotaru();
  }

};

// 位置の更新
var update = function() {

  for (var i = 0; i < hotaruArr.length; i++) {
    hotaruArr[i].updateHotaru();
  }

};

// 描画・更新のループ
var loop = function() {

  draw();
  update();
  setTimeout(loop, 1000 / FPS);

};

// オブジェクトのコンストラクタ
var Hotaru = function() {};

// メソッド・プロパティ
Hotaru.prototype = {

  setConfig: function() {

    this.posX = Math.random() * SCREEN_WIDTH;
    this.posY = Math.random() * SCREEN_HEIGHT;
    this.mX = Math.random() * 0.5 - 0.25;
    this.mY = Math.random() * 0.5 - 0.25;
    this.r = Math.random() * 5 + 2;
    this.rdm = Math.floor(Math.random() * 360);
    this.alpha = 1.0;
    this.color = 'rgba(202, 251, 74,' + this.alpha + ')';

  },

  // プロパティに対応したオブジェクトを描画
  drawHotaru: function() {

    ctx.beginPath();
    ctx.fillStyle = this.color;
    ctx.arc(this.posX, this.posY, this.r, 0, Math.PI * 2, false);
    ctx.fill();

  },

  // オブジェクトの更新
  updateHotaru: function() {

    if (this.posX < 0 - this.r) {
      this.posX = SCREEN_WIDTH + this.r;
    } else if (this.posX > SCREEN_WIDTH + this.r) {
      this.posX = 0 - this.r;
    }
    if (this.posY < 0) {
      this.posY = SCREEN_HEIGHT + this.r;
    } else if (this.posY > SCREEN_HEIGHT) {
      this.pos = 0 - this.r;
    }

    this.posX += this.mX;
    this.posY += this.mY;
    this.rdm += 1.2;
    this.alpha = Math.abs(Math.cos(this.rdm * Math.PI / 180) * 0.5 + 0.5);
    this.color = 'rgba(202, 251, 74,' + this.alpha + ')';

  }

};

時間がかかった部分

ホタルの光を表現しようとした時、点滅する動作をどう表現しようか悩みました。
単純に増減させるだけでは機械的になっちゃいますし。 なんとなーく2次曲線ぽく表現したかったんですよ。なめらかに点滅する感じで。
そこで三角関数を使うことにしました。

this.alpha = Math.abs(Math.cos(this.rdm * Math.PI / 180) * 0.5 + 0.5);

この部分ですね。

まとめ

今回の制作で、canvasの基本や、オブジェクト指向に(なるべく)則った(つもりである)コードの書き方がなんとなく掴めてきた気がします。

次は花火とか面白そうだな〜なんて思ってます。

オブジェクト指向を意識してコードを書いてみた

今までオブジェクト指向に則ったコードの書き方とか全く意識したことがなくてですね、
ワークショップとかセミナーとかで現場で働いている方のコードを見た時に「なんだこれ」っていう顔にならないように確認しておこうと思いました。

そもそもオブジェクト指向って

オブジェクト指向とは - wikipedia

オブジェクト指向(オブジェクトしこう)とは、オブジェクト同士の相互作用として、システムの振る舞いをとらえる考え方である。英語の object-oriented (直訳は、「対象物志向の」・「目的重視の」という意味の形容詞) の日本語訳である。

うーん、わからん。

すごく乱暴な言い方だけど、オブジェクトごとに振るまいや状態を定義して管理しやすくしたり、機能を再利用して、効率よくコードを書く考え方って感じでしょうか。

この辺りについては他サイトさんで詳しく説明されていると思うので、そちらを参考にしてみてください。

(できるだけ)オブジェクト指向に則ってコードを書いてみた感想、メモ

【prototype内で宣言したメソッドはメソッド・プロパティ内において「this.メソッド・プロパティ名」で呼び出すことができる】
いや、これ当たり前なんですが、スコープやthisが何を返すのかをしっかり把握しておかなかったのでハマりました。

window.onload = function() {
  var test = new Test();
};

var Test = function() {
  this.timer = null;
  this.init();
}

Test.prototype.init = function() {
  this.timer = setInterval(function() {
    this.countUp();    // この場合のthisはグローバルオブジェクト
  }, 1000);
};

Test.prototype.countUp = function() {
  console.log('hello');
};

thisを退避してあげれば解決です。

window.onload = function() {
  var test = new Test();
};

var Test = function() {
  this.timer = null;
  this.init();
}

Test.prototype.init = function() {
  var self = this;    // thisを退避
  this.timer = setInterval(function() {
    self.countUp();    // このthisにはインスタンスが入ってます
  }, 1000);
};

Test.prototype.countUp = function() {
  console.log('hello');
};

まとめ

thisを書き忘れて怒られたり、関数とメソッド、変数とプロパティの違いに中々慣れることができませんでした。
当然ですが、ひたすら書いて覚えていくしかないですね。
prototypeや__proto__についての理解がまだ浅いので、今後記事にまとめたいと思います。

Javascriptを使ってテキストフィールドの内容をリアルタイムで表示させる

これまたお仕事関係で使いそうなので先回りでコードを書いてみました。

コード

chrome想定

<textarea id="text"></textarea>
<div id="output"></div>
window.onload = function() {

    var txtEl = document.getElementById('text'),
        outputEl = document.getElementById('output');

    txtEl.addEventListener('keyup', function() {

        var txt = txtEl.value;
        outputEl.innerHTML = txt;

    }, false);

}

サンプル

ポイント

keyupイベントを指定するとキーを離したタイミングでイベントを発火させることができます。
あとはtextareaの入力内容はvalueプロパティで取得することに注意するくらいでしょうか。

まとめ

AngularJS使えば一瞬じゃんとか言わないでください(・∀・)