Add options for custom card back and theme color
parent
8b2eb9e544
commit
9347b17279
86
card.js
86
card.js
|
@ -8,8 +8,9 @@
|
||||||
* @param {Object} image
|
* @param {Object} image
|
||||||
* @param {number} id
|
* @param {number} id
|
||||||
* @param {string} [description]
|
* @param {string} [description]
|
||||||
|
* @param {Object} [styles]
|
||||||
*/
|
*/
|
||||||
MemoryGame.Card = function (image, id, description) {
|
MemoryGame.Card = function (image, id, description, styles) {
|
||||||
/** @alias H5P.MemoryGame.Card# */
|
/** @alias H5P.MemoryGame.Card# */
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -88,8 +89,8 @@
|
||||||
self.appendTo = function ($container) {
|
self.appendTo = function ($container) {
|
||||||
// TODO: Translate alt attr
|
// TODO: Translate alt attr
|
||||||
$card = $('<li class="h5p-memory-wrap"><div class="h5p-memory-card" role="button" tabindex="1">' +
|
$card = $('<li class="h5p-memory-wrap"><div class="h5p-memory-card" role="button" tabindex="1">' +
|
||||||
'<div class="h5p-front"></div>' +
|
'<div class="h5p-front"' + (styles && styles.front ? styles.front : '') + '>' + (styles.backImage ? '' : '<span></span>') + '</div>' +
|
||||||
'<div class="h5p-back">' +
|
'<div class="h5p-back"' + (styles && styles.back ? styles.back : '') + '>' +
|
||||||
'<img src="' + path + '" alt="Memory Card" style="width:' + width + ';height:' + height + '"/>' +
|
'<img src="' + path + '" alt="Memory Card" style="width:' + width + ';height:' + height + '"/>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'</div></li>')
|
'</div></li>')
|
||||||
|
@ -141,4 +142,83 @@
|
||||||
params.match.path !== undefined);
|
params.match.path !== undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the theme for how the cards should look
|
||||||
|
*
|
||||||
|
* @param {string} color The base color selected
|
||||||
|
* @param {number} invertShades Factor used to invert shades in case of bad contrast
|
||||||
|
*/
|
||||||
|
MemoryGame.Card.determineStyles = function (color, invertShades, backImage) {
|
||||||
|
var styles = {
|
||||||
|
front: '',
|
||||||
|
back: '',
|
||||||
|
backImage: !!backImage
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create color theme
|
||||||
|
if (color) {
|
||||||
|
var frontColor = shade(color, 43.75 * invertShades);
|
||||||
|
var backColor = shade(color, 56.25 * invertShades);
|
||||||
|
|
||||||
|
styles.front += 'color:' + color + ';' +
|
||||||
|
'background-color:' + frontColor + ';' +
|
||||||
|
'border-color:' + frontColor +';';
|
||||||
|
styles.back += 'color:' + color + ';' +
|
||||||
|
'background-color:' + backColor + ';' +
|
||||||
|
'border-color:' + frontColor +';';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add back image for card
|
||||||
|
if (backImage) {
|
||||||
|
var backgroundImage = 'background-image:url(' + backImage + ')';
|
||||||
|
|
||||||
|
styles.front += backgroundImage;
|
||||||
|
styles.back += backgroundImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prep style attribute
|
||||||
|
if (styles.front) {
|
||||||
|
styles.front = ' style="' + styles.front + '"';
|
||||||
|
}
|
||||||
|
if (styles.back) {
|
||||||
|
styles.back = ' style="' + styles.back + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
return styles;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert hex color into shade depending on given percent
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} color
|
||||||
|
* @param {number} percent
|
||||||
|
* @return {string} new color
|
||||||
|
*/
|
||||||
|
var shade = function (color, percent) {
|
||||||
|
var newColor = '#';
|
||||||
|
|
||||||
|
// Determine if we should lighten or darken
|
||||||
|
var max = (percent < 0 ? 0 : 255);
|
||||||
|
|
||||||
|
// Always stay positive
|
||||||
|
if (percent < 0) {
|
||||||
|
percent *= -1;
|
||||||
|
}
|
||||||
|
percent /= 100;
|
||||||
|
|
||||||
|
for (var i = 1; i < 6; i += 2) {
|
||||||
|
// Grab channel and convert from hex to dec
|
||||||
|
var channel = parseInt(color.substr(i, 2), 16);
|
||||||
|
|
||||||
|
// Calculate new shade and convert back to hex
|
||||||
|
channel = (Math.round((max - channel) * percent) + channel).toString(16);
|
||||||
|
|
||||||
|
// Make sure to always use two digits
|
||||||
|
newColor += (channel.length < 2 ? '0' + channel : channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newColor;
|
||||||
|
};
|
||||||
|
|
||||||
})(H5P.MemoryGame, H5P.EventDispatcher, H5P.jQuery);
|
})(H5P.MemoryGame, H5P.EventDispatcher, H5P.jQuery);
|
||||||
|
|
|
@ -46,6 +46,20 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"englishLabel": "Look and feel",
|
||||||
|
"englishDescription": "Control the visuals of the game.",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"englishLabel": "Theme Color",
|
||||||
|
"englishDescription": "Choose a color to create a theme for your card game."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"englishLabel": "Card Back",
|
||||||
|
"englishDescription": "Use a custom back for your cards."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "الأقلمة",
|
"label": "الأقلمة",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
|
|
@ -46,6 +46,20 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"englishLabel": "Look and feel",
|
||||||
|
"englishDescription": "Control the visuals of the game.",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"englishLabel": "Theme Color",
|
||||||
|
"englishDescription": "Choose a color to create a theme for your card game."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"englishLabel": "Card Back",
|
||||||
|
"englishDescription": "Use a custom back for your cards."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Übersetzung",
|
"label": "Übersetzung",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
|
|
@ -46,6 +46,20 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"englishLabel": "Look and feel",
|
||||||
|
"englishDescription": "Control the visuals of the game.",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"englishLabel": "Theme Color",
|
||||||
|
"englishDescription": "Choose a color to create a theme for your card game."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"englishLabel": "Card Back",
|
||||||
|
"englishDescription": "Use a custom back for your cards."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Interface",
|
"label": "Interface",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
|
|
@ -46,6 +46,20 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"englishLabel": "Look and feel",
|
||||||
|
"englishDescription": "Control the visuals of the game.",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"englishLabel": "Theme Color",
|
||||||
|
"englishDescription": "Choose a color to create a theme for your card game."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"englishLabel": "Card Back",
|
||||||
|
"englishDescription": "Use a custom back for your cards."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Localizzazione",
|
"label": "Localizzazione",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
|
|
@ -52,6 +52,26 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"englishLabel": "Look and feel",
|
||||||
|
"label": "Tilpass utseende",
|
||||||
|
"englishDescription": "Control the visuals of the game.",
|
||||||
|
"description": "Kontroller de visuelle aspektene ved spillet.",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"englishLabel": "Theme Color",
|
||||||
|
"label": "Temafarge",
|
||||||
|
"englishDescription": "Choose a color to create a theme for your card game.",
|
||||||
|
"description": "Velg en farge for å skape et tema over kortspillet ditt."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"englishLabel": "Card Back",
|
||||||
|
"label": "Kortbaksiden",
|
||||||
|
"englishDescription": "Use a custom back for your cards.",
|
||||||
|
"description": "Bruk en tilpasset kortbakside for kortene dine."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"englishLabel": "Localization",
|
"englishLabel": "Localization",
|
||||||
"label": "Oversettelser",
|
"label": "Oversettelser",
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
{
|
|
||||||
"semantics": [
|
|
||||||
{
|
|
||||||
"label": "Cards",
|
|
||||||
"entity": "card",
|
|
||||||
"field": {
|
|
||||||
"label": "Card",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"label": "Image"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Matching Image",
|
|
||||||
"description": "An optional image to match against instead of using two cards with the same image."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Description",
|
|
||||||
"description": "An optional short text that will pop up once the two matching cards are found."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Behavioural settings",
|
|
||||||
"description": "These options will let you control how the game behaves.",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"label": "Position the cards in a square",
|
|
||||||
"description": "Will try to match the number of columns and rows when laying out the cards. Afterward, the cards will be scaled to fit the container."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Number of cards to use",
|
|
||||||
"description": "Setting this to a number greater than 2 will make the game pick random cards from the list of cards."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Add button for retrying when the game is over"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Localization",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"label": "Card turns text",
|
|
||||||
"default": "Card turns"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Time spent text",
|
|
||||||
"default": "Time spent"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Feedback text",
|
|
||||||
"default": "Good work!"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Try again button text",
|
|
||||||
"default": "Try again?"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -36,5 +36,12 @@
|
||||||
"majorVersion": 0,
|
"majorVersion": 0,
|
||||||
"minorVersion": 4
|
"minorVersion": 4
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"editorDependencies": [
|
||||||
|
{
|
||||||
|
"machineName": "H5PEditor.ColorSelector",
|
||||||
|
"majorVersion": 1,
|
||||||
|
"minorVersion": 2
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
display: inline-block !important;
|
display: inline-block !important;
|
||||||
margin: auto !important;
|
margin: auto !important;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.h5p-memory-game .h5p-memory-wrap {
|
.h5p-memory-game .h5p-memory-wrap {
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -55,6 +56,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: #cfcfcf;
|
background: #cfcfcf;
|
||||||
|
background-size: cover;
|
||||||
border: 2px solid #d0d0d0;
|
border: 2px solid #d0d0d0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
|
@ -75,14 +77,30 @@
|
||||||
.h5p-memory-game .h5p-memory-card .h5p-front {
|
.h5p-memory-game .h5p-memory-card .h5p-front {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
color: #909090;
|
||||||
}
|
}
|
||||||
.h5p-memory-game .h5p-memory-card .h5p-front:hover {
|
.h5p-memory-game .h5p-memory-card .h5p-front:before,
|
||||||
background: #dfdfdf;
|
.h5p-memory-game .h5p-memory-card .h5p-back:before {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #fff;
|
||||||
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.h5p-memory-game .h5p-memory-card .h5p-front:before {
|
.h5p-memory-game.h5p-invert-shades .h5p-memory-card .h5p-front:before,
|
||||||
|
.h5p-memory-game.h5p-invert-shades .h5p-memory-card .h5p-back:before {
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5p-memory-game .h5p-memory-card .h5p-front:hover:before {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.h5p-memory-game .h5p-memory-card .h5p-front > span:before {
|
||||||
|
position: relative;
|
||||||
content: "?";
|
content: "?";
|
||||||
font-size: 3.75em;
|
font-size: 3.75em;
|
||||||
color: #909090;
|
|
||||||
line-height: 1.67em;
|
line-height: 1.67em;
|
||||||
}
|
}
|
||||||
.h5p-memory-game .h5p-memory-card .h5p-front:after {
|
.h5p-memory-game .h5p-memory-card .h5p-front:after {
|
||||||
|
@ -102,13 +120,16 @@
|
||||||
.h5p-memory-game .h5p-memory-card .h5p-back {
|
.h5p-memory-game .h5p-memory-card .h5p-back {
|
||||||
line-height: 5.9em;
|
line-height: 5.9em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
-webkit-transform: rotateY(-180deg);
|
-webkit-transform: rotateY(-180deg);
|
||||||
-moz-transform: rotateY(-180deg);
|
-moz-transform: rotateY(-180deg);
|
||||||
transform: rotateY(-180deg);
|
transform: rotateY(-180deg);
|
||||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
|
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
|
||||||
-ms-transform: scale(0,1.1);
|
-ms-transform: scale(0,1.1);
|
||||||
}
|
}
|
||||||
|
.h5p-memory-game .h5p-memory-card .h5p-back:before {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
.h5p-memory-game .h5p-memory-card.h5p-flipped .h5p-back {
|
.h5p-memory-game .h5p-memory-card.h5p-flipped .h5p-back {
|
||||||
-webkit-transform: rotateY(0deg);
|
-webkit-transform: rotateY(0deg);
|
||||||
-moz-transform: rotateY(0deg);
|
-moz-transform: rotateY(0deg);
|
||||||
|
@ -124,6 +145,8 @@
|
||||||
}
|
}
|
||||||
.h5p-memory-game .h5p-memory-card.h5p-matched {
|
.h5p-memory-game .h5p-memory-card.h5p-matched {
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
.h5p-memory-game .h5p-memory-card.h5p-matched img {
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -247,21 +247,30 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
|
||||||
return cardsToUse;
|
return cardsToUse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var cardStyles, invertShades;
|
||||||
|
if (parameters.lookNFeel) {
|
||||||
|
// If the contrast between the chosen color and white is too low we invert the shades to create good contrast
|
||||||
|
invertShades = (parameters.lookNFeel.themeColor &&
|
||||||
|
getContrast(parameters.lookNFeel.themeColor) < 1.7 ? -1 : 1);
|
||||||
|
var backImage = (parameters.lookNFeel.cardBack ? H5P.getPath(parameters.lookNFeel.cardBack.path, id) : null);
|
||||||
|
cardStyles = MemoryGame.Card.determineStyles(parameters.lookNFeel.themeColor, invertShades, backImage);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize cards.
|
// Initialize cards.
|
||||||
var cardsToUse = getCardsToUse();
|
var cardsToUse = getCardsToUse();
|
||||||
for (var i = 0; i < cardsToUse.length; i++) {
|
for (var i = 0; i < cardsToUse.length; i++) {
|
||||||
var cardParams = cardsToUse[i];
|
var cardParams = cardsToUse[i];
|
||||||
if (MemoryGame.Card.isValid(cardParams)) {
|
if (MemoryGame.Card.isValid(cardParams)) {
|
||||||
// Create first card
|
// Create first card
|
||||||
var cardTwo, cardOne = new MemoryGame.Card(cardParams.image, id, cardParams.description);
|
var cardTwo, cardOne = new MemoryGame.Card(cardParams.image, id, cardParams.description, cardStyles);
|
||||||
|
|
||||||
if (MemoryGame.Card.hasTwoImages(cardParams)) {
|
if (MemoryGame.Card.hasTwoImages(cardParams)) {
|
||||||
// Use matching image for card two
|
// Use matching image for card two
|
||||||
cardTwo = new MemoryGame.Card(cardParams.match, id, cardParams.description);
|
cardTwo = new MemoryGame.Card(cardParams.match, id, cardParams.description, cardStyles);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Add two cards with the same image
|
// Add two cards with the same image
|
||||||
cardTwo = new MemoryGame.Card(cardParams.image, id, cardParams.description);
|
cardTwo = new MemoryGame.Card(cardParams.image, id, cardParams.description, cardStyles);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add cards to card list for shuffeling
|
// Add cards to card list for shuffeling
|
||||||
|
@ -280,6 +289,9 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
|
||||||
this.triggerXAPI('attempted');
|
this.triggerXAPI('attempted');
|
||||||
// TODO: Only create on first attach!
|
// TODO: Only create on first attach!
|
||||||
$wrapper = $container.addClass('h5p-memory-game').html('');
|
$wrapper = $container.addClass('h5p-memory-game').html('');
|
||||||
|
if (invertShades === -1) {
|
||||||
|
$container.addClass('h5p-invert-shades');
|
||||||
|
}
|
||||||
|
|
||||||
// Add cards to list
|
// Add cards to list
|
||||||
var $list = $('<ul/>');
|
var $list = $('<ul/>');
|
||||||
|
@ -380,5 +392,18 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
|
||||||
MemoryGame.prototype = Object.create(EventDispatcher.prototype);
|
MemoryGame.prototype = Object.create(EventDispatcher.prototype);
|
||||||
MemoryGame.prototype.constructor = MemoryGame;
|
MemoryGame.prototype.constructor = MemoryGame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine color contrast level compared to white(#fff)
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} color hex code
|
||||||
|
* @return {number} From 1 to Infinity.
|
||||||
|
*/
|
||||||
|
var getContrast = function (color) {
|
||||||
|
return 255 / ((parseInt(color.substr(1, 2), 16) * 299 +
|
||||||
|
parseInt(color.substr(3, 2), 16) * 587 +
|
||||||
|
parseInt(color.substr(5, 2), 16) * 144) / 1000);
|
||||||
|
};
|
||||||
|
|
||||||
return MemoryGame;
|
return MemoryGame;
|
||||||
})(H5P.EventDispatcher, H5P.jQuery);
|
})(H5P.EventDispatcher, H5P.jQuery);
|
||||||
|
|
|
@ -75,6 +75,38 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "lookNFeel",
|
||||||
|
"type": "group",
|
||||||
|
"label": "Look and feel",
|
||||||
|
"importance": "low",
|
||||||
|
"description": "Control the visuals of the game.",
|
||||||
|
"optional": true,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "themeColor",
|
||||||
|
"type": "text",
|
||||||
|
"label": "Theme Color",
|
||||||
|
"importance": "low",
|
||||||
|
"description": "Choose a color to create a theme for your card game.",
|
||||||
|
"optional": true,
|
||||||
|
"default": "#909090",
|
||||||
|
"widget": "colorSelector",
|
||||||
|
"spectrum": {
|
||||||
|
"showInput": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cardBack",
|
||||||
|
"type": "image",
|
||||||
|
"label": "Card Back",
|
||||||
|
"importance": "low",
|
||||||
|
"optional": true,
|
||||||
|
"description": "Use a custom back for your cards.",
|
||||||
|
"ratio": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Localization",
|
"label": "Localization",
|
||||||
"importance": "low",
|
"importance": "low",
|
||||||
|
|
Loading…
Reference in New Issue