step1 - 小demo页面搭建
主页面
<template>
<div>
<div class="colors">
<div class="item" v-for="(item, index) in colorsData" :key="item">
{{ item.name }}
<span class="colors-tick-checked"
><el-icon><Check /></el-icon>
</span>
</div>
</div>
<div class="tip">
请选择以下有<span class="name">{{ colorsData[0].name }}</span
>颜色的标志
</div>
<div class="logos">
<el-carousel
ref="cardShow"
height="480px"
indicator-position="none"
:autoplay="false"
arrow="never"
trigger="click"
>
<el-carousel-item v-for="(item, index) in colorsData" :key="item">
<div class="logosImg">
<div v-for="iitem in item.chooseData" v-if="item.chooseData.length">
<div class="carouselImgOuter">
<img :src="getAssets('sprites', iitem.imgName)" alt="" />
</div>
<div class="checkIcon">
<el-tag type="success" round effect="dark">
<el-icon><Select /></el-icon>
</el-tag>
<!-- <el-tag type="danger" round effect="dark">
<el-icon><CloseBold /></el-icon>
</el-tag> -->
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { getAssets } from "../utils/getAssets";
const colorsData = ref([
{
name: "BLUE",
rightData: [{ imgName: "blue.png" }],
chooseData: [
{
imgName: "pink.png",
},
{
imgName: "blue.png",
},
{
imgName: "red.png",
},
],
},
{
name: "PINK",
rightData: [{ imgName: "pink.png" }],
chooseData: [
{
imgName: "orange.png",
},
{
imgName: "blue.png",
},
{
imgName: "pink.png",
},
],
},
{
name: "RED",
rightData: [{ imgName: "red.png" }],
chooseData: [
{
imgName: "pink.png",
},
{
imgName: "green.png",
},
{
imgName: "yellow.png",
},
],
},
{
name: "YELLOW",
rightData: [{ imgName: "yellow.png" }],
chooseData: [
{
imgName: "yellow.png",
},
{
imgName: "blue.png",
},
{
imgName: "red.png",
},
],
},
]);
</script>
<style lang="scss" scoped>
.colors {
margin: 10px 0 60px;
padding: 0 160px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 30px;
.item {
user-select: none;
position: relative;
font-size: 28px;
height: 26px;
line-height: 26px;
padding: 30px;
background: #efeeff;
border-radius: 34px;
color: #2f2e41;
&:hover,
&.active {
cursor: pointer;
background-color: #6c63ff;
color: #ffffff;
transition: background-color 0.8s ease;
}
}
.colors-tick-checked {
position: absolute;
top: -4px;
right: -2px;
width: 29px;
height: 29px;
border-radius: 50%;
background-color: #6c63ff;
line-height: 29px;
cursor: pointer;
text-align: center;
.el-icon {
font-size: 20px;
color: white;
}
}
}
.tip {
font-weight: 400;
font-size: 30px;
color: #2f2e41;
text-align: center;
.name {
color: #6c63ff;
}
}
.logos {
height: 480px;
margin: 40px 0;
width: 100%;
.logosImg {
box-sizing: border-box;
padding: 0 160px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
div {
box-sizing: border-box;
width: 30%;
text-align: center;
margin: 0 auto;
display: flex; //
justify-content: center; //
align-items: center; //
flex-wrap: wrap; //
.carouselImgOuter {
width: 100%; //
&:hover {
cursor: pointer;
}
}
.checkIcon {
width: 100%; //
display: inline-block;
margin-top: 10px;
height: 25px;
text-align: center;
.el-tag {
box-sizing: border-box;
padding: 0 4px;
.el-icon {
margin-right: 0;
}
}
}
}
}
}
</style>
配件 - getAssets.js
function getAssets(task, name) {
return new URL(`../assets/images/${task}/${name}`, import.meta.url).href;
}
export { getAssets };
将相对路径改为绝对路径,避免部署上服务器后找不到图片资源
如果是vue-cli搭建,可使用require引用资源 如
step2 - 上面按钮与el-carousel-item里的图片联系起来
<template>
<div>
<div class="colors">
<div
class="item"
v-for="(item, index) in colorsData"
:key="item"
@click="select(index)" //1、添加点击事件
:class="computedClass(index)" //2、添加样式判断逻辑
>
{{ item.name }}
<span class="colors-tick-checked"
><el-icon><Check /></el-icon>
</span>
</div>
</div>
<div class="tip">
请选择以下有<span class="name">{{ colorsData[0].name }}</span
>颜色的标志
</div>
<div class="logos">
<el-carousel
ref="cardShow"
height="480px"
indicator-position="none"
:autoplay="false"
arrow="never"
trigger="click"
>
<el-carousel-item
v-for="(item, index) in colorsData"
:key="item"
:name="'i' + index" //新增name属性,与cardShow的setActiveItem对应
>
<div class="logosImg">
<div v-for="iitem in item.chooseData" v-if="item.chooseData.length">
<div class="carouselImgOuter">
<img :src="getAssets('sprites', iitem.imgName)" alt="" />
</div>
<div class="checkIcon">
<el-tag type="success" round effect="dark">
<el-icon><Select /></el-icon>
</el-tag>
<!-- <el-tag type="danger" round effect="dark">
<el-icon><CloseBold /></el-icon>
</el-tag> -->
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
</template>
<script setup> //只展示修改部分
const cardShow = ref(null);
const selected = ref([0]); //记录被选择的盒子
const select = (index) => {
selected.value.length >= 1
? (selected.value[0] = index)
: selected.value.push(index);
cardShow.value.setActiveItem("i" + index); //与轮播页展示内容连接
};
function computedClass(index) {
let list = [];
if (selected.value.indexOf(index) > -1) {
list.push("selected");
}
return list.join(" ");
}
</script>
<style lang="scss" scoped>
.item {
&.selected {
cursor: pointer;
background: #6c63ff;
color: #ffffff;
transition: background-color 0.8s ease;
}
}
</style>
step3 - 点击图片进行判断,然后图标显示
<template> //只展示修改部分
<span class="colors-tick-checked" v-if="item.isright" //新增判断
><el-icon><Check /></el-icon>
</span>
<div class="carouselImgOuter">
<img
:src="getAssets('sprites', iitem.imgName)"
alt=""
@click="check($event, item, iitem)" //新增点击事件
/>
</div>
<div class="checkIcon">
<el-tag
type="success"
round
effect="dark"
v-if="iitem.ischecked == 'right'" //新增判断
>
<el-icon><Select /></el-icon>
</el-tag>
<el-tag
type="danger"
round
effect="dark"
v-else-if="iitem.ischecked == 'wrong'" //新增判断
>
<el-icon><CloseBold /></el-icon>
</el-tag>
</div>
</template>
<script setup>
const colorsData = ref([ //改善一下数据结构
{
name: "BLUE",
rightData: [{ imgName: "blue.png", isImgRight: false }],
rightCnt: 0,
chooseData: [
{
imgName: "pink.png",
ischecked: "",
},
{
imgName: "blue.png",
ischecked: "",
},
{
imgName: "red.png",
ischecked: "",
},
],
},
{
name: "PINK",
rightData: [{ imgName: "pink.png", isImgRight: false }],
rightCnt: 0,
chooseData: [
{
imgName: "orange.png",
ischecked: "",
},
{
imgName: "blue.png",
ischecked: "",
},
{
imgName: "pink.png",
ischecked: "",
},
],
},
{
name: "RED",
rightData: [{ imgName: "red.png", isImgRight: false }],
rightCnt: 0,
chooseData: [
{
imgName: "red.png",
ischecked: "",
},
{
imgName: "green.png",
ischecked: "",
},
{
imgName: "yellow.png",
ischecked: "",
},
],
},
{
name: "YELLOW",
rightData: [{ imgName: "yellow.png", isImgRight: false }],
rightCnt: 0,
chooseData: [
{
imgName: "yellow.png",
ischecked: "",
},
{
imgName: "blue.png",
ischecked: "",
},
{
imgName: "red.png",
ischecked: "",
},
],
},
]);
const check = (ev, item, iitem) => {
iitem.ischecked = "wrong"; //先全部设置成错误
item.rightData.forEach((el) => {
//对比该index正确答案数组
if (el.imgName == iitem.imgName) {
//一出现就正确
iitem.ischecked = "right";
item.rightCnt++;
el.isImgRight = true;
}
});
if (item.rightCnt == item.rightData.length) { //如果全部正确选项选择完成
item.isright = true;
}
};
</script>
step4 - 优化-部分浏览器点击图片会有预览效果,将img标签变成背景图片引用
<template> //只展示修改部分
<div class="logosImg">
<div v-for="iitem in item.chooseData" v-if="item.chooseData.length">
<div
class="carouselImgOuter"
:style="`background-image: url(${getAssets(
'sprites',
iitem.imgName
)})`" //
@click="check($event, item, iitem)"
></div>
<div class="checkIcon">
<el-tag
type="success"
round
effect="dark"
v-if="iitem.ischecked == 'right'"
>
<el-icon><Select /></el-icon>
</el-tag>
<el-tag
type="danger"
round
effect="dark"
v-else-if="iitem.ischecked == 'wrong'"
>
<el-icon><CloseBold /></el-icon>
</el-tag>
</div>
</div>
</div>
</template>
<style lang="scss" scoped> //只展示修改部分
.carouselImgOuter { //设置固定宽度高度,方便居中
width: 64px;
height: 64px;
background-size: 100% 100%;
background-repeat: no-repeat;
&:hover {
cursor: pointer;
}
}
</style>
:style="
background-image: url(${getAssets('sprites', iitem.imgName )})" ,注意搭配url使用
step5 - 功能-如果有答错情况直接重做
<script setup> //只展示修改部分
const colorsData = ref([ //增加部分测试数据
{
name: "BLUEGREEN",
rightData: [
{ imgName: "blue.png", isImgRight: false },
{ imgName: "green.png", isImgRight: false },
],
rightCnt: 0,
chooseData: [
{
imgName: "pink.png",
ischecked: "",
},
{
imgName: "blue.png",
ischecked: "",
},
{
imgName: "red.png",
ischecked: "",
},
{
imgName: "green.png",
ischecked: "",
},
],
},
{
name: "PINK",
rightData: [{ imgName: "pink.png", isImgRight: false }],
rightCnt: 0,
chooseData: [
{
imgName: "orange.png",
ischecked: "",
},
{
imgName: "blue.png",
ischecked: "",
},
{
imgName: "pink.png",
ischecked: "",
},
],
},
{
name: "RED",
rightData: [{ imgName: "red.png", isImgRight: false }],
rightCnt: 0,
chooseData: [
{
imgName: "red.png",
ischecked: "",
},
{
imgName: "green.png",
ischecked: "",
},
{
imgName: "yellow.png",
ischecked: "",
},
],
},
{
name: "YELLOW",
rightData: [{ imgName: "yellow.png", isImgRight: false }],
rightCnt: 0,
chooseData: [
{
imgName: "yellow.png",
ischecked: "",
},
{
imgName: "blue.png",
ischecked: "",
},
{
imgName: "red.png",
ischecked: "",
},
],
},
]);
const check = (ev, item, iitem) => {
iitem.ischecked = "wrong"; //先全部设置成错误
item.rightData.forEach((el) => {
//对比该index正确答案数组
if (el.imgName == iitem.imgName) {
//一出现就正确
iitem.ischecked = "right";
item.rightCnt++;
el.isImgRight = true;
}
});
//如果全部正确选项选择完成
if (item.rightCnt == item.rightData.length) {
item.chooseData.forEach((eel) => {
//其他错误选项全部出现
if (eel.ischecked != "right") {
eel.ischecked = "wrong";
}
});
item.isright = true;
}
//一出现错误清空退出
if (iitem.ischecked == "wrong") {
//colorsData状态就要恢复默认
item.rightCnt = 0;
item.rightData.forEach((el) => {
el.isImgRight = false;
});
setTimeout(() => { //这里选择使用定时器,使错误标志出现一会儿后消失
item.chooseData.forEach((eel) => {
eel.ischecked = "";
});
}, 1000);
}
};
</script>
setTimeout(() => { //这里选择使用定时器,使错误标志出现一会儿后消失 item.chooseData.forEach((eel) => { eel.ischecked = ""; }); }, 500);
step6 - 优化-如果点击过快-定时器带来一点问题
<script setup> //只展示修改部分
let flag = false; //标志
const check = (ev, item, iitem) => {
if (flag) return; //还在定时器期间点击不会有反应
iitem.ischecked = "wrong";
item.rightData.forEach((el) => {
if (el.imgName == iitem.imgName) {
iitem.ischecked = "right";
item.rightCnt++;
el.isImgRight = true;
}
});
if (item.rightCnt == item.rightData.length) {
item.chooseData.forEach((eel) => {
if (eel.ischecked != "right") {
eel.ischecked = "wrong";
}
});
item.isright = true;
}
if (iitem.ischecked == "wrong") {
flag = true; //标志状态更改
item.rightCnt = 0;
item.rightData.forEach((el) => {
el.isImgRight = false;
});
setTimeout(() => {
item.chooseData.forEach((eel) => {
eel.ischecked = "";
});
flag = false; //标志状态更改
}, 500);
}
};
</script>
灵活使用标志来对不进行判断的情况及时return
step7 - 优化-已成功的关卡不能再进行点击
<script setup> //只展示修改部分
const check = (ev, item, iitem) => {
if (item.isright || iitem.ischecked == "right" || flag) { //新增判断
return;
}
iitem.ischecked = "wrong";
item.rightData.forEach((el) => {
if (el.imgName == iitem.imgName) {
iitem.ischecked = "right";
item.rightCnt++;
el.isImgRight = true;
}
});
if (item.rightCnt == item.rightData.length) {
item.chooseData.forEach((eel) => {
if (eel.ischecked != "right") {
eel.ischecked = "wrong";
}
});
item.isright = true;
}
if (iitem.ischecked == "wrong") {
flag = true;
item.rightCnt = 0;
item.rightData.forEach((el) => {
el.isImgRight = false;
});
setTimeout(() => {
item.chooseData.forEach((eel) => {
eel.ischecked = "";
});
flag = false;
}, 500);
}
};
</script>
step8 - 成功!完整代码!快跟着一起试试吧!
<template>
<div>
<div class="colors">
<div
class="item"
v-for="(item, index) in colorsData"
:key="item"
@click="select(index)"
:class="computedClass(index)"
>
{{ item.name }}
<span class="colors-tick-checked" v-if="item.isright"
><el-icon><Check /></el-icon>
</span>
</div>
</div>
<div class="tip">
请选择以下有<span class="name">{{ colorsData[0].name }}</span
>颜色的标志
</div>
<div class="logos">
<el-carousel
ref="cardShow"
height="480px"
indicator-position="none"
:autoplay="false"
arrow="never"
trigger="click"
>
<el-carousel-item
v-for="(item, index) in colorsData"
:key="item"
:name="'i' + index"
>
<div class="logosImg">
<div v-for="iitem in item.chooseData" v-if="item.chooseData.length">
<div
class="carouselImgOuter"
:style="`background-image: url(${getAssets(
'sprites',
iitem.imgName
)})`"
@click="check($event, item, iitem)"
></div>
<div class="checkIcon">
<el-tag
type="success"
round
effect="dark"
v-if="iitem.ischecked == 'right'"
>
<el-icon><Select /></el-icon>
</el-tag>
<el-tag
type="danger"
round
effect="dark"
v-else-if="iitem.ischecked == 'wrong'"
>
<el-icon><CloseBold /></el-icon>
</el-tag>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { getAssets } from "../utils/getAssets";
const cardShow = ref(null);
const selected = ref([0]);
const colorsData = ref([
{
name: "BLUEGREEN",
rightData: [
{ imgName: "blue.png", isImgRight: false },
{ imgName: "green.png", isImgRight: false },
],
rightCnt: 0,
chooseData: [
{
imgName: "pink.png",
ischecked: "",
},
{
imgName: "blue.png",
ischecked: "",
},
{
imgName: "red.png",
ischecked: "",
},
{
imgName: "green.png",
ischecked: "",
},
],
},
{
name: "PINK",
rightData: [{ imgName: "pink.png", isImgRight: false }],
rightCnt: 0,
chooseData: [
{
imgName: "orange.png",
ischecked: "",
},
{
imgName: "blue.png",
ischecked: "",
},
{
imgName: "pink.png",
ischecked: "",
},
],
},
{
name: "RED",
rightData: [{ imgName: "red.png", isImgRight: false }],
rightCnt: 0,
chooseData: [
{
imgName: "red.png",
ischecked: "",
},
{
imgName: "green.png",
ischecked: "",
},
{
imgName: "yellow.png",
ischecked: "",
},
],
},
{
name: "YELLOW",
rightData: [{ imgName: "yellow.png", isImgRight: false }],
rightCnt: 0,
chooseData: [
{
imgName: "yellow.png",
ischecked: "",
},
{
imgName: "blue.png",
ischecked: "",
},
{
imgName: "red.png",
ischecked: "",
},
],
},
]);
const select = (index) => {
selected.value.length >= 1
? (selected.value[0] = index)
: selected.value.push(index);
cardShow.value.setActiveItem("i" + index);
};
function computedClass(index) {
let list = [];
if (selected.value.indexOf(index) > -1) {
list.push("selected");
}
return list.join(" ");
}
let flag = false; //标志
const check = (ev, item, iitem) => {
if (item.isright || iitem.ischecked == "right" || flag) {
return;
}
iitem.ischecked = "wrong"; //先全部设置成错误
item.rightData.forEach((el) => {
//对比该index正确答案数组
if (el.imgName == iitem.imgName) {
//一出现就正确
iitem.ischecked = "right";
item.rightCnt++;
el.isImgRight = true;
}
});
//如果全部正确选项选择完成
if (item.rightCnt == item.rightData.length) {
item.chooseData.forEach((eel) => {
//其他错误选项全部出现
if (eel.ischecked != "right") {
eel.ischecked = "wrong";
}
});
item.isright = true;
}
//一出现错误清空退出
if (iitem.ischecked == "wrong") {
flag = true;
//colorsData状态就要恢复默认
item.rightCnt = 0;
item.rightData.forEach((el) => {
el.isImgRight = false;
});
setTimeout(() => {
item.chooseData.forEach((eel) => {
eel.ischecked = "";
});
flag = false;
}, 500);
}
};
</script>
<style lang="scss" scoped>
.colors {
margin: 10px 0 60px;
padding: 0 160px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 30px;
.item {
user-select: none;
position: relative;
font-size: 28px;
height: 26px;
line-height: 26px;
padding: 30px;
background: #efeeff;
border-radius: 34px;
color: #2f2e41;
&:hover,
&.active {
cursor: pointer;
background-color: #6c63ff;
color: #ffffff;
transition: background-color 0.8s ease;
}
&.selected {
cursor: pointer;
background: #6c63ff;
color: #ffffff;
transition: background-color 0.8s ease;
}
}
.colors-tick-checked {
position: absolute;
top: -4px;
right: -2px;
width: 29px;
height: 29px;
border-radius: 50%;
background-color: #6c63ff;
line-height: 29px;
cursor: pointer;
text-align: center;
.el-icon {
font-size: 20px;
color: white;
}
}
}
.tip {
font-weight: 400;
font-size: 30px;
color: #2f2e41;
text-align: center;
.name {
color: #6c63ff;
}
}
.logos {
height: 480px;
margin: 40px 0;
width: 100%;
.logosImg {
box-sizing: border-box;
padding: 0 160px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
div {
box-sizing: border-box;
width: 30%;
text-align: center;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
.carouselImgOuter {
width: 64px;
height: 64px;
background-size: 100% 100%;
background-repeat: no-repeat;
&:hover {
cursor: pointer;
}
}
.checkIcon {
width: 100%;
display: inline-block;
margin-top: 10px;
height: 25px;
text-align: center;
.el-tag {
box-sizing: border-box;
padding: 0 4px;
.el-icon {
margin-right: 0;
}
}
}
}
}
}
</style>