La problématique

Beaucoup n’ont pas encore franchi le pas d’utiliser les sous-titres de Storyline et continuent de les intégrer à la main afin de garder le contrôle sur leur mise en forme. Une solution qui peut parfois (souvent), s’avérer pénible, qu’on les gère sur la scène principale ou sur un calque (à prendre en compte dans les activités qui utilisent elles-mêmes des calques) .

Toutefois, le gain de temps pour le sous-titrage avec l’outil de Storyline est tel qu’il serait dommage de s’en priver. Aussi, ai-je développé ce script relativement facile à mettre en place afin de personnaliser ces sous-titres.

Le concept

On peut considérer deux façons d’envisager la résolution de ce problème :

  1. Soit on modifie la mise en forme de l’objet des sous-titres ;
  2. Soit on récupère le texte des sous-titres pour l’afficher dans un bloc de texte dans le module.

Dans la première version de ce script, j’étais parti sur la première solution qui me semblait plus simple puisqu’il suffisait de modifier du CSS, mais cela impliquait de connaître le CSS et de faire les modifications à la main dans le code. Puis j’ai tenté de récupérer les propriétés d’un objet modèle du contenu afin que les principaux paramètres puis être défini via l’interface de Storyline mais avec quelques limitations comme les dégradés (bien qu’avec un certain nombre de lignes de code supplémentaires, ce devait être possible) ou les animations.

Il m’est alors apparu beaucoup plus pratique de récupérer le texte du sous-titre dans une variable afin de l’afficher dans bloc de texte de Storyline. Ainsi, tous les paramètres de l’objet, animations comprises sont conservés. Il n’y a qu’une petite limitation par rapport au gras et l’italique qui seront « simulés », c’est à dire que ce ne sera pas le vrai gras et italique typographique de la police utilisée (ce qui ne me semblait pas critique pour des sous-titres). La raison étant que chaque police utilisée dans un module n’embarque que les caractères nécessaires, il faudrait donc avoir un bloc de texte avec l’ensemble de l’alphabet (minuscules et majuscules), les accents et les chiffres en gras, en italique et en gras italique… Peut-être dans une prochaine version s’il s’agit d’une demande indispensable.

Le résultat

Voici donc à quoi peuvent ressembler ces sous-titres :

Écran d’un module Storyline avec des sous-titres personnalisés

Il est possible d’animer le bloc de texte en entrée et en sortie ; on peut également choisir si le bloc doit se masquer automatiquement lorsque le sous-titre se vide (comme à la fin de la voix-off) ou bien à la demande de l’utilisateur lors du clic sur le bouton idoine.

Les fichiers sources (archive 7zip, 265 ko) :

Avertissements

Ce script va modifier certains éléments du contenu, pensez à bien tester votre module afin de vous assurer que le script ne génère pas de bugs (normalement, non, mais il peut y avoir des configurations particulières auxquelles je n’ai pas pensé).

Le script étant contenu dans un objet web, il ne fonctionnera pas (sans aide) en local. Toutefois, grâce au projet Laragon qui va créer un serveur local (beaucoup plus facilement que EsayPHP par exemple), il est possible de le tester assez simplement.

Pour cela, téléchargez la version portable puis décompressez là. Pour tester votre module, il vous suffit de copier son export (web, scorm, etc) dans le dossier « www », puis exécutez le fichier « laragon.exe » (Windows devrait vous demander quelques autorisations à accepter), cliquez ensuite sur le bouton « Démarrer » en bas à gauche. Enfin, cliquez sur le bouton « Web » qui vous ouvrira une page avec la liste des fichiers de votre export, il ne vous restera qu’à cliquer sur le fichier « story.html ».

Le paramétrage du module

Les variables

Liste des variables nécessaires pour l’exécution du script

Pour ce script, nous aurons besoin de 3 variables :

  1. ccDisplay : qui va nous permettre d’afficher/masquer le bloc de sous-titre automatiquement si cette option a été choisie ;
  2. ccOptions : cette variable va stocker les options qui seront utilisées par le script ;
  3. ccText : c’est la variable qui va stocker le sous-titre et l’afficher dans le bloc défini.

Le masque

Les objets

Les noms des objets et des états sont arbitraires, si vous choisissez de les changer, soyez attentifs lors du paramétrage des déclencheurs.

Le script
Fenêtre des paramètres de l’objet web pointant vers le script

Afin d’optimiser la gestion du code (pour qu’il ne soit pas recopié autant de fois qu’il y a de pages dans le module), le code sera intégré à partir d’un « objet web » que nous faisons pointer sur le dossier « custom_closed-caption » de l’archive téléchargeable et plaçons en dehors de la scène (pour de ne pas gêner l’intégration du module).

Le bouton des sous-titres

Ce bouton n’est pas obligatoire, vous pouvez utiliser celui du lecteur de Storyline, mais quitte à avoir des sous-titres personnalisés, autant personnaliser également le bouton pour les afficher.

Les 3 états du bouton des sous-titres

Commençons par créer une forme rectangle que nous nommerons « btn openCC » avec 3 états natifs : normal, survol et sélectionné. J’ai rajouté les pictos dans ses états. Il n’y a pas de picto dans l’état survolé afin qu’il conserve le picto de l’état dans lequel il se trouvait avant le survol. À l’état normal, les sous-titres sont masqué, à l’état sélectionné, ils sont affichés.

Pour l’accessibilité, nous décochons l’option du bouton « L’objet est visible par les outils d’acccessibilité » car les apprenants utilisant un lecteur d’écran n’auront pas besoin des sous-titres.

Le bloc des sous-titres
Les 3 états du bloc des sous-titres

Celui-ci est un peu complexe bien qu’il ne contienne que 3 états également.

La complexité est due aux animations possibles sur le bloc qu’il va falloir gérer finement afin qu’elles ne se déclenchent pas intempestivement.

Prenons donc le cas le plus compliqué avec une animation en entrée et en sortie.

Commençons tout d’abord par créer une forme rectangle que nous nommons « CC », avec les dimensions et positions finales de notre bloc de sous-titres, dont nous supprimons le contour et définissons le fond avec une transparence de 100 %. Cet objet recevra notre « vrai » bloc de sous-titre.

Ajoutons lui deux nouveaux états personnalisés : « open » et « alreadyOpen », l’un pour l’ouverture lors de l’activation des sous-titres et l’autre pour l’affichage en entrée de page et éviter ainsi la répétition de l’animation d’ouverture (si on a appliqué une animation).

Créons à présent notre bloc qui accueillera les sous-titres, dans mon exemple, un rectangle bleu, du texte blanc, une animation « Balayer » en entrée et une animation « Flottant vers le bas » en sortie. Nous y insérons également la variable des sous-titres « ccText ». Il devrait normalement avoir les mêmes dimensions et coordonnées que le précédent rectangle transparent (« CC »).

Afin que le script le retrouve, nous lui ajoutons un texte alternatif spécifique : « closed caption », dans les options d’accessibilité (ctrl+majuscule+entrée).

À présent, il faut le copier et venir le coller (ctrl+alt+D) dans l’état « open » de notre rectangle transparent (« CC »).

Revenons sur notre rectangle bleu et supprimons l’animation d’entrée, puis coupons-le et collons-le dans l’état « alreadyOpen » de notre rectangle transparent (il n’aura donc pas d’animation d’entrée à jouer lors de l’affichage de cet état).

Afin que le texte alternatif de notre rectangle bleu ne soit pas lu par les lecteurs d’écran, nous décochons l’option « L’objet est visible par les outils d’acccessibilité » de notre rectangle transparent « CC ».

Si vous ne souhaitez pas d’animation, il suffit de créer uniquement le rectangle bleu (sans oublier le texte alternatif) et de le paramétrer en masqué comme état par défaut ; son état normal sera alors sa version « ouverte ». Le texte alternatif de notre rectangle ne sera pas lu puisque dans ce cas là, l’objet est masqué et ne sera jamais affiché (puisque le bouton d’affichage des sous-titres n’est pas vu par les lecteurs d’écran).

Nos objets sont à présents créés, il nous reste à paramétrer les déclencheurs afin qu’ils réagissent correctement.

Les déclencheurs

Les déclencheurs du masque principal

S’ils peuvent faire peur de prime abord par leur nombre, ils sont en réalité assez simple à comprendre.

Prenons les dans l’ordre :

  1. Il faut tout d’abord définir les options de notre script (le nom de notre bloc de sous-titre, ici: « closed captions » et si les sous-titre doivent se masquer automatiquement) :
var player = GetPlayer();
var options = {
		id : "closed captions",
		autoDisplay : false
	}
player.SetVar("ccOptions", JSON.stringify(options));
  1. Le deuxième et troisième déclencheur, en entrée de diapositive, vont afficher ou masquer les sous-titres et l’état correspondant du bouton de sous-titres lorsqu’on arrive sur une nouvelle diapositive, en fonction de la valeur des variables « PlayerDisplayCaptions » (la variable du lecteur qui gère l’affichage des sous-titres) et « ccDisplay » (notre variable qui nous permet de gérer l’affichage/masquage automatique des sous-titres) ;
  2. Nous avons ensuite des déclencheurs de variables qui vont réaliser des actions lorsque les variables changent de valeur :
    1. Lorsque la variable « ccDisplay » passe à « Vrai », on affiche les sous-titres, à « Faux », on les masque, à la condition que « Player.DisplayCaptions » soit vrai également afin de ne pas les afficher alors que l’activation des sous-titres n’était pas réclamée par l’utilisateur ;
    2. Lorsque la variable « Player.DisplayCaptions » passe à « Faux », le bouton « btn openCC » et le rectangle « CC » reprennent leur état normal (on masque les sous-titres), à « Vrai », le rectangle « CC » passe à l’état « open » ;
  3. Enfin, lorsqu’on clique sur le bouton « btn openCC », la variable « Player.DisplayCopations » est permutés, c’est à dire qu’elle passe alternativement de « Vrai » à « Faux ».

Le paramétrage du calque est ainsi terminé ; pour résumer, nous avons besoin d’un bouton (btn openCC) pour afficher/masquer les sous-titres (qui peut-être celui du lecteur) en basculant la variable « Player.DisplayCaptions » entre « Vrai » et « Faux », ce qui déclenchera ensuite le changement d’état du bloc de sous-titres (« CC »). Comme nous aurons probablement de la voix-off sur plusieurs pages à la suite, nous avons également un déclencheur à l’entrée sur la diapositive afin que l’utilisateur n’ai pas à rouvrir ses sous-titres sur chaque diapositive. Enfin, à cause de l’option qui permet d’afficher/masquer automatiquement les sous-titres, nous ajoutons des déclencheurs selon la valeur de « ccDisplay ».

Passons à présent aux options personnalisables du script (uniquement deux).

Les options du script

Le script pourra être paramétré grâce à deux options pour rester simple :

var options = {
		id : "closed captions",
		autoDisplay : false
	}
  1. id est le texte alternatif choisi pour le bloc des sous-titres (collé dans le bloc « CC » si il est animé) ;
  2. Enfin, autoDisplay précise si les sous-titres doivent être affiché/masqué automatiquement (deux valeurs possibles : true ou false)

À présent le script devrait fonctionner et vous permettre d’afficher vos sous-titres.

Dans la partie suivante j’explique plus précisément le fonctionnement du script.

Explication du script

Le script en lui-même est relativement court et simple, mais comprendre le comportement des différents éléments était plus complexe.

var customCC = function(){
	var player = GetPlayer();
	var options = {
		id : "closed captions",
		autoDisplay : false
	}

Le script commence par créer la fonction « customCC » afin de pouvoir l’optimiser ultérieurement, c’est pour le moment, tout à fait non nécessaire à son fonctionnement.

Au début du script, il initialise à nouveau l’API du lecteur car il en aura besoin pour récupérer et définir des variables.

Viennent ensuite les 2 options du script : id et autoDisplay.

//Get the closed captions container
	var ccContainer = document.querySelector(".caption");
	//Hide the original closed captions
	ccContainer.style.display = "none";

Il récupère le conteneur des sous-titres de Storyline afin de le masquer (puisqu’il va être remplacé par notre propre objet).

//Get the base layer and the new box recieving the closed caption
	var baseLayer = document.querySelector('.base-layer');
	var ccBox = document.querySelector('[data-acc-text="'+options.id+'"]');

Le script récupère ensuite le calque de base et notre bloc de sous-titres personnalisés afin de les modifier.

//Change the depth of the base layer objects to be under any other layer (because the base layer has no more z-index)
	baseLayer.style.removeProperty("z-index");
	for(var i = 0; i < baseLayer.children.length; i++){
		if(baseLayer.children[i].style.zIndex >= 0){
			baseLayer.children[i].style.zIndex = -1000+parseInt(baseLayer.children[i].style.zIndex);
		}
	}

Tout d’abord, afin de mieux appréhender la problématique, voici une courte explication de la gestion de la profondeur des objets dans Storyline :

  1. Calque de base : profondeur 0 par rapport à la page
    1. Objet 1 : profondeur 0 par rapport au calque de base
    2. Objet 2 : profondeur 1 par rapport au calque de base
  2. Calque 1 (affiché en premier, n’a aucun rapport avec l’ordre des calques dans Storyline) : profondeur 1 par rapport à la page
    1. Objet 1 : profondeur 0 par rapport au calque 1
    2. Objet 2 : profondeur 1 par rapport au calque 1
  3. Calque 2 (affiché en dernier) : profondeur 2 par rapport à la page
    1. Objet 1 : profondeur 0 par rapport au calque 2
    2. Objet 2 : profondeur 1 par rapport au calque 2

Ici le code supprime le z-index du calque de base afin que la profondeur des objets ne soit plus circonscrite à son niveau (pour que le bloc de sous-titre puisse s’afficher au dessus de tous les objets, calque compris). Le problème étant que le reste des objets du calque de base vont également passer par dessus le reste. Par exemple, le troisième objet de la page(z-index : 2) passerait au-dessus du calque 1 (z-index : 1).

Afin de corriger ce problème, le script boucle sur tous les objets du calque de base afin de leur attribuer un z-index négatif tout en conservant leur ordre d’affichage (z-index : -999, z-index : -998, etc) à la condition que leur z-index d’origine soit supérieur à zéro (pour qu’il ne répète pas l’opération si on clique sur le bouton « rejouer la diapositive ») .

Pourquoi jouer avec les z-index et ne pas tout simplement déplacer le bloc de sous-titres dans le DOM ? Parce que Storyline va rechercher tous les objets de la diapositive lorsqu’il la quitte (probablement pour les détruire), et s’il ne les retrouve pas, une erreur sera renvoyée, ce qui bloquera totalement le module.

//Set the CC box above all others
	if(ccBox.parentNode.classList.contains('group')){
		ccBox.parentNode.parentNode.style.zIndex = 1000;
	}
	else{
		ccBox.style.zIndex = 1000;
	}

Le script va à présent placer notre bloc de sous-titres au dessus de tous les autres éléments de la page (ou presque) en lui attribuant un z-index de 1000. Selon que notre bloc est animé ou non, la méthode de sélection diffère.

//Set the depth of the slide background under the base layer
	document.querySelector('.slide-transition-container > .slide > .acc-blocker').style.zIndex = -1001;
	//Hack for webkit browser which seems to have difficulties with z-index and the cursor events
	var backgroundContainer = document.createElement('div');
	var background = document.querySelector('.slide-transition-container > .slide > svg');
	background.parentNode.insertBefore(backgroundContainer, background);
	backgroundContainer.appendChild(background);
	backgroundContainer.style.zIndex = -1001;
	backgroundContainer.style.position = "relative";

Une petite correction de z-index sur les éléments qui se trouvaient en dessous du calque de base pour les y remettre (notamment l’arrière plan de la diapositive) avec un petit hack pour webkit (pour une fois) qui semble avoir un soucis avec le z-index et les événements du curseur pour les SVG (le fond passait bien en dessous, mais il bloquait les actions du curseur sur les objets du calque de base). J’ai donc dû placer l’arrière plan de la diapositive dans une <div>.

//Function to insert the closed captions text into the variable
	var copyText = function(c){
		if(c.querySelector('p')){
			player.SetVar("ccText", c.querySelector('p').innerHTML);
			
			//Auto display
			if(options.autoDisplay === true)player.SetVar("ccDisplay", true);
		}
		else{
			player.SetVar("ccText", "");
			//Auto display
			if(options.autoDisplay === true)player.SetVar("ccDisplay", false);
		}
		
		
	}
	copyText(ccContainer);

À présent, la fonction principale du script qui sera appelée à chaque changement dans la zone de sous-titres du lecteur et qui va en récupérer le texte si elle détecte la présence d’un paragraphe (<p>), sinon, elle vide la variable. Selon que l’option « autoDisplay » est activée ou non, la fonction basculera la variable « ccDisplay » lorsque les sous-titres se remplissent ou se vident. Puis le script lance la fonction une première fois dans l’éventualité où la voix-off démarrerait dès le début de la diapositive et qu’il n’ai pas remarqué le changement dans la zone de sous-titres.

//Function to update the closed captions
	var mutationObserver = new MutationObserver(function(mutations) {
		copyText(mutations[0].target);
	});

	mutationObserver.observe(ccContainer, {subtree: true, childList: true});	
}
customCC();

Enfin, le script va créer un observateur des mutations du contenu de notre zone de sous-titres du lecteur afin d’exécuter la fonction « copyText » dès qu’un changement (nouveau texte ou fin du sous-titre) est observé.

Puis, tout à la fin, la fonction est exécutée.

Le code a été testé avec IE11, Edge, Chrome et Firefox, mais n’hésitez pas à me remonter tout dysfonctionnement que vous pourriez rencontrer ou si certains points ne sont pas assez explicites en me contactant sur linkedin : http://www.linkedin.com/in/gregoryfauchille

Merci à Stackoverflow sans qui tout cela n’aurait pas été possible.