春节快到了,快来猜谜语,集碎片,合成3D灯笼吧!(CSS+JS实现)

PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看春节创意投稿大赛

1.gif

前言

上一篇文章承蒙大家喜爱,取得了我意想不到的成绩,居然上了掘金热榜

热榜.png 如果您还没有看过上一篇文章,您可以点击这个链接去查看:上一篇

今天分享的内容

猜谜语合成3D灯笼

进入项目时的界面

进入.jpg

这里我添加了玩法提示,因为上一篇文章中,有热心的网友,提出了类似的建议
猜谜界面

答题界面.png 猜对后的界面

猜对后.png 全部猜对后的界面

全部猜对后.png

上面我将所有谜题的答案以及灯笼上的文字全部都打了马赛克,大家可以去代码中找答案哦,另外可以去尝试玩一下哦,体验链接我会放在文章最后。

接下我们看看技术实现

所涉及技术: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;
}
/*设置对错提示框的样式*/
复制代码

灯笼的初始模型图 1.png

以上就是这个项目的所有css代码,css中值得我们避坑的就是它的覆盖,我在写的时候出过很多次无法达到预期效果的现象,都是因为css覆盖,比如注释中说的padding 和padding-top的书写顺序问题,背景设置问题。

我们再来看看js代码

看代码之前我们先来看看每个交互效果
1、点击‘我已知晓’按钮后的效果 2.gif 需要实现的功能:点击按钮之后,玩法提示消失,第一题输入框显示
2、输入错误答案的交互效果 3.gif 需要实现的功能:提示用户输入错误
3、用户输入正确

4.gif

猜对后.png 需要实现的功能:弹窗告知的用户正确且显示获得的碎片数、碎片数量+1、灯笼面滑动出现、显示下一题
4、用户全部答对后的效果

5.gif 需要实现功能:使灯笼开始旋转

开始码


/*获取页面中需要操作的元素*/
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这个数组的长度),从而处理不同的代码逻辑。这里我偷懒了,大家可以进一步实现哦。

该项目的体验链接:www.starqin920.cn/denglong/in…

猜你喜欢

转载自juejin.im/post/7054077249003192328