Skip to content

Commit

Permalink
API поддерживает callback-и
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmz committed Jun 14, 2011
1 parent e3c4a62 commit b4e3b9d
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 104 deletions.
63 changes: 26 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,51 @@
Canvas APNG
APNG-canvas
==============

Библиотека для отображения Animated PNG в браузерах, которые поддерживают canvas
(т. е. в Google Chrome, Internet Explorer 9, Apple Safari).
Библиотека для отображения Animated PNG в браузерах, которые поддерживают canvas (т. е. в Google Chrome, Internet Explorer 9, Apple Safari).

API
-----------

Бибилиотека создаёт глобальный объект **APNG**, имеющий несколько методов.
Все методы работают асинхронно и возвращают *promise*-объекты. Если на странице присутствует jQuery,
то используются его [promises](http://api.jquery.com/category/deferred-object/), в противном случае
используется совместимый интерфейс, поддерживающий методы `done`, `fail`, `then` и `always`.
Бибилиотека создаёт глобальный объект **APNG**, имеющий несколько методов. Все методы работают асинхронно и принимают необязательный callback-аргумент. Методы должны вызываться после загрузки DOM-дерева.

### APNG.checkFeatures()
Для тех, кому удобнее deferred-вызовы, методы возвращают *promise*-объекты. Если на странице присутствует jQuery, то используются его [promises](http://api.jquery.com/category/deferred-object/), в противном случае используется совместимый интерфейс, поддерживающий методы `done`, `fail`, `then` и `always`. В случае успешного выполнения метода, вызывается `callback` и обработчики `done` (с одинаковыми параметрами), в случае ошибки вызываются обработчики `fail` с сообщением об ошибке.

Проверяет, поддерживает ли браузер `APNG` и `canvas`. Может вызываться независимо от прочих методов.
### APNG.checkNativeFeatures(callback?)

**`done`** Аргумент — объект с двумя булевыми полями: `apng` и `canvas`. True в любом поле означает поддержку
браузером соответствующей технологии.
Проверяет, поддерживает ли браузер `APNG` и `canvas`. Может вызываться независимо от прочих методов. В `callback` передаётся объект с двумя булевыми полями: `apng` и `canvas`. True в любом поле означает поддержку браузером соответствующей технологии.

**`fail`** Не вызывается.
### APNG.ready(callback?)

### APNG.init()
Подготовка библиотеки к работе. Должен быть вызван хотя бы один раз перед вызовом остальных методов (кроме `checkNativeFeatures`).

Подготовка библиотеки к работе. Должен вызываться один раз, остальные методы должны вызываться
из обработчика `done`.
`callback` вызывается без аргументов, и только в том случае, если браузер поддерживает `canvas` и не поддерживает `APNG`. Только в этом случае имеет смысл применение этой библиотеки.

**`done`** Вызывается без аргументов только в том случае, если браузер поддерживает `canvas`
и не поддерживает `APNG`. Только в этом случае имеет смысл применение этой библиотеки.

**`fail`** Вызывается если браузер поддерживает `APNG` или не поддерживает `canvas`. Аргумент —
результат вызова `APNG.checkFeatures`.

### APNG.createAPNGCanvas(url)
### APNG.createAPNGCanvas(url, callback?)

Загружает PNG-файл по данному `url`, разбирает его, создаёт элемент `canvas` и запускает анимацию.

**`done`** Аргумент — созданный элемент `canvas`, в котором проигрывается анимация.
Элемент не включён в DOM-дерево, это нужно сделать вручную.

**`fail`** Вызывается если полученные данные не являются корректным PNG-файлом или если произошла
ошибка при разборе данных. Аргумент — строка сообщения об ошибке.
`callback` вызывается только если полученные данные являются корректным PNG-файлом. Аргумент — созданный элемент `canvas`, в котором проигрывается анимация. Элемент не включён в DOM-дерево, это нужно сделать вручную.

### APNG.replaceImage(img)

Заменяет элемент `img` (`HTMLImageElement`) на `canvas` с анимацией. Параметры обработчиков такие же,
как и у `APNG.createAPNGCanvas`. Замена происходит только если `img` содержит корректный PNG-файл.
Заменяет элемент `img` (`HTMLImageElement`) на `canvas` с анимацией. Замена происходит только если `img` содержит корректный PNG-файл. Этот метод вызывается без `callback`-а.

Пример использования
--------------------

APNG.ready(function() {
for (var i = 0; i < document.images.length; i++) {
if (/\.png$/i.test(img.src)) APNG.replaceImage(img);
}
});

Ограничения
-----------

Поскольку изображения загружаются при помощи `XMLHttpRequest`, то домен, на котором расположена
картинка, должен быть тем же, что и домен, на котором расположена страничка.
Поскольку изображения загружаются при помощи `XMLHttpRequest`, то домен, на котором расположена картинка, должен быть тем же, что и домен, на котором расположена страничка.

Если домены разные, то в Chrome/Safari можно использовать [CORS](http://www.w3.org/TR/cors/ "Cross-Origin Resource Sharing"), настроив сервер картинок на отдачу заголовка `Access-Control-Allow-Origin: *`.

Если домены разные, то в Chrome/Safari можно использовать
[CORS](http://www.w3.org/TR/cors/ "Cross-Origin Resource Sharing"),
настроив сервер картинок на отдачу заголовка `Access-Control-Allow-Origin: *`.
К сожалению, применить CORS в случае с IE, по видимому, невозможно, так как соотв. объект `XDomainRequest` не позволяет получить ответ в виде двоичных данных (`XMLHttpRequest` позволяет это сделать через свойство `responseBody`).

К сожалению, применить CORS в случае с IE, по видимому, не получится, так как соотв. объект
`XDomainRequest` не позволяет получить ответ в виде двоичных данных
(`XMLHttpRequest` позволяет это сделать через свойство `responseBody`).
По той же причине (использование `XMLHttpRequest`) прилагаемый тестовый пример не будет работать с локальной машины (по протоколу file://), он должен быть выложен на реез-сервер.
96 changes: 53 additions & 43 deletions apng-canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,32 @@

self.APNG = {};

// todo Сделать возможность многократного вызова
self.APNG.checkFeatures = function() {
var d = new Deferred();
var features = {
apng: false,
canvas: false
};
self.APNG.checkNativeFeatures = function(callback) {
/* Блок для однократного исполнения метода */
var firstCall = !arguments.callee.d;
var d = firstCall ? (arguments.callee.d = new Deferred()) : arguments.callee.d;
if (callback) d.promise().done(function(res) { callback(res); });
if (!firstCall) return d.promise();

var res = { canvas: false, apng: false };
var canvas = document.createElement("canvas");
if (typeof canvas.getContext == "undefined") {
// canvas is not supported
d.resolve(features);
d.resolve(res);
} else {
// canvas is supported
res.canvas = true;
// see http://eligrey.com/blog/post/apng-feature-detection
features.canvas = true;
var img = new Image();
img.onload = function() {
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
features.apng = (ctx.getImageData(0, 0, 1, 1).data[3] === 0);
d.resolve(features);
if (ctx.getImageData(0, 0, 1, 1).data[3] === 0 ) {
res.apng = true;
d.resolve(res);
} else {
d.resolve(res);
}
};
// frame 1 (skipped on apng-supporting browsers): [0, 0, 0, 255]
// frame 2: [0, 0, 0, 0]
Expand All @@ -32,32 +37,32 @@
return d.promise();
};

// todo Сделать возможность многократного вызова
self.APNG.init = function() {
var d = new Deferred();
this.checkFeatures().done(function(f) {
if (!f.apng && f.canvas) {
// Если всё хорошо, то создаём VBScript-функцию для IE9
// see http://miskun.com/javascript/internet-explorer-and-binary-files-data-access/
if (typeof XMLHttpRequest.prototype.responseBody != "undefined") {
var script = document.createElement("script");
script.setAttribute('type','text/vbscript');
script.text = "Function IEBinaryToBinStr(Binary)\r\n" +
" IEBinaryToBinStr = CStr(Binary)\r\n" +
"End Function\r\n";
document.body.appendChild(script);
}
d.resolve();
} else {
d.reject(f);
self.APNG.ready = function(callback) {
/* Блок для однократного исполнения метода */
var firstCall = !arguments.callee.d;
var d = firstCall ? (arguments.callee.d = new Deferred()) : arguments.callee.d;
if (callback) d.promise().done(callback);
if (!firstCall) return d.promise();

this.checkNativeFeatures().fail(function() {
// Если всё хорошо, то создаём VBScript-функцию для IE9
// see http://miskun.com/javascript/internet-explorer-and-binary-files-data-access/
if (typeof XMLHttpRequest.prototype.responseBody != "undefined") {
var script = document.createElement("script");
script.setAttribute('type','text/vbscript');
script.text = "Function IEBinaryToBinStr(Binary)\r\n" +
" IEBinaryToBinStr = CStr(Binary)\r\n" +
"End Function\r\n";
document.body.appendChild(script);
}
});
d.resolve();
}).done(function() { d.reject(); });
return d.promise();
};

self.APNG.createAPNGCanvas = function(url) {
self.APNG.createAPNGCanvas = function(url, callback) {
var d = new Deferred();

if (callback) d.promise().done(callback);
loadBinary(url)
.done(function(imageData) {
parsePNGData(imageData)
Expand Down Expand Up @@ -150,27 +155,30 @@
if (typeof jQuery != "undefined" && typeof jQuery.Deferred != "undefined") {
Deferred = jQuery.Deferred;
} else {
// Custom minimal implementation
/**
* Custom Deferred implementation
* @constructor
*/
Deferred = function() {
this.doneList = [];
this.failList = [];
this.isResolved = false;
this.isRejected = false;
this._isResolved = false;
this._isRejected = false;
this.args = null;
};
Deferred.prototype.promise = function() { return this; };
Deferred.prototype.done = function(callback) {
if (this.isResolved) {
if (this._isResolved) {
callback.apply(this, this.args);
} else if (!this.isRejected) {
} else if (!this._isRejected) {
this.doneList.push(callback);
}
return this;
};
Deferred.prototype.fail = function(callback) {
if (this.isRejected) {
if (this._isRejected) {
callback.apply(this, this.args);
} else if (!this.isResolved) {
} else if (!this._isResolved) {
this.failList.push(callback);
}
return this;
Expand All @@ -181,18 +189,20 @@
Deferred.prototype.always = function(callback) {
return this.then(callback, callback);
};
Deferred.prototype.isResolved = function() { return this._isResolved; };
Deferred.prototype.isRejected = function() { return this._isRejected; };
Deferred.prototype.resolve = function() {
if (!this.isRejected && !this.isResolved) {
this.isResolved = true;
if (!this._isRejected && !this._isResolved) {
this._isResolved = true;
this.args = arguments;
while (this.doneList.length) this.doneList.shift().apply(this, this.args);
this.failList = [];
}
return this;
};
Deferred.prototype.reject = function() {
if (!this.isRejected && !this.isResolved) {
this.isRejected = true;
if (!this._isRejected && !this._isResolved) {
this._isRejected = true;
this.args = arguments;
while (this.failList.length) this.failList.shift().apply(this, this.args);
this.doneList = [];
Expand Down
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
10 changes: 9 additions & 1 deletion test.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@
<script src="apng-canvas.js"></script>
<script>
function load() {
APNG.init().done(function() {
APNG.ready(function() {
for (var i = 0; i < document.images.length; i++) {
APNG.replaceImage(document.images[i]);
}
});
APNG.checkNativeFeatures(function(b) { console.log(b); });
APNG.checkNativeFeatures(function(b) { console.log(b); });
APNG.checkNativeFeatures(function(b) { console.log(b); });
APNG.checkNativeFeatures(function(b) { console.log(b); });

APNG.ready(function() {
console.log("hi");
});
}
</script>
</head>
Expand Down
23 changes: 0 additions & 23 deletions xdr-test.html

This file was deleted.

0 comments on commit b4e3b9d

Please sign in to comment.