Simplicityを少し改造してみた part14 関連記事のスライダー化 スワイプPC・Mobile兼用

Simplicityに限らず他のテーマでも、関連記事でサムネイルを載せる場合、結構な量になりますよね。
うちのブログみたいに、結構長文で書いたりスクリプトを掲載したりしていると、そうでなくてもメインの記事が長くなってしまい、関連記事どころかコメントまでも辿り着くのが大変です。

以前、Simplicityのフォーラムのトピックにコメントの場所に飛ぶリンクが付けられないかというのが立てられたことがあったように記憶しています。そのおかげかどうかで、現在のSimplicityにもコメントへのリンクがあるわけなんですが、やはりそこに到達するまでにスクロールで結構大変な思いをすることもありますよね。

マウスジェスチャーを入れていれば、サックリとページ下部まで移動できるんですが、そうでない場合は、上記のようにコメントなどへのリンクをつけるか、あるいは、フッターへスクロールするギミックを入れないと大変なわけです。
かと言って、関連記事をどこに入れる?って話になって、色々と頭を悩ませる感じになります。

うちのサイトでは、あんまり高さ自体は変わってないんですが(むしろ高くなってしまった感がありますが)、2列で表示するようにしています。これを解消するために、スライダー(カルーセル)化したらどうだろうとふと思ったわけです。

いくつかサンプルを作りました。

縦自動スクロールバージョン

See the Pen 関連記事のスライダー化 WordPress theme Simplicity用 by Hidekichi (@Hidekichi) on CodePen.

縦自動スクロールバージョン 2列・レスポンシブ

See the Pen 関連記事のスライダー化(2列ver レスポンシブ) WordPress theme Simplicity用 by Hidekichi (@Hidekichi) on CodePen.

こういうのを作っていたのですが、公式フォーラムで、スマホでスワイプできると良いみたいなリクエストをもらい、スマホ関係のはあんまり作ったことがなかったので色々参考にしながら作り始めました。

PCとモバイルと仕様を分けても良かったのです。しかしどうせならPCでもスワイプできるものが作れないかと試行錯誤していると、PCスマホ兼用のswipeを自作してみる | GET HAPPY ←このサイトを見つけて、あ、いけるんじゃん。と、思ってひとまずそのまま実装していったのですが動作しない。

うーむ、去年の記事だから色々と仕様が変わったのかなぁと思っていたのですが、やり始めると凝ってしまう性格なので、ついつい手を入れていくと、色々と動作しない部分が見つかって、結局ロジックは流用させてもらったんですけれども、実装方法はちょっと異なる感じとなりました。で、できあがったのが次のものです。

関連記事のスライダー化 (SwipePC&mobile兼用・Responsive)

See the Pen 関連記事のスライダー化 (SwipePC&mobile兼用・Responsive) by Hidekichi (@Hidekichi) on CodePen.

まだ色々と試験中で、直さないといけない部分もあるかも知れませんが、ひとまずの動作はしているみたいなので、その内容を少し解説しておきたいと思います。

現在Simplicityは新系列のバージョン2がリリースされており、関連記事の部分は同じかも知れないんですけれども、1.x系とhtmlの構造やタグ・セレクタ等が変わっている(今後も変わる?)かも知れないので、そのままでは実装できない可能性もあります。予めお断りしておきます。

動作の内容としては、関連記事の内部を書き換える所から始まっています。
元々の記事のリストを、.sslide-wrapと言う新設したブロックの中に複製して、それらのタグを書き換えます。

タグの書き換えなどは以下のブログカード参考

jQuery 欲しい物を欲しい所に書く
最近とは…と言わずとも、結構な割合で「Simplicityのグローバルメニュー」と言うような検索ワードが集まるようになりました。 まぁ...

そして、サムネイルをimgタグで読まずに、imgタグが入っていた親セレクタの背景画像として読み込みます。背景画像で読み込むのには理由があり、サイズがレスポンシブに対応でき、かつ好きな比率で表示できるということにあります。
100%表示の時は16:9で、ブラウザの画面が狭くなり、スマホサイズぐらいになれば1:1で表示するというような事もできるわけです。

これらに併せて、元々のimgタグで読み込んでいたサムネイルの大きさを100×100から300×300に変更しました。少なからず画像自体を巨大化させるので少しだけ画質をあげるためです。これは、wordpressのサムネイルのサイズ設定でデフォルトのサイズ設定のままなら機能しますが、サイズ自体を任意のものになるようにカスタマイズされている場合は上手く動作しないかも知れません。

元々、関連記事は、各記事タイトルと「記事を読む」がリンクになっていたのですが、記事全体をそれらリンクのアドレスのaタグで囲むことで記事タイトルに元々あったリンクを外し、記事を読む自体も無くしました。これにより、表示される記事のどこをクリックしてもその記事に飛べるようになります。

仕様としてはこんな感じです。

タッチ(マウス)操作・スワイプ部分

元々のサンプルでは、スライドして記事が切り替わるのをjQueryのanimateを利用して動作させていました。しかしスマホなどでは一部これが上手く動作しないと言う記事をどこかで読んだので、何かしら他の方法はないものかと探していたわけです。で、PCスマホ兼用のswipeを自作してみる | GET HAPPYさん所を見つけたわけです。

記事がループして表示される部分のギミックとしては、範囲が端に達した時に、中身を入れ替えるということなので同じなわけです。どういうことかと言いますと、

1 ・ 2 ・ 3

とある場合に、左に動かせば、

2 ・ 3 ・ 1

となると言う具合です。

これは、

とあった場合、li:firstをli:lastの後ろに移動すると言う感じになります。もし、中身の子要素を2つ組で同時に動かしたい場合は、

このように何かしらで囲めばよいわけです。で、.wrap:firstを.wrap:lastの後に持っていく。やってることは同じです。css等で、上手く表示するようにスタイルしないといけませんが、こうすれば、2つ組のがループできる感じになります。
どのようにして、liを.wrapで囲むかと言いますと、例えば、

こんな感じでしょうか。

タッチ操作の仕組み

モバイルファーストなんて言葉がありますが、設計的にはそちらを優先するべきです。そんな言葉を信じていた時代が僕にもありました(笑)
しかし、モバイルの方はそれようの記事が色々と出てくるんですが、兼用にするにはどうするんだい?って話はなかなか出てきません。昔からjavascriptを触っていた人にはそれほど難しい事ではないんでしょうけれども、もう少し情報を出しておけよと小一時間・・・。
特に今回、chromeでは普通に動作したものがfirefoxでは動作しないというので頭を悩ませました。

世間では、

というので、タッチイベントが存在するか等を考慮して、このisTouchがtrueの時に、タッチイベントの値を拾いに行くと言う考え方で実装しているものが多くて、僕も多分そうなんだろうと思いながらやっていたのですが、どうやってもfirefoxで目的の値が拾えないわけです。かと言って、このisTouchがfalseになるわけでもないというのがハマる原因でした。

まず、順番として、タッチした→動かした→離したと言う一連の情報を取得しなくてはなりません。そのため、以下のような状態をまず作る必要があります。

画面をタッチした→動かした→離したというのはこんな感じで書けます。兼用にするためには、ここにPC用の記述が必要です。

こんな感じになります。先にモバイルのイベントを書くのはモバイルの方はいわゆるマウスの操作が無いために、無いものを補う処理をするためです。つまり、touchstartを最初に書いておけば、それをさっくり読み込むので反応が速いということです。
これができたらfunctionのイベントを拾う設定をします。

まずtouchstartの部分から。

e.preventDefault();はデフォルトの動作を止めておくおまじないです。
次のe.originalEventと、e.originalEvent.touchesの部分。これがスマホやらにはあってPCには無いイベントです。
ここがfirefoxで拾いたかった値を処理する部分なのですが、ググっていたサイトのどれもが、このe.originalEvent.touchesを探すようになっていたのです。無い場合はe.pageXと言うxの値という事で、例えば、

このような条件でタッチイベントがtrueであれば、e.originalEvent.touches[0].pageXをtPosXに入れる、そうでない場合はe.posXを入れると言うことなんですが、そもそもisTouchはデスクトップ版のfirefoxでもtrueで帰ってくんですよ。そしたらe.originalEvent.touches[0].pageXを代入することになるでしょう?
しかしそんな値はデスクトップ版のfirefoxにはないわけです。そしたらもちろん値がないぜとエラーが出る。これがハマり倒した部分です。

で、今回の処理なんですが、じゃあ逆にそれら値がtrueの時だけタッチイベントがあると考えて、!== undefined、つまりこれらがどちらも何かしらあるのであれば、それはタッチできる端末であると言うことで、変数touchesにe.originalEvent.touches[0]を入れます。
ここでもし、touchesに何かしらあったら(touches !== undefinedがtrueであれば)、タッチイベントからtouches(e.originalEvent.touches[0]).pageXを取り出し、無い場合は(PCの時は)e.clientXを取り出します。

これらは、どちらもx座標を取り出します。つまり、最初に画面にタッチした(マウスダウンした)時の座標のx座標をtPosXに入れるわけです。この時は移動していなのでtMovX = 0になります。
これでひとまず値は取れるようになりました。

次に、スワイプの動作でタッチしてから指が移動した時の座標を求めます。

ここもtouchstartで求めたような内容が入っていますが、今度は移動先のx座標からタッチした時の座標を引くことでどちらに移動したかとどれだけ移動したかがわかりますので、それを親要素のcssにtranslate3dでx部分に入れることで、さも中身が動いたような表示をすることになるわけです。対象の中身そのものを動かしても他の中身はついてきませんからそれらを囲むガワを動かします。そうすると中身が全部移動方向・移動量に沿って動きます。ガワが動いているので中身が一緒に動いているということになるわけです。

最後にスワイプを完了して指を離した時の設定をします。

ここは条件式なので特に説明はアレですけれども、タッチ移動量が、動かす中身が入っている親要素の1/3以上動いていればswipe.nextの処理へ、移動量が0よりも小さくて、動かす中身が入っている親要素の1/3以上マイナス側に動いていればswipe.prevの処理へ、そのどちらでもない場合(移動量が少ない時など)はswipe.restoreの処理へとなります。

まず最初の状態が表示された時、左へスワイプするということは「動かす中身が入っている親要素」の座標からしてみればプラスに動くことになります。座標は左側がマイナスで右側がプラスなので。逆に右にスワイプすると動かす中身が入っている親要素は右に動くので座標自体はマイナスになるわけです。
これ、横向きで考えるからわかりにくいんですが、右90度回転させたら分かり良いかと思います。
つまり原点が下に行けばプラスで上に行けばマイナスということです。

アイテム送りの処理

最初に、中身の要素をtransformで横並びにしています。

ここのselector部分は親要素の中の中身にあたります。ulで言えばliです。それぞれのliに対して(.each)処理をするわけですが、translate3dはx,y,zの座標を持っています。この部分のxに当たる部分を計算して出します。上記translate3d部分のtranslateは変数で100が入っています。
中身が5つあるとして、iの値が、以下のように変わると
0: ( 100 * 0 – 100 )%, 0, 0 → -100%, 0, 0
1: ( 100 * 1 – 100 )%, 0, 0 → 0%, 0, 0
2: ( 100 * 2 – 100 )%, 0, 0 → 100%, 0, 0
3: ( 100 * 3 – 100 )%, 0, 0 → 200%, 0, 0
4: ( 100 * 4 – 100 )%, 0, 0 → 300%, 0, 0
このような計算になって、並ぶことになります。つまり、2つ目の記事が最初に表示されることになるわけです。左側にひとつ作っておくのは、最初のアイテムを右スワイプで移動させた時に、まだ中身のアイテムを移動させる処理がしていないので、左側にスペースが開くのが不自然であるからです。左側にひとつ作っておけば、移動が完了した後に中身のアイテムを移動させる処理が行えます。

wordpressの関連記事はおそらくランダムに拾ってくるんだろうと思うんですけれども、何かしら規則性があった場合には、最初の1枚を-100%で左に配置するとマズイと言う場合があるかも知れません。そういった時には、予めhtmlの構造自体を変えておく必要があります。
考え方としては、最後の子要素を一番最初にもって来ておくと言う具合です。その後で、今回のスワイプの処理をしておけば、

-100% 0 100%
最後の子要素 1つ目の子要素 2つ目の子要素

こんな感じに配置できるので、1つ目の記事が一番最初に表示されると言うことになります。

これを踏まえて左スワイプ(アイテム送り)の処理は次のようになっています。

“transform”: “translate3d(100%, 0, 0)”の100%とは1画面分(親要素の幅分)座標がプラスの方に動くので中身は左に動く。つまり、アイテム送りをするわけです。で、移動が完了したら(transitionEnd←変数中身は’webkitTransitionEnd oTransitionEnd transitionend’)で、移動のアニメーションの終了を探し、transitionをカラにすることで停止させるわけです。
prevも移動方向が違うだけで同じようなことをしています。
で、swipe.resetで、移動後の処理をします。

移動後の処理とは、アニメーションで例えば左に動いた場合、一番左にあったアイテムを最後尾に持っていくような処理です。これによって無限ループが可能になります。カルーセル、つまりはメリーゴーラウンドです。

スワイプの距離が十分でない場合、元に戻す必要があります。その状態に戻すのがrestoreの部分です。

位置を元に戻すのが”transform”: “translate3d(0, 0, 0)”というのは理解してもらえると思うんですが、その後の見慣れぬ.oneと言うやつ。これは1度だけ動作するメソッドで、トランジション(移動.1s ease all)の終了直後にこのトランジションを無くします。
というのは、元々のトランジションが.3s easeで設定してあるので、元に戻すためです。

最後がresetの部分になります。ここは、先にも書いたように、移動が終了した時に、例えば最初にある子要素を最後尾に持っていくというような動作をします。

移動させるだけではhtmlの構造が変わるだけで並びが規則通りにならないので、再度横に並ばせる処理をします。
このような処理をswipeと言うオブジェクトに入れておきます。

スワイプ処理のスクリプト全容

タッチ(スワイプ)部分の処理は以下のような感じです。セレクタの部分などは本体そのままを掲載しているのでcodepenのサンプルを参照して下さい。

で、swipe.setPosition(swa); で動作させます。

記事を囲むように配置しなおしたリンクについて

元々がどういう常態かと言いますと、サンプルとは違いますが一般的にはこんな感じです。

このようになっているので、記事のどこをクリックしてもそのリンクが機能していたわけですが、タッチ操作をするにあたり、aが有効になっていると何かとマズイので、aはひとまず無効にしておきます。

などとしてリンククリックによりページが飛ぶのを無効にします。無効にしてあるので、aのリンク自体は機能していないのですが、これをタッチしたら機能させるようにするには、

つまり、タッチして指を離した(マウスを離した)時にその移動量が0であれば、その場をタッチ(クリック)したことだと判断して、クリックしたターゲットの一番近い親に当たるaを探し、そのaのhrefが何かしらあれば、該当のアドレスへ飛ぶと言うことをしています。

ここの仕組みを変えれば、aタグでなくても、何でも良いのが理解して頂けると思います。例えば、

こんなふうにしておけば、タッチ(クリック)したかのチェック時にdata-linkを調べ、そのアドレスでリンクとして機能すると思います。ここらは色々とアイデアが出せる部分でもあるかなぁと思ったりも。

全体の動作としてレスポンシブに対応させるために他に色々とリサイズ処理とかしている部分もあるので、ちょっとモタツキみたいのがあるような気がします。ここらは再考する必要があるのかも知れません。

だいぶ長なりましたが、こんな感じでひとまず動作するものができました。サンプルは継続的に直すかも知れないので、この記事の内容と変わるかも知れません。予めご了承下さい。

スポンサーリンク

シェアする

フォローする