Kuidas luua HTML-i ja JS-iga Apple App Store'i kaarte

Hiljuti tahtsin tööl käiva projekti jaoks luua nimekirja kaartidest, mis nägid välja ja käitusid sarnaselt kaartidega, mida nähti Apple App Store'is iOS-i jaoks. Pärast veebist näidete otsimist ei leidnud ma päris ühte näidet, mis nägi välja ja käitus üsna täpselt nii, nagu ma tahtsin, nii et otsustasin luua oma ja nüüd olen siin, et seda teiega kõigiga jagada.

Üks nõue, mis mul oli, on see, et kaardi laiendamisel ei tohiks koputatava kaardi asukoht muutuda, nii et kaardi sulgemisel olen kohe tagasi samasse keritud positsiooni. Alustame siis!

Selle näite jaoks kasutasin ainult järgmisi raamatukogusid:

  • jQuery 3.3.1
  • Bootstrap CSS 4.1.0
  • Fantastiline font 4.7.0
Mida me saavutame

Ma ei hakka HTML-i ega CSS-i osa lahti seletama, nii et jätan teile siin ühe kaardi märgistuse ja kogu meie näite visuaali jaoks vajaliku CSS-i.

HTML

  
    
      
        
                   
        
          
            
              
28. aprill 2018 / kategooria
              
                 6               
              
            
            

Nibh Tellus Pharetra Fusce

            
              

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean eu leo ​​quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nullam quis risus eget urna mollis ornare vel eu leo. Donec sed odio dui. Nullam id dolor id nibh ultricies vehicula ut id elit. Donec ullamcorper nulla non metus auctor fringilla.

            
          
        
        
      
    
  

CSS

* .lukk {
  ülevool: varjatud;
}
.list-wrapper {
  positsioon: absoluutne;
  ülemine: 0;
  vasakul: 0;
  paremal: 0;
  põhi: 0;
  ülevool: automaatne;
}
.cards-list {
  positsioon: sugulane;
  polster: 15x 15xx 20xx;
  polsterdus-põhi: calc (20 pikslit + env (turvalise alaga sisestuspõhi)); /* IPhone X jaoks */
  max laius: 400 pikslit;
  veeris: auto;
}
.card {
  positsioon: sugulane;
  kõrgus: 45vh;
  laius: 100%;
  ääreraadius: 20 pikslit;
  ülevool: varjatud;
  box-shadow: 0 pikslit 10 pikslit 30 pikslit 0 pikslit rgba (0, 0, 0, 0,1);
  üleminek: läbipaistmatus 0,2s lihtsus, kastivari 0,2s lihtsus;
  läbipaistmatus: 1;
  veerise põhi: 40 pikslit;
}
.card.open {
  piiri raadius: 0;
}
.card.hover,
.card: hõljutage {
  box-shadow: 0 px 0 px 10 px 0 px rgba (0, 0, 0, 0,1);
}
.card-sisu {
  positsioon: sugulane;
  laius: 100%;
  kõrgus: 100%;
  taust: rgba (255, 255, 255, 0);
  ülevool: varjatud;
  üleminek: piiriraadius on 0,15s lihtne;
  ääreraadius: 20 pikslit;
  kursor: osuti;
}
.card.open .card-content {
  z-indeks: 500! oluline;
  taust: #fff;
  ääreraadius: 0 px;
  kursor: vaikimisi;
}
.bannerihoidja {
  positsioon: absoluutne;
  laius: 100%;
  ülevool: varjatud;
  kuva: flex;
  joonda üksused: keskel;
  õigusta-sisu: kese;
}
.banner {
  positsioon: sugulane;
  kõrgus: 20vh;
  laius: 100%;
  polster: 15xx;
  tausta suurus: kaas;
  taust-positsioon: kesklinn;
  taust kordama: ei korda;
  taustavärv: läbipaistev;
  ülevool: varjatud;
  z-indeks: 1;
}
.card.open .banner {
  kõrgus: 45vh;
  piiri raadius: 0;
}
.sisuomanik {
  max laius: 600 pikslit;
  polster: 0;
  z-indeks: 1;
  kõrgus: 100%;
  taustavärv: läbipaistev;
  ülevool: varjatud;
  positsioon: sugulane;
}
.card.open .content-holder {
  ülevool: automaatne;
}
.innisisu {
  ülemine: 20vh;
  polster: 20 px;
  positsioon: sugulane;
  taustavärv: #fff;
}
.card.open .inner-content {
  ülemine: 40vh;
  polstri-põhi: env (turvalise ala-sisestuspõhi); /* IPhone X jaoks */
  raadiuse ülaserv-vasak raadius: 20 pikslit;
  raadiuse üla-parempoolne raadius: 20 pikslit;
}
.info-omanik {
  kuva: flex;
  joonda üksused: keskel;
}
.date-kategooria {
  teksti teisendamine: suurtähed;
  kirjasuurus: 12 pikslit;
  läbipaistmatus: 0,5;
  veerise põhi: 0;
  flex: 1;
  varu: 0 1,4rem 0 0;
}
.like-ümbris {
  positsioon: sugulane;
  kursor: osuti;
  polster-vasak: 40 pikslit;
}
.like-ümbris .fa,
.silti-ümbris .fa {
  läbipaistmatus: 0,3;
}
.like-wrapper.btn-meeldinud .fa {
  läbipaistmatus: 1;
  värv: punane;
}
.like-wrapper span {
  positsioon: absoluutne;
  ülemine: 0;
  vasakul: 0;
  teksti joondamine: paremal;
  läbipaistmatus: 0,3;
  laius: 35 pikslit;
  joone kõrgus: 24 pikslit;
  kirjasuurus: 12 pikslit;
}
.tähiste ümbris {
  veeris-vasak: 8 pikslit;
  laius: 24xx;
  teksti joondamine: paremal;
  kursor: osuti;
}
.pealkiri {
  positsioon: sugulane;
  veeris: 10 pikslit 0 20 pikslit 0;
  kirjasuurus: paks;
}
.description {
  kirjasuurus: 16 pikslit;
  joone kõrgus: 1,5;
  läbipaistmatus: 0,6;
}
.kaart: mitte (.open) .kirjeldus {
  ülevool: varjatud;
  / * Lõikab kaardi kokkuvarisemise korral sisu 3 rida * /
  kuva: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertikaalne;
  kõrgus: 4,2em;
}
.close-btn {
  positsioon: absoluutne;
  paremal: 15 pikslit;
  ülaosa: 15 pikslit;
  kõrgus: 28 px;
  laius: 28 pikslit;
  joone kõrgus: 27 pikslit;
  taust: rgba (255, 255, 255, 0,5);
  piiri raadius: 50%;
  värv: # 333;
  teksti joondamine: keskel;
  kursor: osuti;
  läbipaistmatus: 0;
  nähtavus: varjatud;
  üleminek: kõik 0,15 sekundit lihtsustatud sisse-välja;
}
.card.open .close-btn {
  z-indeks: 600;
  läbipaistmatus: 0,9;
  nähtavus: nähtav;
}

JavaScript

Lähme nüüd lõbusasse ossa :)

Alustame oma algse funktsiooni loomisega ja nimetagem seda siis InitiseCards (). Selle funktsiooni puhul paigutame selle, mida peame lehe laadimisel täitma.

funktsiooni algkaardid () {
  // Kinnitage kuulajad
  // Tehke muid asju, jõuame selleni
}
// Käivita funktsioon laadimisel
algkaardid ();

Nüüd, kui see on loodud, loogem funktsioon, mis meie sündmuste kuulajaid kinnitaks.

funktsioon AttaListeners () {
  $ (dokument)
    .on ("klõps", ".kaardi sisu", funktsioon () {
      if ($ (see) .vanemad (". kaart"). hasClass ("avatud")) {
        tagasi;
      }
      expandElement ($ (see));
    })
    .on ("klõps", ".card .close-btn", funktsioon (sündmus) {
      event.stopPpapagation ();
      collapseElement ($ (see));
    })
    .on ('touchstart', '.card', function () {
      $ (see) .addClass ('hõljuda');
    })
    .on ('touchend touchmove touchcancel', '.card', function () {
      $ (this) .removeClass ('hõljuda');
    });
}

Selgitan neid nelja sündmusekäitlejat, alustades ülalt:

.on ("klõps", ".kaardi sisu", funktsioon () {})

Selle sündmuse käigus käsitletakse iga kaardi avamist. Kui lause väldib funktsiooni käitamist, kui kaart on juba avatud.

.on ("klõpsake", ".card .close-btn", funktsioon (sündmus) {})

See teine ​​sündmus tegeleb avatud kaardi sulgemisega. Olen kasutanud sündmust.stopPropagation (); veendumaks, et mälukaardi sulgemise ajal ei vallandata midagi muud, kuna kaardi sulgemiseks on vaja animatsiooni ajastamist ja klõpsasündmus võib läbida elemente.

.on ('touchstart', '.card', function () {})
.on ('touchend touchmove touchcancel', '.card', function () {})

Kaks viimast elementi on mõeldud ainult mobiilseadmetele ja see on lihtsalt mõnus puudutus visuaalse efekti saavutamiseks. Veebis, kui hõljutate iga kaardi kursorit, lükatakse see alla, mobiilseadmetes: hover ei tööta päris hästi, seetõttu lisan selle asemel sama efektiga klassi. See klass lisatakse touchstarti ja eemaldatakse kolmel üritusel, mis puudutavad touchmove ja touchcancel. Võite nendega vabalt ringi mängida.

Pärast oma AttaListeners () loomist kutsume seda oma InitiseCards () -st. Täpselt nii:

funktsiooni algkaardid () {
  AttaListeners ();
  // Tehke muid asju, jõuame selleni
}

Järgmisena loome funktsiooni, mis arvutab sõltuvalt sisust iga kokkuvarisenud kaardi kõrguse. See on kasulik, kuna kaardid vajavad fikseeritud kõrgust, kuid kui teie kaartidel on dünaamiline sisu, peate seda suutma näidata piiranguteta, sellepärast arvutan iga kaardi kõrguse, nii et näib, et need on nii suured kui sisu sees.

Looge setCardHeight ()

funktsioon setCardHeight () {
  $ (". kaart") .each (funktsioon (register, element) {
    var slideHeight = $ (element)
      .find (". ribareklaami hoidja")
      .outerHeight ();
    var containerHeight = $ (element)
      .find (". sisemine sisu")
      .outerHeight ();
    var contentHeight = slideHeight + konteinerHeight;
  $ (element) .css ({
      kõrgus: sisuKõrgus
    });
  });
}

Selle funktsiooni abil saate ribareklaami kõrguse ja riba alumise sisu kõrguse kokku liita, luues meie kaardi kõrguse ja rakendades seda kaardile. Nüüd, kui see funktsioon on olemas, kutsume seda oma algkaartidest (), näiteks järgmiselt:

funktsiooni algkaardid () {
  AttaListeners ();
  setCardHeight ();
}

Lõpuks vajame lihtsalt kaartide laiendamiseks ja ahendamiseks vajalikke funktsioone. Alustame laiendamisega.

funktsioon expandElement (elementToExpand) {
  // Lisab klassi 'avatud', et aidata stiilimist
  elementToExpand.parents (". kaart"). addClass ("avatud");
  // Takistab keha kerimist
  $ ("body"). addClass ("lock");
  // Muutuv komplekt
  var elementOffset = $ (". list-wrapper"). offset ();
  var elementScrollTop = $ ("body"). scrollTop ();
  var netOffset = elementOffset.top - elementScrollTop;
  var expandPosition = $ (". list-wrapper"). ofset ();
  var expandTop = expandPosition.top;
  var expandLeft = expandPosition.left;
  var expandWidth = $ (". list-wrapper"). externalWidth ();
  var expandHeight = $ (". list-wrapper"). externalHeight ();
  $ (". list-wrapper"). css ({
    ülaosa: netOffset,
    positsioon: "fikseeritud",
    ülevool: "peidetud",
    "z-register": "11"
  });
  // teisendage laiendatav element fikseeritud asendisse seda liigutamata
  elementToExpand.css ({
    ülaosa: elementToExpand.offset (). ülaosa - $ ("keha"). scrollTop (),
    vasak: elementToExpand.offset (). vasak,
    kõrgus: elementToExpand.height (),
    laius: elementToExpand.width (),
    "maksimaalne laius": laienda laius,
    positsioon: "fikseeritud"
  });
  // muudab ribareklaami kõrgust
  var laiendatudHeight = elementToExpand.find (". ribareklaam"). andmed ("laiendatud kõrgus");
  elementToExpand.find (". ribareklaam"). animeerima ({
      kõrgus: laiendatudKõrgus
    },
    laiendamineAnimationTiming,
    "easyOutBack"
  );
  // Muudab sisu positsiooni
  var laiendatud positsioon = elementToExpand
    .find (". sisemine sisu")
    .data ("positsioon laiendatud");
  elementToExpand.find (". sisemine sisu"). animeerima ({
      ülemine: laiendatud seisukoht
    },
    laiendamineAnimationTiming,
    "easyOutBack"
  );
  // alusta laiendatava üksuse animatsiooni laiendusümbriseks
  // laiendage elementi klassiga .about-tile-bg-image
  elementToExpand.animate ({
      vasakul: expandLeft,
      ülaosa: expandTop,
      kõrgus: laiendaKõrgus,
      laius: laiendamalaius,
      "maksimaalne laius": laiendage laiust
    },
    400, // animatsiooni ajastus millisekkides
    "easOutBack", // animatsiooni kergendamine
    funktsioon () {
      elementToExpand.css ({
        paremal: 0,
        alt: 0,
        laius: "auto",
        kõrgus: "auto"
      });
  elementToExpand.find (". ribareklaamide hoidja"). css ({
        positsioon: "fikseeritud"
      });
    }
  );
}

Jätsin koodi mõned kommentaarid, kuid lubasin proovida selgitada, mis siin toimub:

  1. Esmalt lisame kaardile avatava klassi. See on oluline, kuna käsitleme enamikku asju CSS-iga ja see on meie valik, kui kaart on avatud.
  2. Hoiame ära keha tahtmatu kerimise.
  3. Järgmisena seadsime enamuse oma muutujatest ja teeme mõned arvutused. Nendes muutujates saame kaardid sisaldava ümbrise asukoha ja suuruse. Need väärtused on reeglid, mille alusel meie kaardid laienevad.
  4. Panime ümbrise fikseerima nii, et meie laiendatud kaart jääks selle sisse.
  5. Seejärel muudame oma kaardi fikseeritud elemendiks, hoides seda siiski oma kohal.
  6. Liigutame oma sisu positsiooni ja suurendame riba riba.
  7. Lõpuks animeerime laienema.

Nüüd vajame lihtsalt kokkuvarisemisfunktsiooni, mis põhimõtteliselt tagurdab kõik ülaltoodud toimingud.

funktsioon collapseElement (collapseButton) {
  // leia element, mida ahendada
  var elementToCollpseParent = collapseButton.parents (". kaart");
  var elementToCollpse = elementToCollpseParent.find (". card-content");
  // leidke kohahoidja asukoht
  var elementToCollpsePlaceholder = elementToCollpse.parents (". kaart");
  var elementToCollpsePlaceholderTop =
    elementToCollpsePlaceholder.offset (). ülaosa - $ ("body"). scrollTop ();
  var elementToCollpsePlaceholderLeft = elementToCollpsePlaceholder.offset ()
    . vasak;
  var elementToCollpsePlaceholderHeight = elementToCollpsePlaceholder.outerHeight ();
  var elementToCollpsePlaceholderWidth = elementToCollpsePlaceholder.outerWidth ();
  elementToCollpse.find (". ribareklaami hoidja"). css ({
    positsioon: "absoluutne"
  });
  // teisendage laius ja kõrgus numbrilisteks väärtusteks
  elementToCollpse.css ({
    paremal: "auto",
    alt: "auto",
    laius: elementToCollpse.outerWidth (),
    kõrgus: elementToCollpse.outerHeight ()
  });
  $ (". list-wrapper"). css ({
    ülemine: 0,
    positsioon: "absoluutne",
    ülevool: "auto",
    "z-register": "1"
  });
  // muudab ribareklaami kõrgust
  var collapsedHeight = elementToCollpse.find (". riba") .andmed ("kõrgus");
  elementToCollpse.find (". ribareklaam"). animeerima ({
      kõrgus: kokku varisenudKõrgus
    },
    kokkuvarisevAnimationTiming,
    "lineaarne"
  );
  // Muudab sisu positsiooni
  var collapsedPosition = elementToCollpse
    .find (". sisemine sisu")
    .data ("positsioon");
  elementToCollpse.find (". sisemine sisu"). animeerima ({
      ülaosa: ahendatudPositsioon
    },
    kokkuvarisevAnimationTiming,
    "lineaarne"
  );
  elementToCollpse.animate ({
    vasakul: elementToCollpsePlaceholderLeft,
    ülaosa: elementToCollpsePlaceholderTop,
    kõrgus: elementToCollpsePlaceholderHeight,
    laius: elementToCollpsePlaceholderWidth
  },
    200, // animatsiooni ajastus millisekkides
    "lineaarne", // animatsiooni kergendamine
    funktsioon () {
      // eemaldab klassi "avatud"
      elementToCollpseParent.removeClass ("avatud");
      elementToCollpse.css ({
        positsioon: "sugulane",
        ülaosa: "auto",
        vasakul: "auto",
        laius: "100%",
        kõrgus: "100%"
      });
    }
  );
  // Peatab keha kerimise takistamise
  $ ("body"). removeClass ("lock");
}

Viimane detail, mida ma pole maininud, on see, et laiendasin kaardi laiendamisel jQuery vaikimisi kergendamise funktsioone koos põrgete efektiga. Võite kasutada jQuery UI, et hõlmata hunnik muid kergendamisfunktsioone, kuid selle näite jaoks oli mul vaja just ühte, nii et kasutasin järgmist:

/ * Laiendab jQuery animeerivat leevendamist * /
$ .easing = Object.assign ({}, $ .easing, {
  easOutBack: funktsioon (x, t, b, c, d, s) {
    if (s == määratlemata) s = 1,70158;
    tagasi c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
  }
});

Nii saame laiendamisel kasutada faili .animate () kergendamise parameetris easyOutBack.

Selles etapis peaks teil olema midagi, mis näeb välja ja töötab nagu minu näide. Jätan teile pastapliiatsi kõigega, millest me just rääkisime.

See on see kõik! Loodan, et naudite seda ja kasutate seda hästi. Kopeerige ja muutke seda vastavalt oma vajadustele.

Jätke kommentaaridesse oma mõtted ja muudatused nendes. Hea meelega näeksid teie andekaid töid.

Järgmise korrani jätkake probleemide lahendamist!