Help us understand the problem. What is going on with this article?

Canvasで不気味で怖い?ブラウン管テレビのようなエフェクトを書いてみた

More than 3 years have passed since last update.

はじめに

Canvasを利用して、不気味で怖いブラウン管テレビのようなエフェクトを書いてみました。
CSS3のfilterプロパティを使っても似たようなことはできるかもしれませんが、ザラザラしたアニメーションをさせたかったためCanvasを使いました(もしくはfilterプロパティと組み合わせてより雰囲気を出すこともできそうです)。

画像にCanvas要素を重ねているだけなので何でも応用できるかと思います。何かのお役に立てば幸いです。

なお実際の動作は以下で確認することができます。
http://codepen.io/nekoneko-wanwan/pen/xZegVE

[動作イメージ]
def.gif
......おや!?ネコのようすが......!

anim.gif
おめでとう!ネコはホラーなネコにしんかした!
(もしくはノスタルジー?w)

特長・仕様

  • Canvasサイズに依存しない
  • ノイズの色は自由に変更可能
  • ノイズの濃さは自由に変更可能
  • Canvasのピクセルデータを直接操作することで、高速にアニメーションをさせる

Canvasのピクセルサイズと動作環境によっては重たいかもしれません

ソースコード

index.html
<canvas id="canvas" width="300" height="200"></canvas>
style.css
canvas {
  background-image: url('ネコ画像');
  background-position: 0 0;
  background-size: cover; 
}
canvas.js
var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d'),
    w = ctx.canvas.width,
    h = ctx.canvas.height,

    // noise関係
    rgb = {
      // ノイズの点に使う色を設定(↓はセピア色)
      r: 107,
      g: 74,
      b: 43
    },
    idata = ctx.createImageData(w, h),
    data  = idata.data,
    dotAlpha = 100, // max256(数値が大きいほどノイズが強くなる=点の不透明度が下がる)

    // 円形グラデーション関係
    x = w / 2,
    y = h / 2,
    radius = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) | 0, // canvasの外接円の半径
    grad   =  ctx.createRadialGradient(x,y,0,x,y,radius),
    blend  = 'screen';  // ブレンドモードを定義

var draw = {
  // canvas全体にnoise(1pxごとにランダムな不透明度を持つ点)を作成
  noise: function() {
    for(var i = 0, l = data.length; i < l; i += 4) {
      data[i + 0] = rgb.r;
      data[i + 1] = rgb.g;
      data[i + 2] = rgb.b;
      data[i + 3] = Math.random() * dotAlpha | 0;
    }
    ctx.putImageData(idata, 0, 0);
  },
  // Gradation領域の作成
  grad: function() {
    grad = ctx.createRadialGradient(x,y,0,x,y,radius);
    grad.addColorStop(0,'rgba(0,0,0,0)');
    grad.addColorStop(1,'rgba(0,0,0,1)');
  },
  // ループさせたい描画
  render: function() {
    ctx.clearRect(0,0,w,h);
    this.noise();

    // 円形グラデーションの描画
    ctx.beginPath();
    ctx.fillStyle = grad;
    ctx.rect(0,0, w,h);
    ctx.fill();
    requestAnimationFrame(this.render.bind(this));
  },
  initialize: function() {
    ctx.globalCompositeOperation = blend;  // canvasのブレンドモードを定義
    this.grad();
    this.render();
  }
}

draw.initialize();

作り方・考え方

大きく分けて2つ、Canvas全体へのノイズ円形グラデーションを作成します。

ノイズの作り方

Canvasにはピクセルデータを直接操作できる機能があります。数値の入った配列を直接操作するので、ピクセル単位の描画などを高速に処理することができます。
ここではImageDataオブジェクトを利用し、Canvasのピクセルごとに点(データ)を格納していきノイズを表現します。

ImageDataオブジェクトとは
簡単にいうとCanvasのバイト・ピクセルデータが操作できるオブジェクトです。
dataプロパティには指定したピクセル範囲における、rbgaデータが順番に格納された1次元配列が入っています。

0: r  // ここから
1: g
2: b
3: a  // ここまでで1pxのrgba=1色を表す
4: r
5: g
6: b
.
.
.

- rgbaはそれぞれ0-255の範囲で数値を格納することができます
- alpha成分(透明度)も0-255の範囲で表現します
- 初期だとすべて0(透明な黒)が格納されています

そしてこれが指定したピクセル分、つまりwidth:100px, height:100pxのCanvas全体では100 * 100 * 4(r+g+b+a) = 40,000の長さを持つ配列を操作することになります。

具体的な手順

  • createImageDataメソッドでCanvas全体を対象としたImageDataオブジェクトを生成します
  • ImageData.dataプロパティの配列に点の色データを格納していきます
  • ここでは点の色を統一するため決め打ちにしています(Math.random()にすればカラフルなノイズができます)
  • CanvasにImageDataオブジェクトを描画させるために、putImageDataでデータを書き戻します

[ノイズを適用したイメージ]
noize-only.gif

円形グラデーション

怖そうな雰囲気(もしくはノスタルジー)を出すために、透明→黒の円形グラデーションを使ってトンネルエフェクトを表現します

トンネルエフェクトについては以下に詳しい説明があります
トイカメラ百科

  • Canvasの四隅を暗くするために、Canvasとなる四角形の外接円の半径を算出し、中心からその半径の範囲で円形グラデーションを作成します
  • 円形グラデーションにはcreateRadialGradientメソッドを使用します(基本メソッドなので省略)
  • ブレンドモードを指定することで、ノイズの色によって微妙に色合いが変化します
  • また半径を増やしたり、グラデーションの透明度やベース色を調整することで暗さの調整が容易にできます(アニメーションさせても面白いかも)。

[トンネルエフェクトを適用したイメージ]
grad-only.gif

終わりに

今回は何となく怖そうな雰囲気を目指してみましたが、アイデアしだいで色々な表現が実現できそうですね。

[参考サイト]
canvasをバイト単位で修正する方法(ImageDataの使い方)
putImageDataによるCanvasの画像データ設定

今まで投稿したCanvasエフェクトシリーズ

Canvasでティウンティウンティウン...
Canvasでビックリマンなシールを書いてみた(光るゾ!)
Canvasで斬!っと画面が斬れるようなエフェクトを書いてみた
Canvasで漫画にあるような吹き出しを書いてみた
Canvasで漫画にあるような "ざわ・・・" を書いてみた
Canvasで漫画にあるような集中線を書いてみた

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away