利用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指南