[Dragon Boat Festival] Experiencia novedosa, uso reaccionar para realizar todo el proceso de los juegos web (incluido el diseño de reglas)

"Estoy participando en el "Concurso creativo de principios de verano" Para obtener más información, consulte: Concurso creativo de principios de verano "

Inspiración para juegos

Durante el Festival de los Faroles de este año, hubo tareas de tiempo limitado en los minijuegos que jugué, que podían desbloquear elementos limitados por el festival, así que jugué feliz y activamente en esos días. Antes del Dragon Boat Festival, me gustaría desempeñar un papel en el cambio de identidad, desde el cambio de jugador hasta la planificación del juego. Una idea interesante se aclara gradualmente en mi mente.

Si yo fuera un planificador de juegos

Si fuera un planificador de juegos, primero le preguntaría a mi alma tres veces: ¿Cuál es el contenido del evento? ¿Cómo funciona el evento? ¿Cuáles son las recompensas del evento?

Existen ideas generales, y luego se dividen en detalles individuales.

Debido a que algunas colocaciones de escenas, nombres de actividades diarias, títulos, etc. en el juego extraen lecciones del juego "Beauty Biography" al que soy adicto recientemente, este juego es solo para aprender y practicar, no para ningún uso comercial.

perspectiva del producto

Pensando en el diseño de eventos desde la perspectiva del producto, mi perspectiva del producto es la siguiente:

Tan pronto como comienza el verano, espero con ansias las vacaciones. Después del Primero de Mayo, pronto llegará el Festival del Bote del Dragón. Cuando pienso en el Festival del Bote del Dragón, no puedo evitar pensar en deliciosas albóndigas de arroz. Así que aquí viene el Festival del Bote del Dragón, haciendo albóndigas. Como todos sabemos, hacer zongzi requiere materiales esenciales como arroz glutinoso y hojas de zongzi, y hay muchos tipos de rellenos en zongzi, y se necesitan dátiles rojos en esta actividad. Por lo tanto, los materiales para hacer dumplings son arroz glutinoso, hojas de dumpling y dátiles rojos. (Cuál es el contenido del evento)

Hay tareas de recolección diarias en el juego, y los materiales arrojados por cada tarea de recolección son fijos. Durante el evento, los materiales del evento generalmente se dejarán caer por un tiempo limitado, por lo que durante el evento, los materiales necesarios para hacer albóndigas de arroz se dejarán caer durante la recolección diaria y se dejarán caer diferentes materiales para diferentes tareas de recolección. (Cómo jugar el evento)

Si acumula una cierta cantidad de Zongzi, puede cambiarlo por artículos limitados de vacaciones. En general, los elementos del juego limitados por el festival están cuidadosamente diseñados, pero debido al tiempo y la energía limitados, diseñé este evento para que sea relativamente simple. Se pueden intercambiar diferentes cantidades de albóndigas de arroz por diferentes títulos. (Cuál es la recompensa del evento)

(^U^)ノ~YO, todo está listo, comencemos a trabajar.

Diseño interactivo

Dibujé un boceto aproximado del diseño para ayudar a aclarar las ideas de diseño. (Dibujo por primera vez, todavía necesita mejorarse).

página delantera

tareas diarias

Festival del Bote del Dragón

diseño de características

página delantera

contenido

Incluye principalmente la visualización de información del usuario, entrada de tareas, entrada de actividades, etc.

regla de título

El título correspondiente y la cantidad de albóndigas de arroz glutinoso son los siguientes:

título Dumpling de arroz glutinoso cantidad
dama del palacio <50
encanto shuyi >=50 && < 100
primera floración >=100 && < 200
花成蜜就 >=200 && < 300
宠冠六宫 >=300 && < 400
凤仪千载 >=400

功能实现

首页页面

文件路径:/home/index.jsx

/**
 * @description 首页
 */
import React from 'react';
import { useHistory } from 'react-router-dom';
import Avatar from '@/components/Avatar';
import FlowerCluster from '@/components/FlowerCluster';
import { Button } from 'antd-mobile';
import './index.less';

const Home = () => {
  const history = useHistory();

  // 页面跳转
  const goTo = path => {
    history.push(path);
  };

  // 入口展示
  const entranceContent = () => {
    return (
      <div className='home-entrance'>
        <Button block shape='rounded' className='entrance-btn' onClick={() => goTo('/tasks')}>
          日常任务
        </Button>
        <Button block shape='rounded' className='entrance-btn' onClick={() => goTo('/festival')}>
          端午活动
        </Button>
      </div>
    );
  };
  return (
    <div className='home'>
      <div className='home-head'>
        <Avatar />
      </div>
      <div className='home-center'></div>
      <div className='home-bg'>
        {/* 门 */}
        <div className='door'>
          <div className='door-beam'>
            <div className='tiaoliang'></div>
          </div>
          <div className='door-frame'>
            <div className='door-top'></div>
            <div className='door-line door-line-left'></div>
            <div className='door-line door-line-right'></div>
            <div className='door-line door-line-bottom'></div>
            <div className='door-frame'>
              <div className='stick-h stick-h1'></div>
              <div className='stick-h stick-h2'></div>
              <div className='stick-h stick-h3'></div>
              <div className='stick-h stick-h4'></div>
              <div className='stick-h stick-h5'></div>
              <div className='stick-h stick-h6'></div>
              <div className='stick-h stick-h7'></div>
              <div className='stick-h stick-h8'></div>
              <div className='stick-h stick-h9'></div>
              <div className='stick-h stick-h10'></div>
              <div className='stick-h stick-h11'></div>
              <div className='stick-h stick-h12'></div>
              <div className='stick-d stick-d1'></div>
              <div className='stick-d stick-d2'></div>
              <div className='stick-d stick-d3'></div>
              <div className='stick-d stick-d4'></div>
              <div className='stick-d stick-d5'></div>
              <div className='stick-d stick-d6'></div>
            </div>
            <div className='door-opening'>
              <div className='door-opening-center'>{entranceContent()}</div>
              <div className='door-opening-decorate door-opening-decorate1'></div>
              <div className='door-opening-decorate door-opening-decorate2'></div>
              <div className='door-opening-decorate door-opening-decorate3'></div>
              <div className='door-opening-flowers'>
                <FlowerCluster />
              </div>
            </div>
          </div>
        </div>
        {/* 地板 */}
        <div className='floor'>
          <div className='floor-line floor-line1'></div>
          <div className='floor-line floor-line2'></div>
          <div className='floor-line floor-line3'></div>
          <div className='floor-line floor-line4'></div>
          <div className='floor-line floor-line5'></div>
          <div className='floor-line floor-line6'></div>
          <div className='floor-line floor-line7'></div>
          <div className='floor-line floor-line8'></div>
          <div className='floor-line floor-line9'></div>
          <div className='floor-line floor-line10'></div>
          <div className='home-cat'>
            <div className='body'></div>
            <div className='head'>
              <div className='ear ear-left'></div>
              <div className='ear ear-right'></div>
              <div className='nose'></div>
              <div className='whisker whisker-left'></div>
              <div className='whisker whisker-right'></div>
            </div>
            <div className='tail'>
              <div className='tail-line'></div>
              <div className='tail-round'></div>
              <div className='tail-end'></div>
            </div>
          </div>
          <div className='home-table'></div>
        </div>
      </div>
    </div>
  );
};

export default Home;
复制代码

样式:/home/index.less

.home {
  width: 100%;
  height: 100vh;
  position: relative;
  background: #46272d;
  &-head {
    width: 100%;
    height: 60px;
    background: #f3a29f;
    position: relative;
  }
  &-center {
    width: 200px;
    height: 200px;
    z-index: 99;
    margin-top: 60px;
  }
  &-bg {
    width: 100%;
    position: absolute;
    top: 70px;
    left: 0;
    z-index: 10;
    .door {
      &-beam {
        width: 100%;
        height: 90px;
        border-top:3px solid #9b6d59;
        background: #825146;
        position: relative;
        .tiaoliang {
          width: 100%;
          height: 50px;
          background: #4e2e29;
          background-image: repeating-linear-gradient(45deg, transparent, transparent 13px, #9b6d59 13px, #9b6d59 15px), repeating-linear-gradient(-45deg, transparent, transparent 13px, #9b6d59 13px, #9b6d59 15px);
          border-top: 5px solid #f5a672;
          border-bottom: 5px solid #f5a672;
          position: absolute;
          top: 20px;
          left: 0;
        }
      }
      &-frame {
        width: 100%;
        height: 300px;
        position: relative;
        overflow: hidden;
        .door-top {
          width: 100%;
          height: 30px;
          border-top: 4px solid #f5a672;
          border-bottom: 4px solid #f5a672;
          background: #89544c;
          position: absolute;
          top: 0;
          left: 0;
          z-index: 99;
        }
        .door-line {    
          background: #673a35;
          position: absolute;
          z-index: 89;
          &-left{
            width: 15px;
            height: 100%;
            top: 0;
            left: 0;
            border-right: 2px solid #815345;
          }
          &-right{
            width: 15px;
            height: 100%;
            top: 0;
            right: 0;
            border-left: 2px solid #815345;
          }
          &-bottom{
            width: 100%;
            height: 15px;
            bottom: 0;
            left: 0;
            z-index: 87;
            border-top: 2px solid #815345;
          }
        }
        .door-frame {
          width: 100%;
          height: 100%;
          position: absolute;
          top: 0;
          left: 0;
          .stick-h {
            width: 6px;
            height: 100%;
            background: #774747;
            position: absolute;
            top: 50px;
          }
          .stick-h1 {
            left: 30px;
          }
          .stick-h2 {
            left: 70px;
          }
          .stick-h3 {
            left: 85px;
          }
          .stick-h4 {
            left: 100px;
          }
          .stick-h5 {
            left: 115px;
          }
          .stick-h6 {
            left: 130px;
          }
          .stick-h7 {
            right: 130px;
          }
          .stick-h8 {
            right: 115px;
          }
          .stick-h9 {
            right: 100px;
          }
          .stick-h10 {
            right: 85px;
          }
          .stick-h11 {
            right: 70px;
          }
          .stick-h12 {
            right: 30px;
          }
          .stick-d {
            width: 30px;
            height: 6px;
            background: #774747;
            position: absolute;
          }
          .stick-d1 {
            width: 100%;
            top: 50px;
            left: 0;
          }
          .stick-d2 {
            top: 65px;
            left: 86px;
          }
          .stick-d3 {
            width: 20px;
            top: 80px;
            left: 70px;
          }
          .stick-d4 {
            top: 65px;
            right: 86px;
          }
          .stick-d5 {
            width: 20px;
            top: 80px;
            right: 70px;
          }
          .stick-d6 {
            width: 100%;
            bottom: 30px;
            left: 0;
          }
        }
        .door-opening {
          width: 300px;
          height: 300px;
          border-radius: 50%;
          position: absolute;
          top: 35px;
          left: 50%;
          margin-left: -150px;
          background: #7c5655;
          overflow: hidden;
          &-center{
            width: 250px;
            height: 250px;
            border-radius: 50%;
            position: absolute;
            top: 25px;
            left: 25px;
            background: #fff;
          }
          &-decorate {
            width: 50px;
            height: 80px;
            border-radius: 50%;
            background: #f3c068;
            position: absolute;
          }
          &-decorate1 {
            left: -30px;
            top: 100px;
          }
          &-decorate2 {
            left: 50%;
            top: -43px;
            margin-left: -25px;
            transform: rotate(90deg);
          }
          &-decorate3 {
            right: -30px;
            top: 100px;
          }
          &-flowers {
            position: absolute;
            bottom: 55px;
            right: 43px;
            .flowercluster {
              transform: scale(0.85);
            }
          }
        }
      }
    }
    .floor {
      width: 100%;
      height: 300px;
      position: relative;
      background: #946962;
      overflow: hidden;
      &-line {
        width: 1px;
        height: 100%;
        background: linear-gradient( to bottom, #b48e5e 20%, #eebe88 40%, #fce49c 60%, #9f725a 80%, #f7c887 100%);
        position: absolute;
        top: 0;
      }
      &-line1 {
        left: 0;
        transform: rotate(10deg);
      }
      &-line2 {
        left: 10%;
        transform: rotate(10deg);
      }
      &-line3 {
        left: 23%;
        transform: rotate(5deg);
      }
      &-line4 {
        left: 34%;
        transform: rotate(2deg);
      }
      &-line5 {
        left: 45%;
      }
      &-line6 {
        right: 43%;
        transform: rotate(-2deg);
      }
      &-line7 {
        right: 32%;
        transform: rotate(-5deg);
      }
      &-line8 {
        right: 20%;
        transform: rotate(-8deg);
      }
      &-line9 {
        right: 10%;
        transform: rotate(-10deg);
      }
      &-line10 {
        right: 0;
        transform: rotate(-10deg);
      }
    }
  }
  &-cat {
    width: 200px;
    height: 60px;
    position: absolute;
    top: 95px;
    right: 10px;
    .body {
      width: 110px;
      height: 50px;
      background-color: #745341;
      position: absolute;
      top: -4px;
      border-top-left-radius: 90px;
      border-top-right-radius: 90px;
      animation: catbody 10s none infinite;
    }
    @keyframes catbody {
      5% {
        transform: scaleY(1);
      }
      10% {
        transform: scaleY(1.15);
      }
      15% {
        transform: scaleY(1);
      }
      20% {
        transform: scaleY(1.25);
      }
      25% {
        transform: scaleY(1);
      }
      30% {
        transform: scaleY(1.15);
      }
      40% {
        transform: scaleY(1);
      }
      50% {
        transform: scaleY(1.15);
      }
    }
    .head {
      width: 70px;
      height: 34px;
      background-color: #745341;
      position: absolute;
      top: 13px;
      left: -45px;
      border-top-left-radius: 70px;
      border-top-right-radius: 70px;
    }
    .ear {
      width: 0;
      height: 0;
      position: absolute;
      left: 5px;
      top: -4px;
      border-left: 12px solid transparent;
      border-right: 12px solid transparent;
      border-bottom: 20px solid #745341;
      transform: rotate(-30deg);
      animation: catearleft 10s both infinite;
    }
    .ear-right {
      top: -11px;
      left: 21px;
      animation: catearright 10s both infinite;
    }
    @keyframes catearleft {
      0% {
        transform: rotate(-20deg);
      }
      5% {
        transform: rotate(-5deg);
      }
      15% {
        transform: rotate(-15deg);
      }
      25% {
        transform: rotate(-15deg);
      }
      35% {
        transform: rotate(-30deg);
      }
      40% {
        transform: rotate(-30deg);
      }
      45% {
        transform: rotate(0deg);
      }
      50% {
        transform: rotate(0deg);
      }
      80% {
        transform: rotate(-15deg);
      }
      90% {
        transform: rotate(-5deg);
      }
      100% {
        transform: rotateZ(-5deg);
      }
    }
    @keyframes catearright {
      0% {
        transform: rotateZ(-15deg);
      }
      15% {
        transform: rotateZ(-20deg);
      }
      25% {
        transform: rotateZ(-20deg);
      }
      30% {
        transform: rotateZ(-30deg);
      }
      34% {
        transform: rotateZ(-20deg);
      }
      38% {
        transform: rotateZ(-30deg);
      }
      40% {
        transform: rotateZ(-20deg);
      }
      42% {
        transform: rotateZ(-20deg);
      }
      44% {
        transform: rotateZ(-30deg);
      }
      45% {
        transform: rotateZ(-20deg);
      }
      50% {
        transform: rotateZ(-10deg);
      }
      55% {
        transform: rotateZ(-10deg);
      }
      60% {
        transform: rotateZ(-20deg);
      }
      61% {
        transform: rotateZ(-30deg);
      }
      62% {
        transform: rotateZ(-20deg);
      }
      63% {
        transform: rotateZ(-20deg);
      }
      64% {
        transform: rotateZ(-30deg);
      }
      65% {
        transform: rotateZ(-20deg);
      }
      80% {
        transform: rotateZ(-20deg);
      }
      90% {
        transform: rotateZ(-15deg);
      }
      100% {
        transform: rotateZ(-15deg);
      }
    }
    .nose {
      width: 5px;
      height: 5px;
      background-color: #dc9d90;
      position: absolute;
      bottom: 10px;
      left: 30px;
      border-radius: 50%;
    }
    .whisker {
      width: 16px;
      height: 10px;
      position: absolute;
      bottom: 5px;
      left: 7px;
      transform-origin: right;
    }
    .whisker::before,
    .whisker::after {
      content: '';
      width: 100%;
      position: absolute;
      top: 0;
      border: 1px solid #fff;
      transform-origin: 100% 0;
      transform: rotate(10deg);
    }
    .whisker::after {
      transform: rotate(-20deg);
    }
    .whisker-left {
      animation: catwhiskerleft 10s both infinite;
    }
    .whisker-right {
      left: 27px;
      bottom: 12px;
      transform: rotate(180deg);
      animation: catwhiskerright 10s both infinite;
    }
    @keyframes catwhiskerleft {
      5% {
        transform: rotate(0);
      }
      10% {
        transform: rotate(0deg);
      }
      15% {
        transform: rotate(-5deg);
      }
      20% {
        transform: rotate(0deg);
      }
      25% {
        transform: rotate(0deg);
      }
      30% {
        transform: rotate(10deg);
      }
      40% {
        transform: rotate(-5deg);
      }
      50% {
        transform: rotate(10deg);
      }
    }
    @keyframes catwhiskerright {
      5% {
        transform: rotate(180deg);
      }
      10% {
        transform: rotate(190deg);
      }
      15% {
        transform: rotate(180deg);
      }
      20% {
        transform: rotate(175deg);
      }
      25% {
        transform: rotate(190deg);
      }
      30% {
        transform: rotate(180deg);
      }
      40% {
        transform: rotate(185deg);
      }
      50% {
        transform: rotate(175deg);
      }
    }
    .tail {
      width: 14px;
      height: 100px;
      position: absolute;
      top: 42px;
      right: 90px;
      z-index: 99;
    }
    .tail-line {
      width: 14px;
      height: 60px;
      background: #745341;
      position: absolute;
      left: 0;
      top: 0;
      z-index: 99;
    }
    .tail-round {
      width: 48px;
      height: 48px;
      background: #745341;
      position: absolute;
      top: 36px;
      left: -34px;
      border-radius: 50%;
    }
    .tail-round::before {
      content: '';
      width: 20px;
      height: 20px;
      background: #946962;
      position: absolute;
      top: 14px;
      left: 14px;
      border-radius: 50%;
    }
    .tail-round::after {
      content: '';
      width: 48px;
      height: 22px;
      background: #946962;
      position: absolute;
      top: 0;
      left: 0;
    }
    .tail-end {
      width: 14px;
      height: 10px;
      background: #745341;
      border-radius: 14px 14px 0 0;
      position: absolute;
      bottom: 39px;
      left: -34px;
      z-index: 99;
    }
  }
  &-table {
    width: 200px;
    height: 20px;
    background-color: #e3895e;
    position: absolute;
    top: 140px;
    right: 80px;
    border-radius: 20px;
    z-index: 9;
  }
  &-entrance {
    position: absolute;
    top: 60px;
    left: 35px;
    .entrance-btn {
      width: 180px;
      line-height: 28px;
      font-size: 16px;
      font-weight: 600;
      color: #fff;
      border: 0;
      background-image: linear-gradient(to right, #ed6ea0, #ec8c69, #f7186a, #FBB03B);
      background-size: 300% 100%;
      box-shadow: 0 4px 15px 0 #ed6ea0;
      margin-bottom: 20px;
      animation: 5s ease-in-out entrance infinite;
    }
  }
}
@keyframes entrance {
  0% {
    background-image: linear-gradient(to right, #ed6ea0, #ec8c69, #f7186a, #FBB03B);
    background-size: 300% 100%;
  }
  100% {
    background-image: linear-gradient(to right, #FBB03B, #ec8c69, #f7186a, #ed6ea0);
    background-position: 100% 0;
  }
}
复制代码

头像组件

文件路径:/components/Avatar/index.jsx

/**
 * @description 头像组件
 */
import React from 'react';
import './index.less';
import util from '../../utils/util';

const Avatar = () => {
  const userInfo = util.getUserInfo() || {};

  const getDesignationByZongziNum = () => {
    const festival = userInfo.festival ? userInfo.festival : {};
    const zongzi = festival.zongzi ? festival.zongzi : 0;
    let name = '殿上佳人';
    if (zongzi < 50) {
      name = '殿上佳人';
    } else if (zongzi <= 100) {
      name = '淑仪倾城';
    } else if (zongzi <= 200) {
      name = '花容初绽';
    } else if (zongzi <= 300) {
      name = '花成蜜就';
    } else if (zongzi <= 400) {
      name = '宠冠六宫';
    } else if (zongzi > 400) {
      name = '凤仪千载';
    }
    return name;
  };

  return (
    <div className='avatar'>
      <img className='avatar-img' src='https://p6-passport.byteacctimg.com/img/user-avatar/c6c1a335a3b48adc43e011dd21bfdc60~300x300.image' alt='' />
      <div className='avatar-nickname'>叶一一</div>
      <div className='avatar-designation'>
        <span>{getDesignationByZongziNum()}</span>
        <div className='avatar-flower'>
          <div className='avatar-flower-leaf avatar-flower-leaf1'></div>
          <div className='avatar-flower-leaf avatar-flower-leaf2'></div>
          <div className='avatar-flower-leaf avatar-flower-leaf3'></div>
          <div className='avatar-flower-leaf avatar-flower-leaf4'></div>
          <div className='avatar-flower-leaf avatar-flower-leaf5'></div>
          <div className='avatar-flower-circle'></div>
        </div>
      </div>
    </div>
  );
};

export default Avatar;
复制代码

样式:/components/Avatar/index.less

.avatar {
  width: 100%;
  height: 60px;
  position: relative;
  &-img {
    width: 70px;
    height: 70px;
    border-radius: 50%;
    z-index: 99;
    position: absolute;
    left: 10px;
    bottom: -20px;
    border: 3px solid #c03e34;
  }
  &-nickname {
    height: 24px;
    line-height: 24px;
    background: #ff8fa7;
    border-radius: 24px;
    position: absolute;
    left: 60px;
    top: 35px;
    color: #fff;
    font-size: 14px;
    font-weight: 300;
    text-align: center;
    z-index: 89;
    border:1px solid #fff;
    padding: 0 10px 0 25px;
  }
  &-designation {
    width: 110px;
    height: 32px;
    line-height: 32px;
    position: absolute;
    right: 0;
    top: 15px;
    z-index: 89;
    text-align: center;
    padding-left: 10px;
    border-radius: 28px 0 0 0;
    background-color: #f0ecfc;
    background-image: linear-gradient(315deg,#ffeded 0,#fed6d6 74%);
    span {
      font-size: 18px;
      color: #fff;
      text-shadow:  1px 1px #ffb53a,-1px -1px #ffb53a,1px -1px #ffb53a,-1px 1px #ffb53a;
    }
    
  }
  &-flower {
    position: absolute;
    top: 5px;
    left: 10px;
    transform: rotate(-30deg) scale(0.8);
    &-leaf {
      position: absolute;
      border-radius: 51% 49% 47% 53%;
      background-color: #a7ffee;
      background-image: linear-gradient(to top, #ffeded 15%, #ff8fa7 100%);
      transform-origin: bottom center;
      opacity: 0.9;
      box-shadow: inset 0 0 6px #fed6d6;
    }
    &-leaf1 {
      width: 28px;
      height: 34px;
      bottom: -10px;
      left: -14px;
      transform: translate(-10%, 1%) rotateY(40deg) rotateX(-50deg);
    }
    &-leaf2 {
      width: 23px;
      height: 32px;
      bottom: -4px;
      left: -5px;
      transform: translate(-50%, -4%) rotateX(40deg);
    }
    &-leaf3 {
      width: 28px;
      height: 30px;
      bottom: -3px;
      left: 0px;
      transform: translate(-90%, 0%) rotateY(45deg) rotateX(50deg);
    }
    &-leaf4 {
      width: 28px;
      height: 24px;
      bottom: -5px;
      left: 6px;
      transform: translate(-61%, -19%) rotateX(67deg) rotate(193deg);
    }
    &-leaf5 {
      width: 28px;
      height: 25px;
      bottom: -5px;
      left: -4px;
      transform: translate(-55%, -20%) rotateX(71deg) rotate(211deg);
    }
    &-circle {
      position: absolute;
      left: -12px;
      top: -10px;
      width: 16px;
      height: 8px;
      border-radius: 50%;
      background-color: #fdfd8e;
    }
  }
}
复制代码

花丛组件

这个是参考的网站是的,参考地址我放到了文章末尾。

文件路径:/components/FlowerCluster/index.jsx

/**
 * @description 花丛组件
 */
import React from 'react';
import './index.less';

const FlowerCluster = () => {
  return (
    <div className='flowercluster'>
      <div className='flower-leaves'></div>
      <div className='bunch'>
        <div className='flower'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='flower'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='flower'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
      </div>
    </div>
  );
};
export default FlowerCluster;
复制代码

样式:/components/FlowerCluster/index.less

.flowercluster {
  width: 60px;
  height: 60px;
  .flowers:after {
    content: '';
    position: absolute;
    width: 60px;
    height: 35px;
    background-color: rgba(0, 0, 0, 0.1);
    bottom: 0;
    z-index: -2;
    border-radius: 100%;
    left: -10px;
    bottom: -15px;
  }
  .flower-leaves {
    position: relative;
    width: 100%;
    height: 20px;
    background-color: #a8e6ba;
    border-radius: 100% 10%;
    top: 80%;
    left: 5px;
    box-shadow: -1px 1px black, 1px 1px black, 1px -1px black;
  }
  .flower-leaves:before,
  .flower-leaves:after {
    content: '';
    position: absolute;
    background-color: #a8e6ba;
  }
  .flower-leaves:before {
    width: 60px;
    height: 20px;
    border-radius: 100% 10%;
    transform: rotate(30deg);
    right: 10px;
    box-shadow: -1px 1px black, 1px 1px black, 1px -1px black;
  }
  .flower-leaves:after {
    width: 50px;
    height: 20px;
    border-radius: 100% 10%;
    transform: rotate(15deg);
    top: 2px;
  }
  .flower {
    position: absolute;
    width: 30px;
    height: 30px;
  }
  .flower:after {
    content: '';
    position: absolute;
    width: 8px;
    height: 8px;
    border-radius: 100%;
    left: 6px;
    top: 8px;
    background-image: radial-gradient(8px 8px at center, #9379aa 30%, #521c81 41%, 60%, transparent);
  }
  .flower > .petal {
    position: absolute;
    width: 10px;
    height: 10px;
    background-color: #f8f8ff;
    background-image: linear-gradient(45deg, #f8f8ff, #d3cce3);
    border-radius: 50% 80%;
    box-shadow: -0.04em -0.04em purple, -0.05em -0.05em black;
  }
  .flower > .petal:nth-child(1) {
    transform: rotate(40deg);
    left: 5px;
    top: 2px;
  }
  .flower > .petal:nth-child(2) {
    transform: rotate(-20deg);
    top: 6px;
    left: 0;
  }
  .flower > .petal:nth-child(3) {
    transform: rotate(-90deg);
    top: 12px;
    left: 2px;
  }
  .flower > .petal:nth-child(4) {
    transform: rotate(180deg);
    top: 12px;
    left: 10px;
  }
  .flower > .petal:nth-child(5) {
    transform: rotate(100deg);
    top: 5px;
    left: 10px;
  }
  .bunch .flower:nth-child(1) {
    left: 33px;
    transform: scale(1.5) rotate(30deg);
  }
  .bunch .flower:nth-child(2) {
    left: 10px;
    transform: scale(1.5) rotate(-20deg);
  }
  .bunch .flower:nth-child(3) {
    left: 25px;
    top: 40px;
    transform: scale(1.5) rotate(5deg);
  }
}
复制代码

最终UI

设计为古代的室内,参考的《美人传》小游戏中的UI设计,包括木质的墙壁、门和地板。除此之外还加了一些动画效果增加趣味性:

  • 称号上面加了一个花朵做装饰;
  • 任务和活动入口上加了光效闪动的效果;
  • 地板上的猫咪耳朵和肚子随着呼吸而动;

日常任务

日常任务收集规则

  • 每天0点开始进行资源生产,每个小时生产1万资源,不足1个小时的时候不产生,满足1个小时的时候产生;
  • 可以进行资源收集,每次收集完成,对应的资源值进行叠加;
  • 不同资源收集时,随机掉落不同的活动材料。对应如下:
任务名称 活动材料名称 活动材料数量
开源节流 粽叶 5~10
助宫易物 糯米 5~10
布施济民 红枣 2~5

功能实现

日常页面

文件路径:/tasks/index.jsx

/**
 * @description 日常任务
 */
import React, { useState, useEffect } from 'react';
import classnames from 'classnames';
import moment from 'moment';
import Back from '@/components/Back';
import Flower from '@/components/Flower';
import FlowerTree from '@/components/FlowerTree';
import { Modal } from 'antd-mobile';
import { QuestionCircleFill, KoubeiFill, FireFill, HeartFill } from 'antd-mobile-icons';
import util from '../../utils/util';
import './index.less';

const Tasks = () => {
  const userInfo = util.getUserInfo() || {};
  const [tasksObj, setTasksObj] = useState(
    userInfo.tasks
      ? userInfo.tasks
      : {
          zheng: 0,
          cai: 0,
          mei: 0,
          creatAt: 0,
        },
  );
  const listInit = [
    {
      key: 'zheng',
      title: '政',
      name: '开源节流',
      num: 0,
      harvestFalg: true,
      taskKey: 'zongye',
      icon: <KoubeiFill fontSize={16} color='#fcb887' />,
    },
    {
      key: 'cai',
      title: '才',
      name: '助宫易物',
      num: 0,
      harvestFalg: true,
      taskKey: 'nuomi',
      icon: <FireFill fontSize={16} color='#f6f6f6' />,
    },
    {
      key: 'mei',
      title: '魅',
      name: '布施济民',
      num: 0,
      harvestFalg: true,
      taskKey: 'hongzao',
      icon: <HeartFill fontSize={16} color='#59ca94' />,
    },
  ];

  const [list, setList] = useState(listInit);

  // 获取当前内务展示数据
  const getNewNum = () => {
    // 梯龄换算成月
    const newData = new Date();
    let diffData = tasksObj.creatAt;
    if (!tasksObj.creatAt) {
      // 如果收获时间默认活动开始时间
      diffData = moment('2022-06-01');
    }
    let hour = moment(newData).diff(moment(diffData), 'hours');

    console.log(hour, 'hour');
    let numCurr = hour * 1000;
    const listInit = [...list];
    listInit.map(item => {
      item.num += numCurr;
    });
    setList(listInit);
  };

  useEffect(() => {
    getNewNum();
  }, []);

  // 获取随机数
  const getRandomNumber = key => {
    const randomObj = {
      zheng: [5, 10],
      cai: [5, 10],
      mei: [2, 5],
    };
    const randomItem = randomObj[key];
    const m = randomItem[1];
    const n = randomItem[0];
    let randomNum = Math.random() * (m - n) + n;
    randomNum = Math.round(randomNum);
    console.log(randomNum, 'randomNum');
    return randomNum;
  };

  // 收获
  const handleHarvest = index => {
    const newData = new Date();
    let userInfoInit = { ...userInfo };
    const handleList = [].concat(list);
    let item = handleList[index];
    let tasksObjInit = { ...tasksObj };
    tasksObjInit.creatAt = newData;
    const festivalObjInit = userInfo.festival
      ? userInfo.festival
      : {
          nuomi: 0,
          zongye: 0,
          hongzao: 0,
          zongzi: 0,
        };
    // 收获操作
    if (item.harvestFalg) {
      tasksObjInit[item.key] += item.num;
      item.num = 0;
      festivalObjInit[item.taskKey] = getRandomNumber(item.key);
      // 设置缓存
      userInfoInit.festival = festivalObjInit;
      userInfoInit.tasks = tasksObjInit;
      util.saveUserInfo(userInfoInit);
      setList(list);
      setTasksObj(tasksObjInit);
    }
    item.harvestFalg = !item.harvestFalg;
    setList(handleList);
  };

  // 顶部提示
  const headTip = () => {
    return Modal.show({
      title: '内务',
      content: (
        <div className='tasks-modal'>
          <div className='tasks-modal-title'>内务打理</div>
          <div className='tasks-modal-content mb10'>
            <p className='mb10'>内务分为“开源节流”,“助宫易物”,“布施济民”三种类型,分别可以获得铜币、珍品和名望。</p>
            <p>打理内务有一定几率获得包粽子的材料。</p>
          </div>
          <div className='tasks-modal-title'>内务奖励</div>
          <div className='tasks-modal-content'>
            <p className='mb10'>开源节流有一定几率获得粽叶。</p>
            <p className='mb10'>助宫易物有一定几率获得糯米。</p>
            <p>布施济民有一定几率获得红枣。</p>
          </div>
        </div>
      ),
      showCloseButton: true,
    });
  };

  // 将数据除以10000进行展示
  const getTaskNumContent = num => {
    num = num / 10000;
    return num;
  };

  return (
    <div className='tasks'>
      <Back />
      <div className='tasks-info'>
        {list.map(item => {
          return (
            <div className='tasks-info-item' key={item.key}>
              <div className='tasks-info-item-icon'>{item.icon}</div>
              <span>
                {getTaskNumContent(tasksObj[item.key])} {tasksObj[item.key] > 0 ? '万' : ''}
              </span>
            </div>
          );
        })}
      </div>
      <div className='tasks-head'>
        <div className='tasks-head-tip' onClick={headTip}>
          <QuestionCircleFill fontSize={28} color='#f69bad' />
        </div>
        <div className='tasks-head-title'>内务打理</div>
      </div>
      <div className='tasks-list'>
        {list.map((item, index) => {
          return (
            <div className='tasks-item' key={item.key}>
              <div className='tasks-item-top'></div>
              <div className='tasks-item-title'>{item.title}</div>
              <div className='tasks-item-name'>
                <span>{item.name}</span>
                <div className='name-circular name-circular1'></div>
                <div className='name-circular name-circular2'></div>
                <div className='name-circular name-circular3'></div>
                <div className='name-circular name-circular4'></div>
                <div className='name-circular name-circular5'></div>
                <div className='name-circular name-circular6'></div>
              </div>
              <div className='tasks-item-num'>{item.num}</div>
              <div className={classnames('tasks-item-btn', { inactive: !item.harvestFalg })} onClick={() => handleHarvest(index)}>
                <div className='btn-flower1'>
                  <Flower />
                </div>
                <div className='btn-flower2'>
                  <Flower />
                </div>
                <span>{item.harvestFalg ? '收获' : '恢复'}</span>
              </div>
            </div>
          );
        })}
      </div>
      <div className='tasks-footer'></div>
      <div className='tasks-tree'>
        <FlowerTree />
      </div>
      <div className='tasks-rule'>
        <div className='tasks-rule-title'>
          <span>宫规</span>
        </div>
        <div className='tasks-rule-text'>内务收获 +5%</div>
      </div>
    </div>
  );
};

export default Tasks;
复制代码

样式:/tasks/index.less

.tasks {
  width: 100%;
  max-width: 100%;
  height: 100vh;
  background: #ffe7e7;
  padding-top: 36px;
  position: relative;
  overflow: hidden;
  &-info {
    width: 70%;
    position: absolute;
    top: 15px;
    right: 5px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    &-item {
      width: 28%;
      height: 17px;
      border-radius: 0 20px 20px 0; 
      background: #a5888c;
      position: relative;
      span {
        font-size: 12px;
        color: #fff;
        line-height: 17px;
        text-align: center;
        position: absolute;
        left: 10px;
        top: 0;
        z-index: 99;
      }
      &-icon {
        position: absolute;
        top: -3px;
        left: -16px;
        width: 22px;
        height: 22px;
        border-radius: 50%;
        background: #a5888c;
        z-index: 89;
        display: flex;
        justify-content: center;
        align-items: center;
      }
    }
  }
  &-head {
    width: 100%;
    height: 50px;
    display: flex;
    justify-content: flex-start;
    align-items: center;
    &-tip {
      margin-left: 80px;
      margin-right: 40px;
    }
    &-title {
      color: #a08cc9;
      line-height: 50px;
      font-size: 26px;
      text-align: center;
      font-weight: 500;
    }
  }
  &-modal {
    width: 100%;
    padding: 0 5px;
    &-title {
      position: relative;
      height: 22px;
      line-height: 22px;
      text-align: center;
      margin-bottom: 10px;
      color: #af8368;
      &::before {
        content: '';
        width: 40px;
        height: 2px;
        background: #eec2c1;
        position: absolute;
        top: 10px;
        left: 15px;
        border-radius: 2px;
      }
      &::after {
        content: '';
        width: 40px;
        height: 2px;
        background: #eec2c1;
        position: absolute;
        top: 10px;
        right: 15px;
        border-radius: 2px;
      }
    }
    p {
      line-height: 1.4;
      font-weight: 300;
      font-size: 13px;
      position: relative;
      padding-left: 10px;
      &::before {
        content: '';
        width: 4px;
        height: 4px;
        background: #af8368;
        border-radius: 50%;
        position: absolute;
        top: 6px;
        left: -2px;

      }
    }
  }
  &-list {
    display: flex;
    justify-content:space-between;
    padding: 0 15px;
    margin-top: 25px;
    z-index: 20;
  }
  &-item {
    width: 30%;
    height: 340px;
    border: 5px solid #ffb4c0;
    border-radius: 0 0 30px 30px;
    position: relative;
    &:nth-child(2) {
      .tasks-item-title {
        background: #8fc4f6;
      }
    }
    &:nth-child(3) {
      .tasks-item-title {
        background: #da9ce9;
      }
    }
    &-top {
      position: absolute;
      left: -13px;
      top: -11px;
      height: 10px;
      width: 124%;
      background: #fca0ab;
      border: 1px solid #f5d896;
      border-radius: 10px;
    }
    &-title {
      position: absolute;
      left: 2px;
      top: 2px;
      width: 42px;
      height: 42px;
      border: 2px solid #fff;
      background: #fcaf5d;
      color: #fff;
      font-size: 20px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    &-name {
      position: absolute;
      top: 60px;
      left: 7px;
      width: 80px;
      height: 80px;
      transform: scale(0.85);
      span {
        display: block;
        width: 40px;
        line-height: 15px;
        font-size: 15px;
        color: #b67b53;
        position: absolute;
        top: 43%;
        left: 34%;
        z-index: 99;
      }
      .name-circular {
        width: 36px;
        height: 36px;
        background: #ffb4c0;
        border-radius: 50%;
        position: absolute;
      }
      .name-circular1  {
        top: 3px;
        left: 20px;
      }
      .name-circular2  {
        top: 20px;
        left: 46px;
      }
      .name-circular3  {
        top: 49px;
        left: 38px;
      }
      .name-circular4  {
        top: 51px;
        left: 11px;
      }
      .name-circular5  {
        top: 25px;
        left: -1px;
      }
      .name-circular6  {
        width: 45px;
        height: 45px;
        top: 26px;
        left: 20px;
        background: #ffe7e7;
      }
    }
    &-num {
      position: absolute;
      bottom: 120px;
      left: 5px;
      width: 90%;
      height: 22px;
      line-height: 22px;
      border: 1px solid #f6e2db;
      background: #fff;
      color: #89775f;
      font-size: 14px;
      font-weight: 300;
      border-radius: 22px;
      text-align: center;
    }
    &-btn {
      position: absolute;
      bottom: 30px;
      left: 15px;
      width: 64px;
      height: 64px;
      line-height: 64px;
      border: 1px solid #fed18d;
      background: #fff6d6;
      border-radius: 50%;
      text-align: center;
      &::after {
        content: '';
        width: 56px;
        height: 56px;
        background: #fed18d;
        border-radius: 50%;
        position: absolute;
        top: 3px;
        left: 3px;
        z-index: 89;
      }
      span {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        color: #fff;
        font-size: 16px;
        z-index: 99;
      }
      .btn-flower1 {
        position: absolute;
        top: -6px;
        left: 36px;
        z-index: 990;
        transform: scale(0.9);
      }
      .btn-flower2 {
        position: absolute;
        top: -2px;
        left: 40px;
        z-index: 990;
        transform: scale(0.6);
      }
      &.inactive {
        border: 1px solid #e5c7fd;
        &::after {
          background: #e5c7fd;
        }
      }
    }
    &::before {
      content: '';
      width: 60px;
      height: 45px;
      background: #ffe7e7;
      position: absolute;
      bottom: -49px;
      left: 20px;
      z-index: 99;
      border-radius: 0;
    }
    &::after {
      content: '';
      width: 60px;
      height: 60px;
      background: #ffe7e7;
      border: 5px solid #ffb4c0;
      border-radius: 50%;
      position: absolute;
      bottom: -44px;
      left: 20px;
      z-index: 98;
    }
  }
  &-footer {
    position: absolute;
    top: 480px;
    left: -10%;
    background: #ffc3d2;
    width: 120%;
    height: 100px;
    border-radius: 0 0 50% 50%;
    z-index: 1;
    &::before {
      position: absolute;
      bottom: 10px;
      left: 10px;
      background: #ffc3d2;
      width: 200px;
      height: 200px;
    }
    &::after {
      content: '';
      position: absolute;
      bottom: 20px;
      left: -10%;
      background: #ffe7e7;
      width: 120%;
      height: 100px;
      border-radius: 0 0 50% 50%;
    }
  }
  &-tree {
    position: absolute;
    top: 88%;
    left: 60px;
    z-index: 999;
  }
  &-rule {
    width: 200px;
    position: absolute;
    top: 88%;
    right: 10px;
    &-title {
      width: 70px;
      height: 70px;
      line-height: 70px;
      position: absolute;
      top: 0;
      left: 0;
      background: #fff;
      border: 2px solid #f8d4d6;
      border-radius: 50%;
      text-align: center;
      z-index: 90;
      span {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        color: #fff;
        font-size: 18px;
        z-index: 99;
      }
      &::after {
        content: '';
        width: 60px;
        height: 60px;
        background: #deb4fc;
        border-radius: 50%;
        position: absolute;
        top: 3px;
        left: 3px;
        z-index: 89;
      }
    }
    &-text {
      width: 140px;
      line-height: 36px;
      font-size: 15px;
      font-weight: 300;
      color: #e34f4b;
      text-align: center;
      position: absolute;
      left: 56px;
      top: 16px;
      border-radius: 0 36px 36px 0;
      background: #fbf1ef;
      z-index: 80;
    }
  }
}
复制代码

返回组件

文件路径:/components/Back/index.jsx

/**
 * @description 回退按钮组件
 */
import React from 'react';
import PropTypes from 'prop-types';
import { useHistory } from 'react-router-dom';
import './index.less';

const Back = ({ ...props }) => {
  const history = useHistory();
  const { path } = props;

  // 点击事件
  const handleClick = () => {
    history.push(path);
  };

  return (
    <div className='back' onClick={handleClick}>
      <div className='back-left'></div>
      <div className='back-right'></div>
    </div>
  );
};
Back.propTypes = {
  path: PropTypes.string, // 跳转路径
};

Back.defaultProps = {
  path: '/home',
};

export default Back;
复制代码

样式:/components/Back/index.less

.back {
  width: 56px;
  height: 56px;
  background: #f69bad;
  border: 2px solid #fef4f3;
  border-radius: 50%;
  position: absolute;
  top: 10px;
  left: 10px;
  &-left {
    position: absolute;
    top: 21px;
    left: 10px;
    &::before {
      content: '';
      width: 22px;
      height: 3px;
      background: #fff;
      position: absolute;
      top: 10px;
      left: 0;
      transform: rotate(30deg);
      border-radius: 2px;
    }
    &::after {
      content: '';
      width: 22px;
      height: 3px;
      background: #fff;
      position: absolute;
      top: -1px;
      left: 0;
      transform: rotate(-30deg);
      border-radius: 2px;
    }
  }
  &-right {
    position: absolute;
    top: 27px;
    left: 27px;
    &::before {
      content: '';
      position: absolute;
      bottom: 0;
      left: 0;
      width: 0;
      height: 0;
      border-bottom: 4px solid #fff;
      border-right: 8px solid transparent;
      border-left: 8px solid transparent;
    }
    &::after {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: 0;
      height: 0;
      border-top: 4px solid #fff;
      border-right: 8px solid transparent;
      border-left: 8px solid transparent;
    }
  }
}
复制代码

花朵组件

文件路径:/components/Flower/index.jsx

/**
 * @description 花朵组件
 */
import React from 'react';
import './index.less';

const Flower = () => {
  return (
    <div className='flower'>
      <div className='flower-leaf flower-leaf1'></div>
      <div className='flower-leaf flower-leaf2'></div>
      <div className='flower-leaf flower-leaf3'></div>
      <div className='flower-leaf flower-leaf4'></div>
      <div className='flower-leaf flower-leaf5'></div>
      <div className='flower-circle'></div>
    </div>
  );
};
export default Flower;
复制代码

样式:/components/Flower/index.less

.flower {
  width: 50px;
  height: 50px;
  transform: rotate(-3deg);
  &-leaf {
    position: absolute;
    width: 6px;
    height: 8px;
    border-radius: 51% 49% 47% 53%;
    background-color: #a7ffee;
    background-image: linear-gradient(to top, #ffeded 15%, #ff8fa7 100%);
  }
  &-leaf1 {
    top: 2px;
    left: 2px;
    transform: rotate(-3deg);
    z-index: 999;
  }
  &-leaf2 {
    top: 5px;
    left: 7px;
    transform: rotate(60deg);
    z-index: 998;
  }
  &-leaf3 {
    top: 10px;
    left: 6px;
    transform: rotate(160deg);
    z-index: 997;
  }
  &-leaf4 {
    top: 11px;
    left: 0px;
    transform: rotate(200deg);
    z-index: 996;
  }
  &-leaf5 {
    top: 6px;
    left: -2px;
    transform: rotate(-75deg);
    z-index: 995;
  }
  &-circle {
    position: absolute;
    left: 3px;
    top: 8px;
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background-color: #fff3b4;
    border: 1px solid #fff;
  }
}
复制代码

开满花的树组件

这个是参考的网站是的,参考地址我放到了文章末尾。

文件路径:/components/FlowerTree/index.jsx

/**
 * @description 开满花的树组件
 */
import React from 'react';
import './index.less';

const FlowerTree = () => {
  return (
    <div className='flowertree'>
      <div className='trunk'>
        <div className='roots'>
          <div className='root'></div>
          <div className='root'></div>
          <div className='root'></div>
          <div className='root'></div>
          <div className='root'></div>
        </div>
      </div>
      <div className='leaves cherry-blossoms'>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
        <div className='cherry-blossom'>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
          <div className='petal'></div>
        </div>
      </div>
    </div>
  );
};
export default FlowerTree;
复制代码

样式:/components/FlowerTree/index.less

.flowertree {
  width: 60px;
  height: 200px;
  transform:  scale(1.1);
  .trunk {
    width: 58%;
    height: 30%;
    background-color: #df916a;
    border-left: 0.09em solid black;
    border-right: 0.09em solid black;
    box-shadow: inset 0 30px 10px #a64f24;
  }
  .trunk:after {
    content: '';
    position: absolute;
    width: 60px;
    height: 28px;
    background-color: rgba(0, 0, 0, 0.2);
    top: 40px;
    z-index: -2;
    right: 15px;
    border-radius: 100%;
  }
  .trunk .roots {
    position: relative;
    z-index: -1;
    top: 95%;
    display: flex;
    justify-content: center;
    align-items: center;
    width: 110%;
    height: auto;
    left: -5%;
  }
  .trunk .roots .root {
    flex: 1 1 0;
    background-color: #dd8b62;
    height: 15px;
    margin: 0 -1px;
    border-radius: 10px;
  }
  .trunk .roots .root:nth-child(1) {
    transform: rotate(30deg);
  }
  .trunk .roots .root:nth-child(2) {
    transform: rotate(15deg);
  }
  .trunk .roots .root:nth-child(3) {
    transform: rotate(0deg);
  }
  .trunk .roots .root:nth-child(4) {
    transform: rotate(-15deg);
  }
  .trunk .roots .root:nth-child(5) {
    transform: rotate(-30deg);
  }
  .trunk .roots .root:first-child {
    box-shadow: -1px 1px #d0632d, -0.1em 1px black;
  }
  .trunk .roots .root:nth-child(n + 2):nth-child(-n + 4) {
    box-shadow: 0 1px #d0632d, 0em 1px black;
  }
  .trunk .roots .root:last-child {
    box-shadow: 1px 1px #d0632d, 0.1em 1px black;
  }
  .leaves,
  .cherry-blossoms {
    position: relative;
    width: 60px;
    height: 60px;
    top: -120px;
    left: -12px;
    background-color: #4cbda4;
    border-radius: 100%;
    box-shadow: inset 4px -10px #41af97;
    border: 1px solid #3a9c87;
    border: 0.1em solid black;
  }
  .leaves:before,
  .cherry-blossoms:before,
  .leaves:after,
  .cherry-blossoms:after {
    content: '';
    position: absolute;
    width: 60px;
    height: 60px;
    background-color: #4cbda4;
    border-radius: 100%;
    top: 35px;
    border: 1px solid #3a9c87;
    border: 0.1em solid black;
    border-top: 0;
    border-bottom: 2px solid #91451f;
    box-shadow: inset 4px -10px #41af97;
  }
  .leaves:before,
  .cherry-blossoms:before {
    left: -20px;
  }
  .leaves:after,
  .cherry-blossoms:after {
    left: 20px;
  }
  .leaves .leaf,
  .cherry-blossoms .leaf {
    position: absolute;
    width: 10px;
    height: 15px;
    background-color: #4cbda4;
    background-image: linear-gradient(to bottom, #097465, transparent);
    border-radius: 10% 80%;
    border-bottom: 1px solid #2c7766;
    border-right: 1px solid #2c7766;
    transform: scale(0.5);
  }
  .leaves .leaf:before,
  .cherry-blossoms .leaf:before,
  .leaves .leaf:after,
  .cherry-blossoms .leaf:after {
    content: '';
    position: absolute;
    background-color: #4cbda4;
    background-image: linear-gradient(to bottom, #008b79, transparent);
    width: 7px;
    height: 12px;
    border-radius: 10% 80%;
    border-right: 1px solid #338a76;
  }
  .leaves .leaf:before,
  .cherry-blossoms .leaf:before {
    left: -3px;
    transform: rotate(40deg);
    border-left: 1px solid #338a76;
  }
  .leaves .leaf:after,
  .cherry-blossoms .leaf:after {
    left: 4px;
    top: -2px;
    transform: rotate(-40deg);
    border-bottom: 1px solid #338a76;
  }
  .leaves .leaf:nth-child(1),
  .cherry-blossoms .leaf:nth-child(1) {
    top: 85px;
    transform: scale(0.8) rotate(-25deg);
  }
  .leaves .leaf:nth-child(2),
  .cherry-blossoms .leaf:nth-child(2) {
    top: 80px;
    left: -15px;
  }
  .leaves .leaf:nth-child(3),
  .cherry-blossoms .leaf:nth-child(3) {
    top: 85px;
    left: 15px;
    transform: scale(1.1) rotate(-25deg);
  }
  .leaves .leaf:nth-child(4),
  .cherry-blossoms .leaf:nth-child(4) {
    top: 80px;
    left: 25px;
    z-index: 3;
  }
  .leaves .leaf:nth-child(5),
  .cherry-blossoms .leaf:nth-child(5) {
    top: 85px;
    left: 60px;
    z-index: 2;
    transform: scale(0.9) rotate(70deg);
  }
  .leaves .leaf:nth-child(6),
  .cherry-blossoms .leaf:nth-child(6) {
    top: 80px;
    left: 45px;
    z-index: 2;
    transform: scale(1.2) rotate(50deg);
  }
  .leaves .leaf:nth-child(7),
  .cherry-blossoms .leaf:nth-child(7) {
    top: 72px;
    left: -20px;
  }
  .leaves .leaf:nth-child(8),
  .cherry-blossoms .leaf:nth-child(8) {
    top: 75px;
    left: -1px;
    transform: scale(1.02) rotate(-25deg);
  }
  .leaves .leaf:nth-child(9),
  .cherry-blossoms .leaf:nth-child(9) {
    top: 70px;
    left: 10px;
  }
  .leaves .leaf:nth-child(10),
  .cherry-blossoms .leaf:nth-child(10) {
    top: 55px;
    left: -20px;
    transform: scale(1.5) rotate(18deg);
  }
  .leaves .leaf:nth-child(11),
  .cherry-blossoms .leaf:nth-child(11) {
    top: 60px;
    transform: scale(0.9);
    left: -5px;
  }
  .leaves .leaf:nth-child(12),
  .cherry-blossoms .leaf:nth-child(12) {
    z-index: 2;
    left: 28px;
    top: 60px;
  }
  .leaves .leaf:nth-child(13),
  .cherry-blossoms .leaf:nth-child(13) {
    z-index: 2;
    left: 40px;
    top: 70px;
    transform: rotate(40deg);
  }
  .leaves .leaf:nth-child(14),
  .cherry-blossoms .leaf:nth-child(14) {
    z-index: 2;
    left: 55px;
    top: 70px;
    transform: rotate(60deg) scale(1.3);
  }
  .leaves .leaf:nth-child(15),
  .cherry-blossoms .leaf:nth-child(15) {
    z-index: 2;
    left: 25px;
    top: 70px;
    transform: rotate(-20deg) scale(1.1);
  }
  .leaves .leaf:nth-child(16),
  .cherry-blossoms .leaf:nth-child(16) {
    z-index: 2;
    left: 70px;
    top: 70px;
    transform: rotate(50deg) scale(0.7);
  }
  .leaves .leaf:nth-child(17),
  .cherry-blossoms .leaf:nth-child(17) {
    z-index: 2;
    left: 70px;
    top: 60px;
    transform: scale(0.6) rotate(60deg);
  }
  .leaves .leaf:nth-child(18),
  .cherry-blossoms .leaf:nth-child(18) {
    z-index: 2;
    left: 50px;
    top: 60px;
    transform: scale(0.7) rotate(60deg);
  }
  .leaves .leaf:nth-child(19),
  .cherry-blossoms .leaf:nth-child(19) {
    z-index: 2;
    left: 65px;
    top: 48px;
    transform: scale(1.5) rotate(50deg);
  }
  .leaves .leaf:nth-child(20),
  .cherry-blossoms .leaf:nth-child(20) {
    z-index: 2;
    left: 40px;
    top: 50px;
    transform: scale(0.8) rotate(70deg);
  }
  .leaves .leaf:nth-child(21),
  .cherry-blossoms .leaf:nth-child(21) {
    z-index: 2;
    left: 58px;
    top: 35px;
    transform: scale(0.7) rotate(60deg);
  }
  .leaves .leaf:nth-child(22),
  .cherry-blossoms .leaf:nth-child(22) {
    z-index: 2;
    left: 20px;
    top: 55px;
    transform: scale(1) rotate(60deg);
  }
  .leaves .leaf:nth-child(23),
  .cherry-blossoms .leaf:nth-child(23) {
    z-index: 2;
    left: 40px;
    top: 60px;
    transform: scale(0.4) rotate(60deg);
  }
  .leaves .leaf:nth-child(24),
  .cherry-blossoms .leaf:nth-child(24) {
    z-index: 2;
    left: 10px;
    top: 60px;
    transform: scale(0.6) rotate(-10deg);
  }
  .leaves .leaf:nth-child(25),
  .cherry-blossoms .leaf:nth-child(25) {
    left: -12px;
    top: 38px;
    transform: scale(1) rotate(60deg);
  }
  .leaves .leaf:nth-child(26),
  .cherry-blossoms .leaf:nth-child(26) {
    left: -5px;
    top: 45px;
    transform: scale(0.8) rotate(60deg);
  }
  .leaves .leaf:nth-child(27),
  .cherry-blossoms .leaf:nth-child(27) {
    left: 8px;
    top: 52px;
    transform: scale(1) rotate(-10deg);
  }
  .leaves .leaf:nth-child(28),
  .cherry-blossoms .leaf:nth-child(28) {
    z-index: 2;
    left: 50px;
    top: 52px;
    transform: scale(1) rotate(-10deg);
  }
  .leaves .leaf:nth-child(29),
  .cherry-blossoms .leaf:nth-child(29) {
    left: 48px;
    top: 10px;
    transform: scale(1) rotate(-10deg);
  }
  .leaves .leaf:nth-child(30),
  .cherry-blossoms .leaf:nth-child(30) {
    left: 30px;
    top: -5px;
    transform: scale(0.8) rotate(-10deg);
  }
  .leaves .leaf:nth-child(31),
  .cherry-blossoms .leaf:nth-child(31) {
    left: 20px;
    top: -5px;
    transform: scale(0.5) rotate(-10deg);
  }
  .leaves .leaf:nth-child(32),
  .cherry-blossoms .leaf:nth-child(32) {
    left: 40px;
    top: -3px;
    transform: scale(0.5) rotate(-10deg);
  }
  .leaves .leaf:nth-child(33),
  .cherry-blossoms .leaf:nth-child(33) {
    left: 10px;
    top: 10px;
    transform: scale(0.7) rotate(-10deg);
  }
  .leaves .leaf:nth-child(34),
  .cherry-blossoms .leaf:nth-child(34) {
    left: 0;
    top: 25px;
    transform: scale(1) rotate(-10deg);
  }
  .leaves .leaf:nth-child(35),
  .cherry-blossoms .leaf:nth-child(35) {
    left: 20px;
    top: 30px;
    transform: scale(1.2) rotate(0deg);
  }
  .leaves .leaf:nth-child(36),
  .cherry-blossoms .leaf:nth-child(36) {
    left: 50px;
    top: 30px;
    transform: scale(1.2) rotate(60deg);
  }
  .leaves .leaf:nth-child(37),
  .cherry-blossoms .leaf:nth-child(37) {
    left: 50px;
    top: 30px;
    transform: scale(1.2) rotate(60deg);
  }
  .leaves .leaf:nth-child(38),
  .cherry-blossoms .leaf:nth-child(38) {
    left: 50px;
    top: 20px;
    transform: scale(1) rotate(40deg);
  }
  .leaves .leaf:nth-child(39),
  .cherry-blossoms .leaf:nth-child(39) {
    left: 40px;
    top: 23px;
    transform: scale(0.8) rotate(50deg);
  }
  .leaves .leaf:nth-child(40),
  .cherry-blossoms .leaf:nth-child(40) {
    left: 30px;
    top: 20px;
    transform: scale(0.5) rotate(-20deg);
  }
  .leaves .leaf:nth-child(40),
  .cherry-blossoms .leaf:nth-child(40) {
    left: 30px;
    top: 10px;
    transform: scale(1.5) rotate(-20deg);
  }
  .leaves .leaf:nth-child(41),
  .cherry-blossoms .leaf:nth-child(41) {
    left: 1px;
    top: 15px;
    transform: scale(0.9) rotate(-5deg);
  }
  .leaves .leaf:nth-child(42),
  .cherry-blossoms .leaf:nth-child(42) {
    left: 10px;
    top: 18px;
    transform: scale(0.5) rotate(5deg);
  }
  .leaves .leaf:nth-child(44),
  .cherry-blossoms .leaf:nth-child(44) {
    left: 5px;
    top: 35px;
    transform: scale(1.5) rotate(-5deg);
  }
  .leaves .leaf:nth-child(45),
  .cherry-blossoms .leaf:nth-child(45) {
    z-index: 2;
    left: 38px;
    top: 35px;
    transform: scale(1.8) rotate(70deg);
  }
  .leaves .leaf:nth-child(46),
  .cherry-blossoms .leaf:nth-child(46) {
    left: 10px;
    top: 5px;
    transform: scale(1.3) rotate(70deg);
  }
  .cherry-blossoms {
    background-color: #ffd9df;
    border: 0.05em solid black;
    box-shadow: none;
  }
  .cherry-blossoms:before,
  .cherry-blossoms:after {
    background-color: #ffd9df;
    border: 0.05em solid black;
    border-top: 0;
    box-shadow: none;
  }
  .cherry-blossoms:after {
    border-left: none;
  }
  .cherry-blossoms .cherry-blossom {
    position: absolute;
    width: 20px;
    height: 20px;
  }
  .cherry-blossoms .cherry-blossom .petal {
    position: absolute;
    width: 10px;
    height: 10px;
    background-color: #ffd9df;
    background-image: linear-gradient(-45deg, #ec87bf 10%, #ffd9df 65%);
    border-radius: 20% 80%;
    box-shadow: -0.04em -0.04em #ec87bf, -0.05em -0.05em black;
  }
  .cherry-blossoms .cherry-blossom .petal:nth-child(1) {
    transform: rotate(40deg);
    left: 25%;
    top: 2px;
  }
  .cherry-blossoms .cherry-blossom .petal:nth-child(2) {
    transform: rotate(-20deg);
    top: 6px;
    left: 0;
  }
  .cherry-blossoms .cherry-blossom .petal:nth-child(3) {
    transform: rotate(-90deg);
    top: 12px;
    left: 2px;
  }
  .cherry-blossoms .cherry-blossom .petal:nth-child(4) {
    transform: rotate(180deg);
    top: 12px;
    left: 10px;
  }
  .cherry-blossoms .cherry-blossom .petal:nth-child(5) {
    transform: rotate(100deg);
    top: 5px;
    left: 10px;
  }
  .cherry-blossoms .cherry-blossom:nth-child(1) {
    z-index: 2;
    left: 30px;
    transform: rotate(10deg) scale(0.8);
    top: 70px;
  }
  .cherry-blossoms .cherry-blossom:nth-child(2) {
    z-index: 2;
    left: 50px;
    transform: rotate(20deg) scale(0.4);
    top: 65px;
  }
  .cherry-blossoms .cherry-blossom:nth-child(3) {
    z-index: 2;
    left: 45px;
    top: 75px;
    transform: scale(0.6);
  }
  .cherry-blossoms .cherry-blossom:nth-child(4) {
    z-index: 2;
    left: 45px;
    top: 50px;
    transform: scale(1);
  }
  .cherry-blossoms .cherry-blossom:nth-child(5) {
    z-index: 2;
    left: 58px;
    top: 70px;
    transform: scale(0.5);
  }
  .cherry-blossoms .cherry-blossom:nth-child(6) {
    z-index: 2;
    left: 62px;
    top: 55px;
    transform: scale(0.5);
  }
  .cherry-blossoms .cherry-blossom:nth-child(7) {
    z-index: 2;
    left: 55px;
    top: 35px;
    transform: scale(0.8);
  }
  .cherry-blossoms .cherry-blossom:nth-child(8) {
    z-index: 2;
    left: 30px;
    top: 55px;
    transform: scale(0.5);
  }
  .cherry-blossoms .cherry-blossom:nth-child(9) {
    z-index: 2;
    left: 30px;
    top: 30px;
    transform: scale(1.1);
  }
  .cherry-blossoms .cherry-blossom:nth-child(10) {
    z-index: 2;
    left: 15px;
    top: 50px;
    transform: scale(0.9);
  }
  .cherry-blossoms .cherry-blossom:nth-child(11) {
    z-index: 2;
    left: -7px;
    top: 40px;
    transform: scale(1.1);
  }
  .cherry-blossoms .cherry-blossom:nth-child(12) {
    left: 6px;
    top: 60px;
    transform: scale(0.8);
  }
  .cherry-blossoms .cherry-blossom:nth-child(13) {
    left: 10px;
    top: 75px;
    transform: scale(0.5);
  }
  .cherry-blossoms .cherry-blossom:nth-child(14) {
    left: 0;
    top: 75px;
    transform: scale(0.8);
  }
  .cherry-blossoms .cherry-blossom:nth-child(15) {
    left: -15px;
    top: 65px;
    transform: scale(0.7);
  }
  .cherry-blossoms .cherry-blossom:nth-child(16) {
    left: -22px;
    top: 55px;
    transform: scale(0.5);
  }
  .cherry-blossoms .cherry-blossom:nth-child(17) {
    left: 5px;
    top: 18px;
    transform: scale(1.2) rotate(-40deg);
  }
  .cherry-blossoms .cherry-blossom:nth-child(18) {
    left: 30px;
    top: 10px;
    transform: scale(1);
  }
  .cherry-blossoms .cherry-blossom:nth-child(19) {
    left: 45px;
    top: 18px;
    transform: scale(0.5);
  }
  .cherry-blossoms .cherry-blossom:nth-child(20) {
    left: 12px;
    top: 38px;
    transform: scale(0.5);
  }
  .cherry-blossoms .cherry-blossom:nth-child(21) {
    left: 12px;
    top: 0;
    transform: scale(0.7) rotate(60deg);
  }
  .cherry-blossoms .cherry-blossom:nth-child(22) {
    left: 25px;
    top: -2px;
    transform: scale(0.4) rotate(60deg);
  }
  .cherry-blossoms .cherry-blossom:nth-child(23) {
    left: -20px;
    top: 45px;
    transform: scale(0.4) rotate(60deg);
  }
  .fruits {
    position: absolute;
    width: 100px;
    height: 100px;
    top: -50%;
    left: -50%;
    z-index: 2;
  }
  .fruits .pear {
    position: absolute;
    width: 13px;
    height: 15px;
    background-color: #ffef94;
    border-radius: 100%;
    box-shadow: inset 2px 0 #fad500;
    border: 0.003em solid black;
  }
  .fruits .pear:before,
  .fruits .pear:after {
    content: '';
    position: absolute;
  }
  .fruits .pear:before {
    width: 20px;
    height: 20px;
    background-color: #ffef94;
    border-radius: 100%;
    top: 6px;
    border-right: 0.003em solid black;
    box-shadow: inset 2px -2px #fad500, -0.06em 0.08em black;
  }
  .fruits .pear:after {
    width: 5px;
    height: 8px;
    border-left: 2px solid #641b1b;
    border-radius: 100%;
    top: -7px;
    left: 3px;
  }
}
复制代码

公共方法

Ruta de la página: /utils/util.js

/**
 * @description 公共方法
 */

// 获取用户信息
const getUserInfo = () => {
  let userInfo = localStorage.getItem('userInfo');
  if (userInfo) {
    return JSON.parse(userInfo);
  }
  return null;
};

// 保存用户信息
const saveUserInfo = userInfo => {
  if (userInfo) {
    localStorage.setItem('userInfo', JSON.stringify(userInfo));
  }
};

/**
 * 两个是否可以整除
 * @param {number} num1 除数
 * @param {number} num2 被除数
 * @return {boolean} 是否整除的布尔值
 */
const getNumDivisibleFlag = (num1, num2) => {
  let flag = false;
  // 如果除数小于被除数 则表示不可以被整除
  if (num1 > num2 && num1 / num2 > 1) {
    flag = true;
  }
  return flag;
};

export default { getUserInfo, saveUserInfo, getNumDivisibleFlag };
复制代码

interfaz de usuario final

Festival del Bote del Dragón

regla de actividad

tiempo de actividad

1. Del 2022-5-31 al 2022-6-5, caliente con 3 días de anticipación.

2. Establecer la cuenta regresiva del evento en la página

  • Antes del final del evento, muestra cuánto tiempo queda hasta el final del evento, el formato de tiempo es DD días hh: mm: ss;
  • Una vez finalizado el evento, el contenido de la pantalla es "El evento ha finalizado";

Reglas de intercambio

relación de intercambio de alimentos

Tipo Zong Zi materiales necesitados
albóndigas de arroz glutinoso 10 * arroz glutinoso + 2 * hojas de zong + 2 * dátiles rojos

reglas de intercambio de alimentos

  1. El canje se realiza a través del botón de la página. Cuando la cantidad de ingredientes es insuficiente, no se puede hacer clic en el botón y se puede hacer clic cuando la cantidad de ingredientes es suficiente.
  2. Haga clic en el botón de redención para evocar la ventana emergente de redención. Puede modificar la cantidad de redención a través de los signos más y menos. Cuando se alcanza el valor máximo canjeable, no se puede hacer clic en el signo más.
  3. Después de que se confirme el intercambio, la cantidad de albóndigas de arroz aumentará y la cantidad de ingredientes disminuirá en consecuencia.

Realización de funciones

página del evento

Ruta del archivo: /festival/index.jsx

/**
 * @description 端午活动
 */
import React, { useEffect, useState } from 'react';
import classnames from 'classnames';
import moment from 'moment';
import Back from '@/components/Back';
import { Modal, Stepper } from 'antd-mobile';
import { QuestionCircleFill } from 'antd-mobile-icons';
import util from '../../utils/util';
import './index.less';

const Festival = () => {
  const userInfo = util.getUserInfo() || {};
  const [festivalObj, setFestivalObj] = useState(
    userInfo.festival
      ? userInfo.festival
      : {
          nuomi: 20,
          zongye: 10,
          hongzao: 10,
          zongzi: 150,
        },
  );
  const list = [
    {
      key: 'nuomi',
      name: '糯米',
    },
    {
      key: 'zongye',
      name: '粽叶',
    },
    {
      key: 'hongzao',
      name: '红枣',
    },
    {
      key: 'zongzi',
      name: '粽子',
    },
  ];

  // 是否可以进行兑换操作的布尔值 true-能 false-不能
  const [activeFlag, setActiveFlag] = useState(false);

  const [visible, setVisible] = useState(false);
  const [count, setCount] = useState(0);
  // 兑换的粽子数量
  const [convertNum, setConvertNum] = useState(1);
  const [countdown, setCountdown] = useState('');
  let timer = null;

  // 获取当前兑换按钮是否可以点击
  const getInactiveFlag = festivalObj => {
    let activeInit = false;
    let nuomi = festivalObj.nuomi;
    let zongye = festivalObj.zongye;
    let hongzao = festivalObj.hongzao;
    if (nuomi && zongye && hongzao) {
      let nuomiFlag = util.getNumDivisibleFlag(nuomi, 10);
      let zongyeFlag = util.getNumDivisibleFlag(zongye, 2);
      let hongzaoFlag = util.getNumDivisibleFlag(hongzao, 2);
      if (nuomiFlag && zongyeFlag && hongzaoFlag) {
        activeInit = true;
      }
    }
    setActiveFlag(activeInit);
  };

  const getCountdown = () => {
    let nowDate = new Date();
    // console.log(nowDate, 'nowDate');
    // 获取的2022-06-05的23:59:59的时间戳
    let endTime = moment('2022-06-05').endOf('day').format('x');

    let countdownInit = '';
    // 剩余时间 毫秒
    let surplusTime = endTime - nowDate.getTime();
    if (surplusTime <= 0) {
      clearTimeout(timer);
      countdownInit = '活动已结束';
      setCountdown(countdownInit);
    } else {
      // 剩余时间 秒
      let runTime = surplusTime / 1000;
      const day = Math.floor(runTime / 86400);
      runTime = runTime % 86400;
      const hour = Math.floor(runTime / 3600);
      runTime = runTime % 3600;
      const minute = Math.floor(runTime / 60);
      runTime = runTime % 60;
      const second = Math.floor(runTime);
      const dayText = day ? `${day}天` : '';
      countdownInit = `剩余时间:${dayText} ${hour}:${minute}:${second}`;
      setCountdown(countdownInit);
      timer = setTimeout(getCountdown, 1000);
    }
  };

  useEffect(() => {
    getInactiveFlag(festivalObj);
    getCountdown();
  }, []);

  useEffect(() => {
    // 清除定时
    return () => {
      clearInterval(timer);
    };
  }, []);

  // 顶部提示
  const headTip = () => {
    return Modal.show({
      title: '"粽"得凤仪',
      content: (
        <div className='festival-modal'>
          <div className='festival-modal-title'>合成粽子</div>
          <div className='festival-modal-content mb10'>
            <p className='mb10'>10*糯米+2*粽叶+2*红枣可以兑换1个糯米粽子。</p>
            <p>当糯米、粽叶、红枣的比例不是5:1:1时,无法进行兑换。</p>
          </div>
          <div className='festival-modal-title'>称号奖励</div>
          <div className='festival-modal-content'>
            <p className='mb10'>当前粽子数量达到50个可获得称号“淑仪倾城”。</p>
            <p className='mb10'>当前粽子数量达到100个可获得称号“花容初绽”。</p>
            <p className='mb10'>当前粽子数量达到200个可获得称号“花成蜜就”。</p>
            <p className='mb10'>当前粽子数量达到300个可获得称号“宠冠六宫”。</p>
            <p className='mb10'>当前粽子数量达到400个可获得称号“凤仪千载”。</p>
            <p>称号自动获取无需额外操作</p>
          </div>
        </div>
      ),
      showCloseButton: true,
    });
  };

  // 粽子展示
  const zongziContent = () => {
    return (
      <div className='festival-zongzi'>
        <div className='festival-zongzi-left'></div>
        <div className='festival-zongzi-center'></div>
        <div className='festival-zongzi-right'></div>
      </div>
    );
  };

  // 兑换确定操作
  const convertOnConfirm = () => {
    setVisible(false);
    let festivalObjInit = { ...festivalObj };
    console.log(convertNum, 'convertNum');
    festivalObjInit.nuomi -= convertNum * 10;
    festivalObjInit.zongye -= convertNum * 2;
    festivalObjInit.hongzao -= convertNum * 2;
    festivalObjInit.zongzi += convertNum;
    console.log(festivalObjInit, 'festivalObjInit');
    // 设置缓存
    let userInfoInit = { ...userInfo };
    userInfoInit.festival = festivalObjInit;
    util.saveUserInfo(userInfoInit);
    setFestivalObj(festivalObjInit);
    getInactiveFlag(festivalObjInit);
  };

  // 获取可以兑换的数量
  const getConvertCount = () => {
    let nuomi = festivalObj.nuomi;
    let zongye = festivalObj.zongye;
    let hongzao = festivalObj.hongzao;
    let nuomiNum = Math.floor((nuomi * 100) / (10 * 100));
    let zongyeNum = Math.floor((zongye * 100) / (2 * 100));
    let hongzaoNum = Math.floor((hongzao * 100) / (2 * 100));
    return Math.min(nuomiNum, zongyeNum, hongzaoNum);
  };

  // 兑换操作
  const handleConvert = () => {
    if (!activeFlag) return;
    const count = getConvertCount();
    setConvertNum(1);
    setCount(count);
    setVisible(true);
  };

  return (
    <div className='festival'>
      <div className='festival-content'>
        <Back />
        <div className='festival-head'>
          <div className='festival-head-tip' onClick={headTip}>
            <QuestionCircleFill fontSize={28} color='#f69bad' />
          </div>
          <div className='festival-head-title'>"粽"得凤仪</div>
        </div>
        <div className='festival-time'>{countdown}</div>
        <div className='festival-convert'>
          <div className='festival-convert-zongzi'>{zongziContent()}</div>
          <div className='festival-convert-zongzi2'>{zongziContent()}</div>
          <div className='festival-convert-num'>
            {list.map(item => {
              return (
                <div className='festival-convert-num-item' key={item.key}>
                  {item.name}: {festivalObj[item.key]}
                </div>
              );
            })}
          </div>
          <div className='festival-convert-rule'>
            <div className='festival-convert-rule-nuomi'></div> x 10<div className='festival-convert-rule-add'></div>
            <div className='festival-convert-rule-zongye'></div> x 2<div className='festival-convert-rule-add'></div>
            <div className='festival-convert-rule-hongzao'></div> x 2
          </div>
          <div className={classnames('festival-convert-btn', { inactive: !activeFlag })} onClick={handleConvert}>
            兑换
          </div>
        </div>
      </div>
      <div className='festival-room'>
        <div className='festival-room-wall'>
          <div className='wall-poetry'>
            <div className='wall-poetry-nail'></div>
            <div className='wall-poetry-shaft wall-poetry-shaft-top'></div>
            <div className='wall-poetry-inner'>
              <div className='wall-poetry-title'>浣溪沙·端午</div>
              <div className='wall-poetry-author'>宋·苏轼</div>
              <div className='wall-poetry-content'>轻汗微微透碧纨,明朝端午浴芳兰。流香涨腻满晴川。彩线轻缠红玉臂,小符斜挂绿云鬟。佳人相见一千年。</div>
            </div>
            <div className='wall-poetry-shaft wall-poetry-shaft-bottom'></div>
          </div>
        </div>
        <div className='festival-room-floor'>
          <div className='floor-line floor-line1'></div>
          <div className='floor-line floor-line2'></div>
          <div className='floor-line floor-line3'></div>
          <div className='floor-line floor-line4'></div>
          <div className='floor-line floor-line5'></div>
          <div className='floor-line floor-line6'></div>
          <div className='floor-line floor-line7'></div>
          <div className='floor-line floor-line8'></div>
          <div className='floor-line floor-line9'></div>
          <div className='floor-line floor-line10'></div>
        </div>
      </div>
      <Modal
        visible={visible}
        content={
          <div className='festival-modal'>
            <div className='festival-modal-text'>最多可以兑换: {count}</div>
            <div className='festival-modal-stepper'>
              <Stepper
                step={1}
                value={convertNum}
                min={1}
                max={count}
                onChange={value => {
                  setConvertNum(value);
                }}
              />
            </div>
            <div className='festival-modal-confirm' onClick={() => convertOnConfirm()}>
              兑换
            </div>
          </div>
        }
        showCloseButton={true}
        closeOnAction
        onClose={() => {
          setVisible(false);
        }}
      />
    </div>
  );
};

export default Festival;
复制代码

estilo: /festival/index.less

.festival {
  width: 100%;
  max-width: 100%;
  height: 100vh;
  background: #46272d;
  position: relative;
  overflow: hidden;
  &-content {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 999;
    width: 100%;
    height: 100%;
  }
  &-room {
    width: 100%;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 99;
    &-wall {
      width: 100%;
      height: 450px;
      background: #e8dfe0;
      position: relative;
      .wall-poetry  {
        position: absolute;
        top: 180px;
        left: 100px;
        width: 170px;
        height: 230px;
        background: #c1a98f;
        &-nail {
          width: 14px;
          height: 14px;
          position: absolute;
          top: -60px;
          left: 50%;
          border: 2px solid #ffedbb;
          border-radius: 50%;
          background: #b5a9a9;
          margin-left: -7px;
          &::before {
            content: '';
            position: absolute;
            top: 27px;
            left: 0;
            width: 90px;
            height: 3px;
            border-radius: 3px;
            background: #b5a9a9;
            transform: rotate(30deg);
            z-index: 20;
          }
          &::after {
            content: '';
            position: absolute;
            top: 27px;
            right: 0;
            width: 90px;
            height: 3px;
            border-radius: 3px;
            background: #b5a9a9;
            transform: rotate(-30deg);
            z-index: 20;
          }
        }
        
        &-shaft {
          position: absolute;
          left: -18px;
          height: 8px;
          width: 120%;
          background: #a97e78;
          border-radius: 8px;
          z-index: 90;
          &-top {
            top: -8px;
          }
          &-bottom {
            bottom: -8px;
          }
          &::before {
            content: '';
            position: absolute;
            top: 0;
            left: 15px;
            height: 100%;
            width: 10px;
            background: #c8b044;
          }
          &::after {
            content: '';
            position: absolute;
            top: 0;
            right: 15px;
            height: 100%;
            width: 10px;
            background: #c8b044;
          }
        }
        
        &-inner {
          position: absolute;
          top: 20px;
          left: 20px;
          width: 130px;
          height: 190px;
          background: #ece8e5;
          font-size: 13px;
          font-weight: 300;
          color: #333;
          padding: 15px 13px;
          z-index: 99;
        }
        &-title {
          font-size: 14px;
          line-height: 1.5;
          margin-bottom: 8px;
        }
        &-author {
          line-height: 1.5;
          margin-bottom: 5px;
        }
      }
    }
    &-floor {
      width: 100%;
      height: 250px;
      position: relative;
      background: #946962;
      overflow: hidden;
      .floor { 
        &-line {
          width: 1px;
          height: 100%;
          background: linear-gradient( to bottom, #b48e5e 20%, #eebe88 40%, #fce49c 60%, #9f725a 80%, #f7c887 100%);
          position: absolute;
          top: 0;
        }
        &-line1 {
          left: 0;
          transform: rotate(10deg);
        }
        &-line2 {
          left: 10%;
          transform: rotate(10deg);
        }
        &-line3 {
          left: 23%;
          transform: rotate(5deg);
        }
        &-line4 {
          left: 34%;
          transform: rotate(2deg);
        }
        &-line5 {
          left: 45%;
        }
        &-line6 {
          right: 43%;
          transform: rotate(-2deg);
        }
        &-line7 {
          right: 32%;
          transform: rotate(-5deg);
        }
        &-line8 {
          right: 20%;
          transform: rotate(-8deg);
        }
        &-line9 {
          right: 10%;
          transform: rotate(-10deg);
        }
        &-line10 {
          right: 0;
          transform: rotate(-10deg);
        }
      }
    }
  }
  &-head {
    width: 100%;
    height: 50px;
    display: flex;
    justify-content: flex-start;
    align-items: center;
    margin-top: 30px;
    &-tip {
      margin-left: 80px;
      margin-right: 30px;
    }
    &-title {
      color: #67b898;
      line-height: 50px;
      font-size: 28px;
      text-align: center;
      font-weight: 500;
    }
  }
  &-time {
    line-height: 30px;
    width: 60%;
    text-align: center;
    margin: 5px auto;
    font-size: 13px;
    color: #e45453;
    background: #fff;
    border-radius: 30px;
  }
  &-modal {
    width: 100%;
    padding: 0 5px;
    &-title {
      position: relative;
      height: 22px;
      line-height: 22px;
      text-align: center;
      margin-bottom: 10px;
      color: #af8368;
      &::before {
        content: '';
        width: 40px;
        height: 2px;
        background: #eec2c1;
        position: absolute;
        top: 10px;
        left: 15px;
        border-radius: 2px;
      }
      &::after {
        content: '';
        width: 40px;
        height: 2px;
        background: #eec2c1;
        position: absolute;
        top: 10px;
        right: 15px;
        border-radius: 2px;
      }
    }
    p {
      line-height: 1.4;
      font-weight: 300;
      font-size: 13px;
      position: relative;
      padding-left: 10px;
      &::before {
        content: '';
        width: 4px;
        height: 4px;
        background: #af8368;
        border-radius: 50%;
        position: absolute;
        top: 6px;
        left: -2px;

      }
    }
    &-text {
      line-height: 20px;
      font-size: 15px;
      color: #cd9769;
      margin: 20px auto 15px;
      text-align: center;
    }
    &-stepper {
      margin: 0 auto;
      width: 40%;
    }
    &-confirm {
      width: 120px;
      height: 30px;
      line-height: 30px;
      background: #d5834b;
      color: #fff;
      font-size: 15px;
      font-weight: 300;
      border-radius: 30px;
      margin: 20px auto 10px;
      text-align: center;
    }
  }
  &-convert {
    width: 90%;
    height: 270px;
    position: absolute;
    top: 250px;
    left: 5%;
    z-index: 999;
    background: linear-gradient(to top right,rgba(148,215,102,0.9) 0,rgba(67,171,174,0.9) 40%);
    box-shadow: 1px 1px 30px #8fde5f;
    border-radius: 10%;
    &-zongzi {
      position: absolute;
      bottom: -14px;
      right: -8px;
      transform: scale(0.8);
      z-index: 1000;
    }
    &-zongzi2 {
      position: absolute;
      bottom: -20px;
      right: 15px;
      transform: scale(0.6) rotate(20deg);
      z-index: 1001;
    }
    &-num {
      width: 90%;
      height: 30px;
      margin-left: 5%;
      margin-top: 40px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      border-bottom: 2px solid #ffdaa3;
      padding-bottom: 10px;
      &-item {
        font-size: 16px;
        color: #ffdaa3;
        font-weight: 500;
      }
    }
    &-rule {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-top: 30px;
      font-size: 20px;
      color: #fff;
      width: 80%;
      margin-left: 10%;
      &-nuomi {
        width: 30px;
        height: 46px;
        background: #fff;
        border-radius: 50%;
        position: relative;
        box-shadow: inset 6px -1px 1px 0 #efefe5;
        transform: rotate(20deg);
      }
      &-zongye {
        width: 24px;
        height: 50px;
        background: #080;
        border-radius: 300%;
        transform: rotate(30deg) skewY(50deg);
      }
      &-hongzao {
        width: 30px;
        height: 46px;
        background: #f65662;
        border-radius: 40%;
        position: relative;
        box-shadow: inset -1px -1px 1px 0 #fff;
        transform: rotate(20deg);
        &::before {
          content: '';
          position: absolute;
          top: -1px;
          left: 10px;
          width: 10px;
          height: 3px;
          background: #a84e43;
          border-radius: 50%;
        }
      }
    }
    &-btn {
      width: 60px;
      height: 60px;
      border: 2px solid #fff8db;
      background: #f295a7;
      color: #fff;
      font-size: 16px;
      border-radius: 50%;
      margin: 30px auto 0;
      display: flex;
      justify-content: center;
      align-items: center;
      &.inactive {
        background: #afa9e7;
      }
    }
  }
  &-zongzi {
    position: relative;
    width: 50px;
    height: 60px;
    z-index: 990;
    overflow: hidden;
    border-radius: 0 0 35% 35%;
    &-left {
      width: 60px;
      height: 40px;
      background: #73bd5c;
      border-radius: 30% 50% 0 0;
      position: absolute;
      bottom: -22px;
      left: -22px;
      z-index: 993;
      transform: rotate(47deg);
    }
    &-center {
      width: 50px;
      height: 70px;
      background: #fff;
      border-radius: 50% 70% 0 0 ;
      position: absolute;
      top: 6px;
      left: 0;
      z-index: 991;
      box-shadow: inset -6px -1px 1px 0 #efefe5;
    }
    &-right {
      width: 60px;
      height: 40px;
      background: #73bd5c;
      border-radius: 0 30% 0 0;
      position: absolute;
      bottom: -17px;
      right: -19px;
      z-index: 993;
      transform: rotate(-47deg);
    }
  }
}
复制代码

interfaz de usuario final

Galería de eventos

Pantalla emergente de intercambio

Resumir

Esta vez la cosecha es bastante.

  1. He usado mucho CSS antes. Excepto por la imagen del avatar, un árbol y un ramo de flores en este juego, escribí todo lo demás en CSS, sin usar materiales de imagen, y el proceso de realización continuó obteniendo nuevas ideas. Hablando de eso, gracias a esta actividad de pepita de código de tiempo, pude usar CSS para implementar funciones tan rápido, ღ( ´・ᴗ・` );
  2. Diseño de juegos, experimentó la sensación de un producto/planificación, pensando en las funciones que deben realizarse desde diferentes ángulos y ejercitó el pensamiento lógico, que es muy gratificante;
  3. La realización de funciones básicas, incluido el cálculo de la recolección de limpieza, el cálculo de la caída aleatoria de ingredientes, el cálculo del intercambio de albóndigas de arroz y otras funciones de cálculo, aunque el método puede no ser el mejor, pero se experimenta cuando se encuentran funciones similares;

Todavía hay una dirección de github. Cuando tenga tiempo de cargar todo el código, agregaré la dirección de github.

Artículo de referencia

Supongo que te gusta

Origin juejin.im/post/7103578323644743717
Recomendado
Clasificación