jigsawpuzzlegame 1.0.7 → 1.0.9
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 +36 -18
- package/jigsaw-puzzle-game.js +93 -83
- package/package.json +1 -1
- package/game.css +0 -23
package/README.md
CHANGED
|
@@ -52,10 +52,11 @@ Make sure your container has a defined size:
|
|
|
52
52
|
|
|
53
53
|
| Option | Type | Default | Description |
|
|
54
54
|
|--------|------|---------|-------------|
|
|
55
|
-
| `image` | string | `null` | URL or data URL of the image to use for the puzzle |
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
55
|
+
| `image` | string | `null` | URL or data URL of the image to use for the puzzle (ignored if `savedData` is provided) |
|
|
56
|
+
| `savedData` | string\|null | `null` | JSON string of saved game state:<br>• Non-empty string: use this saved data<br>• Empty string (`""`): load from localStorage<br>• Not provided or `null`: use normal initialization with `image` and other parameters |
|
|
57
|
+
| `numPieces` | number | `20` | Number of puzzle pieces (approximate - actual count depends on optimal grid layout, ignored if `savedData` is provided) |
|
|
58
|
+
| `shapeType` | number | `0` | Shape type for puzzle pieces (0-3, ignored if `savedData` is provided):<br>• `0` - Classic jigsaw shape (curved tabs)<br>• `1` - Alternative shape 1<br>• `2` - Alternative shape 2<br>• `3` - Straight edges (rectangular pieces) |
|
|
59
|
+
| `allowRotation` | boolean | `false` | Whether pieces can be rotated by clicking/tapping (90° increments, ignored if `savedData` is provided) |
|
|
59
60
|
| `onReady` | function | `null` | Callback function called when the puzzle is ready (image loaded and displayed) |
|
|
60
61
|
| `onWin` | function | `null` | Callback function called when the puzzle is completed |
|
|
61
62
|
| `onStart` | function | `null` | Callback function called when a game starts |
|
|
@@ -119,10 +120,10 @@ puzzle.setOptions({
|
|
|
119
120
|
|
|
120
121
|
### `save([callback])`
|
|
121
122
|
|
|
122
|
-
Saves the current game state.
|
|
123
|
+
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.
|
|
123
124
|
|
|
124
125
|
**Parameters:**
|
|
125
|
-
- `callback` (function, optional) - Function that receives the saved data as JSON string
|
|
126
|
+
- `callback` (function, optional) - Function that receives the saved data as JSON string. If not provided, saves to localStorage automatically.
|
|
126
127
|
|
|
127
128
|
```javascript
|
|
128
129
|
// Save to localStorage (default)
|
|
@@ -135,20 +136,30 @@ puzzle.save((savedData) => {
|
|
|
135
136
|
});
|
|
136
137
|
```
|
|
137
138
|
|
|
138
|
-
###
|
|
139
|
+
### Loading Saved Games
|
|
139
140
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
**Parameters:**
|
|
143
|
-
- `savedData` (string, optional) - JSON string of saved data. If not provided, loads from localStorage
|
|
141
|
+
To load a saved game, create a new puzzle instance with the `savedData` option:
|
|
144
142
|
|
|
145
143
|
```javascript
|
|
146
|
-
// Load from
|
|
147
|
-
|
|
144
|
+
// Load from explicit string
|
|
145
|
+
const savedString = localStorage.getItem('myPuzzleSave');
|
|
146
|
+
const puzzle = new JigsawPuzzle('puzzle-container', {
|
|
147
|
+
savedData: savedString,
|
|
148
|
+
onReady: () => puzzle.start()
|
|
149
|
+
});
|
|
148
150
|
|
|
149
|
-
// Load from
|
|
150
|
-
const
|
|
151
|
-
|
|
151
|
+
// Load from localStorage automatically (pass empty string)
|
|
152
|
+
const puzzle = new JigsawPuzzle('puzzle-container', {
|
|
153
|
+
savedData: "", // Empty string triggers localStorage lookup
|
|
154
|
+
onReady: () => puzzle.start()
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Create new puzzle (no saved data)
|
|
158
|
+
const puzzle = new JigsawPuzzle('puzzle-container', {
|
|
159
|
+
image: 'image.jpg',
|
|
160
|
+
numPieces: 25,
|
|
161
|
+
onReady: () => puzzle.start()
|
|
162
|
+
});
|
|
152
163
|
```
|
|
153
164
|
|
|
154
165
|
### `destroy()`
|
|
@@ -196,11 +207,18 @@ function saveGame() {
|
|
|
196
207
|
});
|
|
197
208
|
}
|
|
198
209
|
|
|
199
|
-
// Load game
|
|
210
|
+
// Load game - create new instance with saved data
|
|
200
211
|
function loadGame() {
|
|
201
212
|
const saved = localStorage.getItem('puzzleSave');
|
|
202
213
|
if (saved) {
|
|
203
|
-
puzzle.
|
|
214
|
+
if (puzzle) puzzle.destroy();
|
|
215
|
+
puzzle = new JigsawPuzzle('puzzle-container', {
|
|
216
|
+
savedData: saved,
|
|
217
|
+
onReady: () => puzzle.start(),
|
|
218
|
+
onWin: () => {
|
|
219
|
+
alert('You won!');
|
|
220
|
+
}
|
|
221
|
+
});
|
|
204
222
|
}
|
|
205
223
|
}
|
|
206
224
|
```
|
package/jigsaw-puzzle-game.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Stripped of all UI code, providing a clean interface for integration.
|
|
6
6
|
*
|
|
7
|
+
* @version 1.0.8
|
|
8
|
+
*
|
|
7
9
|
* @example
|
|
8
10
|
* const puzzle = new JigsawPuzzle('my-container-id', {
|
|
9
11
|
* image: 'https://example.com/image.jpg',
|
|
@@ -1329,10 +1331,14 @@ export class JigsawPuzzle {
|
|
|
1329
1331
|
* Creates a new JigsawPuzzle instance
|
|
1330
1332
|
* @param {string|HTMLElement} containerId - ID of container div or element itself
|
|
1331
1333
|
* @param {Object} options - Configuration options
|
|
1332
|
-
* @param {string} options.image - Image URL or data URL to use
|
|
1333
|
-
* @param {
|
|
1334
|
-
*
|
|
1335
|
-
*
|
|
1334
|
+
* @param {string} options.image - Image URL or data URL to use (ignored if savedData is provided)
|
|
1335
|
+
* @param {string|null} options.savedData - JSON string of saved game state:
|
|
1336
|
+
* - Non-empty string: use this saved data
|
|
1337
|
+
* - Empty string (""): load from localStorage
|
|
1338
|
+
* - Not provided or null: use normal initialization with options.image and other parameters
|
|
1339
|
+
* @param {number} options.numPieces - Number of puzzle pieces (default: 20, ignored if savedData is provided)
|
|
1340
|
+
* @param {number} options.shapeType - Shape type 0-3 (default: 0, ignored if savedData is provided)
|
|
1341
|
+
* @param {boolean} options.allowRotation - Allow piece rotation (default: false, ignored if savedData is provided)
|
|
1336
1342
|
* @param {Function} options.onReady - Callback when puzzle is ready (image loaded, state 15)
|
|
1337
1343
|
* @param {Function} options.onWin - Callback when puzzle is solved
|
|
1338
1344
|
* @param {Function} options.onStart - Callback when game starts
|
|
@@ -1389,9 +1395,68 @@ export class JigsawPuzzle {
|
|
|
1389
1395
|
this.events.push({ event: "resize" });
|
|
1390
1396
|
});
|
|
1391
1397
|
|
|
1392
|
-
//
|
|
1393
|
-
|
|
1394
|
-
|
|
1398
|
+
// Handle saved data based on savedData option:
|
|
1399
|
+
// - Non-empty string: use that string
|
|
1400
|
+
// - Empty string: load from localStorage
|
|
1401
|
+
// - Not provided or null: use normal initialization
|
|
1402
|
+
let savedDataString = null;
|
|
1403
|
+
|
|
1404
|
+
if (options.savedData !== undefined && options.savedData !== null) {
|
|
1405
|
+
if (options.savedData === "") {
|
|
1406
|
+
// Empty string: load from localStorage
|
|
1407
|
+
try {
|
|
1408
|
+
savedDataString = localStorage.getItem("savepuzzle");
|
|
1409
|
+
} catch (exception) {
|
|
1410
|
+
// localStorage not available, continue with normal initialization
|
|
1411
|
+
}
|
|
1412
|
+
} else {
|
|
1413
|
+
// Non-empty string: use it directly
|
|
1414
|
+
savedDataString = options.savedData;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
// If savedData is not provided or null, savedDataString remains null and we use normal initialization
|
|
1418
|
+
|
|
1419
|
+
// If saved data found, parse and validate it
|
|
1420
|
+
if (savedDataString) {
|
|
1421
|
+
try {
|
|
1422
|
+
const parsedData = JSON.parse(savedDataString);
|
|
1423
|
+
// Validate signature and required fields
|
|
1424
|
+
if (parsedData.signature === FILE_SIGNATURE && parsedData.src) {
|
|
1425
|
+
this.restoredState = parsedData;
|
|
1426
|
+
// Set image from saved data (will override options.image)
|
|
1427
|
+
this.puzzle.imageLoaded = false;
|
|
1428
|
+
this.puzzle.srcImage.src = parsedData.src;
|
|
1429
|
+
if (parsedData.origin) {
|
|
1430
|
+
this.puzzle.srcImage.dataset.origin = parsedData.origin;
|
|
1431
|
+
}
|
|
1432
|
+
// Start restore flow - check if image is already loaded (cached)
|
|
1433
|
+
if (this.puzzle.srcImage.complete && this.puzzle.srcImage.naturalWidth > 0) {
|
|
1434
|
+
// Image already loaded, trigger load event manually
|
|
1435
|
+
this._imageLoaded();
|
|
1436
|
+
this.state = 158;
|
|
1437
|
+
} else {
|
|
1438
|
+
// Image will load asynchronously
|
|
1439
|
+
this.state = 158;
|
|
1440
|
+
}
|
|
1441
|
+
} else {
|
|
1442
|
+
// Invalid saved data, continue with normal initialization
|
|
1443
|
+
this.restoredState = null;
|
|
1444
|
+
if (this.options.image) {
|
|
1445
|
+
this.setImage(this.options.image);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
} catch (error) {
|
|
1449
|
+
// Invalid JSON, continue with normal initialization
|
|
1450
|
+
this.restoredState = null;
|
|
1451
|
+
if (this.options.image) {
|
|
1452
|
+
this.setImage(this.options.image);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
} else {
|
|
1456
|
+
// No saved data, load initial image if provided
|
|
1457
|
+
if (this.options.image) {
|
|
1458
|
+
this.setImage(this.options.image);
|
|
1459
|
+
}
|
|
1395
1460
|
}
|
|
1396
1461
|
|
|
1397
1462
|
// Start animation loop
|
|
@@ -1563,9 +1628,6 @@ export class JigsawPuzzle {
|
|
|
1563
1628
|
} else if (event.event === "srcImageLoaded") {
|
|
1564
1629
|
this.state = 10;
|
|
1565
1630
|
return;
|
|
1566
|
-
} else if (event.event === "restore") {
|
|
1567
|
-
this.state = 150;
|
|
1568
|
-
return;
|
|
1569
1631
|
} else return;
|
|
1570
1632
|
|
|
1571
1633
|
case 20:
|
|
@@ -1631,8 +1693,6 @@ export class JigsawPuzzle {
|
|
|
1631
1693
|
if (event.event === "nbpieces") {
|
|
1632
1694
|
this.puzzle.nbPieces = event.nbpieces;
|
|
1633
1695
|
this.state = 20;
|
|
1634
|
-
} else if (event.event === "save") {
|
|
1635
|
-
this.state = 120;
|
|
1636
1696
|
} else if (event.event === "touch") {
|
|
1637
1697
|
this.moving = {
|
|
1638
1698
|
xMouseInit: event.position.x,
|
|
@@ -1814,61 +1874,6 @@ export class JigsawPuzzle {
|
|
|
1814
1874
|
}
|
|
1815
1875
|
break;
|
|
1816
1876
|
|
|
1817
|
-
case 120:
|
|
1818
|
-
const savedData = this.puzzle.getStateData();
|
|
1819
|
-
const savedString = JSON.stringify(savedData);
|
|
1820
|
-
if (event && event.callback) {
|
|
1821
|
-
event.callback(savedString);
|
|
1822
|
-
}
|
|
1823
|
-
this.state = 50;
|
|
1824
|
-
break;
|
|
1825
|
-
|
|
1826
|
-
case 150:
|
|
1827
|
-
this.restoredString = "";
|
|
1828
|
-
if (event && event.data) {
|
|
1829
|
-
this.restoredString = event.data;
|
|
1830
|
-
this.state = 155;
|
|
1831
|
-
} else {
|
|
1832
|
-
try {
|
|
1833
|
-
this.restoredString = localStorage.getItem("savepuzzle");
|
|
1834
|
-
if (!this.restoredString) this.restoredString = "";
|
|
1835
|
-
} catch (exception) {
|
|
1836
|
-
this.restoredString = "";
|
|
1837
|
-
}
|
|
1838
|
-
if (this.restoredString.length === 0) {
|
|
1839
|
-
this.state = 15;
|
|
1840
|
-
break;
|
|
1841
|
-
}
|
|
1842
|
-
this.state = 155;
|
|
1843
|
-
}
|
|
1844
|
-
break;
|
|
1845
|
-
|
|
1846
|
-
case 155:
|
|
1847
|
-
try {
|
|
1848
|
-
this.restoredState = JSON.parse(this.restoredString);
|
|
1849
|
-
} catch (error) {
|
|
1850
|
-
this.restoredState = null;
|
|
1851
|
-
this.state = 10;
|
|
1852
|
-
break;
|
|
1853
|
-
}
|
|
1854
|
-
if (
|
|
1855
|
-
!this.restoredState.signature ||
|
|
1856
|
-
this.restoredState.signature !== FILE_SIGNATURE ||
|
|
1857
|
-
!this.restoredState.src
|
|
1858
|
-
) {
|
|
1859
|
-
this.restoredState = null;
|
|
1860
|
-
this.state = 10;
|
|
1861
|
-
break;
|
|
1862
|
-
}
|
|
1863
|
-
this.puzzle.imageLoaded = false;
|
|
1864
|
-
this.puzzle.srcImage.src = this.restoredState.src;
|
|
1865
|
-
if (this.restoredState.origin)
|
|
1866
|
-
this.puzzle.srcImage.dataset.origin = this.restoredState.origin;
|
|
1867
|
-
else
|
|
1868
|
-
delete this.puzzle.srcImage.dataset.origin;
|
|
1869
|
-
this.state = 158;
|
|
1870
|
-
// fall through
|
|
1871
|
-
|
|
1872
1877
|
case 158:
|
|
1873
1878
|
if (event && event.event === "srcImageLoaded") {
|
|
1874
1879
|
this.state = 160;
|
|
@@ -1878,8 +1883,18 @@ export class JigsawPuzzle {
|
|
|
1878
1883
|
break;
|
|
1879
1884
|
|
|
1880
1885
|
case 160:
|
|
1886
|
+
// Create tmpImage if it doesn't exist (needed when loading from savedData)
|
|
1887
|
+
if (!this.tmpImage) {
|
|
1888
|
+
this.puzzle.container.innerHTML = "";
|
|
1889
|
+
this.tmpImage = document.createElement("img");
|
|
1890
|
+
}
|
|
1881
1891
|
this.tmpImage.src = this.puzzle.srcImage.src;
|
|
1892
|
+
this.puzzle.getContainerSize();
|
|
1882
1893
|
fitImage(this.tmpImage, this.puzzle.contWidth * 0.95, this.puzzle.contHeight * 0.95);
|
|
1894
|
+
this.tmpImage.style.boxShadow = "-4px 4px 4px rgba(0, 0, 0, 0.5)";
|
|
1895
|
+
if (!this.tmpImage.parentNode) {
|
|
1896
|
+
this.puzzle.container.appendChild(this.tmpImage);
|
|
1897
|
+
}
|
|
1883
1898
|
this.state = 20;
|
|
1884
1899
|
break;
|
|
1885
1900
|
}
|
|
@@ -1923,27 +1938,22 @@ export class JigsawPuzzle {
|
|
|
1923
1938
|
* @param {Function} callback - Optional callback to receive saved data as JSON string
|
|
1924
1939
|
*/
|
|
1925
1940
|
save(callback) {
|
|
1941
|
+
// Get state data and convert to string
|
|
1942
|
+
const savedData = this.puzzle.getStateData();
|
|
1943
|
+
const savedString = JSON.stringify(savedData);
|
|
1944
|
+
|
|
1945
|
+
// If callback provided, call it with the string; otherwise save to localStorage
|
|
1926
1946
|
if (callback) {
|
|
1927
|
-
|
|
1947
|
+
callback(savedString);
|
|
1928
1948
|
} else {
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
console.error("Failed to save to localStorage:", exception);
|
|
1935
|
-
}
|
|
1936
|
-
}});
|
|
1949
|
+
try {
|
|
1950
|
+
localStorage.setItem("savepuzzle", savedString);
|
|
1951
|
+
} catch (exception) {
|
|
1952
|
+
console.error("Failed to save to localStorage:", exception);
|
|
1953
|
+
}
|
|
1937
1954
|
}
|
|
1938
1955
|
}
|
|
1939
1956
|
|
|
1940
|
-
/**
|
|
1941
|
-
* Load a saved game state
|
|
1942
|
-
* @param {string} savedData - JSON string of saved data, or null to load from localStorage
|
|
1943
|
-
*/
|
|
1944
|
-
load(savedData) {
|
|
1945
|
-
this.events.push({ event: "restore", data: savedData });
|
|
1946
|
-
}
|
|
1947
1957
|
|
|
1948
1958
|
/**
|
|
1949
1959
|
* Set the puzzle image
|
package/package.json
CHANGED
package/game.css
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
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
|
-
|