“我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛”
关于游戏的灵感来源
今年元宵节的时候,我玩的小游戏里面有限时任务,可以解锁节日限定物品,于是那几天我玩的很欢乐很积极。端午节到来之前,我想玩一下身份转换,从玩家转换到游戏策划。一个有趣的想法在脑海中逐渐清晰。
假如我是游戏策划
假如我是游戏策划,首先会对自己灵魂三连问:活动内容什么?活动怎么玩?活动奖励是什么?
现有大体的想法,然后再拆分到各个细节中去。
因为游戏中的一些场景搭配、日常活动名称、称号等借鉴了我最近沉迷的游戏《美人传》,所以这次的游戏仅供学习练习,不做任何商业用途。
产品视角
站在产品的角度思考活动设计,我的产品视角是这样的:
一入夏,就盼着假期,过了五一很快就会到端午,一想到端午就不由自主的想到美味的粽子。所以端午的活动就来了,包粽子。众所周知,包粽子需要糯米、粽叶等必备材料,而粽子的内馅有很多种,本次活动中需要的是红枣。所以包粽子的材料就选定了糯米、粽叶、红枣三种。(活动内容是什么)
游戏中有日常收集任务,每个收集任务掉落的材料都是固定的。活动期间一般会增加活动材料限时掉落,所以在活动期间,日常收集时会掉落包粽子需要的材料,不同收集任务掉落不同材料。(活动怎么玩)
粽子积累到一定数量就可以兑换节日限定物品。一般游戏中的节日限定物品都是精心设计的,但是由于时间和精力有限,我这次活动设计的比较简单,不同数量的粽子可以兑换不同的称号,最高称号为“荣宠万千”。(活动奖励是什么)
(^U^)ノ~YO,一切准备就绪,开始干活。
交互设计
大致画了一下设计草图,帮助理清楚布局思路。(第一次画,还有待提高。)
首页
日常任务
端午活动
功能设计
首页
内容
主要包括用户信息、任务入口、活动入口等展示。
称号规则
称号和糯米粽子数量对应如下:
称号 | 糯米粽子数量 |
---|---|
殿上佳人 | <50 |
淑仪倾城 | >=50 && < 100 |
花容初绽 | >=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;
}
}
复制代码
公共方法
页面路径:/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 };
复制代码
最终UI
端午活动
活动规则
活动时间
1.2022-5-31 至 2022-6-5,提前预热3天。
2.页面上设置活动倒计时
- 活动结束前,展示距离活动结束还剩多长时间,时间格式为DD天 hh:mm:ss;
- 活动结束后,展示内容为"活动已结束";
兑换规则
食材兑换比例
粽子类型 | 需要材料 |
---|---|
糯米粽子 | 10 * 糯米 + 2 * 粽叶 + 2 * 红枣 |
食材兑换规则
- 通过页面按钮进行兑换,当食材数量不足时,按钮不可点击,当食材数量充足时可以进行点击。
- 点击兑换按钮唤起兑换弹窗,可以通过加减号进行兑换数量的修改,当达到最大可兑换值时,加号不可点击。
- 确定兑换之后,粽子数量增加,食材数量对应减少。
功能实现
活动页面
文件路径:/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;
复制代码
样式:/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);
}
}
}
复制代码
最终UI
活动展示
兑换弹窗展示
总结
本次收获还是挺多的。
- CSS用的别以前熟练了很多,这次的游戏里除了头像图片、一颗树、一簇花,其他的都是我用CSS写出来的,没有用图片素材,实现过程不断收获新的创意。说起来多亏这段时间码上掘金活动,我才能使用CSS实现功能做的如此之快,ღ( ´・ᴗ・` );
- 游戏设计,体验了一把产品/策划的感觉,站在不同的角度去思考需要实现的功能,锻炼逻辑思维,很有收获;
- 核心功能的实现,包括内务收集的计算、食材的随机掉落计算、粽子兑换的计算等多个计算功能,虽然方法可能不是最优,但是在遇到类似的功能实现算是有经验了;
还差一个github的地址,等有时间我把所有代码上传后,补充一下github地址。