您的位置:首页 > Web前端 > HTML5

如何构建一个类似Instagram的照片共享应用程序的HTML5:第2部分

2013-04-30 11:02 731 查看
在第1部分中,我们了解了一些UI布局的实施细节的InstaFuzz应用程序。从这里,你可以得到源代码的应用程序,如果你想在本地运行。在这一期中,我们将采取一些比如如何使用/拖放,文件API,Canvas和Web工作者的其它位看看。



  拖/放


  InstaFuzz支持的事情之一是能够映像文件直接拖放到带黑色/ ??蓝色大盒。这是支持启用画布元素处理“滴”事件。当一个文件被丢弃到一个HTML元素的浏览器触发该元素的“水滴”事件,并通过在dataTransfer对象的,其中包含了文件属性,而被删除的文件的列表,其中包含一个参考。下面是如何处理应用程序中的(“图片报”是在页面上的canvas元素的ID):

var pic = $("#picture");
pic.bind("drop", function (e) {
suppressEvent(e);
var files = e.originalEvent.dataTransfer.files;
// more code here to open the file
});

pic.bind("dragover", suppressEvent).bind("dragenter", suppressEvent);

function suppressEvent(e) {
e.stopPropagation();
e.preventDefault();
}


  文件属性是一个集合,随后用文件API访问文件的内容(包括在下一节中)的文件对象。而且,我们处理的dragover dragEnter事件的事件,基本上防止这些事件传播到浏览器,从而防止了浏览器从处理文件下拉。IE实例可能会卸载当前页面,并试图直接打开该文件,否则。

  文件API

  一旦已被删除的文件,应用程序试图打开图像,并使其在画布上。它通过使用文件API。是W3C文件API的规范,允许以编程方式访问Web应用程序安全的方式从本地文件系统中的文件。在InstaFuzz我们的FileReader对象来读取文件内容作为一个数据URL字符串,像这样使用readAsDataURL的方法:

 
var reader = new FileReader();
reader.onloadend = function (e2) {
drawImageToCanvas(e2.target.result);
};
reader.readAsDataURL(files[0]);

  在这里,文件是文件检索的对象从“滴”事件处理的功能canvas元素的集合。由于我们感兴趣,只在一个单一的文件,我们只需选择从集合中的第一个文件,而忽略其他,如果有任何。实际的文件内容是异步加载,一旦加载完成,的onloadend事件被触发时,我们得到的文件内容数据的URL,然后画到画布上。

  渲染过滤器

  现在,这里的核心功能,当然是过滤器的应用程序。为了能够应用过滤器的形象,我们需要一种方法来访问图像的单个像素。之前,我们可以访问的像素,我们需要有实际呈现到我们的画布上的图像。因此,让我们先来看看在呈现的图像拾取用户的canvas元素的代码。

  渲染图像到画布上

  支持canvas元素的渲染图像对象通过的又一方法。要加载图片实例中的图像文件,InstaFuzz使用以下实用程序:

App.Namespace.define("InstaFuzz.Utils", {
loadImage: function (url, complete) {
var img = new Image();
img.src = url;
img.onload = function () {
complete(img);
};
}
});

  这使得应用程序加载的图像对象使用一个URL如下面的代码:

function drawImageToCanvas(url) {
InstaFuzz.Utils.loadImage(url, function (img) {
// save reference to source image
sourceImage = img;

mainRenderer.clearCanvas();
mainRenderer.renderImage(img);

// load image filter previews
loadPreviews(img);
});
}

  在这里,mainRenderer定义的构造函数过滤renderer.js中FilterRenderer创建的实例。应用程序使用FilterRenderer的对象来管理canvas元素-无论是在预览窗格以及主画布元素的权利。像这样的RenderImage上FilterRenderer方法已经被定义:

 
FilterRenderer.prototype.renderImage = function (img) {
var imageWidth = img.width;
var imageHeight = img.height;
var canvasWidth = this.size.width;
var canvasHeight = this.size.height;
var width, height;

if ((imageWidth / imageHeight) >= (canvasWidth / canvasHeight)) {
width = canvasWidth;
height = (imageHeight * canvasWidth / imageWidth);
} else {
width = (imageWidth * canvasHeight / imageHeight);
height = canvasHeight;
}

var x = (canvasWidth - width) / 2;
var y = (canvasHeight - height) / 2;
this.context.drawImage(img, x, y, width, height);
};

  这似乎是很多的代码,但它所做的一切最终是要弄清楚最好的方式来呈现图像中的可用屏幕区域,考虑到图像的长宽比。代码的关键部分,实际上呈现在画布上的形象出现在最后一行的方法。上下文成员是指收购从画布对象通过调用其getContext方法的2D背景。

  从画布获取像素

  现在的形象已经呈现,我们将需要为了适用不同的过滤器可用的单个像素。这是很容易收购通过调用getImageData画布的上下文对象。这里就是InstaFuzz如何调用这个instafuzz.js。

var imageData = renderer.context.getImageData(
0, 0,
renderer.size.width,
renderer.size.height);

  对象返回getImageData提供访问通过它的数据属性,这又是一个数组类对象,包含的字节值的集合,其中每个值表示的颜色呈现为一个单一通道的单像素的单个像素。每个像素是表示使用4个字节指定的红色,绿色,蓝色和alpha通道值。它也有一个长度 属性返回的缓冲区长度。如果你有一个2D统筹,你可以很容易地转化成一个索引到这个数组,如下面的代码使用。每个通道的颜色强度值的范围从0到255。这里的效用函数从filters.js,接受输入的图像数据对象以及呼叫者感兴趣,并返回一个对象,它包含的颜色值的像素的二维坐标:

function getPixel(imageData, x, y) {
var data = imageData.data, index = 0;

// normalize x and y and compute index
x = (x < 0) ? (imageData.width + x) : x;
y = (y < 0) ? (imageData.height + y) : y;
index = (x + y * imageData.width) * 4;

return {
r: data[index],
g: data[index + 1],
b: data[index + 2]
};
}

  应用过滤器

  现在,我们已经到单个像素的访问,应用过滤器是非常简单的。在这里,例如是适用的加权灰度图像上的过滤器的功能。它只是简单地拿起从红色,绿色和蓝色通道的强度和总结后,施加在每个通道上的一个乘法因子,然后所有3个通道的分配的结果。

 
// "Weighted Grayscale" filter
Filters.addFilter({
name: "Weighted Grayscale",
apply: function (imageData) {
var w = imageData.width, h = imageData.height;
var data = imageData.data;
var index;
for (var y = 0; y < h; ++y) {
for (var x = 0; x < w; ++x) {
index = (x + y * imageData.width) * 4;
var luminance = parseInt((data[index + 0] * 0.3) +
(data[index + 1] + 0.59) +
(data[index + 2] * 0.11));
data[index + 0] = data[index + 1] =
data[index + 2] = luminance;
}

Filters.notifyProgress(imageData, x, y, this);
}

Filters.notifyProgress(imageData, w, h, this);
}
});

  一旦过滤器已经应用,我们可以通过在修改后的图像数据对象调用putImageData的方法,反映在画布上。而加权的灰度过滤器是相当简单的其他过滤器使用的图像处理技术被称为卷积。所有过滤器的代码是在filters.js的被移植的C代码可以在这里和卷积滤波器。

  网络工作者

  正如你可能想象做这一切的数字运算应用过滤器可能会需要很长的时间才能完成。例如运动模糊过滤器使用一个9×9的过滤器矩阵计算每个像素的新值,实际上是在CPU密集型其中过滤器。如果我们做这一切计算在UI线程上的浏览器,然后应用程序将基本上每次冻结一个过滤器被应用。为了提供一个响应的用户体验的应用程序核心的图像处理任务委托到后台脚本使用在现代浏览器支持W3C的Web工人。

  Web工人的允许web应用程序在后台任务运行的脚本一起在UI线程并行执行。工人和UI线程之间的通信是通过传递消息使用postMessage的 API。两端(即在UI线程和辅助)为事件通知这个清单,你可以处理。您只能通过工人和UI线程之间的“数据”,也就是说,你不能传递任何东西,有做与用户界面-例如,你不能传递DOM元素的工人从UI线程。

  InstaFuzz工人实施在文件过滤worker.js。它在工人的onmessage事件处理,并应用一个过滤器,然后将结果传递回通过postMessage的。由于它变成了,即使我们可以不通过DOM元素(这意味着我们不能仅仅交给一个canvas元素的工人,有过滤器应用),我们可以在事实上传递图像数据对象作为返回的getImageData方法前面讨论过。下面是从过滤worker.js的滤波处理代码:

importScripts("ns.js", "filters.js");

var tag = null;
onmessage = function (e) {
var opt = e.data;
var imageData = opt.imageData;
var filter;

tag = opt.tag;
filter = InstaFuzz.Filters.getFilter(opt.filterKey);

var start = Date.now();
filter.apply(imageData);
var end = Date.now();

postMessage({
type: "image",
imageData: imageData,
filterId: filter.id,
tag: tag,
timeTaken: end - start
});
}

  一些脚本文件中的第一行拉,工人取决于调用importScripts的。这类似于在HTML文档中使用SCRIPT标签包括一个JavaScript文件。然后我们设置的onmessage事件的处理程序,简单地套用问题中的过滤器,并将结果传递到UI线程通过调用postMessage的响应。够简单了!

  工人的代码初始化是在instafuzz.js的,看起来像这样:

 
var worker = new Worker("js/filter-worker.js");

  没有太多的是什么?工人当一个消息被发送到UI线程处理,通过指定的onmessage事件的处理程序,工人对象。这里是如何做到这一点的InstaFuzz:

worker.onmessage = function (e) {
var isPreview = e.data.tag;
switch (e.data.type) {
case "image":
if (isPreview) {
previewRenderers[e.data.filterId].
context.putImageData(
e.data.imageData, 0, 0);
} else {
mainRenderer.context.putImageData(
e.data.imageData, 0, 0);
}

break;
// more code here
}
};

  代码应该是不言自明的。它只是挑选工人发送图像数据对象,并把它应用到相关画布上下文对象,使修改后的图像在屏幕上呈现。调度一个滤波器转换与工人是同样简单。下面的例程执行此功能InstaFuzz的:

function scheduleFilter(filterId,
renderer,
img, isPreview,
resetRender) {
if (resetRender) {
renderer.clearCanvas();
renderer.renderImage(img);
}

var imageData = renderer.context.getImageData( 0, 0, renderer.size.width, renderer.size.height);

worker.postMessage({
imageData: imageData,
width: imageData.width,
height: imageData.height,
filterKey: filterId,
tag: isPreview
});
}

  结束语

  源为InstaFuzz是这里可供下载。我们看到相当复杂的用户体验是可能的今天,HTML5的技术,如帆布,拖/降,文件API和Web工人。所有这些技术的支持是相当不错的,在几乎所有的现代浏览器。有一件事,我们并没有在这里讨论的是使应用程序与老版本的浏览器兼容的问题。,说实话,是一个不平凡的,但必要的任务,我会希望能够在以后的文章中谈论。

  本文尝试从Internet Explorer团队的一部分的HTML5高科技系列在这篇文章中的概念用3个月的免费BrowserStack跨浏览器测试http://modern.IE

翻译来源:http://www.sitepoint.com/how-to-build-an-instagram-like-photo-sharing-app-with-html5-part-2/

带个链接:http://www.wangxionghui.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐