16、构建 “Imaginary”:具有类似 Instagram 图像滤镜的应用程序

构建 “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” 应用程序的开发过程有了更清晰的认识,也为后续的开发和优化提供了参考。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值