Maak een HTML5 Canvas Tile Swap Puzzle

In deze tutorial werken we met het HTML5-canvas en Javascript om een ​​dynamisch tegelwisselspel te maken. Het resultaat is een puzzel die met elke afbeelding werkt en flexibele moeilijkheidsniveaus heeft die gemakkelijk kunnen worden aangepast.


De complete HTML5 Canvas puzzel

Hier is een snel overzicht van de puzzel die we gaan bouwen:


Klik om te spelen

Een paar opmerkingen:

  • Compatibiliteit met meerdere browsers: Deze puzzel is getest en werkt in alle versies van Safari, Firefox en Chrome die de canvas element.
  • Mobile: De code die hier wordt verstrekt, werkt in de bovengenoemde desktopbrowser en is niet geoptimaliseerd voor mobiel. De puzzel wordt geladen en weergegeven, maar vanwege het aanraak- en sleepgedrag in mobiele browsers is optimalisatie vereist om correct te kunnen werken. Het optimaliseren van deze puzzel voor mobiel wordt behandeld in een toekomstige zelfstudie.
  • Instelbare moeilijkheidsgraad: De code bevat een constante, PUZZLE_DIFFICULTY, dat bepaalt het aantal stukken. In de bovenstaande demo is dit ingesteld op 4, een 4x4-puzzel geven. We kunnen dit eenvoudig veranderen - deze versie heeft bijvoorbeeld een PUZZLE_DIFFICULTY van 10.

Ermee beginnen

Maak een map voor het project om aan de slag te gaan. Plaats een afbeelding in de map die u als uw puzzel wilt gebruiken. Elke webvriendelijke afbeelding zal het doen, en het kan elke gewenste grootte hebben - zorg er gewoon voor dat het in de vouw van het browservenster past.


Stap 1: de HTML-sjabloon maken

Open een nieuw bestand met je favoriete teksteditor en sla het op in je projectdirectory, naast je afbeelding. Vul vervolgens deze basis in HTML sjabloon.

    HTML5-puzzel      

Het enige wat we hier moeten doen is een standaard maken HTML5 sjabloon met één canvas tag met de ID kaart van "canvas". We zullen een schrijven onload luisteraar in de lichaam tag die ons zal bellen in het() functie wanneer ontslagen.

Begin nu met het plaatsen van je cursor in de script label. Vanaf hier is het alle javascript. Met uitzondering van de beginvariabelen, organiseer ik de secties per functie. Eerst laat je de code zien en vervolgens leg je de logica uit.

Klaar? Laten we erop toezien!


Stap 2: Onze variabelen instellen

Laten we onze variabelen instellen en een ieder bekijken.

 const PUZZLE_DIFFICULTY = 4; const PUZZLE_HOVER_TINT = '# 009900'; var _canvas; var _stage; var _img; var _pieces; var _puzzleWidth; var _puzzleHeight; var _pieceWidth; var _pieceHeight; var _currentPiece; var _currentDropPiece; var _mouse;

Eerst hebben we een paar constanten: PUZZLE_DIFFICULTY en PUZZLE_HOVER_TINT. De PUZZLE_DIFFICULTY constante bevat het aantal stukjes in onze puzzel. In deze toepassing komen de rijen en kolommen altijd overeen, dus door in te stellen PUZZLE_DIFFICULTY naar 4, we krijgen in totaal 16 puzzelstukjes. Door dit te vergroten, vergroot je de moeilijkheidsgraad van de puzzel.

De volgende is een reeks variabelen:

  • _canvas en _stadium bevat een verwijzing naar respectievelijk het canvas en de tekencontext. We doen dit, dus we hoeven niet de hele vraag uit te schrijven telkens we ze gebruiken. En we zullen ze veel gebruiken!
  • _IMG zal een verwijzing zijn naar de geladen afbeelding, waarvan we pixels van de hele applicatie zullen kopiëren.
  • _puzzleWidth, _puzzleHeight, _pieceWidth, en _pieceHeight wordt gebruikt om de afmetingen van zowel de hele puzzel als elk individueel puzzelstuk op te slaan. We hebben deze één keer ingesteld om te voorkomen dat we ze elke keer opnieuw berekenen wanneer we ze nodig hebben.
  • _currentPiece bevat een verwijzing naar het stuk dat momenteel wordt gesleept.
  • _currentDropPiece bevat een verwijzing naar het stuk dat op dat moment in positie is om op te laten vallen. (In de demo is dit stuk groen gemarkeerd.)
  • _muis is een verwijzing die de stroom van de muis zal bevatten X en Y positie. Dit wordt bijgewerkt wanneer op de puzzel wordt geklikt om te bepalen welk stuk wordt aangeraakt, en wanneer een stuk wordt gesleept om te bepalen welk stuk het zweeft.

Nu, op onze functies.


Stap 3: De in het() Functie

 function init () _img = new Image (); _img.addEventListener ( 'load', onImage, false); _img.src = "mke.jpg"; 

Het eerste dat we willen doen in onze applicatie is om de afbeelding voor de puzzel te laden. Het afbeeldingsobject wordt als eerste geïnstantieerd en op onze ingesteld _IMG variabel. Vervolgens luisteren we naar de laden evenement dat ons dan zal ontslaan onImage () functie wanneer het beeld klaar is met laden. Ten slotte stellen we de bron van de afbeelding in, waardoor de belasting wordt geactiveerd.


Stap 4: De onImage () Functie

 function onImage (e) _pieceWidth = Math.floor (_img.width / PUZZLE_DIFFICULTY) _pieceHeight = Math.floor (_img.height / PUZZLE_DIFFICULTY) _puzzleWidth = _pieceWidth * PUZZLE_DIFFICULTY; _puzzleHeight = _pieceHeight * PUZZLE_DIFFICULTY; setCanvas (); initPuzzle (); 

Nu de afbeelding is geladen, kunnen we de meerderheid van de eerder gedeclareerde variabelen instellen. We doen dit hier omdat we nu informatie hebben over de afbeelding en onze waarden op de juiste manier kunnen instellen.

Het eerste dat we doen is de grootte van elk puzzelstuk berekenen. We doen dit door het PUZZLE_DIFFICULTY waarde door de breedte en hoogte van de geladen afbeelding. We snijden ook het vet van de randen af ​​om ons een aantal mooie even nummers te geven om mee te werken en zorgen ervoor dat elk stuk 'slots' op de juiste manier kan omwisselen met anderen.

Vervolgens gebruiken we onze nieuwe puzzelstukwaarden om de totale grootte van de puzzel te bepalen en deze waarden in te stellen _puzzleWidth en _puzzleHeight.

Ten slotte roepen we een aantal functies af - setCanvas () en initPuzzle ().


Stap 5: De setCanvas () Functie

 function setCanvas () _canvas = document.getElementById ('canvas'); _stage = _canvas.getContext ('2d'); _canvas.width = _puzzleWidth; _canvas.height = _puzzleHeight; _canvas.style.border = "1px effen zwart"; 

Nu onze puzzelwaarden compleet zijn, willen we onze canvas element. Eerst stellen we onze _canvas variabele om te verwijzen naar onze canvas element, en _stadium om te verwijzen naar zijn context.

Nu hebben we de breedte en hoogte van onze canvas om de grootte van onze getrimde afbeelding te evenaren, gevolgd door enkele eenvoudige stijlen toe te passen om een ​​zwarte rand om ons heen te creëren canvas om de grenzen van onze puzzel te tonen.


Stap 6: De initPuzzle () Functie

 function initPuzzle () _pieces = []; _mouse = x: 0, y: 0; _currentPiece = null; _currentDropPiece = null; _stage.drawImage (_img, 0, 0, _puzzleWidth, _puzzleHeight, 0, 0, _puzzleWidth, _puzzleHeight); createTitle ("Click to Start Puzzle"); buildPieces (); 

Hier initialiseren we de puzzel. We plaatsen deze functie op een zodanige manier dat we hem later opnieuw kunnen bellen als we de puzzel opnieuw willen spelen. Al het andere dat voorafgaand aan het spelen moet worden ingesteld, hoeft niet opnieuw te worden ingesteld.

Eerst stellen we in _pieces als een leeg rangschikking en maak de _muis object, dat onze muispositie tijdens de hele applicatie zal behouden. Vervolgens stellen we de _currentPiece en _currentPieceDrop naar nul. (Bij het eerste spel zouden deze waarden al zijn nul, maar we willen er zeker van zijn dat ze worden gereset wanneer de puzzel opnieuw wordt gespeeld.)

Eindelijk is het tijd om te tekenen! Eerst tekenen we de volledige afbeelding om aan de speler weer te geven wat ze zullen maken. Daarna maken we enkele eenvoudige instructies door ons te bellen createTitle () functie.


Stap 7: De createTitle () Functie

 function createTitle (msg) _stage.fillStyle = "# 000000"; _stage.globalAlpha = .4; _stage.fillRect (100, _puzzleHeight - 40, _puzzleWidth - 200,40); _stage.fillStyle = "#FFFFFF"; _stage.globalAlpha = 1; _stage.textAlign = "center"; _stage.textBaseline = "middle"; _stage.font = "20px Arial"; _stage.fillText (msg, _puzzleWidth / 2, _puzzleHeight - 20); 

Hier maken we een vrij eenvoudig bericht dat de gebruiker opdraagt ​​op de puzzel te klikken om te beginnen.
Ons bericht zal een semi-transparante rechthoek zijn die als achtergrond voor onze tekst zal dienen. Hierdoor kan de gebruiker de afbeelding erachter zien en verzekert hij ook dat onze witte tekst op elke afbeelding leesbaar zal zijn

We zijn gewoon klaar fillStyle naar zwart en globalAlpha naar .4, voordat u een korte zwarte rechthoek onderaan de afbeelding invult.

Sinds globalAlpha beïnvloedt het hele canvas, we moeten het terugzetten naar 1 (ondoorzichtig) voordat de tekst wordt getekend. Om onze titel in te stellen, hebben we de textAlign om te 'centreren' en de TextBaseline naar 'midden'. We kunnen er ook wat toepassen doopvont eigenschappen.

Om de tekst te tekenen, gebruiken we de fillText () methode. We passeren de msg variabele en plaats deze in het horizontale midden van de canvas, en het verticale midden van de rechthoek.


Stap 8: De buildPieces () Functie

 function buildPieces () var i; var stuk; var xPos = 0; var yPos = 0; voor (i = 0; i < PUZZLE_DIFFICULTY * PUZZLE_DIFFICULTY;i++) piece = ; piece.sx = xPos; piece.sy = yPos; _pieces.push(piece); xPos += _pieceWidth; if(xPos >= _puzzleWidth) xPos = 0; yPos + = _pieceHeight;  document.onmousedown = shufflePuzzle; 

Eindelijk is het tijd om de puzzel te bouwen!

Dit doen we door een voorwerp voor elk stuk. Deze objecten zijn niet verantwoordelijk voor het renderen naar het canvas, maar eerder om referenties te houden over wat te tekenen en waar. Dat gezegd hebbende, laten we er naar toe gaan.

Laten we eerst een paar variabelen declareren die we tijdens de herhaling zullen gebruiken. We willen de lus instellen om het aantal puzzelstukken dat we nodig hebben te herhalen. We krijgen deze waarde door te vermenigvuldigen PUZZLE_DIFFICULTY alleen, dus in dit geval krijgen we er 16.

In de lus:

 voor (i = 0; i < PUZZLE_DIFFICULTY * PUZZLE_DIFFICULTY;i++) piece = ; piece.sx = xPos; piece.sy = yPos; _pieces.push(piece); xPos += _pieceWidth; if(xPos >= _puzzleWidth) xPos = 0; yPos + = _pieceHeight; 

Begin door een leeg te maken stuk voorwerp. Voeg vervolgens de sx en sy eigenschappen van het object. In de eerste iteratie zijn deze waarden 0 en vertegenwoordigen het punt in ons beeld waar we beginnen te tekenen. Duw het nu naar de _pieces [] matrix. Dit object bevat ook de eigenschappen xpos en YPOS, die ons de huidige positie in de puzzel vertelt waar het stuk moet worden getekend. We zullen de objecten in willekeurige volgorde afspelen voordat deze kunnen worden afgespeeld, dus deze waarden hoeven nog niet helemaal te zijn ingesteld.

Het laatste wat we in elke lus doen, is de lokale variabele verhogen xpos door _pieceWidth. Voordat we verder gaan met de lus, bepalen we of we naar de volgende rij stukken moeten gaan door te controleren of xpos is voorbij de breedte van de puzzel. Als dit het geval is, worden we gereset xpos terug naar 0 en verhogen YPOS door _pieceHeight.

Nu hebben we onze puzzelstukjes allemaal mooi opgeborgen in onze _pieces matrix. Op dit punt stopt de code uiteindelijk met uitvoeren en wacht op de gebruiker om te communiceren. We hebben een klik luisteraar ingesteld op de document om de shufflePuzzle () functie wanneer geactiveerd, waardoor het spel begint.


Stap 9: De shufflePuzzle () Functie

 function shufflePuzzle () _pieces = shuffleArray (_pieces); _stage.clearRect (0,0, _puzzleWidth, _puzzleHeight); var i; var stuk; var xPos = 0; var yPos = 0; voor (i = 0; i < _pieces.length;i++) piece = _pieces[i]; piece.xPos = xPos; piece.yPos = yPos; _stage.drawImage(_img, piece.sx, piece.sy, _pieceWidth, _pieceHeight, xPos, yPos, _pieceWidth, _pieceHeight); _stage.strokeRect(xPos, yPos, _pieceWidth,_pieceHeight); xPos += _pieceWidth; if(xPos >= _puzzleWidth) xPos = 0; yPos + = _pieceHeight;  document.onmousedown = onPuzzleClick; 
 functie shuffleArray (o) voor (var j, x, i = o.length; i; j = parseInt (Math.random () * i), x = o [- i], o [i] = o [ j], o [j] = x); terug o; 

Eerste dingen eerst: schud de _pieces [] matrix. Ik gebruik hier een aardige utiliteitsfunctie die de indices van de array in willekeurige volgorde zal doorlopen. De uitleg van deze functie valt buiten het onderwerp van deze tutorial dus we gaan verder, wetende dat we met succes onze stukken hebben geschud. (Voor een basisinleiding over shuffelen, bekijk deze tutorial.)

Laten we eerst alle afbeeldingen verwijderen die zijn getekend door de canvas om plaats te maken voor het tekenen van onze stukken. Stel vervolgens de array in op dezelfde manier als bij het maken van onze stukobjecten.

In de lus:

 voor (i = 0; i < _pieces.length;i++) piece = _pieces[i]; piece.xPos = xPos; piece.yPos = yPos; _stage.drawImage(_img, piece.sx, piece.sy, _pieceWidth, _pieceHeight, xPos, yPos, _pieceWidth, _pieceHeight); _stage.strokeRect(xPos, yPos, _pieceWidth,_pieceHeight); xPos += _pieceWidth; if(xPos >= _puzzleWidth) xPos = 0; yPos + = _pieceHeight; 

Gebruik om te beginnen de ik variabele om onze referentie naar het huidige stukobject in de lus in te stellen. Nu vullen we de xpos en YPOS eigenschappen die ik eerder heb genoemd, welke zal zijn 0 in onze eerste iteratie.

Eindelijk tekenen we onze stukken.

De eerste parameter van drawImage () wijst de bron toe aan de afbeelding waarvan we willen tekenen. Gebruik vervolgens de stukobjecten sx en sy eigenschappen, samen met _pieceWidth en _pieceHeight, om de parameters in te vullen die het gebied van de afbeelding aanduiden waaruit moet worden getekend. De laatste vier parameters bepalen het gebied van de canvas waar we willen tekenen. Wij gebruiken de xpos en YPOS waarden die we allebei in de lus opbouwen en toewijzen aan het object.

Onmiddellijk hierna tekenen we een snelle slag rond het stuk om het een rand te geven, die het mooi van de andere stukken zal scheiden.

Nu wachten we tot de gebruiker een stuk kan pakken door er een te plaatsen Klik luisteraar. Deze keer zal het een vuur maken onPuzzleClick () functie.


Stap 10: De onPuzzleClick () Functie

 function onPuzzleClick (e) if (e.layerX || e.layerX == 0) _mouse.x = e.layerX - _canvas.offsetLeft; _mouse.y = e.layerY - _canvas.offsetTop;  else if (e.offsetX || e.offsetX == 0) _mouse.x = e.offsetX - _canvas.offsetLeft; _mouse.y = e.offsetY - _canvas.offsetTop;  _currentPiece = checkPieceClicked (); if (_currentPiece! = null) _stage.clearRect (_currentPiece.xPos, _currentPiece.yPos, _pieceWidth, _pieceHeight); _stage.save (); _stage.globalAlpha = .9; _stage.drawImage (_img, _currentPiece.sx, _currentPiece.sy, _pieceWidth, _pieceHeight, _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth, _pieceHeight); _stage.restore (); document.onmousemove = updatePuzzle; document.onmouseup = pieceDropped; 

We weten dat er op de puzzel is geklikt; nu moeten we bepalen op welk stuk is geklikt. Deze eenvoudige voorwaarde geeft ons onze muispositie op alle moderne desktopbrowsers die dit ondersteunen canvas, met een van beide e.layerX en e.layerY of e.offsetX en e.offsetY. Gebruik deze waarden om onze _muis object door er een toe te wijzen X en een Y eigenschap om de huidige muispositie vast te houden - in dit geval de positie waarop erop is geklikt.

In lijn 112 zetten we dan meteen in _currentPiece naar de geretourneerde waarde van onze checkPieceClicked () functie. We scheiden deze code omdat we deze later willen gebruiken bij het slepen van het puzzelstukje. Ik zal deze functie in de volgende stap uitleggen.

Als de geretourneerde waarde was nul, we doen gewoon niets, omdat dit impliceert dat de gebruiker niet echt op een puzzelstuk klikte. Als we echter een puzzelstuk ophalen, willen we het aan de muis bevestigen en het een beetje laten vervagen om de onderliggende stukken zichtbaar te maken. Dus hoe doen we dit?

Eerst wissen we de canvas gebied waar het stuk zat voordat we erop klikten. We gebruiken clearRect () nogmaals, maar in dit geval passeren we alleen het gebied verkregen van de _currentPiece voorwerp. Voordat we het opnieuw tekenen, willen we dat opslaan() de context van het canvas voordat je verder gaat. Dit zal ervoor zorgen dat alles wat we na het sparen aantrekken niet zomaar iets op zijn weg trekt. We doen dit omdat we het gesleepte stuk iets laten vervagen en de stukken eronder willen zien. Als we niet hebben gebeld opslaan(), we zouden gewoon afbeeldingen tekenen op de manier - vervaagd of niet.

Nu tekenen we de afbeelding zodat het midden ervan op de muisaanwijzer staat. De eerste 5 parameters van drawImage zal altijd hetzelfde zijn in de hele applicatie. Wanneer u klikt, worden de volgende twee parameters bijgewerkt om zichzelf te centreren op de muisaanwijzer. De laatste twee parameters, de breedte en hoogte tekenen, zal ook nooit veranderen.

Ten slotte noemen we de herstellen() methode. Dit betekent in feite dat we klaar zijn met de nieuwe alpha-waarde en we willen alle eigenschappen terugzetten naar waar ze waren. Om deze functie af te sluiten, voegen we nog twee luisteraars toe. Een voor wanneer we de muis verplaatsen (het puzzelstuk slepen) en een voor wanneer we loslaten (laat het puzzelstuk vallen).


Stap 11: De checkPieceClicked () Functie

 function checkPieceClicked () var i; var stuk; voor (i = 0; i < _pieces.length;i++) piece = _pieces[i]; if(_mouse.x < piece.xPos || _mouse.x > (piece.xPos + _pieceWidth) || _mouse.y < piece.yPos || _mouse.y > (piece.yPos + _pieceHeight)) // PIECE NOT HIT else retourstuk;  return null; 

Nu moeten we een beetje teruggaan. We hebben kunnen bepalen op welk stuk is geklikt, maar hoe hebben we het gedaan? Het is eigenlijk best simpel. Wat we moeten doen is door alle puzzelstukjes bladeren en bepalen of de klik binnen de grenzen van een van onze objecten lag. Als we er een vinden, retourneren we het gematchte object en beëindigen we de functie. Als we niets vinden, keren we terug nul.


Stap 12: De updatePuzzle () Functie

 function updatePuzzle (e) _currentDropPiece = null; if (e.layerX || e.layerX == 0) _mouse.x = e.layerX - _canvas.offsetLeft; _mouse.y = e.layerY - _canvas.offsetTop;  else if (e.offsetX || e.offsetX == 0) _mouse.x = e.offsetX - _canvas.offsetLeft; _mouse.y = e.offsetY - _canvas.offsetTop;  _stage.clearRect (0,0, _puzzleWidth, _puzzleHeight); var i; var stuk; voor (i = 0; i < _pieces.length;i++) piece = _pieces[i]; if(piece == _currentPiece) continue;  _stage.drawImage(_img, piece.sx, piece.sy, _pieceWidth, _pieceHeight, piece.xPos, piece.yPos, _pieceWidth, _pieceHeight); _stage.strokeRect(piece.xPos, piece.yPos, _pieceWidth,_pieceHeight); if(_currentDropPiece == null) if(_mouse.x < piece.xPos || _mouse.x > (piece.xPos + _pieceWidth) || _mouse.y < piece.yPos || _mouse.y > (piece.yPos + _pieceHeight)) // NOT OVER else _currentDropPiece = stuk; _stage.save (); _stage.globalAlpha = .4; _stage.fillStyle = PUZZLE_HOVER_TINT; _stage.fillRect (_currentDropPiece.xPos, _currentDropPiece.yPos, _pieceWidth, _pieceHeight); _stage.restore ();  _stage.save (); _stage.globalAlpha = .6; _stage.drawImage (_img, _currentPiece.sx, _currentPiece.sy, _pieceWidth, _pieceHeight, _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth, _pieceHeight); _stage.restore (); _stage.strokeRect (_mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth, _pieceHeight); 

Nu terug naar het slepen. We noemen deze functie wanneer de gebruiker de muis beweegt. Dit is de grootste functie van de toepassing omdat deze verschillende dingen doet. Laten we beginnen. Ik zal het opsplitsen als we gaan.

 _currentDropPiece = null; if (e.layerX || e.layerX == 0) _mouse.x = e.layerX - _canvas.offsetLeft; _mouse.y = e.layerY - _canvas.offsetTop;  else if (e.offsetX || e.offsetX == 0) _mouse.x = e.offsetX - _canvas.offsetLeft; _mouse.y = e.offsetY - _canvas.offsetTop; 

Begin met instellen _currentDropPiece naar nul. We moeten dit terugzetten naar nul op update vanwege de kans dat ons stuk terug naar huis werd gesleept. We willen de vorige niet _currentDropPiece waarde rondhangen. Vervolgens stellen we de _muis object op dezelfde manier als bij klikken.

 _stage.clearRect (0,0, _puzzleWidth, _puzzleHeight);

Hier moeten we alle afbeeldingen op het canvas wissen. In essentie moeten we de puzzelstukjes opnieuw tekenen omdat het object dat erop wordt gesleept hun uiterlijk zal beïnvloeden. Als we dit niet zouden doen, zouden we een aantal zeer vreemde resultaten zien na het pad van ons gesleept puzzelstukje.

 var i; var stuk; voor (i = 0; i < _pieces.length;i++)

Begin met het opzetten van onze gebruikelijke stukjes loop.

In de lus:

 stuk = _pieces [i]; if (piece == _currentPiece) continue; 

Maak onze stuk referentie zoals gewoonlijk. Controleer vervolgens of het stuk waarnaar we momenteel verwijzen hetzelfde is als het stuk dat we aan het slepen zijn. Als dit het geval is, gaat u verder met de lus. Hierdoor blijft de sleuf van het gesleept stuk leeg.

 _stage.drawImage (_img, piece.sx, piece.sy, _pieceWidth, _pieceHeight, piece.xPos, piece.yPos, _pieceWidth, _pieceHeight); _stage.strokeRect (piece.xPos, piece.yPos, _pieceWidth, _pieceHeight);

Als je verder gaat, maak je het puzzelstuk opnieuw met behulp van de eigenschappen op precies dezelfde manier als toen je ze voor het eerst tekende. Je moet ook de rand tekenen.

 if (_currentDropPiece == null) if (_mouse.x < piece.xPos || _mouse.x > (piece.xPos + _pieceWidth) || _mouse.y < piece.yPos || _mouse.y > (piece.yPos + _pieceHeight)) // NOT OVER else _currentDropPiece = stuk; _stage.save (); _stage.globalAlpha = .4; _stage.fillStyle = PUZZLE_HOVER_TINT; _stage.fillRect (_currentDropPiece.xPos, _currentDropPiece.yPos, _pieceWidth, _pieceHeight); _stage.restore (); 

Omdat we een verwijzing hebben naar elk object in de lus, kunnen we deze gelegenheid ook gebruiken om te controleren of het gesleepte stuk er bovenop staat. We doen dit omdat we de gebruiker feedback willen geven over het onderdeel waarop het kan worden geplaatst. Laten we nu die code eens doornemen.

Eerst willen we zien of deze lus al een druppeldoel heeft geproduceerd. Als dat zo is, hoeven we ons niet druk te maken, omdat er maar één doel kan vallen en elke beweging van een muis. Als niet, _currentDropPiece zal zijn nul en we kunnen doorgaan met de logica. Omdat onze muis zich in het midden van het gesleepte stuk bevindt, hoeven we alleen maar te bepalen met welk ander stuk onze muis is geplaatst.

Gebruik vervolgens onze handige checkPieceClicked () functie om te bepalen of de muis over het huidige stukobject in de lus zweeft. Als dat zo is, stellen we de _currentDropPiece verander en teken een gekleurde doos over het puzzelstukje om aan te geven dat dit nu het doel is.

Onthoud om opslaan() en herstellen(). Anders krijg je de getinte doos en niet de afbeelding eronder.

Out of the Loop:

 _stage.save (); _stage.globalAlpha = .6; _stage.drawImage (_img, _currentPiece.sx, _currentPiece.sy, _pieceWidth, _pieceHeight, _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth, _pieceHeight); _stage.restore (); _stage.strokeRect (_mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth, _pieceHeight);

Last but not least moeten we het gesleepte stuk opnieuw tekenen. De code is dezelfde als toen we er voor het eerst op klikten, maar de muis is verplaatst en zijn positie zal worden bijgewerkt.


Stap 13: De pieceDropped () Functie

 function pieceDropped (e) document.onmousemove = null; document.onmouseup = null; if (_currentDropPiece! = null) var tmp = xPos: _currentPiece.xPos, yPos: _currentPiece.yPos; _currentPiece.xPos = _currentDropPiece.xPos; _currentPiece.yPos = _currentDropPiece.yPos; _currentDropPiece.xPos = tmp.xPos; _currentDropPiece.yPos = tmp.yPos;  resetPuzzleAndCheckWin (); 

OK, het ergste is achter de rug. We zijn nu succesvol een puzzelstukje aan het slepen en krijgen zelfs visuele feedback over waar het valt. Nu rest alleen nog het stuk. Laten we eerst de luisteraars meteen verwijderen, omdat er niets wordt gesleept.

Controleer dat vervolgens _currentDropPiece is niet nul. Als dat zo is, betekent dit dat we het naar het thuisgebied van het stuk hebben gesleept en niet over een ander slot. Als het dat niet is nul, we gaan verder met de functie.

Wat we nu doen is simpelweg de xpos en YPOS van elk stuk. We maken een snel tijdelijk object als een buffer om een ​​van de waarden van het object in het ruilproces te houden. Op dit punt hebben de twee stukken allebei nieuw xpos en YPOS waarden en zullen bij de volgende trekking in hun nieuwe huizen klikken. Dat is wat we nu gaan doen, tegelijkertijd controleren of het spel is gewonnen.


Stap 14: De resetPuzzleAndCheckWin () Functie

 function resetPuzzleAndCheckWin () _stage.clearRect (0,0, _puzzleWidth, _puzzleHeight); var gameWin = true; var i; var stuk; voor (i = 0; i < _pieces.length;i++) piece = _pieces[i]; _stage.drawImage(_img, piece.sx, piece.sy, _pieceWidth, _pieceHeight, piece.xPos, piece.yPos, _pieceWidth, _pieceHeight); _stage.strokeRect(piece.xPos, piece.yPos, _pieceWidth,_pieceHeight); if(piece.xPos != piece.sx || piece.yPos != piece.sy) gameWin = false;   if(gameWin) setTimeout(gameOver,500);  

Nogmaals, wis de canvas en stel een gameWin variabele, instellen op waar standaard. Ga nu verder met onze al te bekende stukjes loop.

De code hier zou er bekend uit moeten zien, dus we zullen er niet overheen gaan. Het trekt gewoon de stukjes terug naar hun originele of nieuwe slots. Binnen deze loop willen we zien of elk stuk in zijn winnende positie wordt getekend. Dit is eenvoudig: we controleren om te zien of onze sx en sy eigenschappen komen overeen met xpos en YPOS. Zo niet, dan weten we dat we de puzzel en de set niet kunnen winnen gameWin naar vals. Als we het luk maakten met iedereen op hun winnende plaatsen, hebben we snel een beslissing genomen time-out om onze te bellen spel is over() methode. (We hebben een time-out ingesteld zodat het scherm niet zo drastisch verandert bij het laten vallen van het puzzelstukje.)


Stap 15: De spel is over() Functie

 function gameOver () document.onmousedown = null; document.onmousemove = null; document.onmouseup = null; initPuzzle (); 

Dit is onze laatste functie! Hier verwijderen we gewoon alle luisteraars en bellen initPuzzle (), die alle noodzakelijke waarden opnieuw instelt en wacht tot de gebruiker opnieuw speelt.


Conclusie

Klik hier om het eindresultaat te bekijken.

Zoals u kunt zien, kunt u in HTML5 veel nieuwe creatieve dingen doen met behulp van geselecteerde bitmapgebieden van geladen afbeeldingen en tekeningen. Je kunt deze applicatie eenvoudig uitbreiden door scoren toe te voegen en misschien zelfs een timer om hem meer te laten spelen. Een ander idee zou zijn om de moeilijkheidsgraad te vergroten en een andere afbeelding te selecteren in de spel is over() functie, waarbij de spelniveaus worden weergegeven.