Membuat Teka-Teki dengan HTML5 Canvas
() translation by (you can also view the original English article)
Dalam tutorial ini kita akan bekerja dengan HTML5 canvas dan Javascript untuk membuat permainan teka-teki yang dinamis. Hasilnya akan menjadi teka-teki yang bekerja dengan gambar yang diberikan, dan memiliki tingkat kesulitan yang fleksibel yang mudah disesuaikan.
Teka-teki dengan Canvas HTML5 Lengkap
Berikut adalah teka-teki yang akan kita bangun:
Beberapa catatan:
- Kompatibilitas lintas-browser: Teka-teki ini telah diuji dan berfungsi di semua versi Safari, Firefox, dan Chrome yang mendukung elemen
canvas
. - Seluler: Kode yang disediakan di sini berfungsi di browser desktop yang disebutkan di atas dan tidak dioptimalkan untuk seluler. Teka-teki akan memuat dan ditampilkan dengan baik, tetapi karena sentuhan dan perilaku seret di browser seluler, diperlukan pengoptimalan agar berfungsi dengan benar. Mengoptimalkan teka-teki untuk seluler ini akan dibahas dalam tutorial mendatang.
- Kesulitan yang Dapat Diatur: Kode berisi konstanta,
PUZZLE_DIFFICULTY
, yang menentukan jumlah potongan. Dalam demo di atas, ini diatur ke4
, memberikan puzzle 4x4. Kami dapat dengan mudah mengubah ini - misalnya, versi ini memilikiPUZZLE_DIFFICULTY
dari10
.
Memulai
Untuk memulai, buatlah direktori untuk proyek tersebut. Tempatkan gambar di direktori yang ingin Anda gunakan sebagai teka-teki Anda. Citra ramah web apa pun akan dilakukan, dan itu bisa menjadi ukuran apa pun yang diinginkan hati Anda - pastikan saja itu pas dengan lipatan jendela browser Anda.
Langkah 1: Membuat Template HTML
Buka file baru menggunakan editor teks favorit Anda dan simpan di dalam direktori proyek Anda, di sebelah gambar Anda. Selanjutnya, isi template HTML
dasar ini.
1 |
|
2 |
<!DOCTYPE html>
|
3 |
<html>
|
4 |
<head>
|
5 |
<title>HTML5 Puzzle</title> |
6 |
<script>
|
7 |
|
8 |
</script>
|
9 |
</head>
|
10 |
|
11 |
<body onload="init();"> |
12 |
<canvas id="canvas"></canvas> |
13 |
</body>
|
14 |
|
15 |
</html>
|
Yang perlu kita lakukan di sini adalah membuat template HTML5
standar yang berisi satu tag canvas
dengan id
"canvas". Kami akan menulis listener onload
di tag body
yang akan memanggil fungsi init()
kami saat diaktifkan.
Sekarang mulailah dengan menempatkan kursor Anda di dalam tag script
. Mulai sekarang semuanya sudah javascript. Dengan pengecualian variabel awal, saya akan mengatur bagian berdasarkan fungsi. Pertama menunjukkan Anda kode dan kemudian menjelaskan logika.
Siap? Mari kita lakukan dengan benar!
Langkah 2: Menyiapkan Variabel
Mari siapkan variabel dan lihat masing-masing.
1 |
|
2 |
const PUZZLE_DIFFICULTY = 4; |
3 |
const PUZZLE_HOVER_TINT = '#009900'; |
4 |
|
5 |
var _canvas; |
6 |
var _stage; |
7 |
|
8 |
var _img; |
9 |
var _pieces; |
10 |
var _puzzleWidth; |
11 |
var _puzzleHeight; |
12 |
var _pieceWidth; |
13 |
var _pieceHeight; |
14 |
var _currentPiece; |
15 |
var _currentDropPiece; |
16 |
|
17 |
var _mouse; |
Pertama-tama, kami memiliki beberapa konstanta: PUZZLE_DIFFICULTY
dan PUZZLE_HOVER_TINT
. Konstanta PUZZLE_DIFFICULTY
menyimpan jumlah potongan dalam teka-teki kami. Dalam aplikasi ini, baris dan kolom selalu cocok, jadi dengan mengatur PUZZLE_DIFFICULTY
ke 4
, kita mendapatkan 16 buah puzzle secara total. Peningkatan ini meningkatkan kesulitan teka-teki.
Selanjutnya adalah serangkaian variabel:
-
_canvas
dan_stage
akan memegang referensi ke kanvas dan konteks gambarnya masing-masing. Kami melakukan ini sehingga kami tidak perlu menuliskan seluruh kueri setiap kali kami menggunakannya. Dan kami akan banyak menggunakannya! -
_img
akan menjadi referensi ke gambar yang dimuat, yang akan kita salin piksel dari seluruh aplikasi. -
_puzzleWidth
,_puzzleHeight
,_pieceWidth
, dan_pieceHeight
akan digunakan untuk menyimpan dimensi dari keseluruhan puzzle dan setiap potongan puzzle individual. Kami mengatur ini sekali untuk mencegah menghitung mereka berulang kali setiap kali kita membutuhkannya. -
_currentPiece
memegang referensi ke bagian yang sedang diseret. -
_currentDropPiece
memegang referensi ke bagian yang saat ini pada posisi yang akan dijatuhkan. (Dalam demo, bagian ini disorot hijau.) -
_mouse
adalah referensi yang akan memegang posisix
dany
mouse saat ini. Ini akan diperbarui ketika teka-teki diklik untuk menentukan bagian mana yang disentuh, dan ketika sebuah bagian diseret untuk menentukan bagian mana yang melayang.
Sekarang, ke fungsi kami.
Langkah 3: Fungsi init()
1 |
|
2 |
function init(){ |
3 |
_img = new Image(); |
4 |
_img.addEventListener('load',onImage,false); |
5 |
_img.src = "mke.jpg"; |
6 |
}
|
Hal pertama yang ingin kami lakukan dalam aplikasi Anda adalah memuat gambar untuk teka-teki. Objek gambar pertama kali dipakai dan disetel ke variabel _img
kita. Selanjutnya, kita load
event yang kemudian akan mengaktifkan fungsi onImage()
kita ketika gambar telah selesai dimuat. Terakhir kita mengatur sumber gambar, yang memicu memuat.
Langkah 4: Fungsi onImage()
1 |
|
2 |
function onImage(e){ |
3 |
_pieceWidth = Math.floor(_img.width / PUZZLE_DIFFICULTY) |
4 |
_pieceHeight = Math.floor(_img.height / PUZZLE_DIFFICULTY) |
5 |
_puzzleWidth = _pieceWidth * PUZZLE_DIFFICULTY; |
6 |
_puzzleHeight = _pieceHeight * PUZZLE_DIFFICULTY; |
7 |
setCanvas(); |
8 |
initPuzzle(); |
9 |
}
|
Sekarang setelah gambar berhasil dimuat, kita dapat mengatur sebagian besar variabel yang dinyatakan sebelumnya. Kami melakukan ini di sini karena kami sekarang memiliki informasi tentang gambar dan dapat menetapkan nilai-nilai kami dengan tepat.
Hal pertama yang kami lakukan adalah menghitung ukuran setiap keping puzzle. Kami melakukan ini dengan membagi nilai PUZZLE_DIFFICULTY
dengan lebar dan tinggi dari gambar yang dimuat. Kami juga memangkas lemak dari bagian tepi untuk memberi kami sejumlah angka yang bagus untuk digunakan dan memastikan bahwa setiap bagian dapat menukar ‘slot’ dengan orang lain dengan tepat.
Selanjutnya kami menggunakan nilai potongan puzzle baru kami untuk menentukan ukuran total teka-teki dan mengatur nilai-nilai ini ke _puzzleWidth
dan _puzzleHeight
.
Terakhir, kami membatalkan beberapa fungsi -setCanvas()
dan initPuzzle()
.
Langkah 5: Fungsi setCanvas()
1 |
|
2 |
function setCanvas(){ |
3 |
_canvas = document.getElementById('canvas'); |
4 |
_stage = _canvas.getContext('2d'); |
5 |
_canvas.width = _puzzleWidth; |
6 |
_canvas.height = _puzzleHeight; |
7 |
_canvas.style.border = "1px solid black"; |
8 |
}
|
Sekarang nilai-nilai puzzle kami sudah lengkap, kami ingin mengatur elemen canvas
kami. Pertama kita mengatur variabel _canvas
kita untuk mereferensikan elemen canvas
kita, dan _stage
untuk mereferensikan context
-nya.
Sekarang kami menetapkan width
dan height
canvas
kami untuk mencocokkan ukuran gambar yang dipotong, diikuti dengan menerapkan beberapa gaya sederhana untuk membuat perbatasan hitam di sekitar canvas
kami untuk menampilkan batas-batas teka-teki kami.
Langkah 6: Fungsi initPuzzle()
1 |
|
2 |
function initPuzzle(){ |
3 |
_pieces = []; |
4 |
_mouse = {x:0,y:0}; |
5 |
_currentPiece = null; |
6 |
_currentDropPiece = null; |
7 |
_stage.drawImage(_img, 0, 0, _puzzleWidth, _puzzleHeight, 0, 0, _puzzleWidth, _puzzleHeight); |
8 |
createTitle("Click to Start Puzzle"); |
9 |
buildPieces(); |
10 |
}
|
Di sini kami menginisialisasi teka-teki. Kami mengatur fungsi ini sedemikian rupa sehingga kami dapat memanggilnya lagi nanti ketika kami ingin memutar ulang teka-teki. Apa pun yang perlu diatur sebelum bermain tidak perlu diatur lagi.
Pertama kita mengatur _pieces
sebagai array
kosong dan membuat objek _mouse
, yang akan menahan posisi mouse kita di seluruh aplikasi. Selanjutnya kita mengatur _currentPiece
dan _currentPieceDrop
ke null
. (Pada permainan pertama, nilai-nilai ini akan menjadi null
, tetapi kami ingin memastikannya disetel ulang saat memutar ulang teka-teki.)
Akhirnya, saatnya menggambar! Pertama kita menggambar seluruh gambar untuk ditampilkan kepada pemain apa yang akan mereka ciptakan. Setelah itu kami membuat beberapa instruksi sederhana dengan memanggil fungsi createTitle()
kami.
Langkah 7: Fungsi createTitle()
1 |
|
2 |
function createTitle(msg){ |
3 |
_stage.fillStyle = "#000000"; |
4 |
_stage.globalAlpha = .4; |
5 |
_stage.fillRect(100,_puzzleHeight - 40,_puzzleWidth - 200,40); |
6 |
_stage.fillStyle = "#FFFFFF"; |
7 |
_stage.globalAlpha = 1; |
8 |
_stage.textAlign = "center"; |
9 |
_stage.textBaseline = "middle"; |
10 |
_stage.font = "20px Arial"; |
11 |
_stage.fillText(msg,_puzzleWidth / 2,_puzzleHeight - 20); |
12 |
}
|
Di sini kita membuat pesan yang cukup sederhana yang menginstruksikan pengguna untuk mengklik teka-teki untuk memulai.
Pesan kami akan menjadi persegi panjang semi-transparan yang akan berfungsi sebagai latar belakang teks kami. Ini memungkinkan pengguna untuk melihat gambar di belakangnya dan juga memastikan teks putih kami akan terbaca pada gambar apa pun
Kami cukup mengatur fillStyle
ke hitam dan globalAlpha
ke .4
, sebelum mengisi persegi panjang hitam pendek di bagian bawah gambar.
Karena globalAlpha
mempengaruhi seluruh kanvas, kita perlu mengaturnya kembali ke 1
(buram) sebelum menggambar teks. Untuk mengatur judul kami, kami mengatur textAlign
ke 'center' dan textBaseline
ke 'middle'
. Kami juga dapat menerapkan beberapa properti font
.
Untuk menggambar teks, kami menggunakan metode fillText()
. Kami meneruskan dalam variabel msg
dan letakkan di tengah horizontal canvas
, dan pusat vertikal persegi panjang.
Langkah 8: Fungsi buildPieces()
1 |
|
2 |
function buildPieces(){ |
3 |
var i; |
4 |
var piece; |
5 |
var xPos = 0; |
6 |
var yPos = 0; |
7 |
for(i = 0;i < PUZZLE_DIFFICULTY * PUZZLE_DIFFICULTY;i++){ |
8 |
piece = {}; |
9 |
piece.sx = xPos; |
10 |
piece.sy = yPos; |
11 |
_pieces.push(piece); |
12 |
xPos += _pieceWidth; |
13 |
if(xPos >= _puzzleWidth){ |
14 |
xPos = 0; |
15 |
yPos += _pieceHeight; |
16 |
}
|
17 |
}
|
18 |
document.onmousedown = shufflePuzzle; |
19 |
}
|
Akhirnya saatnya membangun teka-teki!
Kami melakukan ini dengan membuat object
untuk masing-masing bagian. Benda-benda ini tidak akan bertanggung jawab untuk rendering ke kanvas, melainkan hanya memegang referensi tentang apa yang harus menggambar dan di mana. Itu dikatakan, mari kita lakukan.
Pertama-tama, mari kita nyatakan beberapa variabel yang akan kita gunakan kembali melalui pengulangan. Kami ingin mengatur pengulangan untuk mengulang melalui jumlah potongan puzzle yang kami butuhkan. Kami mendapatkan nilai ini dengan mengalikan PUZZLE_DIFFICULTY
dengan sendirinya - jadi dalam hal ini kita mendapatkan 16.
Di dalam lingkaran:
1 |
|
2 |
for(i = 0;i < PUZZLE_DIFFICULTY * PUZZLE_DIFFICULTY;i++){ |
3 |
piece = {}; |
4 |
piece.sx = xPos; |
5 |
piece.sy = yPos; |
6 |
_pieces.push(piece); |
7 |
xPos += _pieceWidth; |
8 |
if(xPos >= _puzzleWidth){ |
9 |
xPos = 0; |
10 |
yPos += _pieceHeight; |
11 |
}
|
12 |
}
|
Mulai dengan membuat objek piece
kosong. Selanjutnya tambahkan properti sx
dan sy
ke objek. Pada iterasi pertama, nilai-nilai ini adalah 0
dan mewakili titik dalam gambar kita di mana kita akan mulai menggambar. Sekarang dorong ke array _pieces[]
. Objek ini juga akan berisi properti xPos
dan yPos
, yang akan memberi tahu kami posisi saat ini di teka-teki di mana potongan harus ditarik. Kami akan mengocok objek sebelum dapat dimainkan sehingga nilai-nilai ini tidak perlu disetel.
Hal terakhir yang kami lakukan di setiap loop adalah meningkatkan variabel lokal xPos
oleh _pieceWidth
. Sebelum melanjutkan dengan pengulangan, kami menentukan apakah kami perlu turun ke deretan berikutnya dengan memeriksa apakah xPos
berada di luar lebar teka-teki. Jika demikian, kami mengatur ulang xPos
kembali ke 0 dan meningkatkan yPos
menurut _pieceHeight
.
Sekarang kami memiliki potongan puzzle kami semua disimpan dengan baik di array _pieces
kami. Pada titik ini, kode akhirnya berhenti mengeksekusi dan menunggu pengguna untuk berinteraksi. Kami mengatur pendengar klik ke document
untuk mengaktifkan fungsi shufflePuzzle()
saat dipicu, yang akan memulai permainan.
Langkah 9: Fungsi shufflePuzzle()
1 |
|
2 |
function shufflePuzzle(){ |
3 |
_pieces = shuffleArray(_pieces); |
4 |
_stage.clearRect(0,0,_puzzleWidth,_puzzleHeight); |
5 |
var i; |
6 |
var piece; |
7 |
var xPos = 0; |
8 |
var yPos = 0; |
9 |
for(i = 0;i < _pieces.length;i++){ |
10 |
piece = _pieces[i]; |
11 |
piece.xPos = xPos; |
12 |
piece.yPos = yPos; |
13 |
_stage.drawImage(_img, piece.sx, piece.sy, _pieceWidth, _pieceHeight, xPos, yPos, _pieceWidth, _pieceHeight); |
14 |
_stage.strokeRect(xPos, yPos, _pieceWidth,_pieceHeight); |
15 |
xPos += _pieceWidth; |
16 |
if(xPos >= _puzzleWidth){ |
17 |
xPos = 0; |
18 |
yPos += _pieceHeight; |
19 |
}
|
20 |
}
|
21 |
document.onmousedown = onPuzzleClick; |
22 |
}
|
1 |
|
2 |
function shuffleArray(o){ |
3 |
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); |
4 |
return o; |
5 |
}
|
Hal pertama yang pertama: mengacak array _pieces[]
. Saya menggunakan fungsi utilitas yang bagus di sini yang akan mengacak indeks array yang dilewatkan ke dalamnya. Penjelasan tentang fungsi ini berada di luar topik tutorial ini jadi kami akan melanjutkan, mengetahui bahwa kami telah berhasil mengocok bagian kami. (Untuk pengenalan dasar untuk mengacak, lihat tutorial ini.)
Pertama-tama, hapus semua gambar yang ditarik ke canvas
untuk memberi cara menggambar potongan kita. Selanjutnya, atur susunan yang sama dengan yang kita lakukan saat pertama kali membuat objek potong kita.
Di dalam lingkaran:
1 |
|
2 |
for(i = 0;i < _pieces.length;i++){ |
3 |
piece = _pieces[i]; |
4 |
piece.xPos = xPos; |
5 |
piece.yPos = yPos; |
6 |
_stage.drawImage(_img, piece.sx, piece.sy, _pieceWidth, _pieceHeight, xPos, yPos, _pieceWidth, _pieceHeight); |
7 |
_stage.strokeRect(xPos, yPos, _pieceWidth,_pieceHeight); |
8 |
xPos += _pieceWidth; |
9 |
if(xPos >= _puzzleWidth){ |
10 |
xPos = 0; |
11 |
yPos += _pieceHeight; |
12 |
}
|
13 |
}
|
Pertama-tama, gunakan variabel i
untuk mengatur referensi kami ke objek potongan saat ini dalam loop. Sekarang kami mengisi properti xPos
dan yPos
yang saya sebutkan sebelumnya, yang akan menjadi 0
dalam iterasi pertama kami.
Sekarang, akhirnya, kami menggambar potongan kami.
Parameter pertama drawImage()
memberikan sumber gambar yang ingin kita gambar. Kemudian gunakan objek potongan sx
dan properti sy
, bersama dengan _pieceWidth
dan _pieceHeight
, untuk mengisi parameter yang menyatakan area gambar untuk menggambar. Empat parameter terakhir mengatur luas canvas
di mana kita ingin menggambar. Kami menggunakan nilai xPos
dan yPos
yang kami bangun di loop dan menugaskan ke objek.
Segera setelah ini, kami menggambar goresan cepat di sekitar potongan untuk memberikannya perbatasan, yang akan memisahkannya dengan baik dari potongan-potongan lainnya.
Sekarang kita menunggu pengguna untuk mengambil sepotong dengan mengatur click
listener lain. Kali ini akan mengaktifkan fungsi onPuzzleClick()
.



Langkah 10: Fungsi onPuzzleClick()
1 |
|
2 |
function onPuzzleClick(e){ |
3 |
if(e.layerX || e.layerX == 0){ |
4 |
_mouse.x = e.layerX - _canvas.offsetLeft; |
5 |
_mouse.y = e.layerY - _canvas.offsetTop; |
6 |
}
|
7 |
else if(e.offsetX || e.offsetX == 0){ |
8 |
_mouse.x = e.offsetX - _canvas.offsetLeft; |
9 |
_mouse.y = e.offsetY - _canvas.offsetTop; |
10 |
}
|
11 |
_currentPiece = checkPieceClicked(); |
12 |
if(_currentPiece != null){ |
13 |
_stage.clearRect(_currentPiece.xPos,_currentPiece.yPos,_pieceWidth,_pieceHeight); |
14 |
_stage.save(); |
15 |
_stage.globalAlpha = .9; |
16 |
_stage.drawImage(_img, _currentPiece.sx, _currentPiece.sy, _pieceWidth, _pieceHeight, _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth, _pieceHeight); |
17 |
_stage.restore(); |
18 |
document.onmousemove = updatePuzzle; |
19 |
document.onmouseup = pieceDropped; |
20 |
}
|
21 |
}
|
Kami tahu bahwa teka-teki itu diklik; sekarang kita perlu menentukan apa yang diklik. Ini bersyarat sederhana akan memberi kita posisi mouse pada semua browser desktop modern yang mendukung canvas
, baik menggunakan e.layerX
dan e.layerY
atau e.offsetX
dan e.offsetY
. Gunakan nilai-nilai ini untuk memperbarui objek _mouse
kami dengan menugaskannya sebuah x
dan properti y
untuk menahan posisi mouse saat ini - dalam hal ini, posisi di mana ia diklik.
Sejalan 112 kami kemudian segera menetapkan _currentPiece
ke nilai yang dikembalikan dari fungsi checkPieceClicked()
kami. Kami memisahkan kode ini karena kami ingin menggunakannya nanti ketika menyeret potongan puzzle. Saya akan menjelaskan fungsi ini di langkah berikutnya.
Jika nilai yang dikembalikan null
, kami tidak melakukan apa-apa, karena ini menyiratkan bahwa pengguna tidak benar-benar mengeklik potongan teka-teki. Namun, jika kita mengambil potongan puzzle, kita ingin menempelkannya ke mouse dan memudarkannya sedikit untuk mengungkapkan potongan di bawahnya. Jadi bagaimana kita melakukan ini?
Pertama kita bersihkan area canvas
tempat potongan itu duduk sebelum kita mengkliknya. Kami menggunakan clearRect()
sekali lagi, tetapi dalam kasus ini kami hanya meneruskan area yang diperoleh dari objek _currentPiece
. Sebelum kita menggambar ulang, kami ingin save()
konteks kanvas sebelum melanjutkan. Ini akan memastikan bahwa apa pun yang kita hasilkan setelah menabung tidak akan sekadar menarik apa pun yang ada di jalannya. Kami melakukan ini karena kami akan sedikit memudar bagian yang diseret dan ingin melihat potongan di bawahnya. Jika kami tidak memanggil save()
, kami hanya menggambar di atas grafik apa pun dengan cara - memudar atau tidak.
Sekarang kita menggambar gambar sehingga pusatnya diposisikan pada penunjuk tetikus. 5 parameter pertama drawImage
akan selalu sama di seluruh aplikasi. Saat mengklik, dua parameter berikutnya akan diperbarui untuk memusatkan dirinya ke penunjuk mouse. Dua parameter terakhir, width
dan height
untuk menggambar, juga tidak akan pernah berubah.
Terakhir kita memanggil metode restore()
. Ini pada dasarnya berarti kita selesai menggunakan nilai alpha baru dan ingin mengembalikan semua properti kembali ke tempat mereka berada. Untuk menyelesaikan fungsi ini kami menambahkan dua pendengar lagi. Satu untuk ketika kita menggerakkan mouse (menyeret potongan puzzle), dan satu untuk ketika kita melepaskan (menjatuhkan potongan puzzle).
Langkah 11: Fungsi checkPieceClicked()
1 |
|
2 |
function checkPieceClicked(){ |
3 |
var i; |
4 |
var piece; |
5 |
for(i = 0;i < _pieces.length;i++){ |
6 |
piece = _pieces[i]; |
7 |
if(_mouse.x < piece.xPos || _mouse.x > (piece.xPos + _pieceWidth) || _mouse.y < piece.yPos || _mouse.y > (piece.yPos + _pieceHeight)){ |
8 |
//PIECE NOT HIT
|
9 |
}
|
10 |
else{ |
11 |
return piece; |
12 |
}
|
13 |
}
|
14 |
return null; |
15 |
}
|
Sekarang kita perlu mundur sedikit. Kami dapat menentukan bagian apa yang diklik, tetapi bagaimana kami melakukannya? Cukup sederhana sebenarnya. Yang perlu kita lakukan adalah mengulang semua potongan teka-teki dan menentukan apakah klik itu dalam batas-batas salah satu objek kita. Jika kita menemukannya, kita mengembalikan objek yang cocok dan mengakhiri fungsi. Jika kami tidak menemukan apa pun, kami mengembalikan null
.
Langkah 12: Fungsi UpdatePuzzle()
1 |
|
2 |
function updatePuzzle(e){ |
3 |
_currentDropPiece = null; |
4 |
if(e.layerX || e.layerX == 0){ |
5 |
_mouse.x = e.layerX - _canvas.offsetLeft; |
6 |
_mouse.y = e.layerY - _canvas.offsetTop; |
7 |
}
|
8 |
else if(e.offsetX || e.offsetX == 0){ |
9 |
_mouse.x = e.offsetX - _canvas.offsetLeft; |
10 |
_mouse.y = e.offsetY - _canvas.offsetTop; |
11 |
}
|
12 |
_stage.clearRect(0,0,_puzzleWidth,_puzzleHeight); |
13 |
var i; |
14 |
var piece; |
15 |
for(i = 0;i < _pieces.length;i++){ |
16 |
piece = _pieces[i]; |
17 |
if(piece == _currentPiece){ |
18 |
continue; |
19 |
}
|
20 |
_stage.drawImage(_img, piece.sx, piece.sy, _pieceWidth, _pieceHeight, piece.xPos, piece.yPos, _pieceWidth, _pieceHeight); |
21 |
_stage.strokeRect(piece.xPos, piece.yPos, _pieceWidth,_pieceHeight); |
22 |
if(_currentDropPiece == null){ |
23 |
if(_mouse.x < piece.xPos || _mouse.x > (piece.xPos + _pieceWidth) || _mouse.y < piece.yPos || _mouse.y > (piece.yPos + _pieceHeight)){ |
24 |
//NOT OVER
|
25 |
}
|
26 |
else{ |
27 |
_currentDropPiece = piece; |
28 |
_stage.save(); |
29 |
_stage.globalAlpha = .4; |
30 |
_stage.fillStyle = PUZZLE_HOVER_TINT; |
31 |
_stage.fillRect(_currentDropPiece.xPos,_currentDropPiece.yPos,_pieceWidth, _pieceHeight); |
32 |
_stage.restore(); |
33 |
}
|
34 |
}
|
35 |
}
|
36 |
_stage.save(); |
37 |
_stage.globalAlpha = .6; |
38 |
_stage.drawImage(_img, _currentPiece.sx, _currentPiece.sy, _pieceWidth, _pieceHeight, _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth, _pieceHeight); |
39 |
_stage.restore(); |
40 |
_stage.strokeRect( _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth,_pieceHeight); |
41 |
}
|
Sekarang kembali ke menyeret. Kami memanggil fungsi ini ketika pengguna menggerakkan mouse. Ini adalah fungsi terbesar dari aplikasi saat melakukan beberapa hal. Mari kita mulai. Saya akan memecahnya saat kita pergi.
1 |
|
2 |
_currentDropPiece = null; |
3 |
if(e.layerX || e.layerX == 0){ |
4 |
_mouse.x = e.layerX - _canvas.offsetLeft; |
5 |
_mouse.y = e.layerY - _canvas.offsetTop; |
6 |
}
|
7 |
else if(e.offsetX || e.offsetX == 0){ |
8 |
_mouse.x = e.offsetX - _canvas.offsetLeft; |
9 |
_mouse.y = e.offsetY - _canvas.offsetTop; |
10 |
}
|
Mulai dengan mengatur _currentDropPiece
ke null
. Kita perlu mengatur ulang ini kembali ke null
pada pembaruan karena kemungkinan bahwa bagian kami diseret kembali ke rumahnya. Kami tidak ingin nilai _currentDropPiece
sebelumnya berkeliaran. Selanjutnya kita mengatur objek _mouse
dengan cara yang sama seperti yang kita lakukan pada klik.
1 |
|
2 |
_stage.clearRect(0,0,_puzzleWidth,_puzzleHeight); |
Di sini kita perlu menghapus semua grafik di kanvas. Pada dasarnya kita perlu menggambar kembali potongan-potongan puzzle karena objek yang diseret di atas akan mempengaruhi penampilan mereka. Jika kami tidak melakukan ini, kami akan melihat beberapa hasil yang sangat aneh mengikuti jalur potongan teka-teki yang diseret.
1 |
|
2 |
var i; |
3 |
var piece; |
4 |
for(i = 0;i < _pieces.length;i++){ |
Mulailah dengan mengatur lingkaran potongan kita yang biasa.
Di Loop:
1 |
|
2 |
piece = _pieces[i]; |
3 |
if(piece == _currentPiece){ |
4 |
continue; |
5 |
}
|
Buat referensi piece
kami seperti biasa. Selanjutnya periksa apakah bagian yang kita rasakan saat ini sama dengan bagian yang kita seret. Jika demikian, lanjutkan pengulangan. Ini akan membuat slot rumah potong yang diseret kosong.
1 |
|
2 |
_stage.drawImage(_img, piece.sx, piece.sy, _pieceWidth, _pieceHeight, piece.xPos, piece.yPos, _pieceWidth, _pieceHeight); |
3 |
_stage.strokeRect(piece.xPos, piece.yPos, _pieceWidth,_pieceHeight); |
Pindah, buat kembali potongan teka-teki menggunakan propertinya persis dengan cara yang sama ketika pertama-tama menggambarnya. Pindah, buat kembali potongan teka-teki menggunakan propertinya persis dengan cara yang sama ketika pertama-tama menggambarnya.
1 |
|
2 |
if(_currentDropPiece == null){ |
3 |
if(_mouse.x < piece.xPos || _mouse.x > (piece.xPos + _pieceWidth) || _mouse.y < piece.yPos || _mouse.y > (piece.yPos + _pieceHeight)){ |
4 |
//NOT OVER
|
5 |
}
|
6 |
else{ |
7 |
_currentDropPiece = piece; |
8 |
_stage.save(); |
9 |
_stage.globalAlpha = .4; |
10 |
_stage.fillStyle = PUZZLE_HOVER_TINT; |
11 |
_stage.fillRect(_currentDropPiece.xPos,_currentDropPiece.yPos,_pieceWidth, _pieceHeight); |
12 |
_stage.restore(); |
13 |
}
|
14 |
}
|
Karena kami memiliki referensi ke setiap objek dalam loop, kami juga dapat menggunakan kesempatan ini untuk memeriksa apakah bagian yang diseret berada di atasnya. Kami melakukan ini karena kami ingin memberikan umpan balik kepada pengguna tentang potongan apa yang dapat dijatuhkan. Mari kita gali kode itu sekarang.
Pertama kami ingin melihat apakah loop ini telah menghasilkan target penurunan. Jika demikian, kita tidak perlu repot karena hanya satu target penurunan yang dapat dilakukan dan setiap gerakan mouse yang diberikan. Jika tidak, _currentDropPiece
akan menjadi null
dan kita dapat melanjutkan ke logika. Karena mouse kita berada di tengah-tengah bagian yang diseret, yang perlu kita lakukan hanyalah menentukan bagian lain dari mouse kita.
Selanjutnya, gunakan fungsi checkPieceClicked()
kami yang berguna untuk menentukan apakah mouse melayang di atas objek potongan saat ini dalam loop. Jika demikian, kita mengatur variabel _currentDropPiece
dan menggambar kotak berwarna di atas potongan puzzle, menunjukkan bahwa itu sekarang adalah target drop.
Ingat untuk save()
dan restore()
. Jika tidak, Anda akan mendapatkan kotak berwarna dan bukan gambar di bawahnya.
Keluar dari Loop:
1 |
|
2 |
_stage.save(); |
3 |
_stage.globalAlpha = .6; |
4 |
_stage.drawImage(_img, _currentPiece.sx, _currentPiece.sy, _pieceWidth, _pieceHeight, _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth, _pieceHeight); |
5 |
_stage.restore(); |
6 |
_stage.strokeRect( _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth,_pieceHeight); |
Last but not least kita perlu redraw potongan yang diseret. Kode ini sama seperti saat pertama kali kita mengkliknya, tetapi mouse telah pindah sehingga posisinya akan diperbarui.
Langkah 13: Fungsi pieceDropped()
1 |
|
2 |
function pieceDropped(e){ |
3 |
document.onmousemove = null; |
4 |
document.onmouseup = null; |
5 |
if(_currentDropPiece != null){ |
6 |
var tmp = {xPos:_currentPiece.xPos,yPos:_currentPiece.yPos}; |
7 |
_currentPiece.xPos = _currentDropPiece.xPos; |
8 |
_currentPiece.yPos = _currentDropPiece.yPos; |
9 |
_currentDropPiece.xPos = tmp.xPos; |
10 |
_currentDropPiece.yPos = tmp.yPos; |
11 |
}
|
12 |
resetPuzzleAndCheckWin(); |
13 |
}
|
Oke, yang terburuk ada di belakang kita. Kami sekarang berhasil menyeret potongan teka-teki dan bahkan mendapatkan umpan balik visual tentang di mana itu akan dijatuhkan. Sekarang semua yang tersisa adalah menjatuhkannya. Pertama-tama, hapus pendengar segera karena tidak ada yang diseret.
Selanjutnya, periksa bahwa _currentDropPiece
tidak null
. Jika ya, ini berarti kami menyeretnya kembali ke area rumah potong dan bukan di atas slot lain. Jika bukan null
, kami melanjutkan dengan fungsi ini.
Apa yang kita lakukan sekarang hanyalah menukar xPos
dan yPos
masing-masing bagian. Kami membuat objek temp cepat sebagai buffer untuk menahan salah satu nilai objek dalam proses swapping. Pada titik ini, kedua bagian tersebut memiliki nilai xPos
dan yPos
baru, dan akan masuk ke rumah baru mereka pada undian berikutnya. Itulah yang akan kita lakukan sekarang, sekaligus memeriksa apakah game telah dimenangkan.
Langkah 14: Fungsi resetPuzzleAndCheckWin()
1 |
|
2 |
function resetPuzzleAndCheckWin(){ |
3 |
_stage.clearRect(0,0,_puzzleWidth,_puzzleHeight); |
4 |
var gameWin = true; |
5 |
var i; |
6 |
var piece; |
7 |
for(i = 0;i < _pieces.length;i++){ |
8 |
piece = _pieces[i]; |
9 |
_stage.drawImage(_img, piece.sx, piece.sy, _pieceWidth, _pieceHeight, piece.xPos, piece.yPos, _pieceWidth, _pieceHeight); |
10 |
_stage.strokeRect(piece.xPos, piece.yPos, _pieceWidth,_pieceHeight); |
11 |
if(piece.xPos != piece.sx || piece.yPos != piece.sy){ |
12 |
gameWin = false; |
13 |
}
|
14 |
}
|
15 |
if(gameWin){ |
16 |
setTimeout(gameOver,500); |
17 |
}
|
18 |
}
|
Sekali lagi, kosongkan canvas
dan atur variabel gameWin
, atur ke true
secara default. Sekarang lanjutkan dengan pengulangan potongan yang terlalu familiar.
Kode di sini seharusnya terlihat familier jadi kami tidak akan membahasnya. Ini hanya menarik potongan-potongan kembali ke slot asli atau baru. Dalam lingkaran ini, kami ingin melihat apakah setiap bagian ditarik dalam posisi menang. Ini sederhana: kami memeriksa untuk melihat apakah properti sx
dan sy
kami cocok dengan xPos
dan yPos
. Jika tidak, kami tahu kami tidak mungkin memenangkan teka-teki dan menyetel gameWin
menjadi false
. Jika kami berhasil melewati loop dengan semua orang di tempat kemenangan mereka, kami mengatur timeout
cepat untuk memanggil metode gameOver()
kami. (Kami menetapkan batas waktu sehingga layar tidak berubah begitu drastis saat menjatuhkan potongan puzzle.)
Langkah 15: Fungsi gameOver()
1 |
|
2 |
function gameOver(){ |
3 |
document.onmousedown = null; |
4 |
document.onmousemove = null; |
5 |
document.onmouseup = null; |
6 |
initPuzzle(); |
7 |
}
|
Ini adalah fungsi terakhir kami! Di sini kita hanya menghapus semua listener dan memanggil initPuzzle(),
yang me-reset semua nilai yang diperlukan dan menunggu pengguna untuk bermain lagi.
Kesimpulan
Klik di sini untuk melihat hasil akhir.
Seperti yang Anda lihat, Anda dapat melakukan banyak hal kreatif baru di HTML5 menggunakan area bitmap yang dipilih untuk memuat gambar dan gambar. Anda dapat dengan mudah memperluas aplikasi ini dengan menambahkan skor dan mungkin bahkan pengatur waktu untuk memberikan lebih banyak permainan. Ide lain adalah untuk meningkatkan kesulitan dan memilih gambar yang berbeda dalam fungsi gameOver()
, memberikan level permainan.