2016.12.22
2020.1.14

CreateJSを利用してクリスマス仕様のヘッダー作成

せっかくのクリスマスなので当サイトもちょっとくらいは要素を取り入れようと思い、CreateJSを利用してクリスマス仕様のヘッダーを作成してみました。


ヘッダーを表示する

CreateJSのおかげか意外に簡単にできました。楽しかったのでなにかしらのイベントがある度にまた別のコンテンツを用意したいですね。

以下はコードです。

目次

  • HTML
  • JavaScript
  • CSS
  • 参考リンク

HTML


<div class="canvas_area">
    <canvas id="myCanvas"></canvas>
</div>
<div class="command_area">
    <button class="event_button" onclick="emitCracker()">01</button>
    <button class="event_button" onclick="emitFireworks()">02</button>
    <button class="event_button" onclick="emitSori()">03</button>
    <button class="event_button" onclick="emitJump()">04</button>
    <button class="event_button" onclick="registShootingStar()">05</button>
</div>

JavaScript

配列データのコピーにangular.copy()を利用しているので、そのまま利用する場合は、AngularJSを導入してください。

createjs.ImageLoader()を利用している理由は、事前に画像を読み込んでおかないとキャンバスに表示されなかったからです。


var stage;
var particles = [];
var tickCount = 0;
var tickRotation = 0;
var setting = {};
var shootingStarQueue = [];
var mouseOver = false;
var crackerArr = ["chicken_honetsuki.png", "christmas_bell.png", "christmas_candy.png", "christmas_oranament_blue.png", "christmas_oranament_green.png", "christmas_oranament_orange.png", "christmas_oranament_red.png", "christmas_reindeer.png", "christmas_santa.png", "christmas_wreath.png", "christmas_wreath_heart.png", "music_handbell.png"];
var crackerSetting = {"particle_count":1,"particle_size_random":false,"particle_radius":30,"particle_life":200,"particle_life_scaling":"0","emit_delay":0,"emit_type":"1","hsl_hue":-1,"hsl_saturation":50,"hsl_lightness":90,"speed_x":32,"speed_y":32,"acceleration":-0.04,"direction_x":0.8,"direction_y":-1,"gravity":1.0,"roll_radius":0,"reverse_roll":false,"particle_shape":"1","image_url":"images/santa.png","text_str":"Hello World!","frame_left":false,"frame_right":false,"frame_top":false,"frame_bottom":true};
var fireworkSetting = {"particle_count":50,"particle_size_random":true,"particle_radius":20,"particle_life":80,"particle_life_scaling":"1","emit_delay":0,"emit_type":"2","hsl_hue":-1,"hsl_saturation":50,"hsl_lightness":50,"speed_x":10,"speed_y":10,"acceleration":-0.02,"direction_x":0,"direction_y":-0.2,"gravity":0.1,"roll_radius":0,"reverse_roll":false,"particle_shape":"0","image_url":"images/santa.png","text_str":"Hello World!","frame_left":false,"frame_right":false,"frame_top":false,"frame_bottom":false};
var soriArr = ["christmas_santa_sori.png"];
var soriSetting = {"particle_count":1,"particle_size_random":true,"particle_radius":100,"particle_life":120,"particle_life_scaling":"0","emit_delay":0,"emit_type":"2","hsl_hue":-1,"hsl_saturation":80,"hsl_lightness":50,"speed_x":10,"speed_y":0,"acceleration":0,"direction_x":-1,"direction_y":0,"gravity":0,"roll_radius":0,"reverse_roll":false,"particle_shape":"1","image_url":"images/christmas_santa_sori.png","text_str":"Hello World!","frame_left":false,"frame_right":false,"frame_top":false,"frame_bottom":false};
var jumpArr = ["santa_claus_front.png", "santa_claus_back.png"];
var jumpSetting = {"particle_count":1,"particle_size_random":false,"particle_radius":50,"particle_life":60,"particle_life_scaling":"0","emit_delay":0,"emit_type":"2","hsl_hue":-1,"hsl_saturation":50,"hsl_lightness":90,"speed_x":0,"speed_y":15,"acceleration":-0.04,"direction_x":0,"direction_y":-2,"gravity":1,"roll_radius":0,"reverse_roll":false,"particle_shape":"1","image_url":"images/santa.png","text_str":"Hello World!","frame_left":false,"frame_right":false,"frame_top":false,"frame_bottom":false};
var starSetting = {"particle_count":5,"particle_size_random":true,"particle_radius":30,"particle_life":80,"particle_life_scaling":"1","emit_delay":5,"emit_type":"1","hsl_hue":-1,"hsl_saturation":50,"hsl_lightness":50,"speed_x":10,"speed_y":10,"acceleration":-0.04,"direction_x":0,"direction_y":0,"gravity":0.1,"roll_radius":0,"reverse_roll":false,"particle_shape":"4","image_url":"images/santa.png","text_str":"Hello World!","frame_left":false,"frame_right":false,"frame_top":false,"frame_bottom":false};
var snowSetting = {"particle_count":1,"particle_size_random":true,"particle_radius":4,"particle_life":350,"particle_life_scaling":"1","emit_delay":0,"emit_type":"1","hsl_hue":-1,"hsl_saturation":50,"hsl_lightness":90,"speed_x":0,"speed_y":0,"acceleration":-0.1,"direction_x":0,"direction_y":0,"gravity":0.1,"roll_radius":0,"reverse_roll":false,"particle_shape":"0","image_url":"images/santa.png","text_str":"Hello World!","frame_left":false,"frame_right":false,"frame_top":false,"frame_bottom":false};

window.addEventListener("load", init);

// 初期処理
function init() {
    // ステージ作成
    stage = new createjs.Stage("myCanvas");
    handleResize();
    window.addEventListener("resize", handleResize);
    window.addEventListener("mouseover", function(){mouseOver = true;});
    window.addEventListener("mouseout",  function(){mouseOver = false;});
    window.addEventListener("touchstart", function(){mouseOver = true;});
    window.addEventListener("touchend",  function(){mouseOver = false;});
    
    // タッチ操作の有効化
    if (createjs.Touch.isSupported()) {
        createjs.Touch.enable(stage);
    }
    // tick イベントを登録する
    createjs.Ticker.setFPS(60);
    createjs.Ticker.timingMode = createjs.Ticker.TIMEOUT;
    createjs.Ticker.addEventListener("tick", handleTick);
}

// 画像の出力
function emitImage(imageSetting, image, x, y){
    var emitSetting = angular.copy(imageSetting);
    var bkloader=new createjs.ImageLoader("images/" + image, false);
    bkloader.addEventListener("complete",function() {
        setting = angular.copy(emitSetting);
        emitParticles(x, y);
    });
    bkloader.load();
}

// Tick処理
function handleTick(event) {
    // 常時発生(雪)
    emitPermanent();
    // 流れ星発生
    emitShootingStar();
    // パーティクルを更新
    updateParticles();
    // 画面を更新する
    stage.update();
}

// クラッカー
function emitCracker(){
    for(var i = 0; i < 5; i++){
        var targetIndex = Math.floor(Math.random() * crackerArr.length);
        crackerSetting.image_url = "images/" + crackerArr[targetIndex];
        emitImage(crackerSetting, crackerArr[targetIndex], 0, stage.canvas.height);
    }
}

// 花火
function emitFireworks(){
    setting = angular.copy(fireworkSetting);
    emitParticles(Math.random() * stage.canvas.width, Math.random() * stage.canvas.height - 50);
}

// そり
function emitSori(){
    if(Math.random() < 0.5){
        emitImage(soriSetting, soriArr[0], stage.canvas.width, 40);
    }else{
        emitImage(soriSetting, soriArr[0], stage.canvas.width, stage.canvas.height - 40);
    }
}

// ジャンプ
function emitJump(){
    var targetIndex = Math.floor(Math.random() * jumpArr.length);
    jumpSetting.image_url = "images/" + jumpArr[targetIndex];
    emitImage(jumpSetting, jumpArr[targetIndex], Math.random() * stage.canvas.width, stage.canvas.height);
}

// 流れ星の登録
function registShootingStar(){
    if(shootingStarQueue.length > 0){
        return;
    }
    var emitStartPosX = 0;
    var emitStartPosY = 0;
    var emitEndPosX = 0;
    var emitEndPosY = 0;
    // 流れ星の発生位置と流れていく位置を決める
    if(Math.random() - 0.5 < 0){
        // 横断
        if(Math.random() - 0.5 < 0){
            // 左から右へ
            emitStartPosX = -10;
            emitEndPosX = stage.canvas.width + 10;
        }else{
            // 右から左へ
            emitStartPosX = stage.canvas.width + 10;
            emitEndPosX = -10;
        }
        emitStartPosY = Math.random() * stage.canvas.height;
        emitEndPosY = Math.random() * stage.canvas.height;
    }else{
        // 縦断
        if(Math.random() - 0.5 < 0){
            // 上から下へ
            emitStartPosY = -10;
            emitEndPosY = stage.canvas.height + 10;
        }else{
            // 下から上へ
            emitStartPosY = stage.canvas.height + 10;
            emitEndPosY = -10;
        }
        emitStartPosX = Math.random() * stage.canvas.width;
        emitEndPosX = Math.random() * stage.canvas.width;
    }
    var showFrameCount = 30;
    var intervalX = (emitEndPosX - emitStartPosX) / showFrameCount;
    var intervalY = (emitEndPosY - emitStartPosY) / showFrameCount;
    for(var i = 0; i < showFrameCount; i++){
        var star = {};
        star.x = emitStartPosX + (i * intervalX);
        star.y = emitStartPosY + (i * intervalY);
        shootingStarQueue.push(star);
    }
}

// 流れ星
function emitShootingStar(){
    if(shootingStarQueue.length <= 0){
        return;
    }
    setting = angular.copy(starSetting);
    var star = shootingStarQueue[0];
    emitParticles(star.x, star.y);
    shootingStarQueue.splice(0, 1);
}

// 常時発生
function emitPermanent(){
    var canvasWidth = stage.canvas.width;
    var canvasHeight = stage.canvas.height;
    // 60fpsで毎回実行すると雪の量が多いのでランダム値を使って減らす
    if(Math.random() > 0.8){
        // stageのキャンバス幅いっぱいに雪を降らせる
        setting = angular.copy(snowSetting);
        var emitPosX = Math.random() * canvasWidth;
        var emitPosY = 0;
        emitParticles(emitPosX, emitPosY);
    }        
}

// パーティクル発生
function emitParticles(emitPosX, emitPosY) {
    // パーティクルの生成
    var particleEmitCount = setting.particle_count;
    for (var i = 0; i < particleEmitCount; i++) {
        var radius = setting.roll_radius;
        var aroundX = radius * Math.sin((i * 360 / particleEmitCount + tickRotation) * Math.PI / 180);
        var aroundY = radius * Math.cos((i * 360 / particleEmitCount + tickRotation) * Math.PI / 180);

        // カウントの更新
        tickCount += 1;

        // オブジェクトの作成
        var particle;
        var settigHue = (0 <= setting.hsl_hue && setting.hsl_hue <= 360) ? setting.hsl_hue : tickCount;
        var settigSaturation = setting.hsl_saturation;
        var settigLightness = setting.hsl_lightness;
        var settigParticleRadius = setting.particle_radius;
        if(setting.particle_size_random){
            settigParticleRadius *= Math.random();
        }

        if(setting.particle_shape === "1"){
            // イメージ
            particle = new createjs.Bitmap(setting.image_url);
            particle.compositeOperation = "";
            particle.orgScale = (settigParticleRadius * 2) / particle.getBounds().width;
            particle.scaleX = particle.scaleY = particle.orgScale;
            particle.regX = particle.getBounds().width / 2;
            particle.regY = particle.getBounds().height / 2;
        }else if(setting.particle_shape === "2"){
            // テキスト
            particle = new createjs.Text(setting.text_str, (settigParticleRadius * 2) + "px Molle, cursive", createjs.Graphics.getHSL(settigHue, settigSaturation, settigLightness));
            particle.textAlign = "center";
            particle.textBaseline = "middle";
            particle.orgScale = 1;
        }else{
            particle = new createjs.Shape();
            if(setting.particle_shape === "3"){
                // 正方形
                particle.graphics
                        .beginFill(createjs.Graphics.getHSL(settigHue, settigSaturation, settigLightness))
                        .drawRect(0, 0, settigParticleRadius * 2, settigParticleRadius * 2);
            }else if(setting.particle_shape === "4"){
                // 星形
                particle.graphics
                        .beginFill(createjs.Graphics.getHSL(settigHue, settigSaturation, settigLightness))
                        .drawPolyStar(0, 0, settigParticleRadius, 5, 0.6, -90);
            }else{
                // 円
                particle.graphics
                        .beginFill(createjs.Graphics.getHSL(settigHue, settigSaturation, settigLightness))
                        .drawCircle(0, 0, settigParticleRadius);
            }

            particle.compositeOperation = "lighter";
            particle.orgScale = 1;
        }
        stage.addChild(particle);

        // パーティクルの発生場所
        particle.x = emitPosX + aroundX;
        particle.y = emitPosY + aroundY;

        // 速度
        if(setting.speed_x === 0 && setting.speed_y === 0){
            // 速度なし
            particle.vx = 0;
            particle.vy = 0;
        }else{
            // 速度(円状に広がるように補正)
            var speed_x = setting.speed_x * (Math.random() - 0.5 + setting.direction_x);
            var speed_y = setting.speed_y * (Math.random() - 0.5 + setting.direction_y);
            var angle = Math.atan2 (speed_y, speed_x);
            var hypotenuse  = (Math.abs(speed_x) <= Math.abs(speed_y)) ? Math.abs(speed_y) : Math.abs(speed_x);
            particle.vx = hypotenuse * Math.cos(angle);
            particle.vy = hypotenuse * Math.sin(angle);
        }

        // 寿命
        particle.life = setting.particle_life;
        particle.maxLife = setting.particle_life;

        // 加速度
        particle.acceleration = setting.acceleration;

        // 重力
        particle.gravity = setting.gravity;

        // 枠
        particle.frame_top = setting.frame_top;
        particle.frame_bottom = setting.frame_bottom;
        particle.frame_left = setting.frame_left;
        particle.frame_right = setting.frame_right;

        // 時間経過による変化
        particle.scaling = setting.particle_life_scaling;
        particles.push(particle);
    }
    if(setting.reverse_roll){
        tickRotation += 10;
    }else{
        tickRotation -= 10;
    }
}

// パーティクル更新
function updateParticles() {
    for (var i = 0; i < particles.length; i++) {
        // オブジェクトの作成
        var particle = particles[i];

        // 重力
        particle.vy += particle.gravity;

        // 摩擦
        particle.vx *= 1 + particle.acceleration;
        particle.vy *= 1 + particle.acceleration;

        // マウスを避けるように移動
        if(mouseOver){
            if (stage.mouseX - 50 < particle.x && particle.x < stage.mouseX + 50 && 
                stage.mouseY - 50 < particle.y && particle.y < stage.mouseY + 50) {
                var coefficient = (50 - (stage.mouseY - particle.y)) / 50;
                var moveAmount = coefficient * 1.00;
                if(particle.x < stage.mouseX){
                    particle.vx -= moveAmount;
                }else{
                    particle.vx += moveAmount;
                }
            }
        }

        // 速度を位置に適用
        particle.x += particle.vx;
        particle.y += particle.vy;

        // 枠
        if(particle.frame_top){
            if (particle.y < 0) {
                particle.y = 0; // 行き過ぎ補正
                particle.vx += (Math.random() - 0.5) / 10;
                particle.vy *= -1 + (Math.random() / 10); // Y軸の速度を反転
            }
        }

        if(particle.frame_bottom){
            if (particle.y > stage.canvas.height) {
                particle.y = stage.canvas.height; // 行き過ぎ補正
                particle.vx += (Math.random() - 0.5) / 10;
                particle.vy *= -1 + (Math.random() / 10); // Y軸の速度を反転
            }
        }

        if(particle.frame_left){
            if (particle.x < 0) {
                particle.x = 0; // 行き過ぎ補正
                particle.vx *= -1 + (Math.random() / 10); // X軸の速度を反転
                particle.vy += (Math.random() - 0.5) / 10;
            }
        }

        if(particle.frame_right){
            if (particle.x > stage.canvas.width) {
                particle.x = stage.canvas.width; // 行き過ぎ補正
                particle.vx *= -1 + (Math.random() / 10); // Y軸の速度を反転
                particle.vy += (Math.random() - 0.5) / 10;
            }
        }

        if (particle.life > -1) {
            // パーティクルのサイズをライフ依存にする
            var scale = particle.orgScale;
            if(particle.scaling === "1"){
                scale = particle.orgScale * (particle.life / particle.maxLife);   // 縮小
            }else if(particle.scaling === "2"){
                scale = particle.orgScale * (particle.maxLife / particle.life);   // 拡大
            }
            particle.scaleX = particle.scaleY = scale;

            // 寿命を減らす
            particle.life -= 1;

            // 寿命の判定
            if (particle.life === 0) {
                // ステージから削除
                stage.removeChild(particle);

                // 配列からも削除
                particles.splice(i, 1);
            }
        }
    }
}

// リサイズ処理
function handleResize(event) {
    var w = window.innerWidth;
    var h = window.innerHeight;
    var canvasWidth = w;
    if(980 < w){
        canvasWidth = 980;
    }
    var canvasHeight = 250;
    
    stage.canvas.width = canvasWidth;
    stage.canvas.height = canvasHeight;
    stage.update();
}

CSS


body{
    overflow:hidden;
}

.canvas_area{
    position: absolute;
    top: 0;
    left: 0;
    width:100%;
    height:100%;
    overflow:hidden;
    background-color:#222;
}
canvas#myCanvas {
    background:black;
}

.command_area{
    position:absolute;
    bottom:0;
    margin-bottom:10px;
    width:100%;
}
.command_area .event_button{
    width:40px;
    height:40px;
    border-radius:20px;
    margin-right:5px;
    background-color:#333;
    color:#fff;
    outline:none;
    user-select: none;
    -moz-user-select: none;
    -webkit-user-select: none;
    -ms-user-select: none;
}

参考リンク

  • CreateJS入門サイト - ICS MEDIA

    CreateJSで検索するとトップに出てくるサイト。非常にわかりやすく私のような入門者には非常に助かります。上記のコードもこちらのサイトで掲載されているものをベースに作成しています。

  • かわいいフリー素材集 いらすとや

    今回、使用したイラストはすべてこちらのサイトで掲載されているものを利用しています。

CreateJS】関連記事