Translate

Поиск по этому блогу

четверг, 16 мая 2019 г.

React-Сайт ( II ). Инфо-основная секция.

Не отступая от выбранной нами концепции написания сайта легкими - функциональными компонентами без состояния, мы сегодня создадим секции сайта несущие основную информацию, сделаем отдельным компонентом многоразового использования - кнопку и много других интересных вещей.




Все статьи этого проекта:




Мы зададим анимацию -плавное, последовательное проявление верхних информационных блоков и "прокрутка" скидки на билет. Если посмотреть на готовое решение, то наши секции должны выглядеть так.



Секция Инфо

В папке Components создадим папку Info, а в ней файл - index.js, в котором создадим react stateless components (rsc) компонент без состояния.

Так, как вверху у нас будут иконки, то мы импортируем их в наш компонент.Иконки я подготовил заранее и поместил их в отдельную папку в папке resources/img/icons

import icon_calendar from '../../resources/img/icons/calendar.png';
import icon_location from '../../resources/img/icons/location.png';

Так как стилизация компонентов, да и приложения в целом, не является целью данного поста, то я для стилизации этих секций воспользуюсь готовыми стилями, которые приведу ниже. Вы же можете написать их самостоятельно

src/resources/style.css
body {
    padding:0;
    margin:0;
    font-family: 'Roboto', sans-serif;
}

.font_righteous {
    font-family: 'Righteous', cursive;
}

.bck_black {
    background: #3c3c3c;
}
.bck_white {
    background: #ffffff;
}
.bck_red {
    background: #ff4800;
}
.bck_yellow {
    background: #ffa800;
}

.iconImage {
    width: 20px;
    margin-right: 10px;
}

.center_wrapper {
    width: 900px;
    margin:0 auto;
}
/* ===============>>> HEADER <<<================ */
header {
    padding:10px 0px;
    transition: all 300ms ease-in;
}

header .header_logo {
    flex-grow: 1;
}
header .header_logo_title {
    text-transform: uppercase;
    font-size: 40px;
}

header .header_logo_caption {
    text-transform: uppercase;
}

/* =================>>> SLIDER <<<================== */
.event_name {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 400px;
    height: 160px;
    -webkit-transform: translate(-50%, -50%);  
    transform: translate(-50%, -50%);   
    border: 2px solid white;
}

.event_name .wrapper {
    border: 2px solid white;
    height: 121px;
    margin: 6px;
    text-align: center;
    color: #fff;
    font-size: 40px;
    padding: 21px 70px 0px 70px;
    text-transform: uppercase;
}
.carrousel_wrapper {
    height:700px;
    overflow: hidden;
}

.carrousel_image {
    
    background-size: cover !important;
}

/*===================>>>  INFO <<======================*/

.vn_wrapper {
    display: flex;
    padding: 170px 0px;
}

.vn_item {
    width:50%;
    color:#ffffff;
    text-align: center;
}
                      
.vn_item .vn_outer {
    width: 300px;
    border: 2px solid #828282;
    margin: auto;
    padding: 5px;
}

.vn_item .vn_inner { 
    border: 2px solid #828282;
    position: relative;
    padding: 0px 20px;
    transition: all 500ms ease;
}

.vn_item .vn_inner:hover {
    background: #3e3e3e;
}

.vn_item .vn_icon_square {
    width: 100px;
    height: 100px;
    position: absolute;
    -ms-transform: rotate(45deg);
    -webkit-transform: rotate(45deg);
    transform: rotate(45deg);
    top: -54px;
    left: 100px;
}

.vn_item .vn_icon {
    width: 50px;
    height: 50px;
    background-size: contain !important;
    background-repeat: no-repeat !important;
    position: absolute;
    top: -30px;
    left: 124px;
}

.vn_item .vn_title {
    font-size: 28px;
    margin: 90px 0px 20px 0px;
    border-bottom: 1px solid #5d5d5d;
    padding-bottom: 10px;
}

.vn_item .vn_desc {
    font-size: 30px;
    font-weight: 300;
    margin-bottom: 90px;
}

/*==============>>> HIGHLIGHT <<<======================*/


.highlight_wrapper h2 {
    text-align: center;
    text-transform: uppercase;
    color:#2c2c2c;
    font-size: 52px;
}

.highlight_wrapper .highlight_description {
    line-height: 30px;
    font-size: 18px;
    font-weight: 300;
    border-top: 1px solid #dddddd;
    border-bottom: 1px solid #dddddd;
    padding: 30px 0px;
}

.discount_wrapper {
    display: flex;
    padding: 70px 0px;
}

.discount_wrapper .discount_porcentage {
    padding-right: 100px;
}

.discount_wrapper .discount_porcentage span:nth-child(1) {
    color: #ff4800;
    font-size: 125px;
    font-weight: 400;
    display: block;
    line-height: 120px;
}

.discount_wrapper .discount_porcentage span:nth-child(2) {
    color: #2c2c2c;
    font-size: 50px;
    font-weight: 400;
    display: block;
}


.discount_wrapper .discount_description h3 {
    font-size: 34px;
    font-weight: 100;
    margin: 0px;
    color: #2c2c2c;
}

.discount_wrapper .discount_description p { 
    line-height: 30px;
    font-size: 18px;
    font-weight: 300;
}


В компоненте Info просто добавим класс диву и выведем его название. Подключим его в App копоненте и проверим на экране.

src/Components/Info/index.js
import React from "react";
import icon_calendar from "../../resources/images/icons/calendar.png";
import icon_location from "../../resources/images/icons/location.png";
const Info = () => {
  return <div className="bck_black">Info</div>;
};

export default Info;



Теперь уберем инлайновый стиль для компонента App, чтоб не мешал и добавим в Info/index.js разметку. Здесь все как обычно. Остановлюсь только на том, что мы используем уже установленный пакет react-reveal его возможность - Zoom для плавного вывода отдельного блока. Второму блоку придали задержку, для того, чтобы показывать их последовательно.

src/Components/Info/index.js
import React from "react";
import Zoom from "react-reveal/Zoom";

import icon_calendar from "../../resources/img/icons/calendar.png";
import icon_location from "../../resources/img/icons/location.png";
const Info = () => {
  return (
    <div className="bck_black">
      <div className="center_wrapper">
        <div className="vn_wrapper">
          <Zoom duration={500}>
            <div className="vn_item">
              <div className="vn_outer">
                <div className="vn_inner">
                  <div className="vn_icon_square bck_red" />
                  <div
                    className="vn_icon"
                    style={{
                      background: `url(${icon_calendar})`
                    }}
                  />
                  <div className="vn_title">Event Date & Time</div>
                  <div className="vn_desc">7 August 2017 @10.00 pm</div>
                </div>
              </div>
            </div>
          </Zoom>

          <Zoom duration={500} delay={500}>
            <div className="vn_item">
              <div className="vn_outer">
                <div className="vn_inner">
                  <div className="vn_icon_square bck_yellow" />
                  <div
                    className="vn_icon"
                    style={{
                      background: `url(${icon_location})`
                    }}
                  />
                  <div className="vn_title">Event Location</div>
                  <div className="vn_desc">
                    345 Speer Street Oakland, CA 9835
                  </div>
                </div>
              </div>
            </div>
          </Zoom>
        </div>
      </div>
    </div>
  );
};

export default Info;



В итоге мы получаем полностью готовую первую секцию с плавным и последовательным выводом на экран двух ее блоков с иконками.



Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Info section"


Основная секция

Сейчас перейдем к созданию основной секции. Если посмотреть на нее, то мы сможем легко и просто разделить её на несколько компонентов.

Для работы, и чтобы в дальнейшем задать отдельную анимацию, мы разобьем этот блок на два больших компонента - Description (на фото желтым) и Discount (зеленым)

Кнопку (красным на фото)создадим отдельным компонентом, который будет многократно использован в этом приложении, поэтому мы можем вынести её в отдельную папку, но подключим ее в файле компонента Discount



Все эти компоненты мы соберем в отдельный файл index.js и уже его подключим и выведем в основной компонент App

Для компонентов Description и Discount и этого файла index.js создадим отдельную папку и назовем ее Highlights

Сразу в ней создадим файл index.js

src/Components/Highlight/index.js
import React from "react";
import Description from "./Description";


const Highlights = () => {
  return (
    <div className="highlight_wrapper">
      <Description />
    </div>
  );
};

export default Highlights;



Пока что здесь нет ничего интересного, просто мы выводим компонент Description, который сейчас и создадим.

src/Components/Highlight/Description.js
import React from "react";
import Fade from "react-reveal/Fade";

const Description = () => {
  return (
    <Fade>
      <div className="center_wrapper">
        <h2>Highlights</h2>
        <div className="highlight_description">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </div>
      </div>
    </Fade>
  );
};

export default Description;



Этот компонент просто выводит текст и разметку. Из интересного есть то, что мы добавили плавное появление его на странице, с помощью все того же пакета react-reveal.

На странице это будет выглядеть так.



Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Description component"


Счетчик скидки - Discount- компонент.

В этой части у нас будет один компонент, который будет содержать текст и счетчик скидки, который при загрузке (первой прокрутке) будет динамически меняться и останавливаться на определенном значении. Тесктовый блок не представляет интереса - это простая разметка. Единственное что, так это то , что мы добавим плавное появление.

Поэтому, хотя и можно условно (да и не только условно :-)) разделить этот компонент на два, я не хочу создавать отдельный компонент для разметки и просто помещу его в вывод одного компонента. Для себя, вы можете сделать иначе. Таким образом у вас получится счетчик для многоразового использования.

Создадим компонент Discount

src/Components/Highlight/Discount.js
import React, { useState, useEffect } from "react";
import Fade from "react-reveal/Fade";
import Slide from "react-reveal/Slide";

const Discount = () => {
  const [discountStart, setDiscountStart] = useState(0);
  const discountEnd = 30;

  const porcentage = () => {
    if (discountStart < discountEnd) {
      setDiscountStart(discountStart + 1);
    }
  };

  useEffect(() => {
    setTimeout(() => {
      porcentage();
    }, 30);
  }, [discountStart]);

  return (
    <div className="center_wrapper">
      <div className="discount_wrapper">
        <Fade onReveal={() => porcentage()}>
          <div className="discount_porcentage">
            <span>{discountStart}%</span>
            <span>OFF</span>
          </div>
        </Fade>

        <Slide right>
          <div className="discount_description">
            <h3>Purchase tickets before 20th JUNE</h3>
            <p>
              Sed ut perspiciatis unde omnis iste natus error sit voluptatem
              accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
              quae ab illo inventore veritatis et quasi architecto beatae vitae
              dicta sunt explicabo.
            </p>
          </div>
        </Slide>
      </div>
    </div>
  );
};

export default Discount;



В этом файле самая интересная часть это счетчик, который выводит последовательность цифр. Это достигается тем, что мы меняем значение переменной "состояния" discountStart в сторону увеличения на единицу.

Каждый раз при изменении переменной "состояния" discountStart у нас обновится наш компонент, потому что мы передали эту переменную, как массив в хук useEffect вторым аргументом. Подробнее я писал об этом в первой части.

Функцию, которая запустить это процесс мы поместили в Fade для того, чтобы отсчет начался с момента показа пользователю этого компонента.



Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Discount component"


Кнопка

Её мы создадим, как и договорились ранее, в отдельной папке в компонентах. Назовем ее произвольно, например Utils MyButton.js. Так как она будет использоваться многократно, то мы будем в нее передавать стили и линк, по которому будет осуществляться переход, в виде props. Сама же кнопка будет иметь иконку, которую также будет получать от родителя.

В данном случае, дольше рассказывать, чем показать.

src/Components/Utils/MyButton.js

import React from "react";
import Button from "@material-ui/core/Button";

import TicketIcon from "../../resources/img/icons/ticket.png";

const MyButton = props => {
  return (
    <Button
      href={props.link}
      variant="contained"
      size="smal"
      style={{
        background: props.bck,
        color: props.color
      }}
    >
      <img src={TicketIcon} className="iconImage" alt="icon_button" />
      {props.text}
    </Button>
  );
};

export default MyButton;



Эту кнопку мы поместим в компонент Discount и передадим ей нужные свойства -props

src/Components/Highlight/Discount.js
import React, { useState, useEffect } from "react";
import MyButton from "../Utils/myButton";
import Fade from "react-reveal/Fade";
import Slide from "react-reveal/Slide";

const Discount = () => {
  const [discountStart, setDiscountStart] = useState(0);
  const discountEnd = 30;

  const porcentage = () => {
    if (discountStart < discountEnd) {
      setDiscountStart(discountStart + 1);
    }
  };

  useEffect(() => {
    setTimeout(() => {
      porcentage();
    }, 30);
  }, [discountStart]);

  return (
    <div className="center_wrapper">
      <div className="discount_wrapper">
        <Fade onReveal={() => porcentage()}>
          <div className="discount_porcentage">
            <span>{discountStart}%</span>
            <span>OFF</span>
          </div>
        </Fade>

        <Slide right>
          <div className="discount_description">
            <h3>Purchase tickets before 20th JUNE</h3>
            <p>
              Sed ut perspiciatis unde omnis iste natus error sit voluptatem
              accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
              quae ab illo inventore veritatis et quasi architecto beatae vitae
              dicta sunt explicabo.
            </p>
            <MyButton
              text="Purchase tickets"
              bck="#ffa800"
              color="#ffffff"
              link="http://google.com"
            />
          </div>
        </Slide>
      </div>
    </div>
  );
};

export default Discount;



Вся прелесть работы с такими компонентами состоит в том, что мы создаем его один раз, а потом можем передать в него иные свойства и отобразить его в другом месте и с несколько иной функцией.



Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Button Component"


                                                                                                                                                             

Телеграм канал - Full Stack JavaScript Developer

Комментариев нет:

Отправить комментарий



Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.Пишите мне - kolesnikovy70 почта gmail.com