jigsawpuzzlegame 1.0.10 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/favicon.ico +0 -0
- package/game.css +23 -0
- package/game.html +15 -0
- package/game.js +28 -0
- package/index.html +718 -0
- package/jigsaw-puzzle-game.js +36 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,6 +61,9 @@ Make sure your container has a defined size:
|
|
|
61
61
|
| `onWin` | function | `null` | Callback function called when the puzzle is completed |
|
|
62
62
|
| `onStart` | function | `null` | Callback function called when a game starts |
|
|
63
63
|
| `onStop` | function | `null` | Callback function called when a game is stopped |
|
|
64
|
+
| `onMerged` | function | `null` | Callback function called when puzzle pieces are merged together (receives the merged piece as parameter) |
|
|
65
|
+
| `onChanged` | function | `null` | Callback function called when a piece is moved or changed (receives the piece as parameter) |
|
|
66
|
+
| `onDeleted` | function | `null` | Callback function called when a piece is deleted/merged into another piece (receives the deleted piece as parameter) |
|
|
64
67
|
|
|
65
68
|
## API Methods
|
|
66
69
|
|
|
@@ -196,6 +199,15 @@ const puzzle = new JigsawPuzzle('puzzle-container', {
|
|
|
196
199
|
},
|
|
197
200
|
onStop: () => {
|
|
198
201
|
console.log('Game stopped');
|
|
202
|
+
},
|
|
203
|
+
onMerged: (piece) => {
|
|
204
|
+
console.log('Pieces merged!', piece);
|
|
205
|
+
},
|
|
206
|
+
onChanged: (piece) => {
|
|
207
|
+
console.log('Piece moved', piece);
|
|
208
|
+
},
|
|
209
|
+
onDeleted: (piece) => {
|
|
210
|
+
console.log('Piece deleted', piece);
|
|
199
211
|
}
|
|
200
212
|
});
|
|
201
213
|
|
package/favicon.ico
ADDED
|
Binary file
|
package/game.css
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* Demo-specific styles for game.html */
|
|
2
|
+
/* Note: Library styles (.polypiece, .gameCanvas) are automatically injected by jigsaw-puzzle-game.js */
|
|
3
|
+
|
|
4
|
+
* {
|
|
5
|
+
margin: 0;
|
|
6
|
+
padding: 0;
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
body {
|
|
11
|
+
width: 100vw;
|
|
12
|
+
height: 100vh;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
background-color: #f0f0f0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#puzzle-container {
|
|
18
|
+
width: 100%;
|
|
19
|
+
height: 100%;
|
|
20
|
+
position: relative;
|
|
21
|
+
background-color: #e8e8e8;
|
|
22
|
+
}
|
|
23
|
+
|
package/game.html
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
|
7
|
+
<title>Jigsaw Puzzle Game</title>
|
|
8
|
+
<link rel="stylesheet" href="game.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="puzzle-container"></div>
|
|
12
|
+
<script type="module" src="game.js"></script>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
15
|
+
|
package/game.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { JigsawPuzzle } from './jigsaw-puzzle-game.js';
|
|
2
|
+
|
|
3
|
+
// Default image URL - you can change this to any image URL
|
|
4
|
+
const DEFAULT_IMAGE = 'https://assets.codepen.io/2574552/Mona_Lisa.jpg';
|
|
5
|
+
|
|
6
|
+
// Initialize the puzzle
|
|
7
|
+
const puzzle = new JigsawPuzzle('puzzle-container', {
|
|
8
|
+
image: DEFAULT_IMAGE,
|
|
9
|
+
numPieces: 25,
|
|
10
|
+
shapeType: 0,
|
|
11
|
+
allowRotation: true,
|
|
12
|
+
onReady: () => {
|
|
13
|
+
// Puzzle is ready (image loaded), start the game
|
|
14
|
+
console.log('Puzzle ready, starting game...');
|
|
15
|
+
puzzle.start();
|
|
16
|
+
},
|
|
17
|
+
onWin: () => {
|
|
18
|
+
console.log('Congratulations! Puzzle solved!');
|
|
19
|
+
alert('Congratulations! You solved the puzzle!');
|
|
20
|
+
},
|
|
21
|
+
onStart: () => {
|
|
22
|
+
console.log('Game started');
|
|
23
|
+
},
|
|
24
|
+
onStop: () => {
|
|
25
|
+
console.log('Game stopped');
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
package/index.html
ADDED
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
|
7
|
+
<title>JigsawPuzzle Class - Usage Guide</title>
|
|
8
|
+
<style>
|
|
9
|
+
body {
|
|
10
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
11
|
+
line-height: 1.6;
|
|
12
|
+
max-width: 1000px;
|
|
13
|
+
margin: 0 auto;
|
|
14
|
+
padding: 20px;
|
|
15
|
+
background-color: #f5f5f5;
|
|
16
|
+
}
|
|
17
|
+
h1 {
|
|
18
|
+
color: #333;
|
|
19
|
+
border-bottom: 3px solid #4CAF50;
|
|
20
|
+
padding-bottom: 10px;
|
|
21
|
+
}
|
|
22
|
+
h2 {
|
|
23
|
+
color: #555;
|
|
24
|
+
margin-top: 30px;
|
|
25
|
+
border-bottom: 2px solid #ddd;
|
|
26
|
+
padding-bottom: 5px;
|
|
27
|
+
}
|
|
28
|
+
h3 {
|
|
29
|
+
color: #666;
|
|
30
|
+
margin-top: 20px;
|
|
31
|
+
}
|
|
32
|
+
code {
|
|
33
|
+
background-color: #f4f4f4;
|
|
34
|
+
padding: 2px 6px;
|
|
35
|
+
border-radius: 3px;
|
|
36
|
+
font-family: 'Courier New', monospace;
|
|
37
|
+
font-size: 0.9em;
|
|
38
|
+
}
|
|
39
|
+
pre {
|
|
40
|
+
background-color: #2d2d2d;
|
|
41
|
+
color: #f8f8f2;
|
|
42
|
+
padding: 15px;
|
|
43
|
+
border-radius: 5px;
|
|
44
|
+
overflow-x: auto;
|
|
45
|
+
margin: 15px 0;
|
|
46
|
+
}
|
|
47
|
+
pre code {
|
|
48
|
+
background-color: transparent;
|
|
49
|
+
padding: 0;
|
|
50
|
+
color: inherit;
|
|
51
|
+
}
|
|
52
|
+
.option-table {
|
|
53
|
+
width: 100%;
|
|
54
|
+
border-collapse: collapse;
|
|
55
|
+
margin: 15px 0;
|
|
56
|
+
background-color: white;
|
|
57
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
58
|
+
}
|
|
59
|
+
.option-table th,
|
|
60
|
+
.option-table td {
|
|
61
|
+
padding: 12px;
|
|
62
|
+
text-align: left;
|
|
63
|
+
border-bottom: 1px solid #ddd;
|
|
64
|
+
}
|
|
65
|
+
.option-table th {
|
|
66
|
+
background-color: #4CAF50;
|
|
67
|
+
color: white;
|
|
68
|
+
font-weight: bold;
|
|
69
|
+
}
|
|
70
|
+
.option-table tr:hover {
|
|
71
|
+
background-color: #f9f9f9;
|
|
72
|
+
}
|
|
73
|
+
.method-section {
|
|
74
|
+
background-color: white;
|
|
75
|
+
padding: 15px;
|
|
76
|
+
margin: 15px 0;
|
|
77
|
+
border-left: 4px solid #4CAF50;
|
|
78
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
79
|
+
}
|
|
80
|
+
.example-box {
|
|
81
|
+
background-color: #e8f5e9;
|
|
82
|
+
border-left: 4px solid #4CAF50;
|
|
83
|
+
padding: 15px;
|
|
84
|
+
margin: 15px 0;
|
|
85
|
+
}
|
|
86
|
+
.warning-box {
|
|
87
|
+
background-color: #fff3cd;
|
|
88
|
+
border-left: 4px solid #ffc107;
|
|
89
|
+
padding: 15px;
|
|
90
|
+
margin: 15px 0;
|
|
91
|
+
}
|
|
92
|
+
ul {
|
|
93
|
+
padding-left: 20px;
|
|
94
|
+
}
|
|
95
|
+
li {
|
|
96
|
+
margin: 5px 0;
|
|
97
|
+
}
|
|
98
|
+
.button-container {
|
|
99
|
+
text-align: center;
|
|
100
|
+
margin-bottom: 30px;
|
|
101
|
+
}
|
|
102
|
+
.button-container a {
|
|
103
|
+
display: inline-block;
|
|
104
|
+
background-color: #4CAF50;
|
|
105
|
+
color: white;
|
|
106
|
+
padding: 12px 24px;
|
|
107
|
+
text-decoration: none;
|
|
108
|
+
border-radius: 5px;
|
|
109
|
+
font-weight: bold;
|
|
110
|
+
font-size: 16px;
|
|
111
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|
112
|
+
margin: 5px;
|
|
113
|
+
min-width: 150px;
|
|
114
|
+
text-align: center;
|
|
115
|
+
box-sizing: border-box;
|
|
116
|
+
}
|
|
117
|
+
.button-container a:hover {
|
|
118
|
+
background-color: #45a049;
|
|
119
|
+
}
|
|
120
|
+
@media (max-width: 600px) {
|
|
121
|
+
body {
|
|
122
|
+
padding: 10px;
|
|
123
|
+
}
|
|
124
|
+
.button-container a {
|
|
125
|
+
display: block;
|
|
126
|
+
width: 100%;
|
|
127
|
+
max-width: 100%;
|
|
128
|
+
min-width: auto;
|
|
129
|
+
margin: 8px 0;
|
|
130
|
+
}
|
|
131
|
+
h1 {
|
|
132
|
+
font-size: 1.5em;
|
|
133
|
+
}
|
|
134
|
+
h2 {
|
|
135
|
+
font-size: 1.3em;
|
|
136
|
+
}
|
|
137
|
+
pre {
|
|
138
|
+
font-size: 0.85em;
|
|
139
|
+
padding: 10px;
|
|
140
|
+
}
|
|
141
|
+
.option-table th,
|
|
142
|
+
.option-table td {
|
|
143
|
+
padding: 8px;
|
|
144
|
+
font-size: 0.9em;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
</style>
|
|
148
|
+
</head>
|
|
149
|
+
<body>
|
|
150
|
+
<div class="button-container">
|
|
151
|
+
<a href="game.html">Demo</a>
|
|
152
|
+
<a href="jigsaw-puzzle-game.js" download="jigsaw-puzzle-game.js">Download Source</a>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<h1>JigsawPuzzle Class - Usage Guide</h1>
|
|
156
|
+
|
|
157
|
+
<p>This guide explains how to use the <code>JigsawPuzzle</code> class to create interactive jigsaw puzzle games in your web application.</p>
|
|
158
|
+
|
|
159
|
+
<h2>1. Importing the Class</h2>
|
|
160
|
+
|
|
161
|
+
<p>The class is exported as an ES6 module. Import it in your JavaScript file:</p>
|
|
162
|
+
|
|
163
|
+
<pre><code>import { JigsawPuzzle } from './jigsaw-puzzle-game.js';</code></pre>
|
|
164
|
+
|
|
165
|
+
<p><strong>Note:</strong> Make sure your HTML file uses <code>type="module"</code> in the script tag:</p>
|
|
166
|
+
|
|
167
|
+
<pre><code><script type="module" src="your-script.js"></script></code></pre>
|
|
168
|
+
|
|
169
|
+
<h2>2. Basic Setup</h2>
|
|
170
|
+
|
|
171
|
+
<p>You need an HTML container element where the puzzle will be displayed:</p>
|
|
172
|
+
|
|
173
|
+
<pre><code><div id="puzzle-container"></div></code></pre>
|
|
174
|
+
|
|
175
|
+
<p>Then create a new puzzle instance:</p>
|
|
176
|
+
|
|
177
|
+
<pre><code>const puzzle = new JigsawPuzzle('puzzle-container', {
|
|
178
|
+
image: 'https://example.com/image.jpg',
|
|
179
|
+
numPieces: 20,
|
|
180
|
+
shapeType: 0,
|
|
181
|
+
allowRotation: false
|
|
182
|
+
});</code></pre>
|
|
183
|
+
|
|
184
|
+
<h2>3. Configuration Options</h2>
|
|
185
|
+
|
|
186
|
+
<p>The constructor takes two parameters:</p>
|
|
187
|
+
<ul>
|
|
188
|
+
<li><code>containerId</code> - String ID of the container element, or the DOM element itself</li>
|
|
189
|
+
<li><code>options</code> - Configuration object (see table below)</li>
|
|
190
|
+
</ul>
|
|
191
|
+
|
|
192
|
+
<table class="option-table">
|
|
193
|
+
<thead>
|
|
194
|
+
<tr>
|
|
195
|
+
<th>Option</th>
|
|
196
|
+
<th>Type</th>
|
|
197
|
+
<th>Default</th>
|
|
198
|
+
<th>Description</th>
|
|
199
|
+
</tr>
|
|
200
|
+
</thead>
|
|
201
|
+
<tbody>
|
|
202
|
+
<tr>
|
|
203
|
+
<td><code>image</code></td>
|
|
204
|
+
<td>string</td>
|
|
205
|
+
<td><code>null</code></td>
|
|
206
|
+
<td>URL or data URL of the image to use for the puzzle. Can be set later with <code>setImage()</code>. Ignored if <code>savedData</code> is provided.</td>
|
|
207
|
+
</tr>
|
|
208
|
+
<tr>
|
|
209
|
+
<td><code>savedData</code></td>
|
|
210
|
+
<td>string\|null</td>
|
|
211
|
+
<td><code>null</code></td>
|
|
212
|
+
<td>JSON string of saved game state:
|
|
213
|
+
<ul style="margin: 5px 0; padding-left: 20px;">
|
|
214
|
+
<li>Non-empty string: use this saved data</li>
|
|
215
|
+
<li>Empty string (<code>""</code>): load from localStorage</li>
|
|
216
|
+
<li>Not provided or <code>null</code>: use normal initialization with <code>image</code> and other parameters</li>
|
|
217
|
+
</ul>
|
|
218
|
+
</td>
|
|
219
|
+
</tr>
|
|
220
|
+
<tr>
|
|
221
|
+
<td><code>numPieces</code></td>
|
|
222
|
+
<td>number</td>
|
|
223
|
+
<td><code>20</code></td>
|
|
224
|
+
<td>Number of puzzle pieces (approximate - actual count depends on optimal grid layout). Ignored if <code>savedData</code> is provided.</td>
|
|
225
|
+
</tr>
|
|
226
|
+
<tr>
|
|
227
|
+
<td><code>shapeType</code></td>
|
|
228
|
+
<td>number</td>
|
|
229
|
+
<td><code>0</code></td>
|
|
230
|
+
<td>Shape type for puzzle pieces (0-3, ignored if <code>savedData</code> is provided):
|
|
231
|
+
<ul style="margin: 5px 0; padding-left: 20px;">
|
|
232
|
+
<li><code>0</code> - Classic jigsaw shape (curved tabs)</li>
|
|
233
|
+
<li><code>1</code> - Alternative shape 1</li>
|
|
234
|
+
<li><code>2</code> - Alternative shape 2</li>
|
|
235
|
+
<li><code>3</code> - Straight edges (rectangular pieces)</li>
|
|
236
|
+
</ul>
|
|
237
|
+
</td>
|
|
238
|
+
</tr>
|
|
239
|
+
<tr>
|
|
240
|
+
<td><code>allowRotation</code></td>
|
|
241
|
+
<td>boolean</td>
|
|
242
|
+
<td><code>false</code></td>
|
|
243
|
+
<td>Whether pieces can be rotated by clicking/tapping (90° increments). Ignored if <code>savedData</code> is provided.</td>
|
|
244
|
+
</tr>
|
|
245
|
+
<tr>
|
|
246
|
+
<td><code>onReady</code></td>
|
|
247
|
+
<td>function</td>
|
|
248
|
+
<td><code>null</code></td>
|
|
249
|
+
<td>Callback function called when the puzzle is ready (image loaded and displayed). Use this to start the game.</td>
|
|
250
|
+
</tr>
|
|
251
|
+
<tr>
|
|
252
|
+
<td><code>onWin</code></td>
|
|
253
|
+
<td>function</td>
|
|
254
|
+
<td><code>null</code></td>
|
|
255
|
+
<td>Callback function called when the puzzle is completed</td>
|
|
256
|
+
</tr>
|
|
257
|
+
<tr>
|
|
258
|
+
<td><code>onStart</code></td>
|
|
259
|
+
<td>function</td>
|
|
260
|
+
<td><code>null</code></td>
|
|
261
|
+
<td>Callback function called when a game starts</td>
|
|
262
|
+
</tr>
|
|
263
|
+
<tr>
|
|
264
|
+
<td><code>onStop</code></td>
|
|
265
|
+
<td>function</td>
|
|
266
|
+
<td><code>null</code></td>
|
|
267
|
+
<td>Callback function called when a game is stopped</td>
|
|
268
|
+
</tr>
|
|
269
|
+
<tr>
|
|
270
|
+
<td><code>onMerged</code></td>
|
|
271
|
+
<td>function</td>
|
|
272
|
+
<td><code>null</code></td>
|
|
273
|
+
<td>Callback function called when puzzle pieces are merged together (receives the merged piece as parameter)</td>
|
|
274
|
+
</tr>
|
|
275
|
+
<tr>
|
|
276
|
+
<td><code>onChanged</code></td>
|
|
277
|
+
<td>function</td>
|
|
278
|
+
<td><code>null</code></td>
|
|
279
|
+
<td>Callback function called when a piece is moved or changed (receives the piece as parameter)</td>
|
|
280
|
+
</tr>
|
|
281
|
+
<tr>
|
|
282
|
+
<td><code>onDeleted</code></td>
|
|
283
|
+
<td>function</td>
|
|
284
|
+
<td><code>null</code></td>
|
|
285
|
+
<td>Callback function called when a piece is deleted/merged into another piece (receives the deleted piece as parameter)</td>
|
|
286
|
+
</tr>
|
|
287
|
+
</tbody>
|
|
288
|
+
</table>
|
|
289
|
+
|
|
290
|
+
<h2>4. Event Callbacks</h2>
|
|
291
|
+
|
|
292
|
+
<p>All callbacks are optional and receive no parameters:</p>
|
|
293
|
+
|
|
294
|
+
<div class="method-section">
|
|
295
|
+
<h3><code>onReady()</code></h3>
|
|
296
|
+
<p>Called when the puzzle image has loaded and the puzzle is ready to start. This is the perfect time to call <code>puzzle.start()</code>.</p>
|
|
297
|
+
<div class="example-box">
|
|
298
|
+
<strong>Example:</strong>
|
|
299
|
+
<pre><code>onReady: () => {
|
|
300
|
+
console.log('Puzzle ready!');
|
|
301
|
+
puzzle.start();
|
|
302
|
+
}</code></pre>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<div class="method-section">
|
|
307
|
+
<h3><code>onStart()</code></h3>
|
|
308
|
+
<p>Called when a game actually starts (pieces are being created and distributed).</p>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
<div class="method-section">
|
|
312
|
+
<h3><code>onWin()</code></h3>
|
|
313
|
+
<p>Called when the puzzle is completed (all pieces connected and in correct position).</p>
|
|
314
|
+
<div class="example-box">
|
|
315
|
+
<strong>Example:</strong>
|
|
316
|
+
<pre><code>onWin: () => {
|
|
317
|
+
alert('Congratulations! You solved the puzzle!');
|
|
318
|
+
// Could show a victory screen, update score, etc.
|
|
319
|
+
}</code></pre>
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
<div class="method-section">
|
|
324
|
+
<h3><code>onStop()</code></h3>
|
|
325
|
+
<p>Called when a game is stopped (when <code>stop()</code> is called).</p>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<div class="method-section">
|
|
329
|
+
<h3><code>onMerged(piece)</code></h3>
|
|
330
|
+
<p>Called when puzzle pieces are merged together. Receives the merged piece as a parameter.</p>
|
|
331
|
+
<div class="example-box">
|
|
332
|
+
<strong>Example:</strong>
|
|
333
|
+
<pre><code>onMerged: (piece) => {
|
|
334
|
+
console.log('Pieces merged!', piece);
|
|
335
|
+
// Could update UI, play sound, etc.
|
|
336
|
+
}</code></pre>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
<div class="method-section">
|
|
341
|
+
<h3><code>onChanged(piece)</code></h3>
|
|
342
|
+
<p>Called when a piece is moved or changed. Receives the piece as a parameter.</p>
|
|
343
|
+
<div class="example-box">
|
|
344
|
+
<strong>Example:</strong>
|
|
345
|
+
<pre><code>onChanged: (piece) => {
|
|
346
|
+
console.log('Piece moved', piece);
|
|
347
|
+
// Could track moves, update statistics, etc.
|
|
348
|
+
}</code></pre>
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
|
|
352
|
+
<div class="method-section">
|
|
353
|
+
<h3><code>onDeleted(piece)</code></h3>
|
|
354
|
+
<p>Called when a piece is deleted/merged into another piece. Receives the deleted piece as a parameter.</p>
|
|
355
|
+
<div class="example-box">
|
|
356
|
+
<strong>Example:</strong>
|
|
357
|
+
<pre><code>onDeleted: (piece) => {
|
|
358
|
+
console.log('Piece deleted', piece);
|
|
359
|
+
// Could track merges, update UI, etc.
|
|
360
|
+
}</code></pre>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
|
|
364
|
+
<h2>5. Public Methods</h2>
|
|
365
|
+
|
|
366
|
+
<div class="method-section">
|
|
367
|
+
<h3><code>start()</code></h3>
|
|
368
|
+
<p>Starts a new game with the current settings. Creates the puzzle pieces and distributes them.</p>
|
|
369
|
+
<div class="example-box">
|
|
370
|
+
<strong>Example:</strong>
|
|
371
|
+
<pre><code>puzzle.start();</code></pre>
|
|
372
|
+
</div>
|
|
373
|
+
<div class="warning-box">
|
|
374
|
+
<strong>Important:</strong> Call this only after the puzzle is ready (use the <code>onReady</code> callback).
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
|
|
378
|
+
<div class="method-section">
|
|
379
|
+
<h3><code>stop()</code></h3>
|
|
380
|
+
<p>Stops the current game and returns to the image preview state.</p>
|
|
381
|
+
<pre><code>puzzle.stop();</code></pre>
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<div class="method-section">
|
|
385
|
+
<h3><code>reset()</code></h3>
|
|
386
|
+
<p>Resets the puzzle to initial state. Reloads the current image and prepares for a new game. Use this to start a new game with the same instance.</p>
|
|
387
|
+
<pre><code>puzzle.reset();
|
|
388
|
+
// Then wait for onReady callback and call puzzle.start()</code></pre>
|
|
389
|
+
</div>
|
|
390
|
+
|
|
391
|
+
<div class="method-section">
|
|
392
|
+
<h3><code>setImage(imageUrl)</code></h3>
|
|
393
|
+
<p>Sets a new image for the puzzle.</p>
|
|
394
|
+
<p><strong>Parameters:</strong></p>
|
|
395
|
+
<ul>
|
|
396
|
+
<li><code>imageUrl</code> (string) - URL or data URL of the image</li>
|
|
397
|
+
</ul>
|
|
398
|
+
<div class="example-box">
|
|
399
|
+
<strong>Example:</strong>
|
|
400
|
+
<pre><code>puzzle.setImage('https://example.com/new-image.jpg');
|
|
401
|
+
// Image will reload, wait for onReady callback</code></pre>
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
|
|
405
|
+
<div class="method-section">
|
|
406
|
+
<h3><code>setOptions(newOptions)</code></h3>
|
|
407
|
+
<p>Updates puzzle options without creating a new instance.</p>
|
|
408
|
+
<p><strong>Parameters:</strong></p>
|
|
409
|
+
<ul>
|
|
410
|
+
<li><code>newOptions</code> (object) - Object with options to update (partial updates allowed)</li>
|
|
411
|
+
</ul>
|
|
412
|
+
<div class="example-box">
|
|
413
|
+
<strong>Example:</strong>
|
|
414
|
+
<pre><code>puzzle.setOptions({
|
|
415
|
+
numPieces: 50,
|
|
416
|
+
allowRotation: true,
|
|
417
|
+
shapeType: 1
|
|
418
|
+
});</code></pre>
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
|
|
422
|
+
<div class="method-section">
|
|
423
|
+
<h3><code>save([callback])</code></h3>
|
|
424
|
+
<p>Saves the current game state. Gets the state data, converts it to a JSON string, then either calls the callback with the string or saves to localStorage.</p>
|
|
425
|
+
<p><strong>Parameters:</strong></p>
|
|
426
|
+
<ul>
|
|
427
|
+
<li><code>callback</code> (function, optional) - Function that receives the saved data as JSON string. If not provided, saves to localStorage automatically.</li>
|
|
428
|
+
</ul>
|
|
429
|
+
<div class="example-box">
|
|
430
|
+
<strong>Examples:</strong>
|
|
431
|
+
<pre><code>// Save to localStorage (default)
|
|
432
|
+
puzzle.save();
|
|
433
|
+
|
|
434
|
+
// Save with custom callback
|
|
435
|
+
puzzle.save((savedData) => {
|
|
436
|
+
// savedData is a JSON string
|
|
437
|
+
localStorage.setItem('myPuzzleSave', savedData);
|
|
438
|
+
// Or send to server, download as file, etc.
|
|
439
|
+
});</code></pre>
|
|
440
|
+
</div>
|
|
441
|
+
</div>
|
|
442
|
+
|
|
443
|
+
<div class="method-section">
|
|
444
|
+
<h3>Loading Saved Games</h3>
|
|
445
|
+
<p>To load a saved game, create a new puzzle instance with the <code>savedData</code> option in the constructor. The <code>load()</code> method has been removed in favor of constructor-based loading.</p>
|
|
446
|
+
<div class="example-box">
|
|
447
|
+
<strong>Examples:</strong>
|
|
448
|
+
<pre><code>// Load from explicit string
|
|
449
|
+
const savedString = localStorage.getItem('myPuzzleSave');
|
|
450
|
+
const puzzle = new JigsawPuzzle('puzzle-container', {
|
|
451
|
+
savedData: savedString,
|
|
452
|
+
onReady: () => puzzle.start()
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// Load from localStorage automatically (pass empty string)
|
|
456
|
+
const puzzle = new JigsawPuzzle('puzzle-container', {
|
|
457
|
+
savedData: "", // Empty string triggers localStorage lookup
|
|
458
|
+
onReady: () => puzzle.start()
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// Create new puzzle (no saved data)
|
|
462
|
+
const puzzle = new JigsawPuzzle('puzzle-container', {
|
|
463
|
+
image: 'image.jpg',
|
|
464
|
+
numPieces: 25,
|
|
465
|
+
onReady: () => puzzle.start()
|
|
466
|
+
});</code></pre>
|
|
467
|
+
</div>
|
|
468
|
+
<div class="warning-box">
|
|
469
|
+
<strong>Note:</strong> When loading a saved game, you must create a new instance. Destroy the old instance first if needed.
|
|
470
|
+
</div>
|
|
471
|
+
</div>
|
|
472
|
+
|
|
473
|
+
<div class="method-section">
|
|
474
|
+
<h3><code>destroy()</code></h3>
|
|
475
|
+
<p>Completely destroys the puzzle instance, cleaning up all resources. Use this when you want to remove the puzzle and create a new one in the same container.</p>
|
|
476
|
+
<div class="warning-box">
|
|
477
|
+
<strong>Note:</strong> After calling <code>destroy()</code>, you must create a new instance. Event listeners remain attached to the container, but this is usually fine for creating new instances.
|
|
478
|
+
</div>
|
|
479
|
+
<div class="example-box">
|
|
480
|
+
<strong>Example:</strong>
|
|
481
|
+
<pre><code>puzzle.destroy();
|
|
482
|
+
puzzle = new JigsawPuzzle('puzzle-container', {
|
|
483
|
+
image: 'new-image.jpg',
|
|
484
|
+
numPieces: 30
|
|
485
|
+
});</code></pre>
|
|
486
|
+
</div>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<h2>6. Starting a Game</h2>
|
|
490
|
+
|
|
491
|
+
<p>Here's the recommended pattern for starting a game:</p>
|
|
492
|
+
|
|
493
|
+
<div class="example-box">
|
|
494
|
+
<strong>Complete Example:</strong>
|
|
495
|
+
<pre><code>import { JigsawPuzzle } from './jigsaw-puzzle-game.js';
|
|
496
|
+
|
|
497
|
+
const puzzle = new JigsawPuzzle('puzzle-container', {
|
|
498
|
+
image: 'https://example.com/image.jpg',
|
|
499
|
+
numPieces: 20,
|
|
500
|
+
shapeType: 0,
|
|
501
|
+
allowRotation: false,
|
|
502
|
+
onReady: () => {
|
|
503
|
+
// Puzzle is ready, start the game
|
|
504
|
+
puzzle.start();
|
|
505
|
+
},
|
|
506
|
+
onWin: () => {
|
|
507
|
+
console.log('Puzzle solved!');
|
|
508
|
+
alert('Congratulations!');
|
|
509
|
+
},
|
|
510
|
+
onStart: () => {
|
|
511
|
+
console.log('Game started');
|
|
512
|
+
},
|
|
513
|
+
onStop: () => {
|
|
514
|
+
console.log('Game stopped');
|
|
515
|
+
},
|
|
516
|
+
onMerged: (piece) => {
|
|
517
|
+
console.log('Pieces merged!', piece);
|
|
518
|
+
},
|
|
519
|
+
onChanged: (piece) => {
|
|
520
|
+
console.log('Piece moved', piece);
|
|
521
|
+
},
|
|
522
|
+
onDeleted: (piece) => {
|
|
523
|
+
console.log('Piece deleted', piece);
|
|
524
|
+
}
|
|
525
|
+
});</code></pre>
|
|
526
|
+
</div>
|
|
527
|
+
|
|
528
|
+
<p><strong>Important:</strong> Always use the <code>onReady</code> callback to start the game. This ensures the image is loaded before starting.</p>
|
|
529
|
+
|
|
530
|
+
<h2>7. Starting a New Game</h2>
|
|
531
|
+
|
|
532
|
+
<p>There are several ways to start a new game:</p>
|
|
533
|
+
|
|
534
|
+
<h3>Option 1: Load Saved Game</h3>
|
|
535
|
+
<p>To load a previously saved game, create a new instance with the <code>savedData</code> option:</p>
|
|
536
|
+
<pre><code>// Load from explicit string
|
|
537
|
+
const savedString = localStorage.getItem('puzzleSave');
|
|
538
|
+
puzzle = new JigsawPuzzle('puzzle-container', {
|
|
539
|
+
savedData: savedString,
|
|
540
|
+
onReady: () => puzzle.start()
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
// Or load from localStorage automatically
|
|
544
|
+
puzzle = new JigsawPuzzle('puzzle-container', {
|
|
545
|
+
savedData: "", // Empty string triggers localStorage lookup
|
|
546
|
+
onReady: () => puzzle.start()
|
|
547
|
+
});</code></pre>
|
|
548
|
+
|
|
549
|
+
<h3>Option 2: Reset and Reuse (Same Instance)</h3>
|
|
550
|
+
<p>If you want to restart with the same image and settings:</p>
|
|
551
|
+
<pre><code>puzzle.reset();
|
|
552
|
+
// The puzzle will reload the image
|
|
553
|
+
// Wait for onReady callback, then:
|
|
554
|
+
puzzle.start();</code></pre>
|
|
555
|
+
|
|
556
|
+
<h3>Option 3: Change Image and Reset</h3>
|
|
557
|
+
<p>If you want to change the image:</p>
|
|
558
|
+
<pre><code>puzzle.setImage('new-image.jpg');
|
|
559
|
+
puzzle.reset();
|
|
560
|
+
// Wait for onReady callback, then:
|
|
561
|
+
puzzle.start();</code></pre>
|
|
562
|
+
|
|
563
|
+
<h3>Option 4: Change Settings, Image, and Reset</h3>
|
|
564
|
+
<p>If you want to change multiple settings:</p>
|
|
565
|
+
<pre><code>puzzle.setImage('new-image.jpg');
|
|
566
|
+
puzzle.setOptions({
|
|
567
|
+
numPieces: 50,
|
|
568
|
+
shapeType: 1,
|
|
569
|
+
allowRotation: true
|
|
570
|
+
});
|
|
571
|
+
puzzle.reset();
|
|
572
|
+
// Wait for onReady callback, then:
|
|
573
|
+
puzzle.start();</code></pre>
|
|
574
|
+
|
|
575
|
+
<h3>Option 5: Destroy and Create New (Clean Slate)</h3>
|
|
576
|
+
<p>For a completely fresh start with potentially different options:</p>
|
|
577
|
+
<pre><code>puzzle.destroy();
|
|
578
|
+
puzzle = new JigsawPuzzle('puzzle-container', {
|
|
579
|
+
image: 'completely-different-image.jpg',
|
|
580
|
+
numPieces: 30,
|
|
581
|
+
shapeType: 2,
|
|
582
|
+
allowRotation: true,
|
|
583
|
+
onReady: () => {
|
|
584
|
+
puzzle.start();
|
|
585
|
+
}
|
|
586
|
+
});</code></pre>
|
|
587
|
+
|
|
588
|
+
<h2>8. User Interactions</h2>
|
|
589
|
+
|
|
590
|
+
<p>The puzzle supports various user interactions:</p>
|
|
591
|
+
<ul>
|
|
592
|
+
<li><strong>Mouse/Touch:</strong> Click and drag pieces to move them</li>
|
|
593
|
+
<li><strong>Rotation:</strong> If <code>allowRotation</code> is enabled, quick click/tap rotates pieces 90°</li>
|
|
594
|
+
<li><strong>Piece Merging:</strong> When pieces are close and correctly aligned, they automatically merge</li>
|
|
595
|
+
<li><strong>Pan:</strong> Click and drag on empty space to pan all pieces</li>
|
|
596
|
+
<li><strong>Zoom:</strong> Mouse wheel to zoom in/out, or pinch gesture on touch devices</li>
|
|
597
|
+
<li><strong>Two-finger Zoom:</strong> Use two fingers on touch devices to zoom</li>
|
|
598
|
+
</ul>
|
|
599
|
+
|
|
600
|
+
<h2>9. Styling</h2>
|
|
601
|
+
|
|
602
|
+
<p>The puzzle container should have a defined size. Full-screen example:</p>
|
|
603
|
+
<pre><code>#puzzle-container {
|
|
604
|
+
width: 100vw;
|
|
605
|
+
height: 100vh;
|
|
606
|
+
position: relative;
|
|
607
|
+
}</code></pre>
|
|
608
|
+
|
|
609
|
+
<p>The puzzle adds these CSS classes you can style:</p>
|
|
610
|
+
<ul>
|
|
611
|
+
<li><code>.polypiece</code> - Individual puzzle pieces</li>
|
|
612
|
+
<li><code>.polypiece.moving</code> - Pieces during animation</li>
|
|
613
|
+
<li><code>.gameCanvas</code> - Reference image canvas (hidden during play)</li>
|
|
614
|
+
</ul>
|
|
615
|
+
|
|
616
|
+
<h2>10. Browser Compatibility</h2>
|
|
617
|
+
|
|
618
|
+
<p>The puzzle requires modern browser features:</p>
|
|
619
|
+
<ul>
|
|
620
|
+
<li>ES6 Modules support</li>
|
|
621
|
+
<li>Canvas API</li>
|
|
622
|
+
<li>Path2D API</li>
|
|
623
|
+
<li>Touch events (for mobile support)</li>
|
|
624
|
+
</ul>
|
|
625
|
+
|
|
626
|
+
<p>Works in all modern browsers (Chrome, Firefox, Safari, Edge).</p>
|
|
627
|
+
|
|
628
|
+
<h2>11. Common Patterns</h2>
|
|
629
|
+
|
|
630
|
+
<h3>Complete Game with Save/Load</h3>
|
|
631
|
+
<pre><code>let puzzle;
|
|
632
|
+
|
|
633
|
+
function startNewGame(imageUrl, numPieces) {
|
|
634
|
+
if (puzzle) puzzle.destroy();
|
|
635
|
+
|
|
636
|
+
puzzle = new JigsawPuzzle('puzzle-container', {
|
|
637
|
+
image: imageUrl,
|
|
638
|
+
numPieces: numPieces,
|
|
639
|
+
allowRotation: false,
|
|
640
|
+
onReady: () => puzzle.start(),
|
|
641
|
+
onWin: () => {
|
|
642
|
+
alert('You won!');
|
|
643
|
+
// Optionally save completion, show next puzzle, etc.
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Save game
|
|
649
|
+
function saveGame() {
|
|
650
|
+
puzzle.save((data) => {
|
|
651
|
+
localStorage.setItem('puzzleSave', data);
|
|
652
|
+
console.log('Game saved!');
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Load game - create new instance with saved data
|
|
657
|
+
function loadGame() {
|
|
658
|
+
const saved = localStorage.getItem('puzzleSave');
|
|
659
|
+
if (saved) {
|
|
660
|
+
if (puzzle) puzzle.destroy();
|
|
661
|
+
puzzle = new JigsawPuzzle('puzzle-container', {
|
|
662
|
+
savedData: saved,
|
|
663
|
+
onReady: () => puzzle.start(),
|
|
664
|
+
onWin: () => {
|
|
665
|
+
alert('You won!');
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
}</code></pre>
|
|
670
|
+
|
|
671
|
+
<h2>12. Troubleshooting</h2>
|
|
672
|
+
|
|
673
|
+
<div class="warning-box">
|
|
674
|
+
<h3>Puzzle doesn't start</h3>
|
|
675
|
+
<ul>
|
|
676
|
+
<li>Make sure you're calling <code>start()</code> in the <code>onReady</code> callback</li>
|
|
677
|
+
<li>Check that the image URL is valid and accessible (CORS issues if loading from different domain)</li>
|
|
678
|
+
<li>Verify the container element exists and has a size</li>
|
|
679
|
+
</ul>
|
|
680
|
+
</div>
|
|
681
|
+
|
|
682
|
+
<div class="warning-box">
|
|
683
|
+
<h3>Image doesn't load</h3>
|
|
684
|
+
<ul>
|
|
685
|
+
<li>Check browser console for CORS errors</li>
|
|
686
|
+
<li>Use data URLs or images from the same domain</li>
|
|
687
|
+
<li>Ensure the image URL is correct</li>
|
|
688
|
+
</ul>
|
|
689
|
+
</div>
|
|
690
|
+
|
|
691
|
+
<div class="warning-box">
|
|
692
|
+
<h3>Pieces don't merge</h3>
|
|
693
|
+
<ul>
|
|
694
|
+
<li>Make sure pieces are close enough (the puzzle calculates optimal distance)</li>
|
|
695
|
+
<li>Check that pieces are in the same rotation if rotation is enabled</li>
|
|
696
|
+
<li>Verify pieces are correctly aligned</li>
|
|
697
|
+
</ul>
|
|
698
|
+
</div>
|
|
699
|
+
|
|
700
|
+
<hr>
|
|
701
|
+
<p style="text-align: center; color: #888; margin-top: 40px;">
|
|
702
|
+
JigsawPuzzle Class Documentation | Version 1.0.8
|
|
703
|
+
</p>
|
|
704
|
+
|
|
705
|
+
<hr style="margin: 40px 0;">
|
|
706
|
+
<div style="background-color: #f9f9f9; padding: 20px; border-radius: 5px; margin: 40px 0;">
|
|
707
|
+
<h2 style="margin-top: 0;">License</h2>
|
|
708
|
+
<pre style="background-color: #f4f4f4; color: #333; padding: 15px; border-radius: 5px; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word; font-family: 'Courier New', monospace; line-height: 1.6;">Copyright (c) 2026 by Dillon (https://codepen.io/Dillo/pen/QWKLYab) & Henrik Rasmussen (https://www.raketten.net)
|
|
709
|
+
|
|
710
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
711
|
+
|
|
712
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
713
|
+
|
|
714
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</pre>
|
|
715
|
+
</div>
|
|
716
|
+
</body>
|
|
717
|
+
</html>
|
|
718
|
+
|
package/jigsaw-puzzle-game.js
CHANGED
|
@@ -381,8 +381,10 @@ class Piece {
|
|
|
381
381
|
// ============================================================================
|
|
382
382
|
|
|
383
383
|
class PolyPiece {
|
|
384
|
-
constructor(initialPiece, puzzleInstance) {
|
|
384
|
+
constructor(initialPiece, puzzleInstance, yIndex, xIndex) {
|
|
385
385
|
this.puzzle = puzzleInstance;
|
|
386
|
+
this.yIndex = yIndex;
|
|
387
|
+
this.xIndex = xIndex;
|
|
386
388
|
this.pckxmin = initialPiece.kx;
|
|
387
389
|
this.pckxmax = initialPiece.kx + 1;
|
|
388
390
|
this.pckymin = initialPiece.ky;
|
|
@@ -414,7 +416,7 @@ class PolyPiece {
|
|
|
414
416
|
const kOther = puzzle.polyPieces.indexOf(otherPoly);
|
|
415
417
|
puzzle.polyPieces.splice(kOther, 1);
|
|
416
418
|
puzzle.container.removeChild(otherPoly.canvas);
|
|
417
|
-
|
|
419
|
+
|
|
418
420
|
for (let k = 0; k < otherPoly.pieces.length; ++k) {
|
|
419
421
|
this.pieces.push(otherPoly.pieces[k]);
|
|
420
422
|
if (otherPoly.pieces[k].kx < this.pckxmin)
|
|
@@ -663,8 +665,8 @@ class PolyPiece {
|
|
|
663
665
|
: "gold"
|
|
664
666
|
: "rgba(0, 0, 0, 0.5)";
|
|
665
667
|
this.ctx.shadowBlur = this.selected ? mmin(8, puzzle.scalex / 10) : 4;
|
|
666
|
-
this.ctx.shadowOffsetX = this.selected ? 0 : -4;
|
|
667
|
-
this.ctx.shadowOffsetY = this.selected ? 0 : 4;
|
|
668
|
+
this.ctx.shadowOffsetX = this.selected ? 0 : ([-4, 4, 4, -4][this.rot]);
|
|
669
|
+
this.ctx.shadowOffsetY = this.selected ? 0 : ([4, 4, -4, -4][this.rot]);
|
|
668
670
|
this.ctx.fill(this.path);
|
|
669
671
|
if (this.selected) {
|
|
670
672
|
for (let i = 0; i < 6; i++) this.ctx.fill(this.path);
|
|
@@ -703,25 +705,19 @@ class PolyPiece {
|
|
|
703
705
|
|
|
704
706
|
this.ctx.drawImage(
|
|
705
707
|
puzzle.gameCanvas,
|
|
706
|
-
srcx,
|
|
707
|
-
|
|
708
|
-
w,
|
|
709
|
-
h,
|
|
710
|
-
destx,
|
|
711
|
-
desty,
|
|
712
|
-
w,
|
|
713
|
-
h
|
|
708
|
+
srcx, srcy, w, h,
|
|
709
|
+
destx, desty, w, h
|
|
714
710
|
);
|
|
715
711
|
this.ctx.lineWidth = puzzle.embossThickness * 1.5;
|
|
716
712
|
|
|
717
|
-
this.
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
);
|
|
713
|
+
const dxemboss = puzzle.embossThickness / 2 * [1, -1, -1, 1][this.rot];
|
|
714
|
+
const dyemboss = puzzle.embossThickness / 2 * [-1, -1, 1, 1][this.rot];
|
|
715
|
+
|
|
716
|
+
this.ctx.translate(dxemboss, dyemboss);
|
|
721
717
|
this.ctx.strokeStyle = "rgba(0, 0, 0, 0.35)";
|
|
722
718
|
this.ctx.stroke(path);
|
|
723
719
|
|
|
724
|
-
this.ctx.translate(-
|
|
720
|
+
this.ctx.translate(-2 * dxemboss, -2 * dyemboss);
|
|
725
721
|
this.ctx.strokeStyle = "rgba(255, 255, 255, 0.35)";
|
|
726
722
|
this.ctx.stroke(path);
|
|
727
723
|
|
|
@@ -911,9 +907,10 @@ class InternalPuzzle {
|
|
|
911
907
|
|
|
912
908
|
this.polyPieces = [];
|
|
913
909
|
if (!baseData) {
|
|
914
|
-
this.pieces.forEach((row) =>
|
|
915
|
-
row.forEach((piece) => {
|
|
916
|
-
|
|
910
|
+
this.pieces.forEach((row,yIndex) =>
|
|
911
|
+
row.forEach((piece,xIndex) => {
|
|
912
|
+
console.log('Piece:', yIndex, xIndex);
|
|
913
|
+
this.polyPieces.push(new PolyPiece(piece, this, yIndex, xIndex));
|
|
917
914
|
})
|
|
918
915
|
);
|
|
919
916
|
arrayShuffle(this.polyPieces);
|
|
@@ -926,6 +923,7 @@ class InternalPuzzle {
|
|
|
926
923
|
const pps = baseData[8];
|
|
927
924
|
const offs = this.rotationAllowed ? 3 : 2;
|
|
928
925
|
pps.forEach((ppData) => {
|
|
926
|
+
console.log('ppData:', ppData);
|
|
929
927
|
let polyp = new PolyPiece(this.pieces[ppData[offs + 1]][ppData[offs]], this);
|
|
930
928
|
polyp.x = ppData[0];
|
|
931
929
|
polyp.y = ppData[1];
|
|
@@ -1362,8 +1360,12 @@ export class JigsawPuzzle {
|
|
|
1362
1360
|
onReady: options.onReady || null,
|
|
1363
1361
|
onWin: options.onWin || null,
|
|
1364
1362
|
onStart: options.onStart || null,
|
|
1365
|
-
onStop: options.onStop || null
|
|
1363
|
+
onStop: options.onStop || null,
|
|
1364
|
+
onMerged: options.onMerged || null,
|
|
1365
|
+
onChanged: options.onChanged || null,
|
|
1366
|
+
onDeleted: options.onDeleted || null,
|
|
1366
1367
|
};
|
|
1368
|
+
console.log('Options2:', this.options);
|
|
1367
1369
|
|
|
1368
1370
|
// Create internal puzzle instance
|
|
1369
1371
|
this.puzzle = new InternalPuzzle(container);
|
|
@@ -1631,6 +1633,7 @@ export class JigsawPuzzle {
|
|
|
1631
1633
|
} else return;
|
|
1632
1634
|
|
|
1633
1635
|
case 20:
|
|
1636
|
+
console.log('state 20');
|
|
1634
1637
|
this.playing = true;
|
|
1635
1638
|
if (this.options.onStart) this.options.onStart();
|
|
1636
1639
|
this.puzzle.rotationAllowed = this.options.allowRotation;
|
|
@@ -1663,6 +1666,7 @@ export class JigsawPuzzle {
|
|
|
1663
1666
|
break;
|
|
1664
1667
|
|
|
1665
1668
|
case 25:
|
|
1669
|
+
console.log('state 25');
|
|
1666
1670
|
this.puzzle.gameCanvas.style.display = "none";
|
|
1667
1671
|
this.puzzle.polyPieces.forEach((pp) => {
|
|
1668
1672
|
pp.canvas.classList.add("moving");
|
|
@@ -1671,12 +1675,14 @@ export class JigsawPuzzle {
|
|
|
1671
1675
|
break;
|
|
1672
1676
|
|
|
1673
1677
|
case 30:
|
|
1678
|
+
console.log('state 30');
|
|
1674
1679
|
this.puzzle.optimInitial();
|
|
1675
1680
|
setTimeout(() => this.events.push({ event: "finished" }), 1200);
|
|
1676
1681
|
this.state = 35;
|
|
1677
1682
|
break;
|
|
1678
1683
|
|
|
1679
1684
|
case 35:
|
|
1685
|
+
console.log('state 35');
|
|
1680
1686
|
if (!event || event.event !== "finished") return;
|
|
1681
1687
|
this.puzzle.polyPieces.forEach((pp) => {
|
|
1682
1688
|
pp.canvas.classList.remove("moving");
|
|
@@ -1686,6 +1692,7 @@ export class JigsawPuzzle {
|
|
|
1686
1692
|
|
|
1687
1693
|
case 50:
|
|
1688
1694
|
if (!event) return;
|
|
1695
|
+
console.log('state 50',event.event);
|
|
1689
1696
|
if (event.event === "stop") {
|
|
1690
1697
|
this.state = 10;
|
|
1691
1698
|
return;
|
|
@@ -1726,6 +1733,7 @@ export class JigsawPuzzle {
|
|
|
1726
1733
|
|
|
1727
1734
|
case 55:
|
|
1728
1735
|
if (!event) return;
|
|
1736
|
+
console.log('state 55-123',event.event);
|
|
1729
1737
|
if (event.event === "stop") {
|
|
1730
1738
|
this.state = 10;
|
|
1731
1739
|
return;
|
|
@@ -1765,10 +1773,14 @@ export class JigsawPuzzle {
|
|
|
1765
1773
|
if (this.moving.pp.ifNear(pp)) {
|
|
1766
1774
|
merged = true;
|
|
1767
1775
|
if (pp.pieces.length > this.moving.pp.pieces.length) {
|
|
1776
|
+
if (this.options.onDeleted) this.options.onDeleted(this.moving.pp);
|
|
1768
1777
|
pp.merge(this.moving.pp);
|
|
1778
|
+
if (this.options.onMerged) this.options.onMerged(pp);
|
|
1769
1779
|
this.moving.pp = pp;
|
|
1770
|
-
} else {
|
|
1780
|
+
} else {
|
|
1781
|
+
if (this.options.onDeleted) this.options.onDeleted(pp);
|
|
1771
1782
|
this.moving.pp.merge(pp);
|
|
1783
|
+
if (this.options.onMerged) this.options.onMerged(this.moving.pp);
|
|
1772
1784
|
}
|
|
1773
1785
|
doneSomething = true;
|
|
1774
1786
|
break;
|
|
@@ -1780,13 +1792,14 @@ export class JigsawPuzzle {
|
|
|
1780
1792
|
this.moving.pp.selected = true;
|
|
1781
1793
|
this.moving.pp.drawImage(true);
|
|
1782
1794
|
this.moving.tInit = tStamp + 500;
|
|
1783
|
-
this.state = 56;
|
|
1795
|
+
this.state = 56;
|
|
1784
1796
|
break;
|
|
1785
1797
|
}
|
|
1786
1798
|
this.state = 50;
|
|
1787
1799
|
if (this.puzzle.polyPieces.length === 1 && this.puzzle.polyPieces[0].rot === 0) {
|
|
1788
1800
|
this.state = 60;
|
|
1789
1801
|
}
|
|
1802
|
+
if (this.options.onChanged) this.options.onChanged(this.moving.pp);
|
|
1790
1803
|
}
|
|
1791
1804
|
break;
|
|
1792
1805
|
|