如何使用HTML5构建像照片共享应用这样的Instagram

分享于 

23分钟阅读

Web开发

  繁體

当我开始这个应用i 我只是真正感兴趣的是,如果网站平台真的evolved,就可以使用 HTML,JavaScript和CSS来构建一个非常流行的应用程序。 事实证明事实上我们可以完全做到。 本文将介绍这些技术,并展示如何构建可以互操作的web应用程序,这是用户运行的浏览器。

If Instagram,你可能很高兴在照片分享和社交网络服务中 hear,你可以很高兴的听到照片分享和社交网络服务,让你 take。 服务如此流行,它是由Facebook收购的,在 2012的一袋现金和股票中都被Facebook收购了。

我希望你可以在 modern green ,Google Chrome,,Firefox,,,,CSS3,CSS3,CSS3等。 而且你可以很容易地使用代码来构建一个商店应用程序( )。

关于这个应用程序的

如果你想看看这个应用程序,那么这里是它承载的地方:

http://blogorama.nerdworks.in/arbit/InstaFuzz/

一旦装载完成,就会显示一个屏幕,看起来像这样:

你可以通过点击左下角的大红色"添加"按钮,或者将图像文件拖放到右边的黑色/蓝色区域。 一旦你做了,你得到的东西看起来像这样:

你会注意到,屏幕左侧列出了一个数字滤波器列表,显示了图像的预览。 应用筛选器是单击左侧的过滤器预览的简单问题。 下面是应用"加权灰度"筛选器后跟"运动模糊"的内容。 可以说过滤器是加 additive 当你点击过滤器时,它们被应用在前面应用的过滤器上:

接下来让我们看一下UI布局是如何被放置在一起的。

用户界面布局

实际上HTML标记很小,所以我可以在这里完全复制正文标记的内容,在这里( 不包括脚本包括):

<header><divid="title">InstaFuzz</div></header><sectionid="container"><canvasid="picture"width="650"height="565"></canvas><divid="controls"><divid="filters-list"></div><buttonid="loadImage">Add</button><inputtype="file"id="fileUpload"style="display: none;"accept="image/gif, image/jpeg, image/png"/></div></section><!-- Handlebar template for a filter UI button --><scriptid="filter-template"type="text/x-handlebars-template"><divclass="filter-container"data-filter-id="{{filterId}}"><divclass="filter-name">{{filterName}}</div><canvasclass="filter-preview"width="128"height="128"></canvas></div></script>

这里没有什么可以做的。 一切都应该是标准的票价。 不过,我将注意到,我使用 Handlebars模板系统在这里呈现屏幕左侧过滤器列表的标记。 模板标记在HTML文件( 代码段中的脚本标记显示为 上面 ) 中声明,然后从JavaScript中使用。 模板标记然后绑定到一个JavaScript对象,该对象提供的值为 {{filterId}}{{filterName}}的手柄表达式值。 下面是来自应用程序的JS相关部分,它有一点DOM操作帮助来自 jQuery插件:

var templHtml = $("#filter-template").html(),
 template = Handlebars.compile(templHtml),
 filtersList = $("#filters-list");var context = {
 filterName: filter.name,
 filterId: index
};
filtersList.append(template(context));

从HTML标记中可以看到,所有筛选器预览框都是一个画布标记,右边的大框是 final 输出。 我们稍后将详细介绍如何使用画布技术来实现这些效果。

应用程序还使用 CSS3 @font-face 字体来呈现 header 和"添加"按钮中的文本。 字体取自优秀的字体Squirrel网站,下面是声明的样子:

@font-face {
 font-family<span class="code-none">: 'TizaRegular'<span class="code-none">;
 src<span class="code-none">: url('fonts/tiza/tiza-webfont.eot')<span class="code-none">;
 src<span class="code-none">: url('fonts/tiza/tiza-webfont.eot?#iefix')
 format('embedded-opentype'),
 url('fonts/tiza/tiza-webfont.woff') format('woff'),
 url('fonts/tiza/tiza-webfont.ttf') format('truetype'),
 url('fonts/tiza/tiza-webfont.svg#TizaRegular') format('svg')<span class="code-none">;
 font-weight<span class="code-none">: normal<span class="code-none">;
 font-style<span class="code-none">: normal<span class="code-none">;
<span class="code-none">}</span></span></span></span></span></span></span></span></span></span></span>

这个指令使 User Agent 在页面中嵌入字体,并在指定给 font-family 规则的NAME 下可用,在本例中为"tizaregular"。 之后,我们可以将这个字体分配给任何 CSS font-family 规则,像我们通常。 此外,我使用以下规则向 header 元素分配字体:

font-family:TizaRegular, Cambria, Cochin, Georgia, Times,
 "TimesNew Roman", serif;

你可能还注意到,容器元素在页面上有一个微妙的阴影。

这可以使用 CSS3 box-shadow 规则实现,这是在 InstaFuzz 中使用的。

-moz-box-shadow:1px0px4px #000000, -1px-1px4px #000000;-webkit-box-shadow:1px0px4px #000000, -1px-1px4px #000000;box-shadow:1px0px4px #000000, -1px-1px4px #000000;

这将使浏览器在相关元素周围呈现阴影。 值中每个逗号分隔部分指定了阴影的以下属性:

  • 水平偏移
  • 竖直偏移
  • 传播距离- 正值有软化阴影的效果
  • 阴影颜色

你可以指定多个用逗号分隔的阴影值,事实上,上面 已经完成。 我还使用 Chrome 和 webkit 前缀指定了 Firefox 和/safari的厂商前缀语法。 这会使影像继续在这些浏览器的版本中使用该规则的供应商前缀版本提供。 请注意,规则的W3C版本- box-shadow 是最后指定的。 这样做是为了确保在浏览器支持两种表单时,只有W3C行为才应用到页面上。

一个经常发现web开发人员不能包含给定的oauth规则的供应商前缀版本。 开发人员通常只会将 webkit 规则put忽略其他浏览器和W3C标准版本。 这导致了两个问题- - 对于使用非webkit浏览器和的用户来说,用户体验不好,最终导致 webkit 成为网站的事实上标准。 理想我们希望W3C能够推动网络的未来,而不是一个特定的浏览器实现。 因此,在使用CSS功能的实验实现时,需要记住以下一些事项:

拖放

InstaFuzz支持的一个特性是能够将图像文件直接拖放到大的黑盒/蓝色框中。 在画布元素上处理"放置"事件可以支持这一点。 将某文件放到HTML元素上时,浏览器触发该元素上的"放置"事件并传递一个 dataTransfer 对象object包含所删除文件的引用的文件属性。 下面是在 app ("图片"页面上的画布元素的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();
}

属性是文件对象的一个 Collection,随后可以将它的与文件API一起使用以访问文件目录。 我们还处理 dragoverdragenter 事件,并基本防止那些事件传播到浏览器,从而防止浏览器处理文件丢失。 例如 IE 可能卸载当前页面,并尝试直接打开该文件。

文件 API

删除文件后,应用程序尝试打开图像并将它的呈现在画布中。 它通过使用文件API来实现这一点。 File是W3C规范,允许web应用程序以安全方式从本地文件系统访问文件。 此外,使用 FileReader对象将文件内容作为数据URL字符串读取,如使用readAsDataURL方法:

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

这里,files 是从画布元素上处理"放置"事件的函数中检索的File 对象的Collection。 因为我们只对单个文件感兴趣,所以只从 Collection 中选取第一个文件,如果有任何其他文件,则忽略其余文件。 我们的文件内容是异步加载的,一旦加载完成,就会触发 onloadend事件,并将文件内容作为数据 URL,随后我们将它的绘制到画布上。

渲染筛选器。

现在,这里的核心功能当然是过滤器的应用。 为了能够将滤镜应用到图像中,我们需要一种方法来访问图像中的单个像素。 在我们能够访问像素之前,我们需要将图像渲染到画布上。 所以让我们首先看一下呈现用户在画布元素中选择的图像的代码。

渲染图像到画布上。

canvas元素支持通过 drawImage 方法绘制图像对象。 要将映像文件装入映像实例,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 是由 filter-renderer.js 中定义的FilterRenderer 构造函数创建的一个实例。 应用程序使用 FilterRenderer 对象管理画布元素- preview预览面板和右边的主画布元素。 FilterRenderer 上的renderImage 方法已经定义如下:

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);
};

这看起来很像代码,但最终是想找出最好的方法来在图像中呈现图像的宽度。 实际上,在画布上呈现图像的关键代码出现在方法的最后一行。 context 成员引用从画布对象获取的2D 上下文,方法是调用它的getContext 方法。

从画布中提取像素

现在已经呈现了图像,我们需要访问单个像素,以应用所有可以用的不同筛选器。 在画布对象的上下文中调用 getImageData插件可以很容易地获取。 下面是 InstaFuzz 如何从 instafuzz.js 调用这个。

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

通过使用由一个字节表示的属性来访问单个像素的对象,该属性依次为一个 array,它包含每个值代表单个像素通道的颜色。 使用 4字节表示每个像素,其中指定了红色。绿色。蓝色和alpha通道的值。 它还有一个长度的属性,它返回缓冲区的长度。 如果你有 2D 个坐标,你可以使用如下代码很容易地将它转换成这个 array的索引。 每个通道范围的颜色强度值从 0到 255. here filters.js utility accepts interested accepts interested interested accepts accepts utility utility utility accepts accepts pixel::::

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" filterFilters.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方法。 而加权灰度滤波器是相当简单的大多数其他滤波器使用的图像处理技术称为 convolution。 在 filters.js 中提供了所有过滤器的代码,并从这里可用的代码移植了卷积滤波器。

网络worker插件

就像你想象的那样,做这些数字处理应用过滤可能需要很长的时间才能完成。 比如运动模糊滤波器使用一个 9滤波器矩阵来计算每个像素的新值,事实上,它是所有像素中最密集的CPU密集过滤器。 如果我们在浏览器的UI线程上做所有这些计算,那么应用程序在每次应用过滤器时都会冻结。 通过对网络worker的支持,应用程序将核心图像处理任务委托给后台脚本,从而提供了一个有响应的用户体验。

Web workers允许Web应用程序在 background 任务中运行脚本,该任务与UI线程并行执行。 工人和UI线程之间通过使用 API传递消息。 在两端(。例如。用户界面线程和工作线程) 都显示为你可以处理的事件通知。 你只能在worker和UI线程之间传递"数据",换句话说,你不能传递任何与用户界面有关的东西,例如从UI线程向worker传递DOM元素。

在中,工作线程在 file filter-worker.js 中实现。 工作在worker处理 onmessage 事件并应用过滤器,然后通过 postMessage 将结果传递回。 实际上,即使我们不能传递DOM元素( 也就是说,我们不能只给工人一个画布元素,让过滤器被应用),我们也可以通过前面讨论的getImageData 方法传递的图像数据对象。 下面是来自 filter-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文档中包含一个JavaScript文件。 然后我们为 onmessage 事件设置一个处理程序,响应它,然后将过滤结果传递给UI线程,并通过调用 postMessage 将结果传递回UI线程。 足够简单 !

初始化工作线程的代码在 instafuzz.js 中,如下所示:

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

不太多当worker向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和网络worker,都有相当复杂的用户体验。 对所有这些技术的支持在几乎所有现代浏览器中都是相当好的。 我们这里没有提到的一个问题是使应用程序与旧浏览器兼容的问题。 事实上,这是一个非常不简单但必要的任务,我希望能够在未来的文章中讨论。

本文是来自 IE 团队的HTML5技术系列的一部分。 在本文中使用3 个月免费的BrowserStack 跨浏览器 测试 @ http://modern.IE 来测试这篇文章中

Rajasekharan Vengalil是公司呼叫微软公司的nerd工作。 he开发和平台Evangelism团队的一部分basically团队 basically team Microsoft从微软 learn interesting interesting Microsoft Microsoft Microsoft Microsoft Microsoft Microsoft Microsoft。 他认为他真的非常幸运地为自己做的事做了一些自由的工作。


构建  SHA  Html5  LIKE  Sharing  Instagram  
相关文章