我们在学习前端开发的时候,除了要学习基础编程知识,也需要项目来练习我们所学的编程知识,让我们学的知识有更深的体会,这样才可以做到学以致用。 最近在学习前端开发,除了学习 html css js 这些基础知识以为 ,也开始学习 vue2 的相关知识,今天就分享一个 使用 vue2 和 element-ui 组件 开发的一个网站拼图小游戏项目。我觉得这个项目是非常有意思的,但是我发现 这个小游戏 能过到第10关 还是有一定难度的。 先给大家分享一下使用的技术: 前端框架:Vue2 UI 组件库:Element-UI(提供交互组件,如按钮、弹窗等) 编程语言:HTML、CSS、JavaScript node版本: 16.20 构建与部署:支持 Nginx 或静态服务器部署
接下来给大家看一下 这个拼图游戏的样子


先简单的介绍一下 这个游戏的规则 :
拼图游戏共 十关,每一关的拼图难度逐步增加:
第 1 关:2×2 拼图
第 2 关:3×3 拼图
第 3 关:4×4 拼图
第 4 关:5×5 拼图
第 5 关:6×6 拼图
第 6 关:7×7 拼图
第 7 关:8×8 拼图
第 8 关:9×9 拼图
第 9 关:10×10 拼图
第 10 关:11×11 拼图
游戏通过 打乱图片顺序 让玩家进行拼接,玩家需要拖拽或点击来移动拼图块,直到完整还原原始图片。随着关卡的提升,拼图块的数量增加,挑战难度逐步加大。
功能特点:
逐级解锁:必须完成当前关卡,才能进入下一关。
随机打乱拼图:每次挑战都充满新鲜感。
计时功能:可统计玩家完成每一关所用时间,增加挑战性。
好了,大概的网站样子已经分享完了。接下来 看一下项目的目录结构

项目目录结构还是比较简单的,实际一共做了两个页面 ,
一个开始页面:
xml
<template>
<div class="home">
<div class="container">
<h1 class="title">拼图十关</h1>
<div class="card game-levels">
<h2>关卡选择</h2>
<p class="level-description">从简单到困难,挑战10个精彩关卡!</p>
<div class="levels-grid">
<div
v-for="level in 10"
:key="level"
class="level-item"
:class="{
active: level === currentLevel,
locked: level > highestLevel
}"
@click="selectLevel(level)"
>
<div class="level-content">
<span class="level-number">{{ level }}</span>
<div class="level-preview">
<img :src="getImageForLevel(level)" alt="关卡预览" />
<div class="level-difficulty">{{ getDifficultyText(level) }}</div>
</div>
<div class="level-status">
<i v-if="level > highestLevel" class="el-icon-lock"></i>
<i v-else-if="level < currentLevel" class="el-icon-check"></i>
<i v-else class="el-icon-right"></i>
</div>
</div>
</div>
</div>
<div class="level-actions">
<el-button type="primary" @click="startGame" :disabled="currentLevel > highestLevel">
开始游戏
</el-button>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'Home',
data() {
return {
currentLevel: 1,
gameImages: [
require('@/assets/images/1.jpeg'),
require('@/assets/images/2.jpeg'),
require('@/assets/images/3.jpeg'),
require('@/assets/images/4.jpeg'),
require('@/assets/images/5.jpeg'),
require('@/assets/images/6.jpeg'),
require('@/assets/images/7.jpeg'),
require('@/assets/images/8.jpeg'),
require('@/assets/images/9.jpeg'),
require('@/assets/images/10.jpeg')
]
}
},
computed: {
...mapGetters([
'getHighestLevel'
]),
highestLevel() {
return this.getHighestLevel
}
},
created() {
// 初始化应用,加载最高关卡
this.$store.dispatch('initApp')
},
methods: {
selectLevel(level) {
// 只能选择已解锁的关卡
if (level <= this.highestLevel) {
this.currentLevel = level
}
},
startGame() {
// 初始化游戏,设置当前关卡
this.$store.dispatch('initGame', {
level: this.currentLevel
})
// 跳转到游戏页面
this.$router.push('/game')
},
getImageForLevel(level) {
return this.gameImages[level - 1]
},
getDifficultyText(level) {
const difficulty = level + 1
return `${difficulty}×${difficulty}`
}
}
}
</script>
<style lang="scss" scoped>
.home {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f7fa;
}
.container {
width: 100%;
max-width: 900px;
padding: 20px;
}
.title {
font-size: 36px;
margin-bottom: 30px;
color: #303133;
text-align: center;
}
.game-levels {
width: 100%;
h2 {
margin-bottom: 10px;
color: #303133;
text-align: center;
}
.level-description {
text-align: center;
color: #606266;
margin-bottom: 30px;
}
}
.levels-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 15px;
margin-bottom: 30px;
@media (max-width: 768px) {
grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 480px) {
grid-template-columns: repeat(2, 1fr);
}
}
.level-item {
border-radius: 8px;
overflow: hidden;
cursor: pointer;
border: 2px solid #dcdfe6;
transition: all 0.3s ease;
&.active {
border-color: #409EFF;
transform: scale(1.05);
box-shadow: 0 0 10px rgba(64, 158, 255, 0.5);
}
&.locked {
opacity: 0.7;
cursor: not-allowed;
filter: grayscale(50%);
}
.level-content {
position: relative;
display: flex;
flex-direction: column;
height: 100%;
}
.level-number {
position: absolute;
top: 5px;
left: 5px;
background-color: rgba(0, 0, 0, 0.7);
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
z-index: 1;
}
.level-preview {
position: relative;
width: 100%;
padding-bottom: 100%;
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.level-difficulty {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 4px;
font-size: 12px;
text-align: center;
}
}
.level-status {
padding: 8px;
text-align: center;
background-color: #f5f7fa;
i {
font-size: 16px;
}
.el-icon-check {
color: #67C23A;
}
.el-icon-lock {
color: #909399;
}
.el-icon-right {
color: #409EFF;
}
}
}
.level-actions {
display: flex;
justify-content: center;
.el-button {
width: 200px;
}
}
</style>
一个玩游戏的页面:
xml
<template>
<div class="game-page">
<!-- 左侧区域 -->
<div class="left-section">
<!-- 原图预览 -->
<div class="preview-container">
<h3>原图预览</h3>
<div class="preview-image">
<img :src="imageUrl" alt="拼图预览" />
</div>
</div>
<!-- 碎片区域 -->
<div class="pieces-container">
<h3>拼图碎片</h3>
<div class="pieces-area" ref="piecesArea"></div>
</div>
</div>
<!-- 右侧区域 -->
<div class="right-section">
<!-- 游戏信息和控制 -->
<div class="game-header">
<h2>拼图游戏</h2>
<div class="level-badge">第 {{ currentLevel }} 关</div>
<div class="game-info">
<div class="info-item">
<span class="info-label">关卡:</span>
<span class="info-value">{{ currentLevel }}/10</span>
</div>
<div class="info-item">
<span class="info-label">难度:</span>
<span class="info-value">{{ difficulty }}×{{ difficulty }}</span>
</div>
<div class="info-item">
<span class="info-label">时间:</span>
<span class="info-value">{{ formatTime }}</span>
</div>
<div class="info-item">
<span class="info-label">步数:</span>
<span class="info-value">{{ moves }}</span>
</div>
</div>
<div class="game-controls">
<el-button
type="warning"
@click="togglePause"
v-if="gameStatus === 'playing' || gameStatus === 'paused'"
>
{{ gameStatus === 'playing' ? '暂停' : '继续' }}
</el-button>
<el-button type="danger" @click="resetGame">重置</el-button>
<el-button @click="backToHome">返回首页</el-button>
</div>
<div class="game-instructions">
<p>游戏说明:将左侧的拼图碎片拖放到右侧的目标区域中,完成拼图。</p>
<p class="tip"><i class="el-icon-back"></i> <strong>重要提示:</strong>已放置的碎片<span style="color: #F56C6C; font-weight: bold;">只能通过点击取回</span>,不能拖动到其他位置</p>
</div>
</div>
<!-- 目标区域(凹槽) -->
<div class="target-container">
<div class="target-area" ref="targetArea">
<template v-if="gameStatus === 'idle'">
<div class="loading-overlay">
<i class="el-icon-loading"></i>
<p>正在加载游戏...</p>
</div>
</template>
<template v-else-if="gameStatus === 'paused'">
<div class="paused-overlay">
<i class="el-icon-video-pause"></i>
<p>游戏已暂停</p>
<el-button type="primary" @click="togglePause">继续游戏</el-button>
</div>
</template>
</div>
<!-- 游戏完成覆盖层 -->
<div class="completed-overlay" v-if="gameStatus === 'completed'">
<i :class="isLastLevel && currentLevel === 10 ? 'el-icon-medal' : 'el-icon-trophy'"></i>
<h2 v-if="isLastLevel && currentLevel === 10">恭喜你通关成功!</h2>
<h2 v-else>恭喜你完成了第{{ currentLevel }}关!</h2>
<p class="level-message">{{ levelCompleteMessage }}</p>
<p>用时: {{ formatTime }}</p>
<p>步数: {{ moves }}</p>
<div class="action-buttons">
<el-button type="primary" @click="nextLevel" v-if="!isLastLevel">下一关</el-button>
<el-button type="success" @click="resetGame" v-if="isLastLevel">再玩一次</el-button>
<el-button @click="backToHome">返回首页</el-button>
</div>
</div>
<!-- 拼图错误覆盖层 -->
<div class="incorrect-overlay" v-if="gameStatus === 'incorrect'">
<i class="el-icon-warning-outline"></i>
<h2>拼图似乎有些问题...</h2>
<p class="error-message">{{ errorMessage }}</p>
<div class="action-buttons">
<el-button type="primary" @click="continueGame">继续调整当前拼图</el-button>
<el-button type="info" @click="showHint">查看提示</el-button>
<el-button type="danger" @click="resetGame">重新开始本关</el-button>
</div>
</div>
</div>
</div>
<!-- 隐藏的拼图组件,用于处理游戏逻辑 -->
<puzzle-board
v-show="false"
ref="puzzleBoard"
:difficulty="difficulty"
:image-url="imageUrl"
@move="handleMove"
@complete="handleComplete"
@incorrect="handleIncorrect"
@pieces-initialized="handlePiecesInitialized"
/>
</div>
</template>
<script>
import PuzzleBoard from '@/components/PuzzleBoard.vue'
import { mapGetters } from 'vuex'
export default {
name: 'Game',
components: {
PuzzleBoard
},
data() {
return {
timer: null,
placedCount: 0,
totalPieces: 0
}
},
computed: {
...mapGetters([
'getGameStatus',
'getDifficulty',
'getTime',
'getMoves',
'getImageUrl',
'getCurrentLevel',
'getLevelCompleteMessage',
'isLastLevel',
'getErrorMessage'
]),
gameStatus() {
return this.getGameStatus
},
difficulty() {
return this.getDifficulty
},
time() {
return this.getTime
},
moves() {
return this.getMoves
},
imageUrl() {
return this.getImageUrl
},
currentLevel() {
return this.getCurrentLevel
},
levelCompleteMessage() {
return this.getLevelCompleteMessage
},
errorMessage() {
return this.getErrorMessage
},
formatTime() {
const minutes = Math.floor(this.time / 60)
const seconds = this.time % 60
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
},
remainingPieces() {
return Math.max(0, this.totalPieces - this.placedCount)
}
},
created() {
// 如果没有设置图片URL,返回首页
if (!this.imageUrl) {
this.$router.push('/')
return
}
},
mounted() {
// 开始游戏
this.startGame()
// 监听窗口大小变化,调整布局
window.addEventListener('resize', this.adjustLayout)
this.adjustLayout()
},
beforeDestroy() {
// 清除计时器
this.clearTimer()
// 移除事件监听
window.removeEventListener('resize', this.adjustLayout)
},
methods: {
startGame() {
this.$store.dispatch('startGame')
this.startTimer()
},
togglePause() {
if (this.gameStatus === 'playing') {
this.$store.dispatch('pauseGame')
this.clearTimer()
} else if (this.gameStatus === 'paused') {
this.$store.dispatch('startGame')
this.startTimer()
}
},
resetGame() {
this.clearTimer()
// 重新初始化游戏
this.$store.dispatch('initGame', {
level: this.currentLevel
})
// 开始游戏
this.startGame()
// 重新获取拼图碎片
this.$nextTick(() => {
this.$refs.puzzleBoard.initializePuzzle()
})
},
nextLevel() {
this.clearTimer()
// 进入下一关
this.$store.dispatch('nextLevel')
// 开始游戏
this.startGame()
// 重新获取拼图碎片
this.$nextTick(() => {
this.$refs.puzzleBoard.initializePuzzle()
})
},
backToHome() {
this.clearTimer()
this.$router.push('/')
},
startTimer() {
this.clearTimer()
this.timer = setInterval(() => {
this.$store.commit('incrementTime')
}, 1000)
},
clearTimer() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
},
handleMove() {
this.$store.commit('incrementMoves')
},
handleComplete() {
console.log('游戏完成事件被触发')
this.clearTimer()
this.$store.dispatch('completeGame')
},
handlePiecesInitialized(pieces, targetCells) {
this.$nextTick(() => {
// 重置已放置计数
this.placedCount = 0
this.totalPieces = pieces.length
// 清空现有内容
this.$refs.piecesArea.innerHTML = ''
this.$refs.targetArea.innerHTML = ''
// 创建目标区域(凹槽)
const targetGrid = document.createElement('div')
targetGrid.className = 'puzzle-target-grid'
targetGrid.style.display = 'grid'
targetGrid.style.gridTemplateColumns = `repeat(${this.difficulty}, 1fr)`
targetGrid.style.gridTemplateRows = `repeat(${this.difficulty}, 1fr)`
targetGrid.style.gap = '1px'
targetGrid.style.width = '100%'
targetGrid.style.height = '100%'
targetGrid.style.border = '2px solid #ddd'
// 添加目标单元格
targetCells.forEach(cell => {
const cellElement = document.createElement('div')
cellElement.className = 'puzzle-cell'
cellElement.dataset.index = cell.index
cellElement.style.backgroundColor = 'rgba(255, 255, 255, 0.1)'
cellElement.style.borderRadius = '2px'
cellElement.style.border = '1px dashed rgba(0, 0, 0, 0.2)'
cellElement.style.position = 'relative'
// 添加序号以显示位置(难度大于6时不显示序号)
if (this.difficulty <= 6) {
const cellNumber = document.createElement('div')
cellNumber.className = 'cell-number'
cellNumber.textContent = cell.index + 1
cellNumber.style.position = 'absolute'
cellNumber.style.top = '50%'
cellNumber.style.left = '50%'
cellNumber.style.transform = 'translate(-50%, -50%)'
cellNumber.style.fontSize = this.difficulty <= 4 ? '14px' : '10px'
cellNumber.style.color = 'rgba(0, 0, 0, 0.3)'
cellNumber.style.pointerEvents = 'none'
cellElement.appendChild(cellNumber)
}
// 添加拖拽事件
cellElement.addEventListener('dragover', (e) => e.preventDefault())
cellElement.addEventListener('drop', (e) => {
e.preventDefault()
const pieceId = e.dataTransfer.getData('text/plain')
this.$refs.puzzleBoard.handleExternalDrop(parseInt(pieceId), cell.index)
})
targetGrid.appendChild(cellElement)
})
this.$refs.targetArea.appendChild(targetGrid)
// 计算碎片大小,根据难度调整
const pieceSize = this.calculatePieceSize(this.difficulty)
// 创建拼图碎片
pieces.forEach(piece => {
const pieceElement = document.createElement('div')
pieceElement.className = 'puzzle-piece'
pieceElement.dataset.id = piece.id
pieceElement.draggable = true
// 设置样式
pieceElement.style.width = `${pieceSize}px`
pieceElement.style.height = `${pieceSize}px`
pieceElement.style.borderRadius = '2px'
pieceElement.style.cursor = 'grab'
pieceElement.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)'
pieceElement.style.transition = 'transform 0.2s ease'
pieceElement.style.margin = '3px'
// 设置背景图片
const pieceWidth = 100 / this.difficulty
const pieceHeight = 100 / this.difficulty
pieceElement.style.backgroundImage = `url(${this.imageUrl})`
pieceElement.style.backgroundSize = `${this.difficulty * 100}% ${this.difficulty * 100}%`
// 使用原始索引(originalIndex)来确定背景位置,而不是当前的row和col
const originalRow = Math.floor(piece.originalIndex / this.difficulty)
const originalCol = piece.originalIndex % this.difficulty
pieceElement.style.backgroundPosition = `-${originalCol * 100}% -${originalRow * 100}%`
// 添加拖拽事件
pieceElement.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', piece.id)
e.dataTransfer.effectAllowed = 'move'
})
// 添加到碎片区域
this.$refs.piecesArea.appendChild(pieceElement)
// 监听拼图碎片状态变化
this.$watch(
() => this.$refs.puzzleBoard.availablePieces.find(p => p.id === piece.id).isPlaced,
(isPlaced) => {
if (isPlaced) {
pieceElement.style.opacity = '0.3'
pieceElement.style.pointerEvents = 'none'
pieceElement.style.filter = 'grayscale(100%)'
pieceElement.style.transform = 'scale(0.85)'
pieceElement.style.border = '2px dashed #999'
} else {
pieceElement.style.opacity = '1'
pieceElement.style.pointerEvents = 'auto'
pieceElement.style.filter = 'none'
pieceElement.style.transform = 'scale(1)'
pieceElement.style.border = 'none'
}
}
)
})
// 监听目标单元格状态变化
targetCells.forEach(cell => {
this.$watch(
() => this.$refs.puzzleBoard.targetCells[cell.index].pieceId,
(pieceId, oldPieceId) => {
// 更新计数
if (pieceId !== null && oldPieceId === null) {
this.placedCount++;
} else if (pieceId === null && oldPieceId !== null) {
this.placedCount--;
}
const cellElement = targetGrid.children[cell.index]
// 清空单元格
while (cellElement.firstChild) {
cellElement.removeChild(cellElement.firstChild)
}
// 如果有拼图碎片,添加到单元格
if (pieceId !== null) {
const piece = this.$refs.puzzleBoard.availablePieces.find(p => p.id === pieceId)
const placedPiece = document.createElement('div')
placedPiece.className = 'puzzle-piece-placed'
placedPiece.style.width = '100%'
placedPiece.style.height = '100%'
placedPiece.style.borderRadius = '2px'
placedPiece.style.cursor = 'pointer'
// 设置背景图片
const pieceWidth = 100 / this.difficulty
const pieceHeight = 100 / this.difficulty
placedPiece.style.backgroundImage = `url(${this.imageUrl})`
placedPiece.style.backgroundSize = `${this.difficulty * 100}% ${this.difficulty * 100}%`
// 使用原始索引(originalIndex)来确定背景位置,而不是当前的row和col
const originalRow = Math.floor(piece.originalIndex / this.difficulty)
const originalCol = piece.originalIndex % this.difficulty
placedPiece.style.backgroundPosition = `-${originalCol * 100}% -${originalRow * 100}%`
// 明确设置为不可拖动
placedPiece.draggable = false
// 添加阻止拖动的事件处理
placedPiece.addEventListener('dragstart', (e) => {
e.preventDefault()
e.stopPropagation()
return false
})
// 添加点击事件,允许取回碎片
placedPiece.addEventListener('click', () => {
this.$refs.puzzleBoard.retrievePiece(cell.index)
})
// 添加悬停效果
placedPiece.addEventListener('mouseover', () => {
placedPiece.style.boxShadow = '0 0 8px rgba(0, 0, 0, 0.5)'
placedPiece.style.transform = 'scale(0.95)'
// 添加提示文本
const tooltip = document.createElement('div')
tooltip.className = 'piece-tooltip'
tooltip.textContent = '点击取回'
tooltip.style.position = 'absolute'
tooltip.style.top = '50%'
tooltip.style.left = '50%'
tooltip.style.transform = 'translate(-50%, -50%)'
tooltip.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'
tooltip.style.color = 'white'
tooltip.style.padding = '2px 6px'
tooltip.style.borderRadius = '4px'
tooltip.style.fontSize = '12px'
tooltip.style.zIndex = '10'
placedPiece.appendChild(tooltip)
})
placedPiece.addEventListener('mouseout', () => {
placedPiece.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)'
placedPiece.style.transform = 'scale(1)'
// 移除提示文本
const tooltip = placedPiece.querySelector('.piece-tooltip')
if (tooltip) {
placedPiece.removeChild(tooltip)
}
})
placedPiece.style.userSelect = 'none'
placedPiece.style.webkitUserDrag = 'none'
placedPiece.style.MozUserSelect = 'none'
placedPiece.style.msUserSelect = 'none'
cellElement.appendChild(placedPiece)
} else {
// 如果没有拼图碎片,添加序号(难度大于6时不显示序号)
if (this.difficulty <= 6) {
const cellNumber = document.createElement('div')
cellNumber.className = 'cell-number'
cellNumber.textContent = cell.index + 1
cellNumber.style.position = 'absolute'
cellNumber.style.top = '50%'
cellNumber.style.left = '50%'
cellNumber.style.transform = 'translate(-50%, -50%)'
cellNumber.style.fontSize = this.difficulty <= 4 ? '14px' : '10px'
cellNumber.style.color = 'rgba(0, 0, 0, 0.3)'
cellNumber.style.pointerEvents = 'none'
cellElement.appendChild(cellNumber)
}
}
}
)
})
})
},
calculatePieceSize(difficulty) {
// 根据难度调整碎片大小
if (difficulty <= 3) return 60;
if (difficulty <= 5) return 50;
if (difficulty <= 7) return 40;
if (difficulty <= 9) return 30;
return 25; // 对于10×10和11×11的难度
},
adjustLayout() {
// 根据窗口大小调整布局
const targetArea = this.$refs.targetArea
if (targetArea) {
const width = targetArea.clientWidth
const height = targetArea.clientHeight
const size = Math.min(width, height) * 0.9
const targetGrid = targetArea.querySelector('.puzzle-target-grid')
if (targetGrid) {
targetGrid.style.width = `${size}px`
targetGrid.style.height = `${size}px`
targetGrid.style.margin = 'auto'
}
}
},
handleIncorrect() {
console.log('拼图错误事件被触发')
this.$store.dispatch('incorrectGame')
},
continueGame() {
// 不重置游戏,只将状态改回 playing,让用户继续尝试调整已放置的拼图
console.log('继续尝试,保留当前拼图状态')
this.$store.dispatch('startGame') // 将状态从 incorrect 改回 playing
},
showHint() {
// 显示提示
this.$alert('提示:仔细对照原图,从边缘开始拼起。特别注意图片的颜色和纹理的连续性。', '拼图提示', {
confirmButtonText: '知道了',
type: 'info',
callback: action => {
this.$message({
type: 'info',
message: '加油!你一定能完成的!'
});
}
});
}
}
}
</script>
<style lang="scss" scoped>
.game-page {
width: 100vw;
height: 100vh;
display: flex;
overflow: hidden;
}
.left-section {
width: 40%;
height: 100%;
display: flex;
flex-direction: column;
background-color: #f0f0f0;
border-right: 1px solid #ddd;
}
.right-section {
width: 60%;
height: 100%;
display: flex;
flex-direction: column;
}
.preview-container {
height: 40%;
padding: 20px;
display: flex;
flex-direction: column;
h3 {
margin-bottom: 10px;
text-align: center;
font-size: 18px;
}
.preview-image {
flex: 1;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
}
.pieces-container {
height: 60%;
padding: 20px;
display: flex;
flex-direction: column;
h3 {
margin-bottom: 10px;
text-align: center;
font-size: 18px;
}
.pieces-area {
flex: 1;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 10px;
overflow-y: auto;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
justify-content: center;
}
}
.game-header {
padding: 20px;
background-color: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
position: relative;
h2 {
margin-bottom: 15px;
text-align: center;
color: #303133;
}
.level-badge {
position: absolute;
top: 20px;
right: 20px;
padding: 5px 10px;
background-color: #409EFF;
color: #fff;
border-radius: 4px;
font-size: 14px;
font-weight: bold;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.game-info {
display: flex;
justify-content: space-around;
margin-bottom: 15px;
.info-item {
text-align: center;
.info-label {
font-weight: bold;
margin-right: 5px;
}
.info-value {
font-size: 18px;
}
}
}
.game-controls {
display: flex;
justify-content: center;
gap: 10px;
margin-bottom: 15px;
}
.game-instructions {
text-align: center;
font-size: 14px;
color: #606266;
p {
margin-bottom: 5px;
}
.tip {
font-size: 12px;
color: #909399;
font-style: italic;
}
}
}
.target-container {
flex: 1;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.target-area {
width: 100%;
height: 100%;
position: relative;
display: flex;
align-items: center;
justify-content: center;
background-color: #f8f8f8;
border-radius: 8px;
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.1);
}
}
.loading-overlay,
.paused-overlay,
.completed-overlay,
.incorrect-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: rgba(255, 255, 255, 0.95);
z-index: 100;
i {
font-size: 48px;
margin-bottom: 20px;
}
p {
font-size: 18px;
margin-bottom: 20px;
}
}
.completed-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: rgba(255, 255, 255, 0.95);
z-index: 100;
i {
font-size: 48px;
margin-bottom: 20px;
color: #E6A23C;
&.el-icon-medal {
color: #F56C6C;
animation: pulse 1.5s infinite;
}
}
h2 {
margin-bottom: 10px;
color: #67C23A;
}
.level-message {
font-size: 18px;
color: #409EFF;
margin-bottom: 20px;
text-align: center;
font-weight: bold;
}
p {
font-size: 18px;
margin-bottom: 10px;
}
.action-buttons {
margin-top: 20px;
display: flex;
gap: 10px;
}
}
.incorrect-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: rgba(255, 255, 255, 0.95);
z-index: 100;
i {
font-size: 48px;
margin-bottom: 20px;
color: #E6A23C;
}
h2 {
margin-bottom: 10px;
color: #E6A23C;
}
.error-message {
font-size: 18px;
color: #F56C6C;
margin-bottom: 20px;
text-align: center;
max-width: 80%;
}
.action-buttons {
margin-top: 20px;
display: flex;
gap: 10px;
}
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
</style>
代码内容过多,这边文章里就简单的分享一部分代码了。
如果我们想快速掌握一门编程语言,最好的办法就是多写代码,多练,然后多去看一些完整的项目,看看别人在开发项目的时候 都使用了什么技术,怎么设计一个软件项目,让项目更健壮稳定。
这个拼图小游戏前端基本上算是完成了。bug 也简单的测了一下,但是不保证项目里是否还存在bug。 如果分享的这个拼图小游戏对你有所帮助,源码也已经整理好了,需要的可以去看看。 www.wwwoop.com/home/Index/...