Pourquoi vous n’avez pas besoin d’une formation en design
Conception De L'interface Utilisateur
Je travaille beaucoup avec des mises en page plein écran personnalisées, pratiquement au quotidien. Habituellement, ces mises en page impliquent une quantité substantielle d'interaction et d'animation. Qu'il s'agisse d'une chronologie complexe de transitions déclenchée par le temps ou d'un ensemble d'événements pilotés par l'utilisateur, dans la plupart des cas, l'interface utilisateur nécessite plus que la simple utilisation d'une solution de plug-in prête à l'emploi avec quelques ajustements et modifications. . Par contre, je vois beaucoup Développeurs JavaScript ont tendance à rechercher leur plugin JS préféré pour rendre leur travail plus facile, même si la tâche n'a pas besoin de toutes les cloches et sifflets qu'un certain plugin fournit.
Avertissement: L'utilisation de l'un des nombreux plugins disponibles a bien sûr ses avantages. Vous obtiendrez une variété d'options que vous pouvez utiliser pour ajuster vos besoins sans avoir à faire beaucoup de codage. En outre, la plupart des auteurs de plugins optimisent leur code, le rendent compatible avec plusieurs navigateurs et plates-formes, etc. Mais quand même, vous obtenez une bibliothèque pleine taille incluse dans votre projet pour peut-être seulement une ou deux choses différentes qu'elle fournit. Je ne dis pas que l'utilisation d'un plugin tiers de quelque nature que ce soit est naturellement une mauvaise chose, je le fais au quotidien dans mes projets, juste que c'est généralement une bonne idée de peser le pour et le contre de chaque approche telle quelle. une bonne pratique de codage. Quand il s'agit de faire votre propre chose de cette façon, il faut un peu plus de connaissances et d'expérience en codage pour savoir ce que vous recherchez, mais à la fin, vous devriez obtenir un morceau de code qui fait une chose et une chose seulement de la manière vous le voulez.
Cet article vise à montrer une approche CSS / JS pure dans le développement d'une disposition de curseur déclenchée par défilement plein écran avec une animation de contenu personnalisée. Dans cette approche réduite, je couvrirai la structure HTML de base que vous attendez d'un CMS back-end, CSS moderne ( SCSS ) techniques de mise en page et codage JavaScript vanille pour une interactivité totale. Étant simple, ce concept peut être facilement étendu à un plugin à plus grande échelle et / ou utilisé dans une variété d'applications n'ayant pas de dépendances en son cœur.
élasticité-prix de la demande exemples dans la vie réelle
Le design que nous allons créer est une vitrine de portefeuille d'architectes minimaliste avec des images et des titres de chaque projet. Le curseur complet avec des animations ressemblera à ceci:
Vous pouvez consulter la démo Ici , et vous pouvez accéder à mon Repo Github pour plus de détails.
Voici donc le code HTML de base avec lequel nous allons travailler:
hero-slider
Un div avec l'id de
est notre principal détenteur. À l'intérieur, la mise en page est divisée en sections: #64 Paradigm
Concentrons-nous sur la section diaporama car c'est notre point d'intérêt dans cet article. Ici, nous avons deux parties - principale et aux . Main est le div qui contient les images en vedette tandis que aux contient les titres des images. La structure de chaque glissière à l'intérieur de ces deux supports est assez basique. Ici, nous avons une diapositive d'image à l'intérieur du support principal:
#hero-slider { position: relative; height: 100vh; display: flex; background: $dark-color; } #slideshow { position: relative; flex: 1 1 $main-width; display: flex; align-items: flex-end; padding: $offset; } #info { position: relative; flex: 1 1 $side-width; padding: $offset; background-color: #fff; }
L'attribut de données d'index est ce que nous allons utiliser pour suivre où nous en sommes dans le diaporama. Le div abs-mask que nous allons utiliser pour créer un effet de transition intéressant et le div slide-image contient l'image sélectionnée spécifique. Les images sont rendues en ligne comme si elles provenaient directement d'un CMS et sont définies par l'utilisateur final.
De même, le titre glisse à l'intérieur du support auxiliaire:
#slideshow { position: relative; flex: 1 1 $main-width; display: flex; align-items: flex-end; padding: $offset; } #slides-main { @extend %abs; &:after { content: ''; @extend %abs; background-color: rgba(0, 0, 0, .25); z-index: 100; } .slide-image { @extend %abs; background-position: center; background-size: cover; z-index: -1; } } #slides-aux { position: relative; top: 1.25rem; width: 100%; .slide-title { position: absolute; z-index: 300; font-size: 4vw; font-weight: 700; line-height: 1.3; @include outlined(#fff); } }
Chaque titre de diapositive est une balise H2 avec l'attribut de données correspondant et un lien permettant de conduire à la page unique de ce projet.
Le reste de notre HTML est également assez simple. Nous avons un logo en haut, des informations statiques qui indiquent à l'utilisateur sur quelle page il se trouve, une description et un indicateur actuel / total du curseur.
Le code CSS source est écrit en SCSS , un préprocesseur CSS qui est ensuite compilé en CSS standard que le navigateur peut interpréter. SCSS vous donne l'avantage d'utiliser des variables, une sélection imbriquée, des mixins et d'autres choses intéressantes, mais il doit être compilé en CSS pour que le navigateur lise le code comme il se doit. Pour les besoins de ce didacticiel, j'ai utilisé Scout-App pour gérer la compilation car je voulais avoir les outils au strict minimum.
J'ai utilisé flexbox pour gérer la disposition côte à côte de base. L'idée est d'avoir le diaporama d'un côté et la section info de l'autre.
background-size: cover
Plongeons dans le positionnement et encore une fois, concentrons-nous sur la section diaporama:
%abs { position: absolute; top: 0; left: 0; height: 100%; width: 100%; }
J'ai défini le curseur principal pour qu'il soit parfaitement positionné et que les images d'arrière-plan étirent toute la zone en utilisant les touches @mixin outlined($color: $dark-color, $size: 1px) { color: transparent; -webkit-text-stroke: $size $color; }
propriété. Pour offrir plus de contraste avec les titres des diapositives, j'ai défini un pseudo-élément absolu qui agit comme une superposition. Le curseur aux contenant les titres des diapositives est positionné en bas de l'écran et au-dessus des images.
Étant donné qu'une seule diapositive sera visible à la fois, j'ai également défini chaque titre comme absolu et j'ai calculé la taille du support via JS pour m'assurer qu'il n'y a pas de coupures, mais plus à ce sujet dans l'une de nos prochaines sections. Ici, vous pouvez voir l'utilisation d'une fonctionnalité SCSS appelée extension:
.slider-title-wrapper { position: absolute; top: $offset; left: calc(100% - #{$offset}); transform-origin: 0% 0%; transform: rotate(90deg); @include outlined; }
Depuis que j'ai beaucoup utilisé le positionnement absolu, j'ai intégré ce CSS dans un fichier extensible pour qu'il soit facilement disponible dans divers sélecteurs. En outre, j'ai créé un mixin appelé «contour» pour fournir une approche DRY lors du stylisme des titres et du titre du curseur principal.
transform-origin
Quant à la partie statique de cette mise en page, elle n'a rien de complexe mais ici vous pouvez voir une méthode intéressante lors du positionnement du texte qui doit être sur l'axe Y au lieu de son déroulement normal:
#logo:after { transform: scaleY(0); transform-origin: 50% 0; transition: transform .35s $easing; } .logo-text { display: block; transform: translate3d(120%, 0, 0); opacity: 0; transition: transform .8s .2s, opacity .5s .2s; } .current, .sep:before { opacity: 0; transition: opacity .4s 1.3s; } #info { transform: translate3d(100%, 0, 0); transition: transform 1s $easing .6s; } .line { transform-origin: 0% 0; transform: scaleX(0); transition: transform .7s $easing 1s; } .slider-title { overflow: hidden; >span { display: block; transform: translate3d(0, -100%, 0); transition: transform .5s 1.5s; } }
Je voudrais attirer votre attention sur le transform
propriété car je la trouve vraiment sous-utilisée pour ce type de mise en page. La façon dont cet élément est positionné est que son ancre reste dans le coin supérieur gauche de l'élément, définissant le point de rotation et faisant circuler le texte en continu de ce point vers le bas sans aucun problème en ce qui concerne les différentes tailles d'écran.
pouvoir de négociation des acheteurs exemples
Jetons un coup d'œil à une partie CSS plus intéressante - l'animation de chargement initial:
Habituellement, ce type de comportement d'animation synchronisé est obtenu à l'aide d'une bibliothèque - GSAP , par exemple, est l'un des meilleurs du marché, offrant d'excellentes capacités de rendu, est facile à utiliser et possède la fonctionnalité de chronologie qui permet au développeur d'enchaîner par programmation des transitions d'éléments les uns dans les autres.
Cependant, comme il s’agit d’un exemple purement CSS / JS, j’ai décidé d’y aller vraiment basique. Ainsi, chaque élément est mis à sa position de départ par défaut - soit masqué par transformation ou opacité et affiché lors du chargement du curseur qui est déclenché par notre JS. Toutes les propriétés de transition sont modifiées manuellement pour assurer un flux naturel et intéressant, chaque transition se poursuivant dans une autre offrant une expérience visuelle agréable.
transform
S'il y a une chose que je voudrais que vous voyiez ici, c'est l'utilisation de heroSlider
propriété. Lors du déplacement d'un élément HTML, qu'il s'agisse d'une transition ou d'une animation, il est conseillé d'utiliser les touches utils
propriété. Je vois beaucoup de gens qui ont tendance à abuser de la marge ou du remplissage ou même des décalages - haut, gauche, etc., ce qui ne produit pas de résultats adéquats en matière de rendu.
Pour mieux comprendre comment utiliser CSS lors de l'ajout d'un comportement interactif, je ne pourrais pas recommander le article suivant assez.
Il est de Paul Lewis, un ingénieur Chrome, et couvre à peu près tout ce que l'on doit savoir sur le rendu des pixels dans le Web, que ce soit CSS ou JS.
Le fichier JavaScript est divisé en deux fonctions distinctes.
Le init
qui prend en charge toutes les fonctionnalités dont nous avons besoin ici, et le resize
fonction où j’ai ajouté plusieurs fonctions utilitaires réutilisables. J'ai commenté chacune de ces fonctions utilitaires pour fournir un contexte si vous souhaitez les réutiliser dans votre projet.
La fonction principale est codée de manière à avoir deux branches: init
et heroSlider
. Ces branches sont disponibles via le retour de la fonction main et sont appelées si nécessaire. const slider = { hero: document.querySelector('#hero-slider'), main: document.querySelector('#slides-main'), aux: document.querySelector('#slides-aux'), current: document.querySelector('#slider-nav .current'), handle: null, idle: true, activeIndex: -1, interval: 3500 };
est l’initialisation de la fonction principale et elle est déclenchée lors du chargement de la fenêtre. De même, la branche de redimensionnement est déclenchée lors du redimensionnement de la fenêtre. Le seul objectif de la fonction de redimensionnement est de recalculer la taille du curseur du titre lors du redimensionnement de la fenêtre, car la taille de la police du titre peut varier.
Dans le handle
fonction, j'ai fourni un objet slider contenant toutes les données et sélecteurs dont nous aurons besoin:
idle
En remarque, cette approche pourrait être facilement adaptée si vous utilisez, par exemple, React, car vous pouvez stocker les données dans l'état ou utiliser les nouveaux hooks. Pour rester précis, examinons simplement ce que représente chacune des paires valeur / clé ici:
activeIndex
La propriété sera utilisée pour démarrer et arrêter la fonctionnalité de lecture automatique.interval
property est un indicateur qui empêchera l'utilisateur de forcer le défilement pendant que la diapositive est en transition.setHeight(slider.aux, slider.aux.querySelectorAll('.slide-title')); loadingAnimation();
nous permettra de garder une trace de la diapositive actuellement activesetHeight
indique l'intervalle de lecture automatique du curseurLors de l'initialisation du curseur, nous invoquons deux fonctions:
là, seul, je suis
const loadingAnimation = function () { slider.hero.classList.add('ready'); slider.current.addEventListener('transitionend', start, { once: true }); }
Le const start = function () { autoplay(true); wheelControl(); window.innerWidth <= 1024 && touchControl(); slider.aux.addEventListener('transitionend', loaded, { once: true }); }
La fonction atteint une fonction utilitaire pour définir la hauteur de notre curseur aux en fonction de la taille maximale du titre. De cette façon, nous nous assurons qu'un dimensionnement adéquat est fourni et qu'aucun titre de diapositive ne sera coupé même si son contenu tombe sur deux lignes.
La fonction loadingAnimation ajoute une classe CSS à l'élément fournissant les transitions CSS d'introduction:
loadingAnimation
Comme notre indicateur de curseur est le dernier élément de la chronologie de la transition CSS, nous attendons la fin de sa transition et invoquons la fonction de démarrage. En fournissant un paramètre supplémentaire en tant qu'objet, nous nous assurons que cela n'est déclenché qu'une seule fois.
Jetons un œil à la fonction de démarrage:
const autoplay = function (initial) { slider.autoplay = true; slider.items = slider.hero.querySelectorAll('[data-index]'); slider.total = slider.items.length / 2; const loop = () => changeSlide('next'); initial && requestAnimationFrame(loop); slider.handle = utils().requestInterval(loop, slider.interval); }
Ainsi, lorsque la mise en page est terminée, sa transition initiale est déclenchée par slideChange
fonction et la fonction de démarrage prend le relais. Il déclenche ensuite la fonctionnalité de lecture automatique, active le contrôle de la molette, détermine si nous sommes sur un appareil tactile ou de bureau, et attend la première transition de la diapositive des titres pour ajouter la classe CSS appropriée.
L'une des principales fonctionnalités de cette mise en page est la fonction de lecture automatique. Passons en revue la fonction correspondante:
requestAnimationFrame
Tout d'abord, nous définissons l'indicateur de lecture automatique sur true, indiquant que le curseur est en mode de lecture automatique. Cet indicateur est utile pour déterminer s'il faut relancer la lecture automatique une fois que l'utilisateur interagit avec le curseur. Nous référençons ensuite tous les éléments de curseur (diapositives), car nous allons changer leur classe active et calculer le total des itérations que le curseur va avoir en additionnant tous les éléments et en divisant par deux car nous avons deux dispositions de curseur synchronisées (principale et auxiliaire). mais un seul «curseur» en soi qui change les deux simultanément.
La partie la plus intéressante du code ici est la fonction de boucle. Il invoque requestAnimationFrame
, fournissant la direction de la diapositive que nous allons parcourir dans une minute, cependant, la fonction de boucle est appelée plusieurs fois. Voyons pourquoi.
Si l'argument initial est évalué comme vrai, nous invoquerons la fonction de boucle en tant que requestInterval
rappeler. Cela ne se produit que lors du premier chargement du curseur, ce qui déclenche un changement de diapositive immédiat. Utilisation de setInterval
nous exécutons le rappel fourni juste avant la prochaine peinture de l'image.
devrais-je apprendre c ou c++
Cependant, comme nous voulons continuer à parcourir les diapositives en mode de lecture automatique, nous utiliserons un appel répété de cette même fonction. Ceci est généralement réalisé avec setInterval. Mais dans ce cas, nous utiliserons l’une des fonctions utilitaires - requestInterval
. Alors que requestAnimationFrame
fonctionnerait très bien, slider.handle
est un concept avancé qui repose sur cancelAnimationFrame
et offre une approche plus performante. Il garantit que la fonction n'est redéclenchée que si l'onglet du navigateur est actif.
Vous trouverez plus d'informations sur ce concept dans cet article génial sur Astuces CSS . Veuillez noter que nous attribuons la valeur de retour de cette fonction à notre slideChange
propriété. Cet identifiant unique que la fonction renvoie nous est disponible et nous l'utiliserons pour annuler la lecture automatique ultérieurement lors de l'utilisation const changeSlide = function (direction) { slider.idle = false; slider.hero.classList.remove('prev', 'next'); if (direction == 'next') { slider.activeIndex = (slider.activeIndex + 1) % slider.total; slider.hero.classList.add('next'); } else { slider.activeIndex = (slider.activeIndex - 1 + slider.total) % slider.total; slider.hero.classList.add('prev'); } //reset classes utils().removeClasses(slider.items, ['prev', 'active']); //set prev const prevItems = [...slider.items] .filter(item => { let prevIndex; if (slider.hero.classList.contains('prev')) { prevIndex = slider.activeIndex == slider.total - 1 ? 0 : slider.activeIndex + 1; } else { prevIndex = slider.activeIndex == 0 ? slider.total - 1 : slider.activeIndex - 1; } return item.dataset.index == prevIndex; }); //set active const activeItems = [...slider.items] .filter(item => { return item.dataset.index == slider.activeIndex; }); utils().addClasses(prevItems, ['prev']); utils().addClasses(activeItems, ['active']); setCurrent(); const activeImageItem = slider.main.querySelector('.active'); activeImageItem.addEventListener('transitionend', waitForIdle, { once: true }); }
.
Le wheelControl
la fonction est la fonction principale de tout le concept. Il modifie les diapositives, que ce soit par lecture automatique ou par déclencheur utilisateur. Il est conscient de la direction du curseur, fournit une boucle, de sorte que lorsque vous arrivez à la dernière diapositive, vous pourrez continuer à la première diapositive. Voici comment je l'ai codé:
touchControl
L'idée est de déterminer la diapositive active en fonction de son index de données obtenu à partir du HTML. Abordons chaque étape:
setCurrent
ou waitForIdle
.const wheelControl = function () { slider.hero.addEventListener('wheel', e => { if (slider.idle) { const direction = e.deltaY > 0 ? 'next' : 'prev'; stopAutoplay(); changeSlide(direction); } }); }
est un rappel qui met à jour l'indicateur de curseur basé sur le activeIndex.stopAutoplay
callback qui redémarre la lecture automatique si elle a été précédemment interrompue par l'utilisateur.En fonction de la taille de l'écran, j'ai ajouté deux types de commandes utilisateur: la molette et le toucher. Commande de roue:
stopAutoplay
Ici, nous écoutons la roue même et si le curseur est actuellement en mode veille (n'animant pas actuellement un changement de diapositive), nous déterminons la direction de la roue, invoquons cancelRequestInterval
pour arrêter la fonction de lecture automatique si elle est en cours et changer la diapositive en fonction de la direction. Le const stopAutoplay = function () { slider.autoplay = false; utils().clearRequestInterval(slider.handle); }
function n'est rien d'autre qu'une simple fonction qui définit notre drapeau de lecture automatique sur la valeur false et annule notre intervalle en invoquant wheelControl
fonction utilitaire en lui passant le handle approprié:
comment coder un robot
touchControl
Similaire à const touchControl = function () { const touchStart = function (e) { slider.ts = parseInt(e.changedTouches[0].clientX); window.scrollTop = 0; } const touchMove = function (e) { slider.tm = parseInt(e.changedTouches[0].clientX); const delta = slider.tm - slider.ts; window.scrollTop = 0; if (slider.idle) { const direction = delta <0 ? 'next' : 'prev'; stopAutoplay(); changeSlide(direction); } } slider.hero.addEventListener('touchstart', touchStart); slider.hero.addEventListener('touchmove', touchMove); }
, nous avons touchstart
qui prend en charge les gestes tactiles:
touchmove
Nous écoutons deux événements: slideChange
et slideChange
. Ensuite, nous calculons la différence. S'il renvoie une valeur négative, nous passons à la diapositive suivante car l'utilisateur a glissé de droite à gauche. En revanche, si la valeur est positive, c'est-à-dire que l'utilisateur a glissé de gauche à droite, on déclenche div
avec la direction passée comme «précédente». Dans les deux cas, la fonctionnalité de lecture automatique est arrêtée.
Il s'agit d'une implémentation de geste utilisateur assez simple. Pour construire là-dessus, nous pourrions ajouter des boutons précédent / suivant pour déclencher abs-mask
en cliquant ou en ajoutant une liste à puces, vous accédez directement à une diapositive en fonction de son index.
Alors voilà, une pure façon CSS / JS de coder une disposition de curseur non standard avec des effets de transition modernes.
J'espère que vous trouverez cette approche utile comme façon de penser et que vous pourriez utiliser quelque chose de similaire dans vos projets frontaux lors du codage d'un projet qui n'a pas nécessairement été conçu de manière conventionnelle.
Pour ceux d’entre vous intéressés par l’effet de transition d’image, je reviendrai là-dessus dans les prochaines lignes.
Si nous revisitons la structure HTML des diapositives que j'ai fournie dans la section d'introduction, nous verrons que chaque diapositive d'image a un div
autour d'elle avec la classe CSS de overflow:hidden
. Qu'est-ce que c'est &.prev { z-index: 5; transform: translate3d(-100%, 0, 0); transition: 1s $easing; .abs-mask { transform: translateX(80%); transition: 1s $easing; } }
fait est qu'il masque une partie de l'image visible d'une certaine quantité en utilisant abs-mask
et en le décalant dans une direction différente de celle de l'image. Par exemple, si nous regardons la façon dont la diapositive précédente est codée:
La diapositive précédente a un décalage de -100% sur son axe X, la déplaçant vers la gauche de la diapositive actuelle, cependant, l'intérieur
div est traduit à 80% vers la droite, offrant une fenêtre d'affichage plus étroite. Ceci, en combinaison avec un plus grand z-index pour la diapositive active entraîne une sorte d'effet de couverture - l'image active couvre la précédente tout en étendant sa zone visible en déplaçant le masque qui fournit la vue complète.Les propriétés CSS les plus standard que nous pouvons animer sont la transformation, l'opacité, la couleur, la couleur d'arrière-plan, la hauteur, la largeur, etc. La liste complète se trouve dans la documentation technique de Mozilla.
Une animation d'image clé CSS est une représentation de 0 à 100% dans le temps de toutes les transitions qui doivent se produire sur l'élément sélectionné pendant une période de temps spécifiée. De cette façon, plusieurs transitions peuvent être combinées en une représentation visuelle transparente.
Une transition est une propriété qui permet aux propriétés CSS d'être transférées entre deux valeurs. Par exemple, survolez un élément et laissez-le faire passer son opacité de 0 à 1.
En interprétant la spécificité, le navigateur décide de la règle CSS à interpréter. La spécificité CSS dépend des types de sélecteurs: sélecteurs de type, sélecteurs de classe, sélecteurs d'ID. La combinaison de plusieurs sélecteurs, l'ajout de combinateurs frères et sœurs et enfants manipule également la spécificité.
Pour que votre contenu HTML soit stylé par CSS, le navigateur utilise la méthode suivante. Lors du chargement du HTML, il combine son contenu avec les informations de style fournies, crée une arborescence DOM et affiche enfin son contenu.