uni-app实现完成任务解锁拼图功能

发布于:2025-05-09 ⋅ 阅读:(23) ⋅ 点赞:(0)

界面如下

在这里插入图片描述

代码如下

<template>
  <view class="puzzle-container">
    <view class="puzzle-title">任务进度 {{completedCount}}/{{totalPieces}}</view>
    <view class="puzzle-grid">
      <view
          v-for="(piece, index) in puzzlePieces"
          :key="index"
          class="puzzle-piece"
          :class="{
          'unlocked': piece.unlocked,
          'locked': !piece.unlocked,
          'unlock-animation': animatingIndex === index
        }"
          @click="onPieceTap(index)"
      >
        <!-- 已解锁显示拼图块,未解锁显示锁图标 -->
        <image
            v-if="piece.unlocked"
            :src="piece.imageUrl"
            mode="aspectFill"
            class="piece-image"
        ></image>
        <image
            v-else
            src="@/static/images/form/close-image.png"
            class="lock-icon"
        ></image>

        <!-- 显示拼图块编号 -->
        <text class="piece-number">{{index + 1}}</text>

        <!-- 粒子效果 -->
        <view class="particle-container" v-if="animatingIndex === index">
          <view
              class="particle"
              v-for="particle in particles"
              :key="particle.id"
              :style="{
              left: particle.left + '%',
              top: particle.top + '%',
              backgroundColor: particle.color
            }"
          ></view>
        </view>
      </view>
    </view>

    <!-- 完成所有拼图后的庆祝弹窗 -->
    <uni-popup ref="completePopup" type="center">
      <view class="celebration-popup">
        <image src="/static/celebration.png" mode="widthFix"></image>
        <text class="celebration-text">恭喜完成所有任务!</text>
        <button @click="viewCompletePuzzle">查看完整拼图</button>
      </view>
    </uni-popup>
  </view>
</template>

<script>
export default {
  data() {
    return {
      totalPieces: 20,
      puzzlePieces: [],
      completedCount: 0,
      animatingIndex: -1,
      particles: [],
      allCompleted: false
    }
  },

  created() {
    this.initPuzzle();
  },

  methods: {
    // 初始化拼图
    initPuzzle() {
      this.puzzlePieces = Array(this.totalPieces).fill().map((_, index) => ({
        index,
        unlocked: false,
        imageUrl: `../../static/pt/pt${index + 1}.png`
      }));

      // 模拟已解锁的拼图块 (实际应从后端获取)
      this.unlockPiece(0);
      this.unlockPiece(1);
      this.unlockPiece(2);
      this.unlockPiece(3);
      this.unlockPiece(4);
      this.completedCount = 5;
    },

    // 解锁拼图块
    unlockPiece(index) {
      if (index >= this.totalPieces || this.puzzlePieces[index].unlocked) return;

      this.$set(this.puzzlePieces, index, {
        ...this.puzzlePieces[index],
        unlocked: true
      });

      this.animatingIndex = index;
      this.completedCount++;

      // 创建粒子效果
      this.createParticles();

      // 检查是否全部完成
      if (this.completedCount === this.totalPieces) {
        this.allCompleted = true;
        setTimeout(() => {
          this.$refs.completePopup.open();
        }, 1000);
      }

      // 动画结束后清除状态
      setTimeout(() => {
        this.animatingIndex = -1;
      }, 1000);
    },

    // 创建粒子效果
    createParticles() {
      this.particles = [];
      const particles = [];

      for (let i = 0; i < 12; i++) {
        particles.push({
          id: i,
          left: Math.random() * 60 + 20,
          top: Math.random() * 60 + 20,
          color: `hsl(${Math.random() * 360}, 100%, 50%)`,
          '--tx': Math.random() * 100,
          '--ty': Math.random() * 100
        });
      }

      this.particles = particles;

      setTimeout(() => {
        this.particles = [];
      }, 1000);
    },

    // 点击拼图块
    onPieceTap(index) {
      if (!this.puzzlePieces[index].unlocked) {
        uni.showToast({
          title: `完成任务${index + 1}后解锁`,
          icon: 'none'
        });
      }
    },

    // 模拟完成任务
    completeRandomTask() {
      const firstLockedIndex = this.puzzlePieces.findIndex(p => !p.unlocked);
      if (firstLockedIndex !== -1) {
        this.unlockPiece(firstLockedIndex);
      } else {
        uni.showToast({
          title: '所有拼图已解锁!',
          icon: 'success'
        });
      }
    },

    // 查看完整拼图
    viewCompletePuzzle() {
      this.$refs.completePopup.close();
      uni.navigateTo({
        url: '/pages/puzzle-preview/puzzle-preview'
      });
    }
  }
}
</script>

<style lang="scss" scoped>
.puzzle-container {
  padding: 20rpx;
  box-sizing: border-box;
}

.puzzle-title {
  text-align: center;
  font-size: 36rpx;
  margin-bottom: 30rpx;
  color: #333;
}

.puzzle-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr); /* 5列布局 */
  gap: 10rpx;
}

.puzzle-piece {
  position: relative;
  aspect-ratio: 1; /* 保持正方形 */
  border-radius: 10rpx;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #f5f5f5;

  &.unlocked {
    box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
  }

  .piece-image, .lock-icon {
    width: 100%;
    height: 100%;
  }

  .lock-icon {
    width: 60% !important;
    height: 60% !important;
    opacity: 0.6;
  }

  .piece-number {
    position: absolute;
    bottom: 8rpx;
    right: 8rpx;
    font-size: 24rpx;
    color: #999;
    background-color: rgba(255, 255, 255, 0.7);
    border-radius: 50%;
    width: 36rpx;
    height: 36rpx;
    display: flex;
    justify-content: center;
    align-items: center;
  }
}

/* 解锁动画 */
.unlock-animation {
  animation: unlockScale 0.6s ease-out;
}

@keyframes unlockScale {
  0% { transform: scale(0.8); opacity: 0; }
  50% { transform: scale(1.1); }
  100% { transform: scale(1); opacity: 1; }
}

/* 粒子效果 */
.particle-container {
  position: absolute;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.particle {
  position: absolute;
  width: 10rpx;
  height: 10rpx;
  border-radius: 50%;
  animation: particleFly 1s ease-out forwards;
}

@keyframes particleFly {
  to {
    transform: translate(
            calc((var(--tx, 0) - 50) * 1rpx),
            calc((var(--ty, 0) - 50) * 1rpx)
    );
    opacity: 0;
  }
}

/* 庆祝弹窗 */
.celebration-popup {
  background: white;
  padding: 40rpx;
  border-radius: 20rpx;
  display: flex;
  flex-direction: column;
  align-items: center;

  image {
    width: 200rpx;
    margin-bottom: 20rpx;
  }

  .celebration-text {
    font-size: 36rpx;
    margin-bottom: 30rpx;
    color: #ff6b81;
    font-weight: bold;
  }

  button {
    background: #ff6b81;
    color: white;
    border: none;
  }
}
</style>

网站公告

今日签到

点亮在社区的每一天
去签到