前言
上一篇文章承蒙大家喜爱,取得了我意想不到的成绩,居然上了掘金热榜
如果您还没有看过上一篇文章,您可以点击这个链接去查看:上一篇
本文掘金地址:跳转(可以去掘金看看我的这篇文章哦)
今天分享的内容
猜谜语合成3D灯笼
进入项目时的界面
这里我添加了玩法提示,因为上一篇文章中,有热心的网友,提出了类似的建议
猜谜界面
猜对后的界面
全部猜对后的界面
上面我将所有谜题的答案以及灯笼上的文字全部都打了马赛克,大家可以去代码中找答案哦,另外可以去尝试玩一下哦,体验链接我会放在文章最后。
接下我们看看技术实现
所涉及技术:HTML、CSS3D、CSS定位、CSS flex布局、js函数封装、js模拟弹窗等
HTML代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>猜谜语合成灯笼</title>
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<!--玩法介绍盒子 这个盒子中有h3,button,文字三种元素,h3用来展示这个盒子的标题,
button用来后期移除这个盒子,以及开始游戏 -->
<div class="instruction">
<h3>猜谜语合成灯笼玩法介绍</h3>
1:共有8道谜语,每答对一道,显示下一道,并可以获得一个灯笼碎片。 <br> 2:灯笼共有8个面,当获得8个灯笼碎片后,合成一个完整的灯笼,灯笼开始旋转。
<button class="ent">我已知晓</button>
</div>
<!--基于屏幕左侧的谜语大盒子 四个p标签作为每一个谜语的展示板,p标签中存有input来供用户输入答案
label来提示用户答案是否正确-->
<div class="l">
<p class="show">谜题一:初一(打一成语) <input type="text" placeholder="请输入谜底1"><label for="">不对哦</label></p>
<p>谜题二:蜜饯黄连(打一成语)<input type="text" placeholder="请输入谜底2"><label for="">不对哦</label></p>
<p>谜题三:一(打一成语)<input type="text" placeholder="请输入谜底3"><label for="">不对哦</label></p>
<p>谜题四:红娘子上高楼,心里疼眼泪流(打一生活用品)<input type="text" placeholder="请输入谜底4"><label for="">不对哦</label></p>
</div>
<!--用来展示灯笼的大盒子-->
<div class="box">
<!--基于box定位的模拟弹窗盒子-->
<div class="tishi">
恭喜获得一个灯笼碎片
</div>
<!--灯笼盒子,里面有8个span,对应的是灯笼的8个面,3D效果就是作用在这个盒子上的 -->
<div class="p">
<span class="span1">五福临门虎年到</span>
<span class="span2">虎到福到财运到</span>
<span class="span3">福临宝地千秋盛</span>
<span class="span4">财进家门万事兴</span>
<span class="span5">事事如意大吉祥</span>
<span class="span6">家家顺心永安康</span>
<span class="span7">财源滚滚随春到</span>
<span class="span8">喜气洋洋伴福来</span>
</div>
<!--基于box定位的盒子,用来提示用户收集到多上灯笼碎片 -->
<div class="sum">灯笼碎片数: <b id="sum">0</b>/8</div>
</div>
<!--基于屏幕右侧的谜语大盒子 四个p标签作为每一个谜语的展示板,p标签中存有input用来供用户输入答案
label用来提示用户答案是否正确-->
<div class="r">
<p>谜题五:老天有眼(打一字)<input type="text" placeholder="请输入谜底5"><label for="">不对哦</label></p>
<p>谜题六:摔跤选手(打一俗语)<input type="text" placeholder="请输入谜底6"><label for="">不对哦</label></p>
<p>谜题七:爬竹竿(打一成语)<input type="text" placeholder="请输入谜底7"><label for="">不对哦</label></p>
<p>谜题八:迎春节(打一字)<input type="text" placeholder="请输入谜底8"><label for="">不对哦</label></p>
</div>
<script src="./js/index.js"></script>
</body>
</html>
以上就是HTML代码的全部,当中最为重要的就是灯笼盒子(.p),谜题盒子(p标签),灯笼面(span标签)
接着让我们一起看看CSS代码
* {
margin: 0;
padding: 0;
}
/* 设置body flex布局,并且给body设置宽和高 */
body,
html {
/* 将body设置为怪异盒子,这样就不用担心被padding撑开 */
box-sizing: border-box;
/* 设置这个页面中字体大小默认为13px,给body设置这个是因为fobt-size是可以继承的属性 */
font-size: 13px;
width: 100%;
height: 100%;
display: flex;
/* 主轴两端对齐 */
justify-content: space-between;
/* 测轴居中对齐 */
align-items: center;
/* 设置一个好看的背景 */
background: url(../images/tm.jpg)no-repeat center center/100% 100%;
/* 禁止用户选中,避免直接赋值谜题百度 */
user-select: none;
}
/*玩法说明盒子样式*/
.instruction {
/* 设置为怪异盒子,这样就不用担心被padding撑开 */
box-sizing: border-box;
/*基于body的绝对定位*/
position: absolute;
/* 一下三句代码设置元素上下左右居中 */
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/*设置层级为当前页面最高*/
z-index: 2;
width: 500px;
height: 400px;
/* 圆角8px */
border-radius: 8px;
background: url(../images/bj.png)no-repeat center center/100% 100%;
/* 一下四句代码是设置文本居中对齐,行高40px,字号14,字体颜色白色 */
text-align: center;
line-height: 40px;
font-size: 14px;
color: #fff;
/*设置内边距,让文字正好处于背景图片的内容区域,注意一下两句代码的顺序不可改变,因为css具有覆盖的特点,
padding:0 30px 第一个参数就是设置上下内边距的*/
padding: 0 30px;
padding-top: 50px;
}
/*我已知晓按钮样式,基于.instruction绝对定位的盒子,设置基于底部居中对齐*/
.ent {
position: absolute;
bottom: 20px;
left: 50%;
transform: translate(-50%);
}
/*我已知晓按钮样式结束*/
/*玩法说明盒子样式部分结束*/
/*灯笼展示大盒子的样式,开启相对定位,因为后面有元素会基于这个定位的*/
.box {
position: relative;
width: 400px;
height: 400px;
display: flex;
justify-content: center;
align-items: center;
/*开启3D景深,使3D效果更明显*/
perspective: 1200px;
}
/*设置猜对后的提示框,基于.box绝对定位*/
.tishi {
position: absolute;
top: -120px;
left: 50%;
transform: translate(-50%);
width: 230px;
height: 50px;
background-color: green;
border-radius: 10px;
text-align: center;
line-height: 50px;
font-size: 18px;
color: #fff;
/*设置过渡效果,使得出现与消失不那么生硬*/
transition: all 2s;
opacity: 0;
}
/*设置猜对后的提示框样式部分结束*/
/*设置放置灯笼的盒子*/
.p {
/* 重点,重点,重点,下面这句代码就是设置元素开启3D效果的哦,
没有它,及时即使写了3d效果代码,展现的也只能是2d效果 */
transform-style: preserve-3d;
position: relative;
width: 40px;
height: 350px;
/*设置中心点,这个是后面灯笼旋转的中心点,三个参数分别设置的x,y,z三个轴*/
transform-origin: center center -146px;
/*过渡效果,用户获得碎片后,碎片滑动出现得效果,就是这么来的*/
transition: all 2s;
}
/*设置放置灯笼盒子样式部分结束*/
/*设置灯笼的每个面的公共样式*/
.p span {
position: absolute;
background-color: rgba(255, 51, 0, 0.95) !important;
top: 0;
display: flex;
justify-content: center;
align-items: center;
width: 300%;
height: 100%;
/*文本垂直显示*/
writing-mode: tb-rl;
/*设置3D效果得中心点*/
transform-origin: center center -146px;
border: 2px solid #AD7530;
/*这里之所以不使用背景的复合写法,是为了避免覆盖问题*/
background-image: url(../images/jb.png);
background-repeat: no-repeat;
background-size: 100% 100%;
/*设置字体样式*/
font-size: 18px;
color: #fff;
/*设置一开始隐藏的,只有用户答对谜题后才会显示*/
opacity: 0;
}
/*设置灯笼每个面公共样式的部分结束*/
/*下面就是设置每个灯笼面基于中心点旋转的角度,角度用单位deg表示*/
.span1 {
transform: rotateY(45deg);
}
.span2 {
transform: rotateY(90deg);
}
.span3 {
transform: rotateY(135deg);
}
.span4 {
transform: rotateY(180deg);
}
.span5 {
transform: rotateY(225deg);
}
.span6 {
transform: rotateY(270deg);
}
.span7 {
transform: rotateY(315deg);
}
.span8 {
transform: rotateY(360deg);
}
/*设置提示用户获得碎片数盒子的样式*/
.sum {
position: absolute;
bottom: -53px;
}
#sum {
color: #ED3434;
}
/*设置提示用户获得碎片数盒子样式的部分结束*/
/*定义灯笼旋转动画,动画名字是z,这个在js中给元素添加*/
@keyframes z {
0% {
transform: rotateX(-2deg) rotateY(0);
}
100% {
transform: rotateX(-2deg) rotateY(360deg);
}
}
/*设置谜题板的样式*/
.l p,
.r p {
position: relative;
box-sizing: border-box;
padding: 35px 55px;
margin-top: 20px;
width: 300px;
height: 150px;
background: url(../images/tm2.jpg)no-repeat center center/100% 100%;
border-radius: 10px;
font-size: 13px;
box-shadow: 0 0 15px #000;
/*设置占位隐藏,方便后期逐个显示*/
opacity: 0;
}
.l p {
margin-left: 100px;
}
.r p {
margin-right: 100px;
}
/*设置谜题板的样式结束*/
/*如果用户答对了,就给下一题的p添加这个类,在js中添加*/
.show {
opacity: 1 !important;
}
/*设置谜题板中,输入框的样式*/
.l p:hover input,
.r p:hover input {
display: block;
}
.l p input,
.r p input {
box-sizing: border-box;
padding-left: 10px;
position: absolute;
width: 150px;
height: 25px;
bottom: 40px;
left: 50%;
transform: translate(-50%);
outline: none;
border: 1px solid #E8C52D;
border-radius: 20px;
background: transparent;
}
/* 设置输入框中提示文字的样式 */
.l p input::placeholder,
.r p input::placeholder {
color: #F8B767;
}
/*设置谜题板中,输入框的样式结束*/
/*设置对错提示框的样式*/
.l p label,
.r p label {
position: absolute;
bottom: 21px;
left: 0;
width: 100%;
text-align: center;
color: #ED3434;
display: none;
}
/*设置对错提示框的样式*/
灯笼的初始模型图
以上就是这个项目的所有css代码,css中值得我们避坑的就是它的覆盖,我在写的时候出过很多次无法达到预期效果的现象,都是因为css覆盖,比如注释中说的padding 和padding-top的书写顺序问题,背景设置问题。
我们再来看看js代码
看代码之前我们先来看看每个交互效果
1、点击‘我已知晓’按钮后的效果
需要实现的功能:点击按钮之后,玩法提示消失,第一题输入框显示
2、输入错误答案的交互效果
需要实现的功能:提示用户输入错误
3、用户输入正确
需要实现的功能:弹窗告知的用户正确且显示获得的碎片数、碎片数量+1、灯笼面滑动出现、显示下一题
4、用户全部答对后的效果
需要实现功能:使灯笼开始旋转
开始码
/*获取页面中需要操作的元素*/
const oP = document.querySelectorAll('p');/*谜题版,后面操作它的显示*/
const oSpan = document.querySelectorAll('span');/*灯笼面,后面操作它显示*/
const oInp = document.querySelectorAll('input');/*输入框,后面根据他的内容判断用户有没有猜对*/
const label = document.querySelectorAll('label');/*提示猜的结果,操作它的内容是显示对还是错*/
const box = document.querySelector('.p');/*灯笼盒子,后期操作其旋转*/
const tishi = document.querySelector('.tishi');/*告知用户获得了多少个碎片的弹窗,后面操作它淡入淡出*/
const sumbox = document.querySelector('#sum');/*显示碎片数量的,后面操作它的内容*/
const ent = document.querySelector('.ent');/*用户已经知晓按钮,后面根据它点击后做一些处理*/
const instruction = document.querySelector('.instruction');/*玩法告知盒子,后面控制移除自身*/
/*声明一个变量,用来记录用户获得的碎片数量*/
let index = 0;
/*页面已加载完就让第一题的输入框隐藏,并且禁用,防止用户输入正确后,
玩法盒子没有消除,看不到其他元素的东画*/
oInp[0].disabled = true;
oInp[0].style.opacity = '0';
/*声明一个数组,存放谜题答案*/
const arr = ['日新月异', '同甘共苦', '接二连三', '红蜡烛', '关', '不打不相识', '节节高升', '昂'];
/*表单事件,当用户输入答案的时候做一些处理,这里调用的是后面封装的函数*/
oInp[0].oninput = function() {
dispose(0);
}
oInp[1].oninput = function() {
dispose(1);
}
oInp[2].oninput = function() {
dispose(2);
}
oInp[3].oninput = function() {
dispose(3);
}
oInp[4].oninput = function() {
dispose(4);
}
oInp[5].oninput = function() {
dispose(5);
}
oInp[6].oninput = function() {
dispose(6);
}
/*最后一题之所以不调用封装的函数,是因为它还有其他的事情要做,比如给让灯笼转起来*/
oInp[7].oninput = function() {
let str = oInp[7].value;
if (str == '昂') {
label[7].innerHTML = '恭喜答对';
label[7].style.color = 'green';
this.style.display = 'block';
oSpan[7].style.opacity = '1';
box.style.transform = 'rotateY(-374deg)';
index = index + 1;
/*调用灯笼旋转函数*/
rotate();
/*调用弹窗函数*/
smak();
/*调用显示碎片的函数*/
sum();
oInp[7].oninput = '';
} else {
label[7].style.display = 'block';
label[7].innerHTML = '不对哦';
label[7].style.color = '#ED3434';
}
}
/*当用户点击‘我已知晓’按钮后,就移除玩法提示盒子和自身,并且让第一题的输入框显示,可用*/
ent.onclick = function() {
instruction.remove();
oInp[0].style.opacity = '1';
oInp[0].disabled = false;
}
/*设置一个函数,这个函数的作用是依据用户输入的内容做出相应的响应*/
function dispose(i) {
/*获取当前输入框的内容*/
let str = oInp[i].value;
/*设置灯笼的初始旋转角度*/
let deg = -38;
/*判断用户输入的内容是否正确*/
if (str == arr[i]) {
/*如果正确,就提示用是对的*/
label[i].innerHTML = '恭喜答对';
label[i].style.color = 'green';
/*当前的输入框不在隐藏*/
oInp[i].style.display = 'block';
/*显示对应的灯笼面(碎片)*/
oSpan[i].style.opacity = '1';
/*显示下一题*/
oP[i + 1].style.opacity = '1';
/*灯笼对应的也旋转 旋转角度是-38的倍数,i+1是因为i初始等于0,0乘以任何数都等于0,没有意义*/
box.style.transform = `rotateY(${deg*(i+1)}deg)`;
/*用户碎片数+1*/
index = index + 1;
/*调用弹窗函数*/
smak();
/*调用显示碎片数的函数*/
sum();
/*移除当前事件,保证答对一题只加一个碎片*/
oInp[i].oninput = '';
} else {
/*如果不正确,就提示用户不正确*/
label[i].style.display = 'block';
label[i].innerHTML = '不对哦';
label[i].style.color = '#ED3434';
}
}
/*让灯笼旋转的函数*/
function rotate() {
/*判断用户拥有的碎片数,如果集齐了*/
if (index == 8) {
/*就给灯笼盒子添加动画*/
box.style.animation = 'z 10s linear infinite';
/*给每一个灯笼面添加阴影,模拟灯笼点亮的效果*/
for (var i = 0; i < oSpan.length; i++) {
oSpan[i].style.boxShadow = '0 0 20px rgba(255, 238, 0, .9)';
}
}
/*设置提示用户获得碎片的淡入淡出*/
function smak() {
/*设置显示*/
tishi.style.opacity = '1';
/*设置提示类容*/
tishi.innerHTML = '恭喜获得' + index + '个灯笼碎片';
/*利用延时器,规定在2秒后,隐藏弹窗*/
setTimeout(function() {
tishi.style.opacity = '0';
}, 2000);
}
/*显示碎片数量的函数*/
function sum() {
sumbox.innerHTML = index;
}
以上就是js部分的全部代码,js中我们需要注意,除了最后一题的事件函数代码不一样以外,其余题目的事件函数代码都是一样的,为了减少代码的冗余,最好使用函数封装的思想。使用函数封装后,为了区别每一题所对应的答案,就需要动态的的去判断了,所以这里我采用了数组,利用下标的方式准确找到对应答案。
之所以最后一题函数代码不一样是因为,它不在需要显示下一题的功能。反而多了一个让灯笼选装的功能,所以,这里我单独给最后一题的事件写了事件函数。其实这里也可以使用给其他事件封装好的函数,我们只需要在封装内部做一个判断,判断这是不是最后一题(oP这个数组的长度),从而处理不同的代码逻辑。这里我偷懒了,大家可以进一步实现哦。
该项目的体验链接:http://www.starqin920.cn/denglong/index.html