vendredi 25 novembre 2011

Coverflow QML 2D

Un caroussel avec de la profondeur obéissant au doigt et à l'oeil procure une expérience utilisateur agréable. Avec QML il est relativement facile d'implémenter un caroussel d'images.



Côté réalisation, c'est très accessible :

  • Créer un projet Qt Quick
  • Importer dans votre projet un jeu d'images
  • Dimensionner votre rectangle de base avec un fond noir :
Rectangle {
    width: 360
    height: 640
    color: "black" 
  • A l"intérieur de ce dernir, créer votre nouvel élément visuel (ou Item) que j'ai identifié comme suit :
Item {
   id: coverFlow
   anchors.fill: parent 
  • Ajouter lui un model de données (dans mon cas j'ai choisi un modèle statique mais il est tout à fait possible d'utiliser un modèle dynamique)
ListModel {
id: appModel
ListElement { name:"Photo 0"; description:"Ceci est la photo n°0"; cover: "0.jpg"}
ListElement { name:"Photo 1"; description:"Ceci est la photo n°1"; cover: "1.jpg"}
ListElement { name:"Photo 2"; description:"Ceci est la photo n°2"; cover: "2.jpg"}
ListElement { name:"Photo 3"; description:"Ceci est la photo n°3"; cover: "3.jpg"}
ListElement { name:"Photo 4"; description:"Ceci est la photo n°4"; cover: "4.jpg"}
ListElement { name:"Photo 5"; description:"Ceci est la photo n°5"; cover: "5.jpg"}
ListElement { name:"Photo 6"; description:"Ceci est la photo n°6"; cover: "6.jpg"}
ListElement { name:"Photo 7"; description:"Ceci est la photo n°7"; cover: "7.jpg"}
ListElement { name:"Photo 8"; description:"Ceci est la photo n°8"; cover: "8.jpg"}
ListElement { name:"Photo 9"; description:"Ceci est la photo n°9"; cover: "9.jpg"}
ListElement { name:"Photo 10"; description:"Ceci est la photo n°10"; cover:"10.jpg"}
}

  • Ensuite il faut créer l'élément clef du caroussel :  "PathView"
PathView{
   id:view
   width: 360
   height: 360
   anchors.horizontalCenter: parent.horizontalCenter
   anchors.verticalCenter: parent.verticalCenter
   model:appModel
   delegate:appDelegate
   path: Path{
       startX: 0
       startY: 180
       PathAttribute { name: "coverScale"; value: 0.5 }
       PathAttribute { name: "z"; value: 0 }
       PathAttribute { name: "coverOpacity"; value:  0.1 }
       PathLine{x:180 ;y: 180}
       PathAttribute { name: "coverScale"; value: 1 }
       PathAttribute { name: "z"; value: 100 }
       PathAttribute { name: "coverOpacity"; value: 1 }
       PathLine{x:380;y: 180}
       PathAttribute { name: "coverScale"; value: 0.5 }
       PathAttribute { name: "z"; value: 0 }
       PathAttribute { name: "coverOpacity"; value:  0.1 }
   }
}

Donc je définis une zone de 360px par 360px  dont les caractéristiques sont les suivantes :
  1. Le modèle associé est ma liste déléments précédemment définie : ce sont ces éléments qui vont être affichés sur le "path"
  2. Un délégué en charge de dessiner chaque élément de mon modèle
  3. Un chemin ("path") rectiligne le long de l'axe des Y avec une variation de taille d'image (facteur "coverScale"), une variation de la profondeur (facteur "z") et une variation de la transparence d'image (facteur "coverOpacity). Le chemin est construit de telle manière à mettre en avant l'image courante au centre et à attenuer les images adjacentes. Plus on s'écarte du centre et plus l'opacitié et la réduction d'image augmente

Le délégué en charge de dessiner l'image est basique :

Component{
   id: appDelegate

   Item {
      z:PathView.z
      scale: PathView.coverScale
      height: imageCover.height
      width: imageCover.width
      opacity: PathView.coverOpacity
      Image {
          id: imageCover
          smooth: true
          width: 225
          height: 225
          source: cover
      }
  }
}

Chaque image possède des propriétés "bindées" aux attributs variables du path : à savoir la profondeur (z), la taille (scale) et la transparence (opacity). Par ailleurs la source de l'image pointe sur le champ "cover" (c'est à dire le fichier image) défini dans le modèle.

Dernier point l'affichage du titre et de la description de l'image :
// Title of current cover
Text {
    id:title
    text: appModel.get(view.currentIndex).name
      color: "white"
    font.bold: true
    font.pointSize: 14
    anchors.horizontalCenter: parent.horizontalCenter
    anchors.top: view.bottom
}

// Description of current cover
Text {
    id:description
    text: appModel.get(view.currentIndex).description
        color: "white"
    font.pointSize: 10
    anchors.horizontalCenter: parent.horizontalCenter
    anchors.top: title.bottom
}

A noter le binding des propriétés "text" avec l'élément courant du modèle.

Alors il n'est pas beau mon coverflow ;-)

Le code source complet est diponible ici

Aucun commentaire:

Enregistrer un commentaire