diff --git a/README.md b/README.md index 3f4fdaa..5a1ec97 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Lorsque la lecture d'une vidéo est achevée, elle disparaît pour faire appara Il ne s'agit donc pas d'un carrousel servant à faire défiler horizontalement des balises vidéos. Mais on peut très bien utiliser ce type de carrousel en parallèle pour naviguer entre les vidéos. +Un exemple d'utilisation avec [Flickity](https://www.npmjs.com/package/flickity) est fourni. ## Les grandes lignes de l'algorithme diff --git a/package-lock.json b/package-lock.json index 7d458ef..3568a1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1296,6 +1296,11 @@ "minimalistic-assert": "^1.0.0" } }, + "desandro-matches-selector": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/desandro-matches-selector/-/desandro-matches-selector-2.0.2.tgz", + "integrity": "sha1-cXvu1NwT59jzdi9wem1YpndCGOE=" + }, "destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", @@ -1515,6 +1520,11 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, + "ev-emitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-1.1.1.tgz", + "integrity": "sha512-ipiDYhdQSCZ4hSbX4rMW+XzNKMD1prg/sTvoVmSLkuQ1MVlwjJQQA+sW8tMYR3BLUr9KjodFV4pvzunvRhd33Q==" + }, "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -1834,6 +1844,27 @@ "resolve-dir": "^1.0.1" } }, + "fizzy-ui-utils": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fizzy-ui-utils/-/fizzy-ui-utils-2.0.7.tgz", + "integrity": "sha512-CZXDVXQ1If3/r8s0T+v+qVeMshhfcuq0rqIFgJnrtd+Bu8GmDmqMjntjUePypVtjHXKJ6V4sw9zeyox34n9aCg==", + "requires": { + "desandro-matches-selector": "^2.0.0" + } + }, + "flickity": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/flickity/-/flickity-2.2.2.tgz", + "integrity": "sha512-yiPMuP8tw/zN7ARgeSLZNvzK11GkzI2mp/zlYBsyttguSCROAqxj6wiN2sSfPfW3xMG3hcUHxWUXNQMlk/wYcg==", + "requires": { + "desandro-matches-selector": "^2.0.0", + "ev-emitter": "^1.1.1", + "fizzy-ui-utils": "^2.0.7", + "get-size": "^2.0.3", + "unidragger": "^2.3.0", + "unipointer": "^2.3.0" + } + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -1935,6 +1966,11 @@ "has-symbols": "^1.0.1" } }, + "get-size": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/get-size/-/get-size-2.0.3.tgz", + "integrity": "sha512-lXNzT/h/dTjTxRbm9BXb+SGxxzkm97h/PCIKtlN/CBCxxmkkIVV21udumMS93MuVTDX583gqc94v3RjuHmI+2Q==" + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -4575,6 +4611,14 @@ "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", "dev": true }, + "unidragger": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unidragger/-/unidragger-2.3.1.tgz", + "integrity": "sha512-u+IgG7AG0MXJTKcdzAIYxCm+W5FcnA9M28203Awl6jIcE3/+9OtEyUX4Wv64y7XNKEVRKPot52IV4V6x7FlF5Q==", + "requires": { + "unipointer": "^2.3.0" + } + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -4587,6 +4631,14 @@ "set-value": "^2.0.1" } }, + "unipointer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/unipointer/-/unipointer-2.3.0.tgz", + "integrity": "sha512-m85sAoELCZhogI1owtJV3Dva7GxkHk2lI7A0otw3o0OwCuC/Q9gi7ehddigEYIAYbhkqNdri+dU1QQkrcBvirQ==", + "requires": { + "ev-emitter": "^1.0.1" + } + }, "unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", diff --git a/package.json b/package.json index 9a75bbe..ffd9611 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vCarousel", - "version": "0.1.0", + "version": "0.2.0", "description": "Video carousel, a new video appears when the previous one finishes playing.", "main": "index.js", "scripts": { @@ -24,5 +24,8 @@ "webpack": "^4.46.0", "webpack-cli": "^3.3.12", "webpack-dev-server": "^3.11.2" + }, + "dependencies": { + "flickity": "^2.2.2" // dépendance pouvant être retirée si vous n'utilisez pas Flickity dans votre application } } diff --git a/public/CSS/flickity.min.css b/public/CSS/flickity.min.css new file mode 100644 index 0000000..18829b0 --- /dev/null +++ b/public/CSS/flickity.min.css @@ -0,0 +1,4 @@ +/*! Flickity v2.2.2 +https://flickity.metafizzy.co +---------------------------------------------- */ +.flickity-enabled{position:relative}.flickity-enabled:focus{outline:0}.flickity-viewport{overflow:hidden;position:relative;height:100%}.flickity-slider{position:absolute;width:100%;height:100%}.flickity-enabled.is-draggable{-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.flickity-enabled.is-draggable .flickity-viewport{cursor:move;cursor:-webkit-grab;cursor:grab}.flickity-enabled.is-draggable .flickity-viewport.is-pointer-down{cursor:-webkit-grabbing;cursor:grabbing}.flickity-button{position:absolute;background:hsla(0,0%,100%,.75);border:none;color:#333}.flickity-button:hover{background:#fff;cursor:pointer}.flickity-button:focus{outline:0;box-shadow:0 0 0 5px #19f}.flickity-button:active{opacity:.6}.flickity-button:disabled{opacity:.3;cursor:auto;pointer-events:none}.flickity-button-icon{fill:currentColor}.flickity-prev-next-button{top:50%;width:44px;height:44px;border-radius:50%;transform:translateY(-50%)}.flickity-prev-next-button.previous{left:10px}.flickity-prev-next-button.next{right:10px}.flickity-rtl .flickity-prev-next-button.previous{left:auto;right:10px}.flickity-rtl .flickity-prev-next-button.next{right:auto;left:10px}.flickity-prev-next-button .flickity-button-icon{position:absolute;left:20%;top:20%;width:60%;height:60%}.flickity-page-dots{position:absolute;width:100%;bottom:-25px;padding:0;margin:0;list-style:none;text-align:center;line-height:1}.flickity-rtl .flickity-page-dots{direction:rtl}.flickity-page-dots .dot{display:inline-block;width:10px;height:10px;margin:0 8px;background:#333;border-radius:50%;opacity:.25;cursor:pointer}.flickity-page-dots .dot.is-selected{opacity:1} \ No newline at end of file diff --git a/public/CSS/vcarousel.css b/public/CSS/vcarousel.css new file mode 100644 index 0000000..3a7b023 --- /dev/null +++ b/public/CSS/vcarousel.css @@ -0,0 +1,21 @@ +body +{ +text-align:center; +background-color:#C0D6D2; +} +#videos +{ +border:1px solid; +width:800px; +margin:auto; +padding:1em; +text-align:center; +} +#video figure +{ +padding-top:2em; +} +#videos video +{ +width:600px; +} \ No newline at end of file diff --git a/public/flickity.html b/public/flickity.html new file mode 100644 index 0000000..7774d8c --- /dev/null +++ b/public/flickity.html @@ -0,0 +1,48 @@ + + + + + + + + + + + vCarousel (démo avec Flickity) + + +
+

vCarousel

+

Dans cet exemple vCarousel est associé à un carrousel d'images plus classique gèré via flickity servant de menu pour choisir la première vidéo à visionner. Ensuite vCarousel enchaîne les vidéos et synchronise l'affichage de flickity.

+ +
+ +
Un clown funanbule !
+
+
+ +
Il faut forger pour devenir forgeron !
+
+
+ +
Drôle de circuit !
+
+ +
+ + + + \ No newline at end of file diff --git a/public/img/clownfunanbule.png b/public/img/clownfunanbule.png new file mode 100644 index 0000000..c445a53 Binary files /dev/null and b/public/img/clownfunanbule.png differ diff --git a/public/img/drole-de-circuit.png b/public/img/drole-de-circuit.png new file mode 100644 index 0000000..8a09842 Binary files /dev/null and b/public/img/drole-de-circuit.png differ diff --git a/public/img/forgeron.png b/public/img/forgeron.png new file mode 100644 index 0000000..14ad3da Binary files /dev/null and b/public/img/forgeron.png differ diff --git a/public/index.html b/public/index.html index c0eac90..7014eaa 100644 --- a/public/index.html +++ b/public/index.html @@ -2,43 +2,48 @@ - vCarousel (démo) - + + + + vCarousel (démo utilisation simple) -

vCarousel

-

Lorsqu'une vidéo se termine, la suivante la remplace, prête à être lancée.

-
- -
Un clown funanbule !
-
-
- -
Il faut forger pour devenir forgeron !
-
-
- -
Drôle de circuit !
-
- - +
+

vCarousel

+

Exemple d'utilisation simple : lorsqu'une vidéo se termine, la suivante la remplace, prête à être lancée. Voir un autre exemple d'utilisation où VCarousel est associé à un carrousel d'images.

+ +
+ +
Un clown funanbule !
+
+
+ +
Il faut forger pour devenir forgeron !
+
+
+ +
Drôle de circuit !
+
+ +
+ \ No newline at end of file diff --git a/src/example.ts b/src/example.ts index 93c5ba5..f22968c 100644 --- a/src/example.ts +++ b/src/example.ts @@ -1,15 +1,15 @@ -/// Exemple d'utilisation de vCarousel avec 3 vidéos +/// Exemple d'utilisation simple de vCarousel avec 3 vidéos import { vCarousel } from "./vCarousel"; try { - // Liste des id des contenants des vidéos à afficher : + // Liste des id des contenants des vidéos à faire tourner : const videosContainers=["vFunanbule", "vForgeron", "vCircuit"]; // Si un id est fourni par l'url, on affiche cette vidéo en premier : if(window.location.hash!==undefined && window.location.hash!=="") vCarousel(videosContainers, window.location.hash.replace("#","")); else - vCarousel(videosContainers);// le deuxième paramètre est facultatif sauf si on souhaite qu'une des vidéos soit affichée par défaut + vCarousel(videosContainers);// le deuxième paramètre est facultatif, sauf si on souhaite qu'une vidéo autre que la première soit affichée par défaut // Dans le cas où l'utilisateur clique pour sélectionner une vidéo à afficher, on actualise l'affichage : let selectLinks=document.querySelectorAll(".selectVideo"); diff --git a/src/exampleFlickity.ts b/src/exampleFlickity.ts new file mode 100644 index 0000000..880800a --- /dev/null +++ b/src/exampleFlickity.ts @@ -0,0 +1,76 @@ +/// Exemple d'utilisation de vCarousel avec 3 vidéos, proposées via un carrousel d'images Flickity +const Flickity = require("flickity"); +import { vCarousel } from "./vCarousel"; + +// Cette fonction sert à synchroniser si besoin l'image sélectionnée dans le carrousel Flickity avec le hash de l'url. +const selectFlickityForHash = (flickityCarousel:any, urlHash:string) : void => +{ + let nb=0, find=false; + // On parcourt les items du carrousel pour trouver celui contenant le lien vers ce hash. + for(let cel of flickityCarousel.cells) + { + let childrens = cel.element.childNodes; + for (let i = 0, c = childrens.length; i < c; i++) + { + if (childrens[i].nodeType === Node.ELEMENT_NODE) + { + if(childrens[i].hash!=undefined && childrens[i].hash===urlHash) + { + flickityCarousel.select(nb);// API Flickity : https://flickity.metafizzy.co/api.html + find=true; + break; + } + } + } + nb++; + } + if(find===false) + console.error("L'ancre fournie par l'url n'a pas été trouvée dans le carrousel Flickity."); +} + +try +{ + // Carrousel Flickity : + const imgCarousel = document.querySelector(".img-carousel"); + const flktyCarousel = new Flickity(imgCarousel, + { + // options : https://flickity.metafizzy.co/options.html + accessibility: true, + cellAlign: "center", + contain: true, + rightToLeft: true, + wrapAround: true, + }); + + // Liste des id des contenants des vidéos à faire tourner : + const videosContainers=["vFunanbule", "vForgeron", "vCircuit"]; + // Si un id est fourni par l'url, on affiche cette vidéo en premier : + if(window.location.hash!==undefined && window.location.hash!=="") + { + vCarousel(videosContainers, window.location.hash.replace("#","")); + selectFlickityForHash(flktyCarousel, location.hash); + } + else + vCarousel(videosContainers, ""); // dans cet exemple, on cache toutes les vidéos avant que l'utilisateur ne fasse son choix dans le carrousel d'images. + + // Lorsque l'utilisateur clique sur une des images pour sélectionner une vidéo à afficher, on actualise l'affichage : + let selectLinks=document.querySelectorAll(".selectVideo"); + for (let i = 0; i < selectLinks.length; i++) + { + let link=selectLinks[i]; + link.addEventListener("click", function(e) + { + vCarousel(videosContainers, link.hash.replace("#","")); + }); + } + // De même quand le hash change, on adapte l'item sélectionné de flickity + // Ceci permet de suivre le parcours des vidéos par vCarousel + window.addEventListener('hashchange', function() + { + selectFlickityForHash(flktyCarousel, location.hash); + }); +} +catch(e) +{ + console.error(e); +} \ No newline at end of file diff --git a/src/vCarousel.ts b/src/vCarousel.ts index ee134fc..dd471a1 100644 --- a/src/vCarousel.ts +++ b/src/vCarousel.ts @@ -1,9 +1,10 @@ +const Flickity = require("flickity"); + /// La fonction vCarousel reçoit un tableau d'ids des conteneurs HTML des vidéos à afficher successivement. /// Le tableau n'est pas dédoublonné, pour rester libre d'afficher plusieurs fois la même vidéo durant un tour du carrousel. /// On peut éventuellement fournir l'id du conteneur de la première vidéo à afficher lors de l'appel /// Ou encore indiqué qu'aucune ne doit être affichée sans action en passant une chaîne vide /// Par défaut la première vidéo de la liste sera affichée - export const vCarousel = (vContainers:string[], firstVideoId?:string) : void => { interface videoDOM diff --git a/webpack.config.js b/webpack.config.js index 241d11e..441c926 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,7 +6,8 @@ module.exports = devtool: "inline-source-map", entry: { - example: "./src/example.ts" + example: "./src/example.ts", + exampleFlickity: "./src/exampleFlickity.ts" }, output: {