「トランジション採用」でフリーランスの正社員転換を支援 他
- 隔週月曜更新!フリーランス・副業ニュース
こんにちは、さくらいみかです。
前回の記事では、JavaScriptのライブラリであるjQueryを使って、「スライドパズル」を生成するアプリを作りました。ブラウザ上で好きな画像のURLを読み込ませ、指定したマス目数のスライドパズルが生成される仕様です。
▲横9マス、縦5マスのスライドパズル
前回のバージョンでは、ブロック移動の操作は「動かしたいブロックをクリックするのみ」でしたが、今回は「スライド操作」を実装し、よりリアルなスライドパズルにアップデートしてみました。
また、それに伴って新機能もつけてみました。
「軽い」に設定しているときは、今までとは変わらないスライドパズルなのですが、
「重い」の方向にスライダーバーを移動させていくと、徐々に錆びていき……
さらにスライダーバーを右方向へ。「重い」をMAXに設定にすると……
完全に錆びます。動きも錆びついた感じになります。
もしこのスライドパズルが金属製だとしたら、時が経てばきっと錆びてきて動かしづらくなるはず……!! という妄想の産物です。
以下がそのスライドパズルです。ぜひ遊んでみてください。
See the Pen スライドパズル(2) by Mika Sakurai (@mika-sakurai) on CodePen.
ここからは「スライド機能をどう実装したか」「ブロックを掴んだとき、離したときの動き」「ブロックに錆びを加える方法」についてそれぞれ解説します。
まず、前バージョンの「Clickイベントによるブロック移動」を簡単におさらいします。
クリック操作のみでブロック(canvas要素)を移動させていたときには、以下のような処理を実装しました。
今回は本物のスライドパズルと同じような操作によって、ブロックをスライドさせてみます。ブロックの上にマウス(タッチパネルの場合は指)を置き、スライド動作によりブロックの移動処理を行います。
ドラッグ操作のような動きをしていますが、実際にはドラッグイベントは使っていません。以下のような手順で実装しました。
// ブロックにイベントを追加する
canvas.addEventListener("mousedown", touchstartEv, true);
canvas.addEventListener("touchstart", touchstartEv, true);
// 【※コードは説明に必要な箇所以外、省略してます】
functiontouchstartEv(ev) {
// ブロックの移動が可能か判定する(ver1 4-1で解説)
moveFlg = checkBlock(comp);
// 移動可能な場合、ブロックに「動かす・離す」イベントを与える
if (moveFlg) {
ev.stopPropagation();
comp.addEventListener("mousemove", moveEv, false);
comp.addEventListener("touchmove", moveEv, false);
comp.addEventListener("mouseout", touchendEv, false);
comp.addEventListener("mouseup", touchendEv, false);
comp.addEventListener("touchend", touchendEv, false);
}
function moveEv(ev) {
// 指の動いた距離(ブロックの動く距離)をセット
var move_x = pre_x-event.pageX;
var move_y = pre_y-event.pageY;
// ブロックの座標を取得
var comp_x = comp.style.left.replace("px","");
var comp_y = comp.style.top.replace("px","");
// ブロックを横に動かす場合
if ((comp.height*moveId0 == comp.height*blankId0) || !((comp.height*moveId0 <= comp_y-move_y && comp_y-move_y <= comp.height*blankId0) || (comp.height*blankId0 <= comp_y-move_y && comp_y-move_y <= comp.height*moveId0))) {
move_y = 0;
}
// ブロックを縦に動かす場合
if ((comp.width*moveId1 == comp.width*blankId1) || !((comp.width*moveId1 <= comp_x-move_x && comp_x-move_x <= comp.width*blankId1) || (comp.width*blankId1 <= comp_x-move_x && comp_x-move_x <= comp.width*moveId1))) {
move_x = 0;
}
// ブロックを移動させる
comp.style.top = comp_y-move_y + "px";
comp.style.left = comp_x-move_x + "px";
// マウスの座標を保持(次にmoveEvが呼ばれたときに利用する)
pre_x = event.pageX;
pre_y = event.pageY;
}
スライドパズルをちょっと扱いやすくするために「ブロックをスライド途中で離すと、自動で所定の位置に戻っていく」ように実装してみます。
掴んでいたブロックを離したときに、より近い方のスペースに移動するようにします。離したときのブロックの位置によって、元々ブロックがあった位置・空きスペースだった位置のいずれかに移動するのです。
// 手を離した後のブロックの位置を設定
var changeFlg = false; // ブロック入れ替えフラグ
// 縦方向に動くとき
if ((moveId0 < blankId0 && comp_y > (comp.height*moveId0+comp.height/2)) || (moveId0 > blankId0 && comp_y < (comp.height*blankId0+comp.height/2))) { // 移動する場合
block_y = comp.height*blankId0; // 空きスペースの座標
changeFlg = true;
} else {
block_y = comp.height*moveId0; // 元の位置に戻す
}
// 横方向に動くとき
if ((moveId1 < blankId1 && comp_x > (comp.width*moveId1+comp.width/2)) || (moveId1 > blankId1 && comp_x < (comp.width*blankId1+comp.width/2))) { // 移動する場合
block_x = comp.width*blankId1; // 空きスペースの座標
changeFlg = true;
} else {
block_x = comp.width*moveId1; // 元の位置に戻す
}
タイマー処理で一定時間ごとにブロックを動かし、2-1で決めた位置まで到達したら完了とします。(移動完了の処理は、ver1.の4-2で解説)
タイマーの間隔や、ブロックの移動距離の設定値を変えると、動き方が変わります(動きの滑らかさ、素早さ等)。
// ブロックが自動で動くときのタイマー呼び出し処理
var msec = 10; // ミリ秒
var distance = 5; // 呼び出すたびに移動する距離(px)
var endTimer = setInterval(function(){
if (moveId0 != blankId0) { // 縦に動くとき
var move_y = parseInt(comp.style.top.replace("px",""));
// 移動させる(5pxずつ移動)
if (block_y - comp_y > 0) { // プラス方向に戻す場合
comp.style.top = move_y+distance + "px";
} else { // マイナス方向に戻す場合
comp.style.top = move_y-distance + "px";
}
move_y = comp.style.top.replace("px",""); // 動いてる途中のブロックの位置
// ちょっとでもはみ出した場合、所定の位置に移動させて完了とする
if (!((block_y < move_y && move_y < comp_y) || (comp_y < move_y && move_y < block_y))) {
comp.style.top = block_y + "px";
clearInterval(endTimer);
complete();
}
}
if (moveId1 != blankId1) { // 横に動くとき
var move_x = parseInt(comp.style.left.replace("px",""));
if (block_x - comp_x > 0) { // プラス方向に戻す場合
comp.style.left = move_x+distance + "px";
} else { // マイナス方向に戻す場合
comp.style.left = move_x-distance + "px";
}
move_x = comp.style.left.replace("px",""); // 動いてる途中のブロックの位置
// ちょっとでもはみ出した場合、所定の位置に移動させて完了とする
if (!((block_x < move_x && move_x < comp_x) || (comp_x < move_x && move_x < block_x))) {
comp.style.left = block_x + "px";
clearInterval(endTimer);
complete(); //完了!!
}
}
} , msec);
「軽い・重い」のスライドバーの設定値によって、ブロックを動かすときの重さを変えてみます。また「重い」にバーを移動するに従って、ブロックを錆びた見た目にしてみましょう。
今回は重みのスライダーバーは6段階に変化するようにしました。重みレベルが1~4の場合は、単純にレベルに応じて重みを加えてるだけですが、5~6の場合は錆によるガタツキも加えてみました。
以下の動きを、重みに応じて変更していきます。
例えば、ブロックを掴んでスライドさせるとき、移動距離を重みで割り、動きが鈍くなるようにしています。
var move_x = (pre_x-event.pageX)/(omomi/2);
var move_y = (pre_y-event.pageY)/(omomi/2);
重みレベルが5~6の場合、以下のような方法でガタツキを表現しています。
if (omomi > 4) {
if (movecnt%15 == 0) { // mousemove、15回に一度少し後退させる(重みを出す)
if (move_x>0) {
move_x = move_x-(omomi/2);
} else if (move_x<0) {
move_x = move_x+(omomi/2);
}
if (move_y>0) {
move_y = move_y-(omomi/2);
} else if (move_y<0) {
move_y = move_y+(omomi/2);
}
}
}
var msec; // ミリ秒
if (omomi > 4) {
msec = omomi*10;
} else {
msec = 10;
}
各ブロックの背景に、錆テクスチャの画像(Photoshopで作成)を配置し、前面の画像を透過させることで錆びてるっぽい表現をさせています。
var sabiLv = [1,1,0.8,0.6,0.4,0.2];
canvas.style.backgroundImage = "url(錆テクスチャの画像URL)";
var context = canvas.getContext("2d");
context.globalAlpha = sabiLv[$("#omomi").val()-1]; // 前面の画像を透過
今回は主にブロックを掴んだとき、離したときの動きについてまとめてみました。前回と比べるとかなりスライドパズルっぽさが増したんじゃないでしょうか。
基本的な動きはもちろん、錆の表現もできて満足してますが、ちゃんとガタついてるように見えましたか!?
スライドパズルについては一旦これで終わりですが、今回実装した機能は今後なにか別のプロダクトにも利用できそうな気がしています。主に「オブジェクトのスライド加減を調整したい!」という人のお役に立ちそうかなと思いますが、もしほかにも参考になりそうなところがあれば幸いです。
前回の記事▼
【初心者向け】jQueryでスライドパズルを作ってみた (1)
Workship MAGAZINE