鸿蒙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指南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/83761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python爬虫实战:获取taobao网最新rtx5060ti显卡销量数据并分析,为消费者做参考

一、系统定义与技术架构 1.1 系统定义 本系统是基于 Python 开发的电商数据采集与分析工具,旨在通过模拟用户行为实现淘宝平台 50 系列显卡(以 RTX 5060 Ti 为例)销售数据的自动化获取、清洗、分析及可视化。核心功能包括: 自动登录:通过 Selenium 模拟浏览器操作完成账…

OCframework编译Swift

建一个OC的framework&#xff1a; 需要对外暴露的OC文件&#xff0c;需要放到OC的.h文件中 framework中&#xff0c;OC类&#xff0c;调用framework中的Swift类&#xff1a; #import "WowAudioFocus/WowAudioFocus-Swift.h" //02 #import "{工程名}/{工程…

每日算法 -【Swift 算法】Two Sum 问题:从暴力解法到最优解法的演进

【Swift 算法】Two Sum 问题&#xff1a;从暴力解法到最优解法的演进 本文通过“Two Sum”问题&#xff0c;带你了解如何从最直观的暴力解法&#xff0c;逐步优化到高效的哈希表解法&#xff0c;并对两者进行对比&#xff0c;适合算法入门和面试准备。 &#x1f4a1; 问题描述 …

【保姆级】Nginx简介以及安装

Nginx简介 ​ Nginx是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点&#xff08;俄文&#xff1a;Рамблер&#xff09;开发的&#xff0c;第一个公开版本0.1.0发布于20…

C++(25): 标准库 <deque>

目录 1、 核心概念 2. 基本语法 3. 特点 4. 特有成员函数 5. 内存与性能 6. 示例代码 7. 成员函数列表 8. 使用场景 9. 注意事项 1、 核心概念 双端队列(Double-Ended Queue,deque) 是一种允许在队列头部和尾部高效插入和删除元素的线性数据结构,同时支持随机访问。…

软件设计师关系代数和元组演算(关联、笛卡尔积、除、映射、分段等问题)考点分析——求三连

一、考点分值占比与趋势分析 综合知识历年统计表 年份考题数量分值分值占比考察重点2018334%自然连接、投影、选择2019222.67%笛卡尔积、条件筛选2020111.33%属性列计算2021334%关系运算综合应用2022222.67%元组演算表达式2023222.67%差运算、连接类型2024111.33%除法运算应用…

卸载云枢(MacOS 版)

删除 APP 和相关文件 sudo chflags -R noschg /Applications/Yunshu.app 2>/dev/null sudo rm -rf /Applications/Yunshu.app sudo rm -rf /Library/Application\ Support/EagleCloud sudo rm -rf /Library/LaunchAgents/com.eagleyun.endpoint.agent.plist sudo rm -rf /L…

在 Ubuntu 20.04 中使用 init.d 或者systemd实现开机自动执行脚本

Ubuntu 20 默认使用的是 systemd 系统管理器&#xff0c;但传统的 SysV Init&#xff08;/etc/init.d/&#xff09;脚本依然兼容并可用。本文将介绍如何通过 init.d 写脚本来在开机时自动设置某个 GPIO&#xff08;如 GPIO407&#xff09;为高电平&#xff0c;适用于嵌入式系统…

苹果的人工智能领域慢热

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

FastAPI使用@app.get/@app.post等装饰器注册路由无效404 Not Found

&#xff08;一&#xff09;问题描述 使用app.get注册路由&#xff0c;前端发送请求访问路径和路由一致&#xff0c;但一直显示404 Not Found&#xff0c;检查了好几遍&#xff0c;确认没有访问路径不一致的问题。 在Swagger文档里查看&#xff0c;也没有找到这个新添加的接口。…

制作我的计算器

1. 界面布局 新建项目 MyCalculator&#xff0c;开始布局。 2. 静态布局 代码如下&#xff1a; // etc/pages/Index.ets Entry Component struct Index {build() {Column() {/*** 运算区*/Column() {TextInput({ text: 12x13 }).height(100%).fontSize(32).enabled(false).f…

2025-5-17Vue3快速上手

1、ref对比reactive 区别第2点&#xff1a;本质是指针指向问题 整体修改reactive的数据时&#xff0c;有坑 使用原则需要根据项目原本的代码灵活参考 如果要更新的数据是从服务器获取回来的&#xff0c;用Object.assign是好方法&#xff0c;需要注意的是&#xff1a;Object.a…

深度学习---模型预热(Model Warm-Up)

一、基本概念与核心定义 模型预热是指在机器学习模型正式训练或推理前&#xff0c;通过特定技术手段使模型参数、计算图或运行环境提前进入稳定状态的过程。其本质是通过预处理操作降低初始阶段的不稳定性&#xff0c;从而提升后续任务的效率、精度或性能。 核心目标&#xf…

加载渲染geojson数据

本节我们学习如何在cesium中加载geojson数据 想要加载geojson数据首先要有数据源,我们以中国地图为例 复制数据的geo api 在cesium的官网库中查询 可以看到如何在cesium中导入数据的方法 //加载geojson数据let dataGeo Cesium.GeoJsonDataSource.load("https://geo.dat…

python:pymysql概念、基本操作和注入问题讲解

python&#xff1a;pymysql分享目录 一、概念二、数据准备三、安装pymysql四、pymysql使用&#xff08;一&#xff09;使用步骤&#xff08;二&#xff09;查询操作&#xff08;三&#xff09;增&#xff08;四&#xff09;改&#xff08;五&#xff09;删 五、关于pymysql注入…

职坐标AIoT技能培训课程实战解析

职坐标AIoT技能培训课程以人工智能与物联网技术深度融合为核心&#xff0c;构建了“理论实战行业应用”三位一体的教学体系。课程体系覆盖Python编程基础、传感器数据采集、边缘计算开发、云端服务部署及智能硬件开发全链路&#xff0c;通过分层递进的知识模块帮助学员建立系统…

MySQL 用户权限管理:从入门到精通

在当今数据驱动的时代&#xff0c;数据库安全已成为企业信息安全体系的核心组成部分。作为最流行的开源关系型数据库之一&#xff0c;MySQL 的用户权限管理系统提供了强大而灵活的访问控制机制。本文将全面解析 MySQL 用户权限管理的各个方面&#xff0c;帮助数据库管理员和开发…

Java常见API文档(下)

格式化的时间形式的常用模式对应关系如下&#xff1a; 空参构造创造simdateformate对象&#xff0c;默认格式 练习.按照指定格式展示 package kl002;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;public class Date3 {publi…

博图1200硬件组态与启保停程序编写步骤详解

一、前言 在工业自动化控制领域&#xff0c;西门子S7-1200 PLC因其性能稳定、编程灵活而广受欢迎。本文将详细介绍使用TIA Portal&#xff08;博图&#xff09;软件进行S7-1200 PLC硬件组态以及编写基本启保停程序的完整步骤&#xff0c;帮助初学者快速掌握这一基础而重要的技…

AutoMouser - 单次AI调用铸就高效自动化脚本

你是否厌倦了反复点点点的枯燥操作&#xff1f;是否希望像科幻电影那样&#xff0c;一句指令&#xff0c;万事搞定&#xff1f;如果告诉你&#xff0c;现在只需要一次AI调用&#xff0c;就能自动执行一整套鼠标脚本操作&#xff0c;你会不会觉得&#xff1a;自动化的时代&#…