微信小程序 实现拼图
效果示例
微信小程序 碎片拼图
功能描述
在微信小程序中,实现一个简单的拼图小游戏。用户需要将四张碎片图片拖动到目标图片的正确位置,具体功能如下:
拖动功能:
用户可以通过手指拖动碎片图片,自由移动到目标图片的任意位置。
位置匹配:
如果碎片被拖动到正确的位置(即与目标图片的预定区域完全重叠或匹配),碎片会自动吸附到目标图片的指定位置,并显示为已完成。
如果碎片被拖动到错误的位置(未能与目标区域匹配),碎片会自动返回到初始位置。
完成检测:
当所有碎片都被正确放置后,触发完成拼图的效果(如显示完整图片、播放动画或提示成功信息)。
代码示例
-
html
<view class="container"> <view class="puzzle-box"> <view class="puzzle-cell" wx:for="{{puzzleCells}}" wx:key="id" id="{{item.id}}" data-id="{{item.id}}"> <image src="{{item.image}}" class="target-image"></image> </view> </view>
</view><!-- 下方的碎片 --> <!-- 添加 data-index 以传递碎片的索引 --> <view class="pieces"> <view class="piece" wx:for="{{pieces}}" wx:key="id" id="{{item.id}}" style="left: {{item.left}}px; top: {{item.top}}px;" data-id="{{item.id}}" data-index="{{index}}" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd" wx:if="{{!item.hidden}}"> <!-- 只有当 hidden 为 false 时才显示碎片 --> <image src="{{item.image}}"></image> </view> </view>
-
css
/* pages/cssCase/puzzle1/index.wxss */
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
}.puzzle-box {
margin: 20rpx;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
padding: 20rpx;
box-sizing: border-box;
}.puzzle-cell {
width: 300rpx;
height: 300rpx;
border: 1rpx solid #ccc;
position: relative;
}
.target-image {
width: 300rpx;
height: 300rpx;
}.pieces {
display: flex;
justify-content: space-between;
width: 100%;
padding: 0 10rpx;
}.piece {
width: 150rpx;
height: 150rpx;
position: absolute;
}.piece image {
width: 100%;
height: 100%;
} -
js
// pages/cssCase/puzzle1/index.js
Page({
data: {
puzzleCells: [
{ id: 'cell-1', image: '', correctPieceId: 'piece-1' },
{ id: 'cell-2', image: '', correctPieceId: 'piece-2' },
{ id: 'cell-3', image: '', correctPieceId: 'piece-3' },
{ id: 'cell-4', image: '', correctPieceId: 'piece-4' },
],
pieces: [
{ id: 'piece-1', image: 'https://static.sprucesmart.com/activity/shandong/jigsawPuzzle20250109/ce1.jpeg', left: 10, top: 380, originalLeft: 10, originalTop: 380, hidden: false },
{ id: 'piece-2', image: 'https://static.sprucesmart.com/activity/shandong/jigsawPuzzle20250109/ce2.jpeg', left: 120, top: 380, originalLeft: 120, originalTop: 380, hidden: false },
{ id: 'piece-3', image: 'https://static.sprucesmart.com/activity/shandong/jigsawPuzzle20250109/ce3.jpeg', left: 210, top: 380, originalLeft: 210, originalTop: 380, hidden: false },
{ id: 'piece-4', image: 'https://static.sprucesmart.com/activity/shandong/jigsawPuzzle20250109/ce4.jpeg', left: 300, top: 380, originalLeft: 300, originalTop: 380, hidden: false },
],
draggingPiece: null,
draggingPieceIndex: null, // 用于记录当前拖拽的碎片在 pieces 中的索引
},onTouchStart(e) { const { id, index } = e.currentTarget.dataset; this.setData({ draggingPiece: id, draggingPieceIndex: index, }); }, onTouchMove(e) { const { pageX, pageY } = e.touches[0]; const updatedPieces = this.data.pieces.map((piece, index) => { if (index === this.data.draggingPieceIndex) { return { ...piece, left: pageX - 75, top: pageY - 75 }; // 让碎片跟随手指移动 } return piece; }); this.setData({ pieces: updatedPieces }); }, onTouchEnd(e) { const { draggingPiece, draggingPieceIndex } = this.data; if (!draggingPiece) return; const query = wx.createSelectorQuery(); this.data.puzzleCells.forEach((cell) => { query.select(`#${cell.id}`).boundingClientRect(); }); query.select(`#${draggingPiece}`).boundingClientRect(); query.exec((res) => { const cellRects = res.slice(0, this.data.puzzleCells.length); const pieceRect = res[res.length - 1]; let placed = false; cellRects.forEach((cellRect, index) => { const cell = this.data.puzzleCells[index]; if (cell.correctPieceId === draggingPiece && this.checkOverlap(cellRect, pieceRect)) { placed = true; this.updatePuzzleCellImage(index, this.data.pieces[draggingPieceIndex].image); this.hidePiece(draggingPieceIndex); this.updatePiecePosition(draggingPieceIndex, cellRect.left, cellRect.top); } }); if (!placed) { this.resetPiecePosition(draggingPieceIndex); } this.setData({ draggingPiece: null }); // 检查是否完成拼图 this.checkCompletion(); }); }, checkOverlap(box1, box2) { return ( box1.left < box2.left + box2.width && box1.left + box1.width > box2.left && box1.top < box2.top + box2.height && box1.top + box1.height > box2.top ); }, updatePuzzleCellImage(cellIndex, image) { const updatedCells = [...this.data.puzzleCells]; updatedCells[cellIndex].image = image; this.setData({ puzzleCells: updatedCells }); }, hidePiece(pieceIndex) { const updatedPieces = this.data.pieces.map((piece, index) => { if (index === pieceIndex) { return { ...piece, hidden: true }; // 设置碎片为隐藏 } return piece; }); this.setData({ pieces: updatedPieces }); }, updatePiecePosition(pieceIndex, left, top) { const updatedPieces = this.data.pieces.map((piece, index) => { if (index === pieceIndex) { return { ...piece, left, top }; } return piece; }); this.setData({ pieces: updatedPieces }); }, resetPiecePosition(pieceIndex) { const updatedPieces = this.data.pieces.map((piece, index) => { if (index === pieceIndex) { return { ...piece, left: piece.originalLeft, top: piece.originalTop }; } return piece; }); this.setData({ pieces: updatedPieces }); }, // 检查拼图是否完成 checkCompletion() { const allPiecesPlaced = this.data.pieces.every((piece) => piece.hidden); if (allPiecesPlaced) { wx.showToast({ title: '拼图完成!', icon: 'success', duration: 2000, }); } },
});