Comment concevoir des mises en page réactives dans Framer

Utilisation d’objets clé-valeur pour créer des prototypes multi-périphériques.

Si vous travaillez assez longtemps avec les prototypes Framer, vous rencontrerez éventuellement le problème de savoir comment adapter la disposition d’un prototype pour gérer plusieurs périphériques et orientations. Plutôt que de créer un prototype distinct pour chaque situation - par exemple, un pour iPhone en orientation portrait et un autre pour iPad en paysage - il est possible de concevoir un prototype réactif qui adapte ses mesures en fonction de la situation. Cela nécessite une certaine configuration, mais le processus peut être grandement simplifié par une structure de données appelée objet.

"Objet" est un terme vague qui pourrait désigner beaucoup de choses. Ici, nous l’utilisons pour faire référence à une collection de paires clé-valeur. Celles-ci fonctionnent comme des variables: la touche vous permet de référencer la paire par un nom, tandis que la valeur contient les données qui lui sont associées.

Dans CoffeeScript, le langage utilisé pour créer les prototypes de Framer, vous pouvez construire un objet tel que:

objectName =
    valeur clé
    anotherKey: une autre valeur

(Notez que le signe égal suit le nom de l'objet, mais que deux points sont utilisés pour séparer les clés et les valeurs.)

Des hiérarchies plus avancées sont possibles avec des objets imbriqués, ce qui rend les choses intéressantes:

objectName =
    keySet1:
        valeur clé
        anotherKey: une autre valeur
    keySet2:
        valeur clé
        anotherKey: une autre valeur

Il est facile de voir comment cela peut se traduire par une solution à notre problème d’agencement:

dimensionnement =
    iphone:
        viewSidePadding: 20
    ipad:
        viewSidePadding: 40

Mais comment accédons-nous à ces valeurs? CoffeeScript propose deux méthodes: la notation par points et la notation par crochets. Pour la notation par points, nous commençons par le nom de l'objet, puis nous descendons dans la hiérarchie en séparant les références par des points:

impression dimensionnement.iphone.viewsidePadding

Avec la notation entre crochets, nous plaçons chaque référence entre crochets et guillemets:

taille d'impression ["iphone"] ["viewSidePadding"]

(Notez que le nom de l'objet n'est pas encapsulé dans quoi que ce soit.)

Les guillemets semblent un peu ennuyeux au début, mais en fait, ils sont inestimables. Ils nous permettent de construire les noms de nos clés via des expressions. Vous pouvez, par exemple, faire quelque chose comme ceci:

taille d'impression ["iphone"] ["view" + "SidePadding"]

Jusqu’à présent, cela n’est plus utile, mais vous pouvez commencer à comprendre comment ces références peuvent être fractionnées et reconstruites selon les besoins.

Il est également possible de mélanger et assortir ces notations comme bon vous semble:

taille d'impression ["iphone"]. viewSidePadding
print sizing.iphone ["viewSidePadding"]

Pour que cette approche de mise en page réactive fonctionne, nous avons besoin de trois choses:

  1. Une méthode pour déterminer le type de périphérique.
  2. Une méthode pour déterminer l'orientation du périphérique.
  3. Une méthode pour saisir la valeur qui correspond à celles-ci.

Par souci de brièveté, nous allons simplement examiner les premier et dernier éléments. (L'un des prototypes joints à la fin montrera le package complet.)

Créez un nouveau prototype dans Framer et ajoutez le code suivant:

# Caractéristiques
dimensionnement =
    viewTopPadding: 30
    iphone:
        viewSidePadding: 20
        articleMarge: 16
        Taille de l'article: 250
    ipad:
        viewSidePadding: 40
        articleMarge: 64
        Taille de l'article: 400

(Notez que certaines paires clé-valeur sont situées au plus haut niveau. Celles-ci sont communes à l'iPhone et à l'iPad et leurs valeurs ne changent pas avec les appareils.)

Ensuite, ajoutez une fonction pour détecter le type de périphérique. La manière dont vous allez procéder dépendra du lieu où vous souhaitez voir votre prototype, mais voici une méthode qui fonctionne dans Framer:

# obtenir le type d'appareil
checkDevice = (deviceType = "iphone") ->
    framerDevice = Framer.Device.deviceType
    deviceType = "iphone" si _.includes (framerDevice, "iphone")
    deviceType = "ipad" si _.includes (framerDevice, "ipad")
    retourner deviceType
Si votre prototype est conçu pour la prévisualisation sur des appareils mobiles, la détection de Screen.width peut s'avérer une méthode plus fiable.

Notre fonction checkDevice () renverra la chaîne «iphone» ou «ipad». Il suffit d'ajouter la clé dans une paire clé-valeur pour obtenir notre spécification numérique. Par exemple, cela va maintenant fonctionner:

deviceType = checkDevice ()
value = "viewSidePadding"
Taille d'impression [Type de périphérique] [Valeur]

Nous pouvons développer cela en une fonction d'utilisation générale qui accepte un nom de clé et renvoie la valeur appropriée pour le périphérique actuel. Vous pouvez utiliser le code ci-dessous, mais notez deux choses à ce sujet:

  • Les valeurs plus profondes dans la hiérarchie des objets remplaceront celles qui sont en haut. Si vous définissez une valeur de spécification multiplate-forme, il est préférable de ne pas répéter cette valeur au niveau du périphérique.
  • Si aucune valeur correspondante n'est trouvée, la fonction affiche un message d'erreur.
# spécification de contrôle
getSpec = (spec) ->
    if! spec affiche alors "Besoin de spécifier un nom de spécification".

    deviceType = checkDevice ()
    lookup = _.assign ({}, dimensionnement, dimensionnement [typeAppareil])
    resultat = _.get (recherche, spec)

    if! result alors print "Valeur introuvable pour la spécification nommée # {spec} sur # {deviceType}."

    résultat retourné

Avec ces fonctions en place, nous devons simplement appeler getSpec («notreNomClé»), où «notreNomClé» est le nom de clé de la valeur que nous voulons. Ainsi, getSpec («viewSidePadding») renverra 20 sur iPhone mais 40 sur iPad. getSpec («viewTopPadding») renverra 30 dans les deux sens.

Nous pouvons rapidement supprimer un carrousel d'éléments basés sur des composants de page en utilisant nos spécifications et en remplaçant les appels de fonctions par l'une des dimensions suivantes:

carrousel = nouveau PageComponent
    largeur: largeur d'écran
    x: getSpec ("viewSidePadding")
    y: getSpec ("viewTopPadding")
    hauteur: getSpec ("itemSize")
    width: getSpec ("itemSize")
    clip: faux
    scrollVertical: false
pour i dans [0..4]
    carrouselItem = nouvelle couche
        parent: carousel.content
        width: getSpec ("itemSize")
        hauteur: getSpec ("itemSize")
        x: i * (getSpec ("itemSize") + getSpec ("itemMargin"))

Cela suffit pour créer des mises en page qui répondent aux changements de type de périphérique. Si vous souhaitez également vous adapter à l'orientation, vous devez détecter l'orientation, puis redessiner tous les calques chaque fois que vous le modifiez. Pour ce faire, le moyen le plus simple consiste à encapsuler toute la création de calques dans une fonction de disposition. Toute modification de l'orientation doit d'abord détruire toutes les couches, puis appeler la fonction de disposition pour les reconstruire. Sinon, la fonction de disposition peut gérer la destruction de la couche elle-même dans un premier temps.

Si les couches existantes ne sont pas détruites sur le commutateur d’orientation, vous obtiendrez des piles de couches en double au cours de l’appel répété de votre fonction de disposition.

Envelopper notre création de carrousel dans une fonction de présentation pourrait ressembler à ceci:

# la fonction de disposition de la vue
doLayout = () ->
    # détruire toutes les couches, pour éviter les doublons
    pour couche dans Framer.CurrentContext.layers
        layer.destroy ()
    
    # créer un carrousel
    carrousel = nouveau PageComponent
        largeur: largeur d'écran
        x: getSpec ("viewSidePadding")
        y: getSpec ("viewTopPadding")
        hauteur: getSpec ("itemSize")
        width: getSpec ("itemSize")
        clip: faux
        scrollVertical: false
    
    # peupler le carrousel avec des cellules
    pour i dans [0..4]
        carrouselItem = nouvelle couche
            parent: carousel.content
            width: getSpec ("itemSize")
            hauteur: getSpec ("itemSize")
            x: i * (getSpec ("itemSize") + getSpec ("itemMargin"))
# initialiser
doLayout ()

Pour expérimenter les prototypes et voir comment ces mécanismes fonctionnent, téléchargez le prototype plus simple et un autre avec la prise en charge du commutateur d'orientation.

Une autre approche consiste à stocker les spécifications de dimensionnement spécifiques à l'iPad dans un objet de substitution distinct. Téléchargez le prototype de substitution pour voir cette méthode en action.

Pour plus d'informations sur la conception et le développement, abonnez-vous à BPXL Craft et suivez Black Pixel sur Twitter.

Black Pixel est une agence de création de produits numériques. En savoir plus sur blackpixel.com.