在移动应用开发中,壁纸类应用的核心体验体现在三个方面:流畅的图片浏览、高效的分类体系和便捷的交互操作。本文将结合实际代码,详细解析基于 UniApp 开发的壁纸应用核心功能实现,包括分页加载、图片预览、滑动切换和弹窗交互等关键模块。

一、分类列表与分页加载功能

壁纸应用的基础是高效展示大量图片资源,分页加载是实现这一目标的核心技术。

1. 核心实现思路

  • 采用 "页码 + 页大小" 的分页参数控制数据加载
  • 监听页面滚动到底部事件触发下一页加载
  • 本地缓存已加载数据,提升页面切换体验
  • 通过状态标识控制加载状态(加载中 / 无更多)

2. 代码实现

列表页(classlist.vue)完整逻辑

<template>
  <view class="classlist">
    <!-- 初始加载状态 -->
    <view class="loadingLayout" v-if="!classList.length && !noDate">
      <uni-load-more status="loading"></uni-load-more>
    </view>
    
    <!-- 壁纸列表网格 -->
    <view class="content">
      <navigator 
        :url="'/pages/preview/preview?id='+item._id" 
        class="item" 
        v-for="item in classList" 
        :key="item._id"
      >
        <image :src="item.smallPicurl" mode="aspectFill"></image>
      </navigator>
    </view>
    
    <!-- 底部加载状态 -->
    <view class="loadingLayout" v-if="classList.length || noDate">
      <uni-load-more :status="noDate ? 'noMore' : 'loading'"></uni-load-more>
    </view>
  </view>
</template>

<script setup>
import { ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { APIgetClassList } from '@/api/apis.js'

// 列表数据存储
const classList = ref([])
// 分页参数
const queryParams = {
  pageNum: 1,       // 当前页码
  pageSize: 9,      // 每页条数
  classid: ''       // 分类ID(从路由获取)
}
// 是否无更多数据
const noDate = ref(false)

// 获取列表数据
const getClassList = async () => {
  const res = await APIgetClassList(queryParams)
  // 拼接新数据(而非覆盖)
  classList.value = [...classList.value, ...res.data.data]
  // 判断是否还有更多数据
  if (res.data.data.length < queryParams.pageSize) {
    noDate.value = true
  }
  // 缓存数据到本地,供预览页使用
  uni.setStorageSync("strogClassList", classList.value)
}

// 页面加载时初始化
onLoad((e) => {
  // 从路由参数获取分类ID和名称
  const { id, name } = e
  queryParams.classid = id
  // 设置导航栏标题
  uni.setNavigationBarTitle({ title: name })
  // 加载第一页数据
  getClassList()
})

// 监听页面滚动到底部
onReachBottom(() => {
  // 如果已无更多数据,停止加载
  if (noDate.value) return
  // 页码自增,加载下一页
  queryParams.pageNum++
  getClassList()
})
</script>

<style lang="scss">
.content {
  display: grid;
  grid-template-columns: repeat(3, 1fr); // 3列网格布局
  gap: 10rpx;
  .item {
    height: 440rpx;
    image {
      width: 100%;
      height: 100%;
      display: block;
    }
  }
}
</style>

3. 技术亮点

  • 网格布局:使用grid-template-columns: repeat(3, 1fr)实现自适应三列布局,无需手动计算宽度
  • 数据缓存:通过uni.setStorageSync将列表数据缓存到本地,避免预览页重复请求
  • 状态管理:使用noDate标识控制加载状态,防止无效请求
  • 路由传参:通过onLoad接收路由参数,实现分类与列表的关联

二、图片预览与滑动切换功能

预览页是壁纸应用的核心交互页面,需要实现流畅的大图浏览和切换体验。

1. 核心实现思路

  • 从本地缓存获取图片列表,避免重复网络请求
  • 实现图片地址转换(缩略图→高清图)
  • 预加载相邻图片,提升滑动流畅度
  • 支持循环滑动和索引同步

2. 代码实现

预览页(preview.vue)核心逻辑

<template>
  <view class="preview">
    <!-- 图片轮播容器 -->
    <swiper 
      :indicator-dots="true" 
      :autoplay="true" 
      :interval="3000" 
      :duration="1000" 
      :current="currentIndex" 
      @change="swiperChange" 
      circular  <!-- 开启循环滑动 -->
    >
      <swiper-item v-for="(item, index) in ClassList" :key="item._id">
        <!-- 只加载预加载列表中的图片 -->
        <image 
          v-if="readImg.includes(index)" 
          :src="item.picurl" 
          mode="aspectFill" 
          @click="maskchange"
        ></image>
      </swiper-item>
    </swiper>
    
    <!-- 计数器显示 -->
    <view class="count" v-show="!ismask">
      {{ currentIndex + 1 }} / {{ ClassList.length }}
    </view>
  </view>
</template>

<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'

// 从本地缓存获取列表数据
const strogClassList = uni.getStorageSync("strogClassList")
const ClassList = ref([])
// 当前图片索引
const currentIndex = ref(0)
// 需要预加载的图片索引列表
const readImg = ref([])
// 遮罩层显示状态
const ismask = ref(false)

// 初始化图片数据(转换为高清图)
ClassList.value = strogClassList.map(item => ({
  ...item,
  // 替换缩略图地址为高清图地址
  picurl: item.smallPicurl.replace("_small.webp", ".jpg")
}))

// 图片预加载逻辑
const readImgfun = () => {
  // 计算需要预加载的索引(当前、上一张、下一张)
  const prevIndex = currentIndex.value - 1 < 0 
    ? ClassList.value.length - 1 
    : currentIndex.value - 1
  const nextIndex = currentIndex.value + 1 >= ClassList.value.length 
    ? 0 
    : currentIndex.value + 1
  
  // 添加到预加载列表并去重
  readImg.value = [...new Set([prevIndex, currentIndex.value, nextIndex])]
}

// 滑动切换事件
const swiperChange = (e) => {
  currentIndex.value = e.detail.current
  // 滑动后重新预加载
  readImgfun()
}

// 页面加载时初始化
onLoad((e) => {
  // 从路由获取当前图片ID
  const { id } = e
  // 找到当前图片在列表中的索引
  currentIndex.value = ClassList.value.findIndex(item => item._id === id)
  // 初始化预加载
  readImgfun()
})

// 点击切换遮罩层显示
const maskchange = () => {
  ismask.value = !ismask.value
}
</script>

3. 技术亮点

  • 图片地址转换:通过replace方法将缩略图地址转换为高清图,节省流量同时保证预览质量
  • 预加载优化:只加载当前、上一张和下一张图片,避免一次性加载所有图片导致的内存占用过高
  • 循环滑动处理:通过三元表达式处理边界索引(首图的上一张是尾图,尾图的下一张是首图)
  • 按需加载:通过v-if="readImg.includes(index)"控制图片加载时机,提升初始加载速度

三、弹窗交互(信息 / 评分)功能

弹窗是展示额外信息和接收用户输入的重要组件,在壁纸应用中用于展示壁纸详情和评分功能。

1. 核心实现思路

  • 使用uni-popup组件实现弹窗基础功能
  • 区分底部弹窗(信息展示)和居中弹窗(评分交互)
  • 通过ref控制弹窗显示 / 隐藏
  • 实现数据联动(当前壁纸信息与弹窗内容)

2. 代码实现

信息弹窗与评分弹窗实现

<template>
  <view class="preview">
    <!-- 底部操作按钮 -->
    <view class="footer" v-show="!ismask">
      <view class="box" @click="clickInfo">
        <uni-icons type="info" size="28"></uni-icons>
        <view class="text">信息</view>
      </view>
      <view class="box" @click="clickScore">
        <uni-icons type="star" size="28"></uni-icons>
        <view class="text">评分</view>
      </view>
    </view>
    
    <!-- 信息弹窗(底部弹出) -->
    <uni-popup ref="infoPopup" type="bottom">
      <view class="infoPopup">
        <view class="popHeader">
          <view class="title">壁纸信息</view>
          <view class="close" @click="clickInfoclose">
            <uni-icons type="closeempty" size="18"></uni-icons>
          </view>
        </view>
        <scroll-view scroll-y>
          <view class="content">
            <view class="row">
              <view class="lable">壁纸id:</view>
              <text selectable class="value">{{ currentInfo._id }}</text>
            </view>
            <view class="row">
              <view class="lable">发布者</view>
              <text selectable class="value">{{ currentInfo.nickname }}</text>
            </view>
            <view class="row">
              <view class="lable">评分</view>
              <view class="value roteBox">
                <uni-rate readonly v-model="currentInfo.score" />
                <text class="score">{{ currentInfo.score }}分</text>
              </view>
            </view>
          </view>
        </scroll-view>
      </view>
    </uni-popup>
    
    <!-- 评分弹窗(居中显示) -->
    <uni-popup ref="scorePopup" :is-mask-click="false">
      <view class="scorePopup">
        <view class="popHeader">
          <view class="title">壁纸评分</view>
          <view class="close" @click="clickScoreclose">
            <uni-icons type="closeempty" size="18"></uni-icons>
          </view>
        </view>
        <view class="content">
          <uni-rate v-model="userScore" allowHalf />
          <text class="text">{{ userScore }}分</text>
        </view>
        <view class="footer">
          <button @click="submitScore">确认评分</button>
        </view>
      </view>
    </uni-popup>
  </view>
</template>

<script setup>
import { ref, watch } from 'vue'

// 弹窗实例
const infoPopup = ref(null)
const scorePopup = ref(null)
// 当前壁纸信息
const currentInfo = ref({})
// 用户评分
const userScore = ref(0)

// 监听当前索引变化,更新当前壁纸信息
watch(currentIndex, (newVal) => {
  currentInfo.value = ClassList.value[newVal]
})

// 信息弹窗控制
const clickInfo = () => {
  infoPopup.value.open()
}
const clickInfoclose = () => {
  infoPopup.value.close()
}

// 评分弹窗控制
const clickScore = () => {
  // 打开弹窗时初始化评分(当前评分)
  userScore.value = currentInfo.value.score
  scorePopup.value.open()
}
const clickScoreclose = () => {
  scorePopup.value.close()
}

// 提交评分
const submitScore = () => {
  // 这里可以添加提交到后端的逻辑
  uni.showToast({ title: `已评分:${userScore.value}分`, icon: 'none' })
  scorePopup.value.close()
}
</script>

3. 技术亮点

  • 弹窗类型区分:底部弹窗适合展示大量信息(带滚动),居中弹窗适合简单交互
  • 数据联动:通过watch监听当前索引变化,自动更新弹窗展示的壁纸信息
  • 评分初始化:打开评分弹窗时自动填充当前评分,提升用户体验
  • 交互细节:评分弹窗设置is-mask-click="false",避免误触遮罩关闭弹窗

四、核心功能总结与扩展建议

关键技术点梳理

功能模块核心技术优化点
分页加载onReachBottom + 分页参数本地缓存、加载状态控制
图片预览swiper + 图片预加载地址转换、按需加载
弹窗交互uni-popup + ref类型区分、数据联动

扩展建议

  1. 图片下载功能:结合uni.downloadFileuni.saveImageToPhotosAlbum实现壁纸下载
  2. 收藏功能:添加本地存储或后端接口,实现壁纸收藏功能
  3. 分享功能:集成uni.share实现壁纸分享到社交平台
  4. 预加载优化:根据网络状态动态调整预加载数量(WiFi 环境多加载,移动网络少加载)

通过以上实现,我们构建了一个功能完整、体验流畅的壁纸应用核心模块。UniApp 的跨平台特性和丰富的组件库,使得这些功能可以轻松运行在微信小程序、App 和 H5 等多个平台,大大提升了开发效率。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐