天天看點

如何建構一個類似Instagram的照片共享應用程式的HTML5:第2部分

         在第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

繼續閱讀