构建 “Imaginary”:具有类似 Instagram 图像滤镜的应用程序
1. 将处理后的照片保存到应用程序文件夹
要将图片保存到应用程序文件夹,需要实现图片的存储和模型以及一些逻辑。
1.1 定义图片模型和存储
图片模型很简单,在
app/model/Picture.js
中创建:
Ext.define('Imaginary.model.Picture', {
extend: 'Ext.data.Model',
config: {
fields: [
{ name: 'id', type: 'int' },
{ name: 'url', type: 'string' }
]
}
});
其中:
-
id
是
localStorage
中生成的标识符。
-
url
是设备上图片的路径。
存储与效果的存储略有不同,在
app/store/Pictures.js
文件中添加以下代码:
Ext.define('Imaginary.store.Pictures',{
extend:'Ext.data.Store',
requires: ['Ext.data.proxy.LocalStorage', 'Imaginary.model.Picture'],
config:{
model:'Imaginary.model.Picture',
storeId: 'Pictures',
autoLoad: true,
autoSync: true,
proxy:{
type:'localstorage'
}
}
});
这里使用
localStorage
作为代理。
autoLoad
属性表示在对象创建后立即调用加载方法,
autoSync
属性指定在对存储中的任何记录进行编辑后,自动将存储与其代理同步。
1.2 将图片保存到文件系统
在图片预览弹出窗口中有三个按钮,其中一个是保存按钮,其点击事件处理程序如下:
Ext.getCmp('savePhotoBtn').on('tap', function() {
var filteredImageURI = Ext.getCmp('photoPreview').getSrc();
self.savePhoto(filteredImageURI, function() {
popup.hide();
});
});
这里获取照片预览的原始源并将其传递给
savePhoto
函数,在回调中隐藏弹出窗口。
savePhoto
函数如下:
savePhoto: function(imageURI, callback) {
var self = this;
self.copyPhotoToPersistentStore(imageURI, function(persistentImageURI) {
var picture = Ext.create('Imaginary.model.Picture', {
url: persistentImageURI
});
var pictureStore = Ext.getStore('Pictures');
pictureStore.add(picture);
// refresh list of pictures goes here
if (callback) callback();
})
}
借助
copyPhotoToPersistentStore
函数将图像保存到文件系统,然后创建具有新 URL 的图片对象,将其保存到存储并执行回调。
copyPhotoToPersistentStore
函数接收两个参数:
- 第一个参数是实际的 Base64 图像数据。
- 第二个参数是处理完成后要调用的回调。
函数内容从变量定义和数据 URI 到二进制转换开始:
copyPhotoToPersistentStore: function(fileURI, callback) {
var self = this;
var d = new Date();
var n = d.getTime();
var newFileName = n + ".jpg";
var myFolderApp = "Imaginary";
function convertDataURIToBinary(dataURI) {
var BASE64_MARKER = ';base64,';
var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
var base64 = dataURI.substring(base64Index);
var raw = window.atob(base64);
var rawLength = raw.length;
var array = new Uint8Array(new ArrayBuffer(rawLength));
for (i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
}
return array;
}
var bin = convertDataURIToBinary(fileURI);
var bb = new Blob([bin], { type: "image/jpeg" });
//ask for permission and save logic goes here
}
其中:
-
newFileName
是基于当前时间戳的所需文件名。
-
myFolderApp
是保存图片的目录。
-
convertDataURIToBinary
是将图片的 Base64 表示形式转换为二进制数据的函数。
-
bin
是存储二进制数据的变量。
-
bb
是在保存时将使用的
image/jpeg
类型的 Blob。
之后,请求文件系统并创建写入器,使用 HTML5 FileSystem API:
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
function (fileSystem) {
fileSystem.root.getFile(newFileName, {create: true, exclusive: false},
function (fileEntry) {
fileEntry.createWriter(function (writer) {
// write to file code goes here
}, onError);
}, onError);
}, onError);
requestFileSystem
函数的参数如下:
| 参数 | 说明 |
| ---- | ---- |
| 第一个参数(type) | 定义文件系统是否持久,可能的值为
window.TEMPORARY
或
window.PERSISTENT
。 |
| 第二个参数 | 应用程序存储所需的大小(以字节为单位),0 表示最大。 |
| 第三个参数 | 成功回调。 |
| 第四个参数 | 可选的失败回调。 |
fileSystem.root.getFile
函数用于查找或创建文件,参数如下:
| 参数 | 说明 |
| ---- | ---- |
| 第一个参数(newFileName) | 基于我们创建的时间戳的新文件名。 |
|
create: true
| 如果文件不存在则创建。 |
createWriter()
方法获取一个
FileWriter
对象,创建写入器后,可以向文件写入以下代码:
writer.seek(0);
writer.write(bb);
seek(0)
从文件开头开始写入位置,
write(bb)
实际将 Blob 写入文件。
写入完成后,将 Blob 复制到文件夹:
writer.onwrite = function(e) {
fileSystem.root.getDirectory(myFolderApp, {
create: true,
exclusive: false
},
function(directory) {
fileEntry.copyTo(directory, newFileName, function(entry) {
if (callback) callback(entry.toURL());
}, onError);
},
onError);
};
getDirectory
查找文件夹,如果不存在则创建。
copyTo
方法将
fileEntry
复制到
Imaginary
目录。最后,使用
entry.toURL()
函数返回图像的路径,并将此 URL 保存在
localStorage
中。
2. 构建自定义插件以将图片保存到 iOS 库
虽然已成功将图片保存到应用程序的
Imaginary
文件夹,但还希望将应用了效果的图片保存到设备库(这里是 iOS 库)。由于 HTML5 FileSystem API 无法直接访问 iOS 库,且没有官方插件,因此需要为 PhoneGap/Cordova 开发自定义插件。
2.1 插件设置
在
Imaginary
项目外创建插件:
$ cd ~/Projects
$ mkdir cordova-pugin-imagetolibrary
插件仓库必须有一个顶级的
plugin.xml
清单文件,创建并添加以下内容:
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
id="com.cybind.imaginary.imagetolibrary" version="0.0.1">
<name>ImageToLibrary</name>
<description>Cordova ImageToLibrary Plugin</description>
<license>Apache 2.0</license>
<keywords>cordova,image,base64,library</keywords>
<engines>
<engine name="cordova" version=">=3.0.0" />
</engines>
<js-module src="www/ImageToLibraryPlugin.js" name="imagetolibrary">
<clobbers target="ImageToLibraryPlugin" />
</js-module>
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="ImageToLibraryPlugin">
<param name="ios-package" value="ImageToLibraryPlugin" />
<param name="onload" value="true" />
</feature>
</config-file>
<header-file src="src/ios/ImageToLibraryPlugin.h" />
<source-file src="src/ios/ImageToLibraryPlugin.m" />
</platform>
</plugin>
其中:
-
id
是用于标识插件包的反向域名格式字符串。
-
js-module
是指定通用 JavaScript 接口路径的部分。
-
platform
部分指定了 iOS 平台的一组本地代码。
-
config-file
表示将插入到
config.xml
中的配置部分。
-
header-file
和
source-file
标签指定了库组件文件的路径,这里是 Objective-C 文件。
插件文件夹结构如下:
.
├── plugin.xml
├── src
│ └── ios
│ ├── ImageToLibraryPlugin.h
│ └── ImageToLibraryPlugin.m
└── www
└── ImageToLibraryPlugin.js
2.2 JavaScript 接口
JavaScript 接口可能是插件最重要的部分,最终需要调用
cordova.exec
与本地平台通信,代码如下:
var exec = require('cordova/exec');
var ImageToLibraryPlugin = {
saveToLibrary: function(types, success, fail) {
exec(success, fail, "ImageToLibraryPlugin", "saveImage", types);
}
};
module.exports = ImageToLibraryPlugin;
这里调用
saveToLibrary
函数,它接收一个包含自定义数据的数组、一个成功回调和一个失败回调。
exec
Cordova 函数的参数如下:
| 参数 | 说明 |
| ---- | ---- |
| 第一个参数 | 成功回调函数。 |
| 第二个参数 | 错误回调函数,返回一个可选的错误参数。 |
| 第三个参数 | 要在本地调用的服务名称,通常是一个类。 |
| 第四个参数 | 要在本地调用的操作名称,通常是一个类方法。 |
| 第五个参数 | 自定义参数数组。 |
这里将参数数组传递给 Objective-C 中
ImageToLibraryPlugin
类的
saveImage
方法。
2.3 原生 iOS 代码
插件的原生实现涉及两个文件:
ImageToLibraryPlugin.h
和
ImageToLibraryPlugin.m
。
ImageToLibraryPlugin.h
文件如下:
typedef void(^SaveImageCompletion)(NSError* error, NSString* url);
@interface ImageToLibraryPlugin : CDVPlugin
{
NSString* callbackID;
}
@property (nonatomic, copy) NSString* callbackID;
@property (strong, atomic) ALAssetsLibrary* library;
// Instance Method
-(void)saveImage:(CDVInvokedUrlCommand*)command;
-(void)removeImage:(NSMutableArray*)arguments
withDict:(NSMutableDictionary*)options;
-(void)saveImageToLibrary: (UIImage *)image;
-(void)saveImage:(UIImage*)image toAlbum:(NSString*)albumName withComp
letionBlock:(SaveImageCompletion)completionBlock;
@end
要调用的方法是
saveImage
,它接收一个
CDVInvokedUrlCommand
类型的参数,其他方法是在本地代码中使用的内部方法。
ImageToLibraryPlugin.m
文件中
saveImage
方法的实现如下:
-(void)saveImage:(CDVInvokedUrlCommand*)command
{
self.callbackID = command.callbackId;
NSString *stringObtainedFromJavascript = [command.arguments objectAtIndex:0];
//Saving image to divice library
NSURL *url = [NSURL URLWithString:stringObtainedFromJavascript];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
[self saveImageToLibrary:image];
}
arguments
参数中的第一个参数是
callbackID
,用于通过
PluginResult
将数据发送回
successCallback
或
failureCallback
。然后获取从 JavaScript 接口传递给插件的 Base64 数据,将其转换为
UIImage
对象并传递给
saveImageToLibrary
方法。
返回插件结果的代码如下:
CDVPluginResult* pluginResult = nil;
// 成功时
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsString: stringToReturn];
// 失败时
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
[self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackID];
2.4 发布和使用插件
可以将插件发布到源代码控制平台,例如 GitHub,发布后其他 PhoneGap/Cordova 开发者可以通过以下命令安装:
$ cd cordova
$ cordova plugin add https://github.com/cybind/cordova-pugin-imagetolibrary.git
安装成功后,可以在主控制器中定义一个方法
saveToLibrary
来将图片保存到库中:
saveToLibrary: function(base64Data, callback) {
window.ImageToLibraryPlugin.saveToLibrary([base64Data],
function(imageUrl) {
if (callback) callback(imageUrl, null);
}, function(error) {
if (callback) callback();
});
}
将
copyPhotoToPersistentStore
函数的内容用
saveToLibrary
方法包装:
copyPhotoToPersistentStore: function(fileURI, callback) {
var self = this;
self.saveToLibrary(fileURI, false, function(imageUrl) {
// the rest of saving logic goes here
});
}
3. 显示照片列表
最后,用所有捕获的图像填充视图。在
app/controller/Pictures.js
文件中创建控制器:
Ext.define('Imaginary.controller.Pictures', {
extend: 'Ext.app.Controller',
config: {
refs: {
photoContainer: '#photos'
}
},
launch: function() {
var pictures = this.getPictures();
var photoContainer = this.getPhotoContainer();
for (var i = 0; i < pictures.length; i++) {
this.addPictureToContainer(pictures[i], photoContainer);
}
}
// getPictures and addPictureToContainer implementation goes here
});
控制器的主要目的是从图片存储中检索所有图片,创建图像并将其添加到容器中,
addPictureToContainer
方法如下:
addPictureToContainer: function(picture, container) {
var self = this;
var thumb = Ext.create('Ext.Img', {
src: picture.get('url'),
height: 80,
width: '20%',
border: 3,
style: 'float: left; border-color: white; border-style: solid;'
});
thumb.picture = picture;
container.add(thumb);
}
这里创建了一个 Sencha Touch 图像对象,其源为存储中的 URL,宽度设置为 20%,每行始终有五个元素,并对每个图片的边框进行了样式设置。之后,将存储中的图片对象分配给图像的自定义属性,以便稍后引用元数据。
4. 实现图片预览
在
app/view/Picture.js
中添加视图,类似于已经实现的弹出式预览,去掉效果列表并在底部添加两个按钮:删除和关闭按钮。
{
xtype: 'button',
id: 'deletePhotoBtn',
text: 'Delete',
iconCls: 'trash',
ui: 'decline',
flext: 1,
margin: '0 5 0 5'
},
{
xtype: 'button',
id: 'closePhotoBtn',
text: 'Close',
iconCls: 'delete',
flext: 1,
margin: '0 5 0 5'
}
为缩略图添加点击事件处理程序以打开弹出窗口:
addPictureToContainer: function(picture, container) {
//...
thumb.on('tap', function() {
var thumb = this;
var popup = Ext.create('Imaginary.view.Picture');
Ext.Viewport.add(popup);
popup.show();
Ext.getCmp('photoPreview').setSrc(thumb.picture.get('url'));
popup.on('hide', function() {
popup.destroy();
});
Ext.getCmp('deletePhotoBtn').on('tap', function() {
// delete picture code goes here
});
Ext.getCmp('closePhotoBtn').on('tap', function() {
popup.hide();
});
});
//...
}
创建
Imaginary.view.Picture
视图的实例,将其添加到视口并显示。然后将缩略图的源分配给弹出窗口中图像预览的源。弹出窗口隐藏时销毁它,点击关闭按钮时关闭弹出窗口。
可以通过以下命令检查预览效果:
$ sencha app build -run native
点击 “My Photos” 标签,然后点击任何缩略图,将看到带有图片和按钮的弹出窗口。
通过以上步骤,完成了 “Imaginary” 应用程序的开发,涉及到在 Cordova 应用程序中使用第三方 JavaScript 库、构建和分发 Cordova 插件等内容。
mermaid 流程图如下:
graph LR
A[保存图片到应用文件夹] --> B[定义图片模型和存储]
B --> C[保存图片到文件系统]
C --> D[构建自定义插件保存到iOS库]
D --> E[发布和使用插件]
E --> F[显示照片列表]
F --> G[实现图片预览]
构建 “Imaginary”:具有类似 Instagram 图像滤镜的应用程序
5. 开发过程总结
在整个 “Imaginary” 应用程序的开发过程中,我们完成了多个关键步骤,下面对这些步骤进行总结:
1.
图片保存到应用文件夹
- 定义图片模型和存储,使用
localStorage
作为代理,实现自动加载和同步。
- 将图片保存到文件系统,涉及数据 URI 到二进制转换、文件创建和写入等操作。
2.
构建自定义插件保存到 iOS 库
- 进行插件设置,创建
plugin.xml
清单文件。
- 实现 JavaScript 接口,通过
cordova.exec
与本地平台通信。
- 编写原生 iOS 代码,处理图片保存到 iOS 库的逻辑。
- 发布插件并在项目中使用。
3.
显示照片列表
- 创建控制器,从存储中检索图片并添加到容器中。
4.
实现图片预览
- 添加视图,包含删除和关闭按钮。
- 为缩略图添加点击事件处理程序,打开弹出窗口显示图片。
6. 开发技巧与注意事项
在开发过程中,有一些技巧和注意事项可以帮助我们更好地完成项目:
-
代码复用
:在定义模型、存储和控制器时,可以尽量复用已有的代码结构,提高开发效率。例如,在创建图片模型和存储时,可以参考其他类似项目的实现。
-
错误处理
:在文件操作和插件调用过程中,要做好错误处理。例如,在
requestFileSystem
和
exec
函数中,都提供了失败回调,要在回调中处理可能出现的错误。
-
性能优化
:在处理大量图片时,要注意性能优化。例如,在显示照片列表时,可以使用懒加载的方式,避免一次性加载所有图片。
7. 未来扩展方向
“Imaginary” 应用程序已经具备了基本的功能,但还有很多可以扩展的方向:
-
更多滤镜效果
:可以添加更多的图像滤镜效果,如复古、黑白、模糊等,提升用户体验。
-
社交分享功能
:实现图片的社交分享功能,让用户可以将处理后的图片分享到社交媒体平台。
-
用户管理
:添加用户注册、登录和个人信息管理功能,让用户可以保存自己的图片和设置。
8. 总结与展望
通过本次开发,我们学习了如何在 Cordova 应用程序中使用第三方 JavaScript 库,掌握了构建和分发 Cordova 插件的方法,以及如何使用 Sencha Touch 进行视图和控制器的开发。
未来,随着移动应用技术的不断发展,我们可以进一步优化和扩展 “Imaginary” 应用程序,为用户提供更好的服务。同时,我们也可以将这些开发经验应用到其他项目中,提升自己的开发能力。
以下是开发步骤的详细表格总结:
| 步骤 | 操作内容 | 代码文件 |
| ---- | ---- | ---- |
| 保存图片到应用文件夹 | 定义图片模型和存储,保存图片到文件系统 |
app/model/Picture.js
、
app/store/Pictures.js
|
| 构建自定义插件保存到 iOS 库 | 插件设置、JavaScript 接口实现、原生 iOS 代码编写、插件发布和使用 |
plugin.xml
、
www/ImageToLibraryPlugin.js
、
src/ios/ImageToLibraryPlugin.h
、
src/ios/ImageToLibraryPlugin.m
|
| 显示照片列表 | 创建控制器,检索图片并添加到容器 |
app/controller/Pictures.js
|
| 实现图片预览 | 添加视图,为缩略图添加点击事件处理程序 |
app/view/Picture.js
|
mermaid 流程图展示开发步骤的依赖关系:
graph LR
A[保存图片到应用文件夹] --> B[构建自定义插件保存到iOS库]
B --> C[显示照片列表]
C --> D[实现图片预览]
E[开发技巧与注意事项] --> A
E --> B
E --> C
E --> D
F[未来扩展方向] --> D
通过以上的总结和分析,我们对 “Imaginary” 应用程序的开发过程有了更清晰的认识,也为后续的开发和优化提供了参考。
超级会员免费看
12

被折叠的 条评论
为什么被折叠?



