JavaScriptで作ったジグソーパズル をスマホにも対応させます。基本的にPC版 に対してコードを追加していきます。
VIDEO
フリックによるスクロールの許可と禁止
フリックすることでピースを移動させるのですが、スクロールもフリックでおこないます。ピースを移動させたいのにスクロールも同時におこなわれては困るのでスクロールを許可したり禁止する処理が必要になります。
function disableScroll ( e ) {
e . preventDefault ( ) ;
}
// これでフリック時のスクロールを禁止できる
window . addEventListener ( 'touchmove' , disableScroll , { passive : false } ) ;
// これでフリック時のスクロールを許可できる
window . removeEventListener ( 'touchmove' , disableScroll ) ;
そこで以下のような関数を定義します。第二引数でフリック時のスクロールを許可したり禁止します。
function enableScrollOnTouchMove ( e , enable ) {
if ( ! enable )
window . addEventListener ( 'touchmove' , disableScroll , { passive : false } ) ;
else
window . removeEventListener ( 'touchmove' , disableScroll ) ;
}
フリック開始時の処理
ディスプレイにタッチしたら指が触れている部分の座標を取得します。そしてその座標にピースがある場合はそのなかでも一番手前にあるものをmovingPieceに格納します。後述しますが、pieces.filter関数が返す配列の一番最後のものがそれに該当します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
window . addEventListener ( 'touchstart' , ( ev ) => {
ev . preventDefault ( ) ; // デフォルトイベントをキャンセル
if ( ev . touches . length ! = 1 ) { // 触れている指が1でない場合
enableScrollOnTouchMove ( ev , true ) ; // フリックでスクロールできるようにする
return ;
}
let x = ev . touches [ 0 ] . pageX ; // 触れている指に関する情報を取得
let y = ev . touches [ 0 ] . pageY ; // 触れている指に関する情報を取得
const rect = can . getBoundingClientRect ( ) ;
let ps = pieces . filter ( piece => piece . IsClick ( x - rect . left , y - rect . top ) ) ;
if ( ps . length == 0 ) {
console . log ( 'どれもクリックされていない' ) ;
enableScrollOnTouchMove ( ev , true ) ; // フリックでスクロールできるようにする
return ;
}
movingPiece = ps [ ps . length - 1 ] ; // 一番手前に描画されているものを取得
} ) ;
ピースを移動させる処理
フリックしているときの処理を示します。movingPieceがnullでないなら、指が触れている部分の座標を調べて、その部分に移動中のピースの中央が描画されるようにします。またピースが見えない位置に移動してしまわないように注意します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
window . addEventListener ( 'touchmove' , ( e ) => {
if ( e . touches . length ! = 1 )
return ;
if ( movingPiece ! = null ) {
const rect = can . getBoundingClientRect ( ) ;
let x = e . touches [ 0 ] . pageX ; // 触れている指に関する情報を取得
let y = e . touches [ 0 ] . pageY ; // 触れている指に関する情報を取得
let newX = x - rect . left - pieceSize * 0.75 ;
let newY = y - rect . top - pieceSize * 0.75 ;
// 見えない位置に移動してしまうのを防ぐ
if ( newX < - pieceSize / 2 )
newX = - pieceSize / 2 ;
if ( newY < - pieceSize / 2 )
newY = - pieceSize / 2 ;
if ( newX > can . width - pieceSize / 2 )
newX = can . width - pieceSize / 2 ;
if ( newY > can . height - pieceSize * 0.75 )
newY = can . height - pieceSize * 0.75 ;
movingPiece . X = newX ;
movingPiece . Y = newY ;
drawAll ( ) ;
}
} ) ;
フリック終了時の処理
フリックが終了したときの処理を示します。PC版とほとんどやっていることは同じなのですが、ディスプレイが狭いのでピースを仮置きできる場所を増やすために、そこにすでにピースがある場合、移動をキャンセルする処理をなくしました。
完成したときにピースが存在する場所に固定されているピースがなく、その位置からズレが小さいときだけフィットさせることにします。
またフリックが終了したタイミングで、フリックによるスクロールを禁止する処理をおこないます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
window . addEventListener ( 'touchend' , ( e ) => {
if ( movingPiece ! = null ) {
let col = Math . round ( movingPiece . X / pieceSize ) ;
let row = Math . round ( movingPiece . Y / pieceSize ) ;
if ( col < 0 )
col = 0 ;
if ( row < 0 )
row = 0 ;
if ( row < rowMax && col < colMax){
let ps = pieces.filter(_ => _.X == col * pieceSize && _.Y == row * pieceSize);
if ( ps . length == 0 ) {
// そこにピースがなくズレが小さいときだけフィットさせる
if ( Math . abs ( col * pieceSize - movingPiece . X ) < 20 && Math.abs(row * pieceSize - movingPiece.Y) < 20){
movingPiece.X = col * pieceSize;
movingPiece . Y = row * pieceSize ;
}
}
}
// 移動したピースを最前面に描画する
pieces = pieces . filter ( piece => piece ! = movingPiece ) ;
pieces . push ( movingPiece ) ;
movingPiece = null ;
drawAll ( ) ;
check ( ) ;
}
enableScrollOnTouchMove ( e , false ) ; // フリックでスクロールできないようにする
} ) ;
リロードの抑止
通常、手前にスワイプしたときにリロードが発生しますが、パズルを解いている最中にリロードされては困るのでリロードを抑止したいのですが完全に抑止することはできないので、リロードが発生するまえに確認のダイアログを表示させます。
// リロードの抑止(完璧ではない)
window . addEventListener ( 'beforeunload' , ( ev ) => {
ev . preventDefault ( ) ;
ev . returnValue = '' ;
} ) ;
VIDEO
鳩でも分かるC#管理人からのお願い
できる仕事であれば請け負います。鳩でもわかるC#管理人はクラウドワークスに在宅ワーカーとして登録しています。お仕事の依頼もお待ちしております。
⇒ 仕事を依頼する
コメントについて
コメントで英語などの外国語でコメントをされる方がいますが、管理人は日本語以外はわからないので基本的に内容が理解できず、承認することもありません。それからへんな薬を売っているサイトやリンク先のサイトが存在しないというスパムコメントも多々あります。
Some people make comments in foreign languages such as English, but since the manager does not understand anything other than Japanese, he basically cannot understand the content and does not approve it. Please use Japanese when making comments.
そんななか日本語のコメントもいただけるようになりました。「○○という変数はどこで宣言されているのか?」「××というメソッドはどこにあるのか」「例外が発生する」「いっそのことソース丸ごとくれ」という質問ですが、管理人としては嬉しく思います。「自分が書いた記事は読まれているんだな」と。疑問点には可能な限り答えます。記事に問題があれば修正いたします。
そのうえでお願いがあります。「匿名」という味も素っ気もない名前ではなく、捨てハンでいいのでなにかハンドルネームをつくってほしいと思います。
管理人のモチベーションアップのために
よろしければご支援お願いします。
⇒ 管理人の物乞いリスト