当前位置: 首页 > news >正文

鸿蒙OSUniApp制作一个小巧的图片浏览器#三方框架 #Uniapp

利用UniApp制作一个小巧的图片浏览器

最近接了个需求,要求做一个轻量级的图片浏览工具,考虑到多端适配的问题,果断选择了UniApp作为开发框架。本文记录了我从0到1的开发过程,希望能给有类似需求的小伙伴一些参考。

前言

移动互联网时代,图片已成为人们日常生活中不可或缺的内容形式。无论是社交媒体还是工作沟通,我们每天都会接触大量图片。因此,一个好用的图片浏览器对用户体验至关重要。

UniApp作为一个使用Vue.js开发所有前端应用的框架,可以实现一套代码、多端运行(iOS、Android、H5、小程序等),是开发跨平台应用的理想选择。今天我就分享一下如何使用UniApp开发一个小巧但功能完善的图片浏览器。

开发环境准备

首先,我们需要搭建UniApp的开发环境:

  1. 安装HBuilderX(官方IDE)
  2. 创建UniApp项目
  3. 配置基础项目结构
# 如果使用CLI方式创建项目
npx @vue/cli create -p dcloudio/uni-preset-vue my-image-browser

项目结构设计

为了保持代码的可维护性,我将项目结构设计如下:

├── components            # 组件目录
│   ├── image-previewer   # 图片预览组件
│   └── image-grid        # 图片网格组件
├── pages                 # 页面
│   ├── index             # 首页
│   └── detail            # 图片详情页
├── static                # 静态资源
├── utils                 # 工具函数
└── App.vue、main.js等    # 项目入口文件

核心功能实现

1. 图片网格列表

首先实现首页的图片网格列表,这是用户进入应用的第一个界面:

<template><view class="container"><view class="header"><text class="title">图片浏览器</text></view><view class="image-grid"><view class="image-item" v-for="(item, index) in imageList" :key="index"@tap="previewImage(index)"><image :src="item.thumb || item.url" mode="aspectFill"lazy-load></image></view></view><view class="loading" v-if="loading"><text>加载中...</text></view></view>
</template><script>
export default {data() {return {imageList: [],page: 1,loading: false}},onLoad() {this.loadImages()},// 下拉刷新onPullDownRefresh() {this.page = 1this.imageList = []this.loadImages(() => {uni.stopPullDownRefresh()})},// 上拉加载更多onReachBottom() {this.loadImages()},methods: {// 加载图片数据loadImages(callback) {if (this.loading) returnthis.loading = true// 这里可以替换为实际的API请求setTimeout(() => {// 模拟API返回数据const newImages = Array(10).fill(0).map((_, i) => ({id: this.imageList.length + i + 1,url: `https://picsum.photos/id/${this.page * 10 + i}/500/500`,thumb: `https://picsum.photos/id/${this.page * 10 + i}/200/200`}))this.imageList = [...this.imageList, ...newImages]this.page++this.loading = falsecallback && callback()}, 1000)},// 预览图片previewImage(index) {const urls = this.imageList.map(item => item.url)uni.previewImage({urls,current: urls[index]})}}
}
</script><style>
.container {padding: 20rpx;
}.header {height: 80rpx;display: flex;align-items: center;justify-content: center;margin-bottom: 20rpx;
}.title {font-size: 36rpx;font-weight: bold;
}.image-grid {display: flex;flex-wrap: wrap;
}.image-item {width: 33.33%;padding: 5rpx;box-sizing: border-box;
}.image-item image {width: 100%;height: 220rpx;border-radius: 8rpx;
}.loading {text-align: center;margin: 30rpx 0;color: #999;
}
</style>

这段代码实现了图片瀑布流展示、下拉刷新和上拉加载更多功能。我使用了懒加载技术来优化性能,同时使用缩略图先加载,提升用户体验。

2. 自定义图片预览组件

虽然UniApp内置了图片预览功能,但为了实现更丰富的交互和动画效果,我决定自己封装一个图片预览组件:

<template><viewclass="image-previewer"v-if="visible"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd"><view class="previewer-header"><text class="counter">{{ current + 1 }}/{{ images.length }}</text><view class="close" @tap="close">×</view></view><swiperclass="swiper":current="current"@change="handleChange":circular="true"><swiper-item v-for="(item, index) in images" :key="index"><movable-area class="movable-area"><movable-viewclass="movable-view":scale="item.scale":scale-min="1":scale-max="4":scale-value="item.scale"direction="all"@scale="handleScale($event, index)"@change="handleMoveChange"><image:src="item.url"mode="widthFix"@load="imageLoaded(index)"></image></movable-view></movable-area></swiper-item></swiper><view class="previewer-footer"><view class="save-btn" @tap="saveImage">保存图片</view></view></view>
</template><script>
export default {name: 'ImagePreviewer',props: {urls: {type: Array,default: () => []},current: {type: Number,default: 0},visible: {type: Boolean,default: false}},data() {return {images: [],startY: 0,moveY: 0,moving: false}},watch: {urls: {handler(val) {this.images = val.map(url => ({url,scale: 1,loaded: false}))},immediate: true}},methods: {handleChange(e) {this.$emit('update:current', e.detail.current)},imageLoaded(index) {this.$set(this.images[index], 'loaded', true)},handleScale(e, index) {this.$set(this.images[index], 'scale', e.detail.scale)},handleMoveChange() {// 处理图片拖动事件},handleTouchStart(e) {this.startY = e.touches[0].clientY},handleTouchMove(e) {if (this.images[this.current].scale > 1) returnthis.moveY = e.touches[0].clientY - this.startYif (this.moveY > 0) {this.moving = true}},handleTouchEnd() {if (this.moving && this.moveY > 100) {this.close()}this.moving = falsethis.moveY = 0},close() {this.$emit('close')},saveImage() {const url = this.images[this.current].url// 先下载图片到本地uni.downloadFile({url,success: (res) => {// 保存图片到相册uni.saveImageToPhotosAlbum({filePath: res.tempFilePath,success: () => {uni.showToast({title: '保存成功',icon: 'success'})},fail: () => {uni.showToast({title: '保存失败',icon: 'none'})}})}})}}
}
</script><style>
.image-previewer {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background-color: #000;z-index: 999;display: flex;flex-direction: column;
}.previewer-header {height: 88rpx;display: flex;align-items: center;justify-content: space-between;padding: 0 30rpx;
}.counter {color: #fff;font-size: 28rpx;
}.close {color: #fff;font-size: 60rpx;line-height: 1;
}.swiper {flex: 1;width: 100%;
}.movable-area {width: 100%;height: 100%;
}.movable-view {width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;
}.movable-view image {width: 100%;
}.previewer-footer {height: 100rpx;display: flex;align-items: center;justify-content: center;
}.save-btn {color: #fff;font-size: 28rpx;padding: 10rpx 30rpx;border-radius: 30rpx;background-color: rgba(255, 255, 255, 0.2);
}
</style>

这个组件实现了以下功能:

  • 图片切换(使用swiper组件)
  • 图片缩放(movable-view的scale属性)
  • 向下滑动关闭预览
  • 保存图片到本地相册

3. 添加图片相册功能

为了增强应用的实用性,我们来添加一个相册分类功能:

<template><view class="album"><view class="tabs"><view class="tab-item" v-for="(item, index) in albums" :key="index":class="{ active: currentAlbum === index }"@tap="switchAlbum(index)"><text>{{ item.name }}</text></view></view><view class="content"><view class="album-info"><text class="album-name">{{ albums[currentAlbum].name }}</text><text class="album-count">{{ imageList.length }}张照片</text></view><view class="image-grid"><view class="image-item" v-for="(item, index) in imageList" :key="index"@tap="previewImage(index)"><image :src="item.thumb || item.url" mode="aspectFill"lazy-load></image></view></view></view></view>
</template><script>
export default {data() {return {albums: [{ id: 1, name: '风景' },{ id: 2, name: '人物' },{ id: 3, name: '动物' },{ id: 4, name: '植物' }],currentAlbum: 0,imageList: []}},onLoad() {this.loadAlbumImages()},methods: {switchAlbum(index) {if (this.currentAlbum === index) returnthis.currentAlbum = indexthis.loadAlbumImages()},loadAlbumImages() {const albumId = this.albums[this.currentAlbum].id// 模拟加载不同相册的图片uni.showLoading({ title: '加载中' })setTimeout(() => {// 模拟API返回数据this.imageList = Array(15).fill(0).map((_, i) => ({id: i + 1,url: `https://picsum.photos/seed/${albumId * 100 + i}/500/500`,thumb: `https://picsum.photos/seed/${albumId * 100 + i}/200/200`}))uni.hideLoading()}, 800)},previewImage(index) {const urls = this.imageList.map(item => item.url)uni.previewImage({urls,current: urls[index]})}}
}
</script><style>
.album {display: flex;flex-direction: column;height: 100vh;
}.tabs {display: flex;height: 80rpx;border-bottom: 1rpx solid #eee;
}.tab-item {flex: 1;display: flex;align-items: center;justify-content: center;position: relative;
}.tab-item.active {color: #007AFF;
}.tab-item.active::after {content: '';position: absolute;bottom: 0;left: 25%;width: 50%;height: 4rpx;background-color: #007AFF;
}.content {flex: 1;padding: 20rpx;
}.album-info {margin-bottom: 20rpx;
}.album-name {font-size: 36rpx;font-weight: bold;
}.album-count {font-size: 24rpx;color: #999;margin-left: 20rpx;
}.image-grid {display: flex;flex-wrap: wrap;
}.image-item {width: 33.33%;padding: 5rpx;box-sizing: border-box;
}.image-item image {width: 100%;height: 220rpx;border-radius: 8rpx;
}
</style>

性能优化

开发过程中,我注意到一些性能问题,特别是图片加载较慢的情况,因此做了以下优化:

  1. 懒加载:使用lazy-load属性延迟加载图片
  2. 缩略图预加载:先加载小图,再加载大图
  3. 图片压缩:在上传和展示时进行适当压缩
// 图片压缩工具函数
export function compressImage(src, quality = 80) {return new Promise((resolve, reject) => {uni.compressImage({src,quality,success: res => {resolve(res.tempFilePath)},fail: err => {reject(err)}})})
}
  1. 虚拟列表:当图片数量很多时,考虑使用虚拟列表技术

踩坑记录

开发过程中遇到了一些坑,在此记录,希望能帮助到大家:

  1. 兼容性问题:H5和App表现一致,但在小程序中movable-view的缩放效果不太理想,需要针对不同平台做兼容处理
  2. 图片预览:小程序的图片预览API不支持长按保存,需要自己实现
  3. 内存问题:加载大量高清图片容易导致内存占用过高,需要做好图片资源管理

总结

通过这个项目,我实现了一个简单但功能完善的图片浏览器。UniApp的跨平台能力确实令人印象深刻,一套代码能够同时运行在多个平台上,大大提高了开发效率。

当然,这个应用还有很多可以改进的地方,比如添加图片滤镜、优化动画效果、增加云存储功能等。希望这篇文章对你有所帮助,有任何问题欢迎在评论区留言讨论!

参考资料

  1. UniApp官方文档
  2. Vue.js指南

相关文章:

  • 深入浅出入侵检测系统(IDS)的工作原理与应用场景
  • 第二章:CSS秘典 · 色彩与布局的力量
  • 如何开发一款 Chrome 浏览器插件
  • 通过泛域名解析把二级域名批量绑定到wordpress的指定页面
  • Java 大视界——Java 大数据在智慧交通智能停车诱导系统中的数据融合与实时更新
  • 分布式1(cap base理论 锁 事务 幂等性 rpc)
  • .Net HttpClient 使用代理功能
  • elpis-core: 基于 Koa 实现 web 服务引擎架构设计解析
  • 应用层协议简介:以 HTTP 和 MQTT 为例
  • STM32 实时时钟(RTC)详解
  • HTTP GET报文解读
  • 胶片转场视频剪辑思路
  • 国产 ETL 数据集成厂商推荐—谷云科技 RestCloud
  • Axure设计之内联框架切换页面、子页面间跳转问题
  • 【爬虫】DrissionPage-2
  • 前端面试宝典---js垃圾回收机制
  • 大模型越狱:技术漏洞与安全挑战——从原理到防御
  • 生活实用小工具-手机号归属地查询
  • Jsp技术入门指南【十四】实现基于MySQL+JDBC+JSP数据库验证的登录界面与登录跳转功能
  • 文章记单词 | 第74篇(六级)
  • 首次采用“顶置主星+侧挂从星”布局,长二丁“1箭12星”发射成功
  • 国台办:台湾自古属于中国,历史经纬清晰,法理事实清楚
  • 视频|王弘治:王太后,“先天宫斗圣体”?
  • 中国至越南河内国际道路运输线路正式开通
  • 优化营商环境,服务上海“五个中心”建设,北外滩有何举措?
  • 筑牢安全防线、提升应急避难能力水平,5项国家标准发布