Leveraging of 3rd party functions


#1

My JS literacy level is still very basic, but I decided to take a challenge
to recreate a matching card game in Hype using this JS tutorial:

I copied the assets into Hype and managed to make them work for the most part.
To be more specific, I’ve done this:

  1. Div classes / data-framework in inner html of elements.
  2. init()-function On Scene Load.
  3. The script in full length + internal css in head HTML.

Now what I am missing, is how to append to that pre-made third-party js-function something like this from Hype:

hypeDocument.startTimelineNamed('TimelineName', hypeDocument.kDirectionForward);
or/and
hypeDocument.showSceneNamed('sceneName', hypeDocument.kSceneTransitionCrossfade, 1.1)

In other words, my idea is to enable the launch of transition to the next scene or trigger another timeline in Hype after all pair of cards are flipped. From within the pre-made js- function.

IMHO, there is a need for an if/else -statement but I don’t have enough skills to write js-code from scratch… my efforts are now reduced to only scratching my head :wink:

Any js-savvy help is welcome!

matchingcards-v3.hype.zip (21.8 KB)


(Hans-Gerd Claßen) #2
function checkForMatch() {
  let isMatch = firstCard.dataset.framework === secondCard.dataset.framework;

  isMatch ? disableCards()/*this is executed when isMatch is true*/ : unflipCards()/*otherwise*/;
}

a check for finish is not within. you’d check for finish every time isMatch == true occurs …


#3

Thank you, Danke @h_classen for taking your time and checking this! I trust your JS expertise.
As my JS literacy is yet very basic, I have to ask you or someone
to explain me in basic terms what are my next steps, because I don’t understand what your statement implies:

Danke!


(Hans-Gerd Claßen) #4

onsceneload, remove the script in the head …

const cards = document.querySelectorAll('.memory-card');
const pairs = cards.length/2;

let matches = 0;
let hasFlippedCard = false;
let lockBoard = false;
let firstCard, secondCard;
cards.forEach(card => card.addEventListener('click', flipCard));



function flipCard() {
  if (lockBoard) return;
  if (this === firstCard) return;

  this.classList.add('flip');

  if (!hasFlippedCard) {
    hasFlippedCard = true;
    firstCard = this;

    return;
  }

  secondCard = this;
  checkForMatch();
}

function checkForMatch() {
  let isMatch = firstCard.dataset.framework === secondCard.dataset.framework;

  isMatch ? disableCards() : unflipCards();
}

function disableCards() {
	matches++;
	checkForFinish();
  firstCard.removeEventListener('click', flipCard);
  secondCard.removeEventListener('click', flipCard);

  resetBoard();
}

function unflipCards() {
  lockBoard = true;

  setTimeout(() => {
    firstCard.classList.remove('flip');
    secondCard.classList.remove('flip');

    resetBoard();
  }, 1100);
}

function resetBoard() {
  [hasFlippedCard, lockBoard] = [false, false];
  [firstCard, secondCard] = [null, null];
}

	function checkForFinish(){
	if(matches === pairs)alert('Game over')
	}

#5

(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)


(Hans-Gerd Claßen) #6

the code is mostly ok and runs in chrome and firefox and opera desktop.

two annotations for the js:
1.)
cards = document.querySelectorAll(’.memory-card’) returns a nodelist, but
cards.forEach(… is an arraymethod. most browsers won’t hurt but it’s not save …
so you may convert it to an array:
cards = Array.prototype.slice.call(cards);

2.) your code is partially ES6. you may use a service like babel (https://es6console.com/) to convert it to ES5 for widely browsersupport …

the css part:
i’m not sure that all used properties are widely supported … you may have to do some prefix …


#7

Thank you again @h_classen for your advice and tips, now I’ve got a lead!


#8

Hi Ed!

Sorry I’m late to the Party! :tada::balloon:

There is another way to approach this situation - it is not better - just another technique in the toolbox - that might come in handy on some other project. This approach is very straightforward but takes a bit of text to explain. We will be adding code just to the Head HTML and no where else.

The reason I am showing this approach is this is how the original document was designed - the preponderance of the code that runs the show is in the Head HTML section. Sometimes when You are using a pre-existing document’s code it is advantageous to go with the flow. While utilizing Hype’s API in the Head HTML section is not intuitive (for a non-coder like myself anyway) it is easy to set up.

Hype Demo: matchingcards-v3_JHSv1.hype.zip (26.6 KB)


Overview

The new code we are adding to the script in the “Head HTML” will track the number of sets successfully matched and when they are all matched - bingo - we go to the next scene (or what ever You want to do). This new code is commented~annotated as “NEW CODE” in the Demo.

We will leave the "init" function as is.

The line numbers sited are the line numbers in the “Head HTML” in the Hype project. All the NEW CODE is annotated~commented in the attached Demo.

There is one major NEW CODE section in the Head HTML script that sets things up - the rest is just a few lines of code.

Details

One drawback with code in the Head HTML is that You can not use Hype’s API straight away as the Head loads before the rest of the document. BUT there is a work around using a callback routine which basically says “Hey! When ‘Hype’ loads run this script”. Inside this callback is a function that runs Hype API code e.g.

     allCardsMatched = function sceneChange(){
       hypeDocument.showNextScene(hypeDocument.kSceneTransitionCrossfade, 1.1);
      }

This callback routine runs lines 9-24 including lines for spacing and comments. Much of it is boilerplate code (used for setting things up). The key items are on Lines 12, 15-16:

Line 12 - find out the number of .memory-cards & divide the number of “cards” by 2 (i.e. 2 per set).
We then store this number (4 in the Demo) in the variable “numOfSets”:
numOfSets = Number((document.querySelectorAll('.memory-card').length)/2);

Lines 15-16 - store sceneChange() in the variable allCardsMatched (used in line 87 below)

allCardsMatched = function sceneChange(){
   hypeDocument.showNextScene(hypeDocument.kSceneTransitionCrossfade, 1.1);
  }

> The rest of the NEW CODE is very basic:

Line 33: Set up a variable “cardSetMatchTotal” to track the number of sets.

Line 66: If there is a card match > add 1 to “cardSetMatchTotal”.

Lines 86-87: if “cardSetMatchTotal” == the numOfSets variable (line 12) then run the function expression stored in the "allCardsMatched" variable (i.e. sceneChange() > lines 15 & 16 above).


The "callback" routine for context:

function myCallback(hypeDocument, element, event) {
       
 //how many sets @ 2 "cards" per set so we divide the number of cards by 2
 numOfSets = Number((document.querySelectorAll('.memory-card').length)/2);
       
 //Below is your "custom" function (placed in a variable which is then called like a function i.e. allCardsMatched();
  allCardsMatched = function sceneChange(){
   hypeDocument.showNextScene(hypeDocument.kSceneTransitionCrossfade, 1.1);
  }
  return true;
  }

  if("HYPE_eventListeners" in window === false) {
      window.HYPE_eventListeners = Array();
   }
  window.HYPE_eventListeners.push({"type":"HypeDocumentLoad", "callback":myCallback});

#9

HI JIM @JimScott
long time no hear :wink:
thanks for joining, in this very case the party have just started.

Thank you very much for your easily digestible comments and spelling out the code you rewrote for use in Hype. I feel more confident now in my guesswork with scripts.
I am happy to see that the scene change works with the function you provided.
I wonder, would it be possible to implement programmatic shuffling of cards,
when the scene transition happens. Otherwise I have to ‘shuffle’ cards manually for the next scene.
I couldn’t make the original code example work just by copypasting it into hype:

function shuffle() {
  cards.forEach(card => {
    let ramdomPos = Math.floor(Math.random() * 8); 
    card.style.order = ramdomPos;
});

I don’t understand much of advanced JS tech jargon, so I couldn’t comprehend author’s comment either : In order to invoke the shuffle function, let’s make it a Immediately Invoked Function Expression (IIFE), which means it will execute itself right after its declaration.

Screenshot source:
https://medium.freecodecamp.org/vanilla-javascript-tutorial-build-a-memory-game-in-30-minutes-e542c4447eae


#10

I will have a look at this tomorrow (Feb 17) - if someone else doesn’t beat me to it. :upside_down_face::zzz: