bata's log

フロントエンド系のTipsとかメモ

GruntでSassのビルドとjsファイルの結合・圧縮を自動化

JSファイルの結合と圧縮を自動化したくて色々調べていたのですが、やはりGruntが一番便利だということで導入してみたました。
結論からいうと、もっと早く導入すればよかった。

Gruntの導入

導入部分は下記を参考に。

Windowsの場合
http://webdrawer.net/javascript/firstgrunt.html

Macの場合
http://catcher-in-the-tech.net/461/

Gruntfile.js を記述

module.exports = function(grunt) {

    grunt.initConfig({
                //Sassをビルド
        sass: {
            options: {
                style: 'compressed',//CSSのスタイル
                sourcemap: true,//ソースマップを書き出す
                noCache: true//キャッシュファイルを生成しない
            },
            styles: {
                src: 'scss/inc_sc.scss',//SCSSファイル
                dest: 'css/inc_sc.css'//CSSファイル
            }
        },

                //JSファイルを圧縮
        uglify: {
            js: {
                src: "js/import.js",//圧縮するファイル
                dest: "js/import.min.js"//圧縮したファイル
            }
        },

                //JSファイルを結合
        concat : {
            js : {
                src : [//結合するファイル
                'js/app.js',
                'js/app.namespace.js',
                'js/app.databind.js',
                'js/app.gallery.js'
                ],
                dest : 'js/import.js'//結合したファイル
            }
        },

                //監視対象のファイル
        watch: {
            sass: {
                files: ["scss/*.scss"],
                tasks: ['sass']//対象が変更されると実行されるタスク
            },
            js: {
                files: ["js/*.js"],
                tasks: ['concat','uglify']//対象が変更されると実行されるタスク
            }
        }

    });

        //使用するなプラグイン
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-sass');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-uglify');

};

Gruntfile.jsの記述は前述のサイトCordGridを参考にしました。

実行(コマンド)

grunt watch

(Windowsの場合)
grunt.cmd watch

実行するとファイルの監視が始まります。
今回の場合はjsフォルダ、scssフォルダ内のファイルに変更があると、それぞれのタスクが実行されます。

おまけ

Gruntの実行にいちいちコマンドを打つのが面倒な場合は、下記の記事の要領でバッチファイルを作成して該当のフォルダに設置しておけば便利です。

Sassのビルドにバッチファイルを利用する - bata's log

CSS3でパラパラアニメ

  • スプライト画像を利用したCSS3を使ったパラパラアニメの作り方。

HTML

<div class="anime"><img src="[画像のURL]" alt=""></div>

SCSS

.anime{
    width: 160px;
    height: 300px;
    overflow: hidden;
    margin: 100px;
    img{
        margin-top: 0;
        -webkit-animation-name: anime;
        -webkit-animation-duration: 1.0s;
        -webkit-animation-timing-function: steps(4,end);
        -webkit-animation-iteration-count: infinite;
        -moz-animation-name: anime;
        -moz-animation-duration: 1.0s;
        -moz-animation-timing-function: steps(4,end);
        -moz-animation-iteration-count: infinite;
        -ms-animation-name: anime;
        -ms-animation-duration: 1.0s;
        -ms-animation-timing-function: steps(4,end);
        -ms-animation-iteration-count: infinite;
    }
}

@-webkit-keyframes anime{
    100% {margin-top: -1200px;}
}

@-moz-keyframes anime{
    100% {margin-top: -1200px;}
}

@-ms-keyframes anime{
    100% {margin-top: -1200px;}
}

画像

f:id:kawabataryo:20140818135319p:plain

画像:ドロイドちゃん
ドロイドちゃんgifアニメ003 -歩く横向き- | ドロイドちゃんまとめサイト アンドロイド擬人化無料フリー素材

animation-timing-functionstepsを指定すると、なめらかなアニメーションではなく、終着点の値まで飛ぶ事が可能です。
この画像の場合は4コマで作っているので、steps(4,end)と指定することで終着点までを4分割した値までアニメーションせずに変化します。

.anime img{
    margin-top: 0;
   -webkit-animation-timing-function: steps(4/* コマ数 */,end);
}

@keyframes anime{
    100% {margin-top: -1200px/* 終着点までの距離(画像の高さ) */;}
}  

実際に動かすとこんな感じ

オブジェクトリテラルを入れ子にした場合のthisの扱いについて

下記のような書き方は機能を切り分けたりする時によくやると思いますが、オブジェクトリテラルが入れ子になった場合、入れ子のなかでthisの扱いが変わってしまいます。

var MyApp = {

    init: function(){
        this.getFugafuga(); //fugafuga!
    },

    util: {

        init: function(){
            this.getHogehoge(); //hogehoge!
            this.getFugafuga(); //undifine is not function

            MyApp.getFugafuga(); // fugafuga!
        },

        getHogehoge: function(){
            console.log('hogehoge!');
        }
    },

    getFugafuga: function(){
        console.log('fugafuga!');
    }

}

MyApp.util内でメソッドを呼び出す場合、thisMyApp.utilを指すことになります。
よってthis.getFugafuga();は実際にはMyApp.util.getFugafuga();となってしまっているのでエラーになります。

まあ、ちゃんと設計すればこういう呼び出し方にはならないのですが、、、

JavaScript(jQuery)で画像のサイズを取得する際のエラー

ドはまりしたのでメモ。

.appendとかで生成した<img />の場合、widthやheightを指定していない事がありますが、これが画像の幅や高さを取得する際にハマる原因になったりします。

高さが取得できない!

<img src="sample.jpg />
var height = $('img').height();
consolo.log(height) // 0

画像にheightの指定がない場合、普通に取得しようとするとと画像の高さは0になってしまいます。

解決策

取得できない原因は、画像の読込が完了していないから

htmlで高さを指定している場合は(CSSでの指定も同様)、DOM構築が終わった段階でHTML上のheightが取得できますが、指定がない場合は、画像の読込完了後に取得することで、画像の高さを取得することが可能になります。

<img src="sample.jpg />
//該当の画像の読込が完了してから高さの取得を実行する。
$('img').on('load',function(){
    var height = $('img').height();
    consolo.log(height) // 200
})

JSONからデータを取得して配列にフィルターをかける

WEBアプリ開発でJSONデータを扱うことになったのでメモ。
実際に作り始めると色々な機能追加がありそうなので、対応出来るような書き方を目指してみた。

JavaScript

(function(window,namespace,$){

    function _Item(dataFilter,i,$_list){
        this.member_id = dataFilter[i].member_id;
        this.member_category = dataFilter[i].member_category;
        this.member_name = dataFilter[i].member_name;
        this.item_id = dataFilter[i].item_id;
        this.item_name = dataFilter[i].item_name;
        this.item_src = dataFilter[i].item_src;
        this.addList($_list);
    }

    _Item.prototype.addList = function($_list){
        $_list.append(
            '<li>'
            + '<span>' + this.member_id + '</span>'
            + '<span>' + this.member_name + '</span>'
            + '<span>' + this.member_category + '</span>'
            + '<span>' + this.item_id + '</span>'
            + '<span>' + this.item_name + '</span>'
            + '<span>' + this.item_src + '</span>'
            + '</li>'
        );
    }

    window[namespace] = {

        init: function(json){
            var data = json;
            this.getDataAll(data);
            this.change(data);
        },

        change: function(data){
            var that = this;

            $('input:text').on('change',function(){
                var value = $(this).val();
                _case(data,'member_name',value);
            })

            $('select').on('change',function(){
                var value = $(this).val();
                _case(data,'member_category',value);
            })

            function _case(data,subject,value){
                if(value == 'all'){
                    that.getDataAll(data);
                }else{
                    that.getDataFilter(data,value,subject);
                }
            }
        },

        getDataAll: function(data){
            var dataFilter = data;
            this.display(dataFilter);
        },

        getDataFilter: function(data,value,subject){
            var dataAll = data;
            var dataFilter = [];
            var str = new RegExp(value,'i');
            dataFilter = dataAll.filter(function(item,index){
                if(item[subject].match(str)){
                    return true;
                };
            });
            this.display(dataFilter);
        },

        display: function(dataFilter){
            var len = dataFilter.length;
            var $_list = $('#list');
            $_list.empty();
            for (var i = 0; i < len; i++) {
                new _Item(dataFilter,i,$_list);
            };
        }
    }

})(this,'DataBind',jQuery);

JSONを読込む

$(function(){
    $.ajax({
        url: '/inc/data/data.json',
        type: 'GET',
        cache: false,
        dataType: 'json',
        success: function(json){
            DataBind.init(json);
        }
    });
});

実行するとこんな感じ

※HTMLその他のソースは上記参照