Posted on

全画面ページを簡単に作成!fullpage.jsの使い方とTIPS集

案件で全画面ページを簡単に作るプラグイン「fullpage.js」を使用する事がありました。

今回、レスポンシブ対応にあたって、文章等が1ページの範囲を超える場合があり、その際はスクロールバーを表示してスクロールできるようにしたいため、scrolloverflow.jsも使用しました。

そちらの簡単な使い方とハマった所をtipsとして取り上げます。

初期設定

まずは、必要な読み込みファイルから。

fullpage.js 公式サイト

まず、表示させたいページに必要なCSSとjsを読み込みます。








全画面ページにしたい所は起点の要素(今回はIDがfullpageの要素)と、その子クラスにsectionの名前を付けます。

横スライドも欲しい場合は、section要素にその子のクラスにslideという名前を付けます。

Some section
Some section
Some section
Slide 1
Slide 2
Slide 3
Slide 4
Some section

jQueryを使って呼び出します。

$('#fullpage').fullpage({
   anchors: ['page1', 'page2', 'page3', 'page4'], // ページのアンカー名を指定
   menu: '#global-menu', // グローバルメニューのID名
   scrollOverflow: true, //全画面よりコンテンツが多い場合スクロールバーを出すかどうか
   loopHorizontal:false, //横スライダーをループさせない
   touchSensitivity: 16, //タッチデバイスでのセクション切替の強さの閾値
   afterLoad: function (anchorLink, index) {
    //セクションが読み込み終わりのイベント
    if (index === 1) {
     //TOPロード時
	
    }else if(index === $('#fullpage .section').length){
     //最下ページ時
    }else{
     //その他のページの時
    }
   },
   afterSlideLoad: function (anchorLink, index, slideAnchor, slideIndex) {
     // スライドが読み込み終わった後のイベント
   },
   onLeave: function (index, nextIndex, direction) {
     // セクションを離れた時のイベント
   },
   onSlideLeave: function (anchorLink, index, slideIndex, direction, nextSlideIndex) {
     // スライドを離れた時のイベント
   },
   afterResize: function () {
    // リサイズ後のイベント
  }
});

グローバルメニューが要る場合には、上記menu:"#global-menu"のように指定して、li要素のdata-menuanchorに上記anchors配列で指定した名前と対応させます。

そうすると、グルーバルメニューのクラスに現在居るページはactiveが付きます。ページが遷移される度に現在居るクラスにactiveが付き、移動元ページのactiveクラスは外れますので、メニューのON/OFFのCSSの装飾などが簡単に作れます。


これで、縦4ページ、3ページ目に横に4スライドある全画面ページが出来上がったと思います。

TIPS

作成に当たって色々とありましたので、そこで得たTipsをドドーンと紹介しちゃいます。

可変サイズ対応

全画面のページのサイズが確定した後にAJAXやdisplay:noneしていた要素を表示させて、表示させている要素が増えた場合、スクロールする領域が足りない事が起きたりします。その際は、rebuild()関数を呼び出して、再度サイズ計算し新たなページのサイズを確定させます。

// フェードイン・フェードアウトの後fullpage.js再計算
$("#recruit-space").fadeToggle(500,function(){
    $.fn.fullpage.reBuild();
})

fullpage.jsの読み込み後に何かをさせたい場合

jQueryのロード時とfullpage.jsのロード時はタイミングとして別になるので、 fullpage.jsが読み込まれた後に処理を走らせたい場合は、afterRender以下に処理を書きます。

afterRender: function () {
    //(例)クリッカブルマップレスポンシブ
     $('img[usemap]').rwdImageMaps();
}

ページを遷移させる度にGoogle Analyticsのコードを送る

fullpage.jsは1ページを複数ページとして見せているだけなので、普通にGoogle Analyticsコードを貼っても該当ページの読み込み時に1回しか送られません。

貼るコードを分けて、縦ページの遷移、横ページをスライドさせた時に、Analyticsのコードを送るように書けば思うようなデータが取れます。

通常のアナリティクスコード読み込みの時に、下の方にあるga('send', 'pageview');を外します。


そして、fullpage.jsの横スライドが読み込まれた際に起きるイベントafterSlideLoad内にga('send'...のコードを書きます。

afterSlideLoad: function(anchorLink, index, slideAnchor, slideIndex) {
   ga('send', 'pageview', { 'page': anchorLink, 'title': slideAnchor });
},

縦ページ遷移に対応するにはadterLoadイベント内に記述します。

  afterLoad: function (anchorLink, index) {
     ga('send', 'pageview', { 'page': anchorLink, 'title':anchorLink });               
  }

まとめ

SPA風にページが手軽に作れたり、アイデア次第で面白いページが作れると思います。その分独自のライフサイクルやイベントがあるのでハマり所があったり、SEO的にはどうなるんだろうと思う所がありましたが、ここまで対応できれば普通に使えそうですね。

また何かあったら追記しようと思います。

fullpage.js 公式サイト

Posted on

ドラッグアンドドロップでファイルをアップロードする

今回ファイルを選択してあげている所をドラッグアンドドロップでファイルを指定して上げたいという要望を受けて対応しました。
jqueryさえあれば簡単に出来ちゃうので一番シンプルなサンプルを作ってみました。
続きを読む ドラッグアンドドロップでファイルをアップロードする

Posted on

Say yes or die!

凄く過激なタイトルを付けてしまいました。
これはあるSQL文を見て、それを短く書き換えたときの感情です。


SELECT SUM(CASE WHEN condition > 100 THEN 0 ELSE 1 END) as positive FROM some_table

1行で書いてしまいましたが、もともとはCASE文が数層入れ子になっていて、複雑な条件式を最後にSUMで括っていた、というのが複数項目にも及んでいた感じのSQLでした。そして、CASE式で評価される結果は必ず0か1。つまるところ、CASE文をたくさん組み合わせて条件を作り、条件に合うものを数えるということをやっていたわけです。

最初に思ったのは「WHERE句で条件絞ってcountすればいいじゃん」ってことです

SELECT COUNT(condition) as positive FROM some_table WHERE condition > 100

そうできればよかったんですが…条件に合う件数を数える項目がほかにもたくさんあり、それぞれで
毎回SQLを発行するのもこれまた頭が悪い。

SQLの返答をphp側でforeach回してオレオレカウントする…というのも考えたのですが、
カウント部分で何をやっているかを把握しようとするとCASE文のピラミッドとどっこいどっこいだなとも。

「テーブル全体を検索しつつ、複数条件それぞれにおいて条件に合うものをcountするには…」と調べていて、
このタイトルの「Say yes or die!」にたどり着いたわけです。

MySQLの論理式にはTrue、FalseのほかにNullを叩き込むことができると言う話があって…

  • True AND Null = Null
  • False AND Null = False
  • True OR Null = True
  • False OR Null = Null

つまるところ、Nullの有無にかかわらずTrue/False断言できる式は断言してもらえるという結果になるわけです。
そして、count()はNullでないものを数える。

というわけで生まれるイディオムが count([何か条件式] OR Null) というわけです。
条件式がTrueならレコードはTrueと評価され、カウントの対象になる、
条件式がFalseまたはNullなら、レコードはFalseと評価され、カウントされない。

このイディオムを使って最初のSQLを書き直すと…

SELECT count((condition > 100) OR NULL)as positive FROM some_table

‘OR NULL’という書き方の意味さえ知っていれば、これが何かを「合計」しているわけではなく「数えている」ことが一目でわかりますし、何より、WHERE句を変えながら何度もSQL発行しなくて済みます。別のカウントをしたいときは条件を変えた検索フィールドを追加するだけですから。

「Say yes or die!」もとい「Be true or null!」使えると思います!

Posted on

JSONで画像データを送信する方法

JSONで単純にファイル名を渡しても、当然ファイルのデータは送ることができません。
そのような場合は、base64_encodeを使って送りましょう。
このサンプルでは、JAVAScriptの時点でPHPのbase64_encodeを使用してデータ化した文字列を送り
受けて側で、base64_decodeしてファイルへ保存で渡すことが可能です。
続きを読む JSONで画像データを送信する方法

Posted on

WebサイトをPDFに変換する

HTMLでシステムの画面のモックを作成していた時に
お客様が全ページ画面キャプチャするのが大変・・・
印刷して朱書きで要望等簡単に書けないか?ということがありました。

社内でも調整してほしい箇所をキャプチャするのは大変だし、何かいい方法ないか調べてみました!

続きを読む WebサイトをPDFに変換する

Posted on

compassからlibsassへ。gulpの設定を見直して大幅速度UP!

gulpを動かしてゴリゴリSCSSを書いています。

当社はPHPを主に扱っているので、PHPが動く環境でかつSCSSが書けてbrowsersyncが効く状態が一番ストレスなく開発が進められます。

そんなストレスフリーな環境を実現するには、gulpfile.jsのbrowsersyncの設定をproxyにすれば可能です。

gulp.task("default-toEccube", function() {
    browser({
        proxy: "xxx.xxx.xxx/project/"
    });
    gulp.watch("scss/**/*.scss",["sass"]);
});

ストレスフリーにらくらく開発を進めていた所、SCSSコンパイルに何秒もかかっている事に気づきました。

よくよくgulpfilesのSCSSコンパイルの所を見直してみると、compassがかなり時間をかける原因になっていたようでした。

gulp.task("sass", function() {
    gulp.src("scss/**/*scss")
        .pipe(plumber({
            errorHandler: notify.onError("Error: <%= error.message %>") 
        }))
        .pipe(sourcemaps.init())
        //.pipe(sass())
         .pipe(compass({
             config_file: './config.rb',
             css: css_path,
             sass: 'scss'
         }))
        .pipe(autoprefixer())
        .pipe(sourcemaps.write(css_path))
        .pipe(gulp.dest(css_path))
        .pipe(browser.reload({stream:true}));
});

このcompassはrubyで動かしていて、これがボトルネックの原因でした。

速くしたいならlibsassを使うような流れになってきているそうで、これまた導入しようと調べた所、実はコメントアウトしていた .pipe(sass()) がそのlibsassという事を知って二度驚きました…。

gulp.task("sass", function() {
    gulp.src("scss/**/*scss")
        .pipe(plumber({
            errorHandler: notify.onError("Error: <%= error.message %>") 
        }))
        .pipe(sourcemaps.init())
        .pipe(sass())
        .pipe(autoprefixer())
        .pipe(sourcemaps.write(css_path))
        .pipe(gulp.dest(css_path))
        .pipe(browser.reload({stream:true}));
});

すぐさま、compassを消去し、sassを復活させましたが、今度は自分が良く使うベースのscssファイル群にcompassの関数が使われているのがあって、コンパイルが通らなくなりました。

そこでcompassの関数をsassのみで使えるように開発された、compass-mixinsを導入してみました。

使用方法は、使いたいプロジェクトファイルのscssフォルダにダウンロードしたcompass-mixinsのcompassフォルダを入れて、インポートしたいscssファイル(例えば、styles.scss)の上の方に、

@import "compass/reset";
@import "compass";

で呼び出すだけで、エラーがなくなりcompassの関数が使えちゃいました!!

そして、コンパイルの速度は6秒から1秒未満に!ctrl-s押したらすぐ完了!みたいなレベルになりました。

スプライトなどをcompassで使っていた場合別の方法を取らないといけないですがそこまで使っていなかった方にはオススメです。

Posted on

Conohaのオブジェクトストレージを試す

システムによって写真を大量に上げられる事があり他のシステムへの影響が出てきたのもあり今回プログラムから上げる画像やPDF等のファイルをシステムが動いているサーバーからオブジェクトストレージサーバーに置くことになりました。
Conohaのオブジェクトストレージを試した際につまずいた事等を書いていこうと思います。
続きを読む Conohaのオブジェクトストレージを試す

Posted on

array_filterの邪道?

まだまだ修行中です。最近は二度手間をできる限り抑えたいので
PHPのリファレンスを読み込んでみて、こんな関数既にあるんだなっていうのを見つけているところです。
しっかり覚えていなくても、頭の片隅に引っかかっていればいずれ使えるよな…という魂胆です。

そんな中でarray_filterの使い方がどうしても気になりました
http://php.net/manual/ja/function.array-filter.php

配列のキーや値を関数に通して、trueが返ってくるものだけを配列に詰め込んで返す、
使う関数は関数名を指定しても無名関数を渡してもいい、と。

ここで、手元のリファレンス書籍には「一次元配列のみに対応」と、
こちらのオンラインマニュアルには書いていない記述があったのがどうにも引っかかったんです。

「なぜ渡したらダメなのか、あるいはダメだったのか」

とりあえず、php5.4環境で渡してみてどうか調べてみました。

$order=array( 'Alice'=>array(
		'coffee'=>array(
			'milk'=>false,
			'sugar'=>true,
		),
	),
	'Bob'=>array(
		'coffee'=>array(
			'milk'=>true,
			'sugar'=>true,
		),
	),
	'Chalie'=>array(
		'coffee'=>array(
			'milk'=>true,
			'sugar'=>false,
		),
		'cake'=>array('chocolate')
	),
	'Deborah'=>array(
		'coffee'=>array(
			'milk'=>false,
			'sugar'=>false
		),
	),
	'Eve'=>array(
		'doughnut'=>array(
			'plain','strawberry'
		),
	),
	'Fred'=>null,
	'George'=>array(
		'coffee'=>array(
			'milk'=>true,
			'sugar'=>false
		)
	),
);

function order_filter($v){
	/* TODO:ここに検索条件を書く */
}

var_dump(array_filter($order,'order_filter'));

ここのorder_filter($v)をいくつか変えて出力を調べてみました。

//コーヒー注文
return isset($v['coffee']);

/* Output:
array (size=5)
  'Alice' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean true
  'Bob' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean true
  'Chalie' => 
    array (size=2)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean false
      'cake' => 
        array (size=1)
          0 => string 'chocolate' (length=9)
  'Deborah' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean false
  'George' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean false
*/
//砂糖有りコーヒーまたはコーヒー注文無し
return (isset($v['coffee']['sugar'])&&$v['coffee']['sugar'])||empty($v['coffee']);

/* Output:
Notice:Undefined index: coffee in (file) on line 43 //43行目=上の条件を書いた行

array (size=4)
  'Alice' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean true
  'Bob' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean true
  'Eve' => 
    array (size=1)
      'doughnut' => 
        array (size=2)
          0 => string 'plain' (length=5)
          1 => string 'strawberry' (length=10)
  'Fred' => null
*/
//砂糖有りコーヒーまたはコーヒー注文無し 改
return (isset($v['coffee']['sugar'])&&$v['coffee']['sugar'])||empty($v['coffee']);

/* Output:
array (size=4)
  'Alice' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean true
  'Bob' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean true
  'Eve' => 
    array (size=1)
      'doughnut' => 
        array (size=2)
          0 => string 'plain' (length=5)
          1 => string 'strawberry' (length=10)
  'Fred' => null
*/

結論としては、「キーの存在」を追加して調べる必要がある(ない場合noticeが出る)…みたいなところです。
この試行だけでは結局「なぜ渡したらダメなのか」という理由はわかりませんでした。
もっと未定義みたいな挙動が出てくるかと思ったら、そういうわけでもなく。
あるいは、バージョンが上がったことで渡せるようになったとか、そういうことなのかもしれないです。
(手元の書籍は5.1ぐらいに対応するよう書いていた9年以上前のものだからあり得る話)

可読性で言えば使わずにforeachループ回すのと五分五分、ってところかなと思います。
array_filterが何をしている関数かピンとこないと読みづらい、みたいな。

他の関数についても、気になったら実験を進めます。
それがリファレンスを頭の片隅に置くために役立つ気がしています。

Posted on

jQueryを用いた気持ちのよいインタラクションとフェードインを考える

気持ちの良いデザインを考える

Webデザインについて再考する時間があったので、インターフェースの心理学やUI GRAPHICS等の本を読んでどんな事がサイトをデザインする時に人を魅了できるかを勉強してみました。

その中で、能動的に選んでいる気でいても、実は反応であったり無意識的に動かされている事が多いという事を学びました。

では、何をもって人は無意識的に動かされるか、という点で考えた時に「ユーザーが何か起こした事によって得られる反応(=インタラクション)を気持ちの良いものにする」事で、自然と楽しい気持ちになったり、もっと触ってみようという気持ちになって、より能動的な働きとして変えていけるんではないかと考えるようになりました。

インタラクションとは

例えば、ソフトウェア、各種製品、携帯機器、環境、サービス、ウェアラブルコンピューティング、組織自体などのシステムに適用される。インタラクションデザインは、人工物やシステムのユーザーへの反応を振る舞い(インタラクション)として定義する。IaD または IxD と略記されることもある。システム開発においてユーザーの人力操作に対するシステムからの適切な反応を設計することで利用目的に合致した両面転移や、グラフィカルユーザーインターフェース(GUI)要素の肖然な振る舞いをデザインする専門的な作業である。

引用:wikipedia

前述の「ユーザーが何か起こした事によって得られる反応」をインタラクションといい、システムにおけるView部分、Webサイトでいうリンクやボタンで表現される入力部分のみならず、画像や文字などの表示部分(視覚的に入力する)をより適切なデザインを施す事で、そのシステム自体をより良いものに見せるという点で、現在注目されている分野であります。

イマドキのWebサイトは、ボタンにマウスをホバーするとふわっと色が変わる事や、画像ギャラリーで画像が、自動的にスライドして見られるスライダーなど動的である事が標準となってきております。

jQueryやJavaScriptが持てはやされているのは、この「当たり前に動く」仕組みを実現するのに不可欠なプログラミング言語で、昨今では、HTML + CSSに加えて、JavaScriptができる事がデザインという分野で求められる傾向にあります。

気持ちいい画像の表示方法を考えてみる

前置きが長くなりましたが、本題はここからです。

「画像を表示させる」というWebサイトでは当たり前の事象についても再考してみました。以下のDEMOはAJAXにて画像データを取得して表示するサンプルですが、表示のさせ方を4パターン用意してみました。

※Chrome、Firefoxでの動作の確認をしました。

See the Pen AJAXを使った画像読み込みの一工夫 by koka (@kokaben) on CodePen.

画像を表示させる方法

画像は画像サイズによって表示までの速度がマチマチになる事や、画像データを取得している時でも他の作業ができるように、バックグラウンド読み込み処理を行いながら用意ができたものから表示させる非同期処理を用いるのが現在の主流です。

当サンプルでは、画像を裏で読み込ませてメモリに展開された時点で、表示させるという方法を用いております。

すぐ表示」ボタンは、別段処理を施さずに、用意できた順から表示するようになっています。

一番最速で表示させたい時はこの方法が良いですが、少し無骨に見えませんか?文字のみならばこの方法が一番良いかもしれませんがもう少し見せ方にこだわりたく思ってしまいます。

フェードインする」ではどうでしょうか。

少し気持ちよくなった気がしませんか?少しずつ表示される事で少しワクワクするようなそんな作用が引き起こされていれば狙い通りです!

ただ、これが何百件だとどうでしょうか。「これはいつまで、どこまで表示されるんだろうか」と多少不安になったり、遅延処理を行っているので「少し遅いな」と感じてしまうんではないでしょうか。

表示する量や、場所を選べば気持ちよさのみの印象を与える事ができそうです。

表示する数と領域をあらかじめ見せる

背景を付けて●●」のボタンが2つありますが、この手法は記事を書いている時期(2017年)によくみられる手法で、ボタンを押すとあらかじめ表示される領域の背景をグレーで表示され、そこに画像や文字を展開していく見せ方です。

この手法は、YouTubeのスマートフォン版を起動した時や、検索した時に見られたり、Googleの画像検索でスクロールしていった時に自動的に次の30件が表示される時に見られる手法をまねたものです。

背景を付けてすぐ表示」ボタンの動きを見てみましょう。

タイミング的には「すぐ表示」で画像が表示されるタイミングで表示されて、そこから0.5秒後に画像が表示されるので、実質は「すぐ表示」の時より遅いのですが、先に領域が出る事で「ああ、ここに何か表示されるんだな」と頭が認識する時間が発生するので、そこまで遅いと感じないのではないでしょうか。

ただまだ、無骨に思えるので、それにフェードインを加えた「背景を付けてフェードイン」を試してみて下さい。

気持ちよくないでしょうか?

領域が決まって、そこから画像と文字のフェードインが起きながら、文字の領域はフェードアウトさせる事で、安定感のある面白くて気持ちの良い見せ方になったのではないかと思います。

感性に訴えたい

以上のサンプルは、必ずしもそんな気持ちになるかどうかは別の話で、私は「そういう風に狙った」というだけな話です。受け手の気持ちは千差万別と思われます。

併せて思うのは使いどころを見誤ったり、要らない所まで動きを付けまくっていると見づらいページになってしまうので、使いどころを見定める事は必要と思います。「コレが利いてて気持ちいいんだよなー」くらいがちょうど良いと思っています。

オーソリティーに受ける方法というのを模索しながら、より面白くてコストパフォーマンスに優れた見せ方をこれからも模索していくぞーと考えながら、日々勉強していこうと思います。

Posted on

JQueryにてJSONをPHPに送り、さらにベーシック認証の指定のURLにPOSTしてJSON形式のデータを受け取る方法

長いタイトルになってしまいましたが、ようするにJSON形式なら受け渡しが楽になりますよということです。
まず、JQueryからPHPにJSON形式のデータを渡す方法(www/index.php)




    
    
    
    Ajax Sample


Ajax sample

JQueryからJSON形式のデータを受け取りPHPで連想配列にして他のURLにポストする方法(www/ajax_api.php)
ちなみに、ベーシック認証がない場合は、define(‘DEMO’, true);をdefine(‘DEMO’, false);へ

 "{$memberId}"
    );
    $data = http_build_query($data, "", "&");

    if (DEMO) {
        //header
        $header = array(
            'User-Agent: My User Agent 1.0',    //ユーザエージェントの指定
            'Authorization: Basic ' . base64_encode('account:password'),//ベーシック認証
            "Content-Type: application/x-www-form-urlencoded",
            "Content-Length: " . strlen($data)
        );
    } else {
        //header
        $header = array(
            "Content-Type: application/x-www-form-urlencoded",
            "Content-Length: " . strlen($data)
        );
    }

    $context = array(
        "http" => array(
            "method"  => "POST",
            "header"  => implode("\r\n", $header),
            "content" => $data
        )
    );
    $res = file_get_contents(SAMPLE_API, false, stream_context_create($context));
    return $res;
}
?>

POSTデータを頂き、情報(連想配列のID指定にしていますが、ご利用の際には、DBから取得する方法へもバージョンアップできます。)
を取得JSON形式の返信を頂く方法(wwwapi/index.php)

 array("id" => 10000,'name' => 'Name10000','tel' => "000100010001"),
	"9999" => array("id" => 9999,'name' => 'Name9999','tel' => "000900090009"),
	"9998" => array("id" => 9998,'name' => 'Name9998','tel' => "000900090008"),
	"9997" => array("id" => 9997,'name' => 'Name9997','tel' => "000900090007"),
	"9996" => array("id" => 9996,'name' => 'Name9996','tel' => "000900090006"),
	"9995" => array("id" => 9995,'name' => 'Name9995','tel' => "000900090005"),
	"9994" => array("id" => 9994,'name' => 'Name9994','tel' => "000900090004"),
	"9993" => array("id" => 9993,'name' => 'Name9993','tel' => "000900090003"),
	"9992" => array("id" => 9992,'name' => 'Name9992','tel' => "000900090002"),
	);

$memberId = $_POST['member_id'];

if(empty($memberArray[$memberId])) {
	echo json_encode(array());
}
echo json_encode($memberArray[$memberId]);
?>

※jquery-1.11.2.min.jsは、特にふれていませんが、実行するには必要になります、ご注意ください。