鸿蒙OSUniApp制作一个小巧的图片浏览器#三方框架 #Uniapp
利用UniApp制作一个小巧的图片浏览器
最近接了个需求,要求做一个轻量级的图片浏览工具,考虑到多端适配的问题,果断选择了UniApp作为开发框架。本文记录了我从0到1的开发过程,希望能给有类似需求的小伙伴一些参考。
前言
移动互联网时代,图片已成为人们日常生活中不可或缺的内容形式。无论是社交媒体还是工作沟通,我们每天都会接触大量图片。因此,一个好用的图片浏览器对用户体验至关重要。
UniApp作为一个使用Vue.js开发所有前端应用的框架,可以实现一套代码、多端运行(iOS、Android、H5、小程序等),是开发跨平台应用的理想选择。今天我就分享一下如何使用UniApp开发一个小巧但功能完善的图片浏览器。
开发环境准备
首先,我们需要搭建UniApp的开发环境:
- 安装HBuilderX(官方IDE)
- 创建UniApp项目
- 配置基础项目结构
# 如果使用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>
性能优化
开发过程中,我注意到一些性能问题,特别是图片加载较慢的情况,因此做了以下优化:
- 懒加载:使用lazy-load属性延迟加载图片
- 缩略图预加载:先加载小图,再加载大图
- 图片压缩:在上传和展示时进行适当压缩
// 图片压缩工具函数
export function compressImage(src, quality = 80) {return new Promise((resolve, reject) => {uni.compressImage({src,quality,success: res => {resolve(res.tempFilePath)},fail: err => {reject(err)}})})
}
- 虚拟列表:当图片数量很多时,考虑使用虚拟列表技术
踩坑记录
开发过程中遇到了一些坑,在此记录,希望能帮助到大家:
- 兼容性问题:H5和App表现一致,但在小程序中movable-view的缩放效果不太理想,需要针对不同平台做兼容处理
- 图片预览:小程序的图片预览API不支持长按保存,需要自己实现
- 内存问题:加载大量高清图片容易导致内存占用过高,需要做好图片资源管理
总结
通过这个项目,我实现了一个简单但功能完善的图片浏览器。UniApp的跨平台能力确实令人印象深刻,一套代码能够同时运行在多个平台上,大大提高了开发效率。
当然,这个应用还有很多可以改进的地方,比如添加图片滤镜、优化动画效果、增加云存储功能等。希望这篇文章对你有所帮助,有任何问题欢迎在评论区留言讨论!
参考资料
- UniApp官方文档
- Vue.js指南