Перейти к основному содержимому

10 записей с тегом "frontend"

Посмотреть все теги

Datasync между браузерами

· 4 мин. чтения

При постройке качественного single-page приложения рано или поздно встаёт вопрос синхронизации данных. Это тем более актуально, когда дело касается работы разных пользователей. Приложения типа google wave, google docs, push-notifications в IOS, подняли планку ожидания пользователей и от обычных веб-систем

Что даёт синхронизация

Увеличивает реактивность, поддерживая данные в реальном времени без обновления страницы. Типичная ситуация когда вы работаете в нескольких табах и данные не обновляются потому что закешировались

Улучшает безопасность — если пользователь выходит из одного таба, его можно выкинуть из остальных автоматически

Снижает нагрузку на сервер — можно высылать оповещение о состоянии файла, если он обрабатывается в реальном времени, подобно ютубу без какого-либо взаимодействия с пользователем или поллинга

Сложности реализации

Если вы решили что это must-have фича, прежде чем начать реализовывать её у себя, задумайтесь о последствиях:

  • Вам понадобится транспортный механизм (см. ниже), который обычно ограничен количеством подключённых пользователей (макс. 10 тыс), размером сообщения и объёмом траффика
  • Безопасность. Транспортный механизм и источник оповещения должен точечно высылать данные только нужному пользователю (без broadcast-а).
    Лучше всего источник оповещения ставить на backend, сразу после закрытия транзакции. В этом случае полезно привязываться к сессии на бэкэнде
  • Я советую не использовать datasync-сервис в качестве источника данных. Это должен быть легковесный вестник адреса по которому можно прочитать детали. Более того, формат этого сообщения должен быть стандартизирован. Например {action:'file.added', id:1345}
  • Весь front-end код должен хранить данные в сыром виде в сервисах/моделях, которые будут точечно обновляться.
    Соответсвенно все события будут делится на прямое вмешательство пользователя и на datasync-действия
    Код при этом будет запускать похожие функции над данными
  • Все use-кейсы усложняются из-за ситуаций 
    • что делать если A удалил данные который B просматривает (редиректить B, блокировать A, показывать оповещение B?)
    • что делать если A и B одновременно сохраняют изменения над одним и тем же обьектом (провести обе операции, кто последний тот и победил? провести первую и заблокировать последующие до окончания операции?)
    • что делать если A составляет новый обьект 3, ссылающийся на обьект 2, который удаляет B, а потом происходит сохранение (удалять include при удалении или при сохранении?)
  • Если вы используете систему привилегий, то надо дополнительно иметь оповещения о присвоении или изъятия обьекта из видимости пользователей
  • Если ваш сервис синхронизации вдруг упадёт, то при поднимании надо сделать так что-бы сообщения не потерялись из-за ещё неподключившихся пользователей (у socket.io 5 сек)

Polling

Не ушёл в небытие, я по-прежнему вижу как некоторые сайты настойчиво стучат в бэкэнд. Например движение общественного транспорта в Таллинне. Видимо это проще всего реализовать и проще всего использовать вместо API. Что может быть проще для сторонних разработчиков - просто читай .json файл так часто как тебе это надо. А он в свою очередь может выплёвываться из кэша-в-памяти

Pusher

Пушер — сервис который вобщем-то следующий по сложности для разработчиков. Внедряете скрипт, подписываетесь на события в js. Высылаете события, которые будут получать все пользователи.

Лучше всего это делать на уровне backend'а, иначе захламите frontend и могут возникнуть редкие баги. Например — поставил заливаться файл и закрыл браузер, а клиент X не получил итогового оповещения. Для более тонкой настройки прийдётся повозиться с интеграцией авторизации и токенами

Firebase

Наконец, ещё один сервис, купленный гуглом, который больше позиционируется как nosql-база данных с синхронизацией между клиентами. Для того что-бы настроить её, как систему оповещений, но при этом оставить хранение данных у себя, прийдётся повозиться. Изначальный setup такой же простой как у pusher'а, да и авторизация по токену такая же, но поскольку это БД, то подписываться надо не на события оповещений, а на изменения json-схемы. Саму json-схему вы делаете сами, но я советую для этого случая использовать что-то типа..

{
notifications:[
2345:[
'file X added by 2346',
'file Y deleted by 2346'
],
2346:[
'file X added by 2345'
]
]
}

Websocket-сервер / Socket.io

Socket.io вы должны хостить сами и для этого нужен nodejs. Socket.io реализует браузерные веб-сокеты, протокол которых менялся несколько раз. Из-за этого могут быть трудности с подключением со стороны php к нему. Я использую elephant.io, пока полёт нормальный. Самый простой начальный вариант —  socket.io принимает подключение и делает broadcast всем подключённым клиентам. Для более тонкой настройки прийдётся повозиться с куки, php сессиями и желательно переносом их хранения в БД, что-бы backend мог высылать безопасно сообщение конкретному подключённому пользователю, после чего контролировать по session_id + socket кто что будет получать.

У socket.io есть альтернативные библиотечки — socky (любит руби), sockJs, вроде как очень быстрый wsengine.iodata.ioFaye я сам не пробовал, но судя по описанию растёт ещё со времени когда терминология с кометами была на слуху. Видимо теперь тоже работает на веб-сокетах

Визуализация степени подгрузки js-файлов

· 2 мин. чтения

Современные приложения всё больше начинают напоминать полноценное desktop решение, где в итоге запускается build-процесс с помощью grunt и мы получаем один кешируемый js файл. Теперь встаёт вопрос, как бы его загрузить так что-бы показать % подгрузки?

Есть два отдельных скрипта — $script, который инжектит новые script-элементы с обратной связью и progress.js, который симпатично показывает степень загрузки. Проблема в том что оба они уже сами по себе тяжёлые, в итоговый загружаемый билд-файл их нельзя вставить, а внедрять в index.html слишком бы его раздуло.

К тому же $script не поддерживает степень загрузки, а progress.js не использует % от ширины страницы. Поэтому я написал более простенький вариант.

$script = function (path, callback, sizeInChars) {
var head = document.getElementsByTagName('head')[0];
var body = document.getElementsByTagName('body')[0];
var div = document.createElement('div');
div.setAttribute('style', 'background-color:#3498db; width:0px;height:3px;transition: width 0.5s linear;');
body.insertBefore(div, body.firstChild);
var src = path + (path.indexOf('?') === -1 ? '?' : '&');
var oReq = new XMLHttpRequest();
oReq.addEventListener("progress", function (e) {
div.style.width = (100 * ( e.lengthComputable ? (e.loaded / e.total) : (e.loaded / sizeInChars))) + '%';
}, false);
oReq.onreadystatechange = function (e) {
if (oReq.readyState != 4) return;
var el = document.createElement('script');
el.text = oReq.responseText;
head.insertBefore(el, head.lastChild);
callback();
window.setTimeout(function () {
div.remove();
}, 500);
};
oReq.open('GET', src, true);
oReq.send(null);
}

Итого — js файлы подгружаются как текст через ajax, внедряются (без eval), вызывается callback, при этом показывается div отражающий степень загрузки. Единственная проблема — вы должны знать размер загружаемого файла, что в случае nginx + gzip не приходит с заголовками. Поэтому приходится использовать например так..

$script('/js/app.js', function () {
angular.bootstrap(document, ['myApp']);
}, 4879435);

О недоступности веб-шрифтов при подгрузке

· 1 мин. чтения

Лет пять назад я писал про типографию для web через @font-face. С того времени прошло достаточно времени, браузеры уже стали полноценно поддерживать шрифты. Для простоты и единства, вышел конструктор Google Fonts и Typekit. По всей истории этого дела хорошо прошёлся Вадим Макеев, поэтому я сфокусируюсь на основном баге.

Аж с 2009 года (а то и раньше) был известен баг многих браузеров об отображении текста без символов из-за того что css подгрузился, указал что должен использоваться какой-то шрифт, а сам шрифт (который может нехило весить) ещё грузится. В зависимости от скорости соединения возникает эффект "моргания". Проблема в том что это до сих пор не исправлено в Webkit (Safari, Chrome), поэтому вот мой уродливый хак (да, избавьтесь как только пофиксят)..

Прежде всего  через jquery заставим загружаться файл шрифтов, добавив пустой элемент.

$('body').append('<span style="font-family: \'CustomFontYouWantToUse\';"></span>');

Поймать событие когда шрифт загрузится получается только таким способом.

document.onreadystatechange = function() { if (document.readyState && document.readyState=="complete"){ $('body').addClass('font_loaded'); } };

Теперь в css к стандартному шрифту прийдётся добавить дубликат..

h1 {font-family:serif;} body.font_loaded h1{font-family:CustomFontYOuWantToUse, serif;}На просторах интернета было замечено интересное решение, подгружать содержание шрифта сразу в css файле, используя data:URL src:url(data:font/opentype;base64

Прячем лишнюю информацию в Gmail с Greasemonkey

· 1 мин. чтения

Greasemonkey - отличный плагин для Firefox, позволяющий выполнять произвольный javascript на стороне пользователя. Тем самым это универсальный плагин заточенный под конкретный сайт, хоть и не дающий браузерного UI как другие плагины, но дающий возможность менять DOM существующих сайтов.

Мне вот надоело что в GMail постоянно показывается блок текста со всякой статистикой и ссылками - поставил Greasemonkey, нашёл XPath адрес этого блока с firebug, добавил его устранение, обернул в таймаут для загрузки страницы и вуаля.. чистота.

Это элементарный пример (проще только по ID элемент найти) скриптов, огромное количество которых можно найти на userscripts.org

// ==UserScript==
// @author Artjom Kurapov <artkurapov@gmail.com>
// @name gmail_modifier
// @namespace gmail_modifier
// @include https://mail.google.com/mail/?shva=1#inbox
// ==/UserScript==

var aWindow = (typeof unsafeWindow != 'undefined')? unsafeWindow: window;

aWindow.setTimeout(function(){

var xpathResult = window.frames[3].document.evaluate('/html/body/div/div[2]/div/div[2]/div/div[2]/div[2]', window.frames[3].document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

for (var i=0; i<xpathResult.snapshotLength; i++) {
var nodeToHide = xpathResult.snapshotItem(i);
nodeToHide.style.display='none';
}

},6000);

Drag-n-drop file upload

· 3 мин. чтения

суббота, 26 сентября 2009 г. в 22:21:16

В продолжение статьи о новинках в html5, вот нашёл демо где уже работает загрузка файлов обычным перетаскиванием в браузер Firefox 3.6. Обычный drag-n-drop файлов пока в стабильных браузерах максимально что могут — создать события аналогичные перетаскиванию dom-элементов в определённые контейнеры.

Если взглянуть в код, то можно заметить что у события появляются такие параметры и методы:

event.dataTransfer.files event.dataTransfer.mozTypesAt(0) event.dataTransfer.files[0].getAsBinary()

А для отсылки файла используется обычный аяксный объект у которого появляется метод sendAsBinary(). Вобщем пока это выглядит как хак и видимо что надо больше внимания уделить спецификации html5, что-бы пользователь случайно не загрузил весь C:\Windows, что-бы можно было временно отложить загрузку или отфильтровать что конкретно надо загружать, или до какой глубины надо структуру подгружать и как обрабатывать.

Jquery dnd-upload

Написал два jquery-плагина - один для поддержки Firefox 3.6 (Namoroka) HTML5 загрузки, другой для поддержки Google gears. Можно использовать оба вместе..

Самый простой вариант для gears:

$('#drop_area').uploadg({gate:'http://somesite.com/upload_here_path/'});

Заметьте что ключ получаемого у сервера файла будет "file" (в пхп соответсвенно $_FILES['file']). А вот расширенный пример кода инициализации..

var dragndropConfig={ beforeLoad:function(){ this.gate=some_dynamic_file_upload_url+'?parentID='+SomeOtherDynamicObject.ID; if(SomeDisableReason){  this.can_proceed=false; } }, onProgress:function(event) { if (event.lengthComputable) { var percentage = Math.round((event.loaded * 100) / event.total); if (percentage &lt; 100) {  $('#some_progress_div').html('Uploading file..'+percentage+'%'); } } }, onComplete:function(event,txt) { $('#some_progress_div').html('done');  } }; if (window.google && google.gears){ $('#drop_area').uploadg(dragndropConfig); } else{ $('#drop_area').upload5(dragndropConfig); }

Осторожно, Google gears всё ещё имеют баги связанные с такой загрузкой.

/**
* HTML5 drag and drop file jquery file uploading
*
* @author Artjom Kurapov <artkurapov@gmail.com>
**/
$.upload5=function(input, opt){
var ME=this;
ME.option = {
gate:'',
can_proceed:true,

dragEnterColor:'#ffc',
dragExitColor:'#fff',

beforeLoad:function() {},
onProgress:function(event) {},
onComplete:function(event) {}
};

if(typeof(opt)=='string'){
opt={'gate':opt};
}

ME.option = $.extend(this.option,opt);


ME.upload = function (event) {

event.stopPropagation();
event.preventDefault();

ME.option.beforeLoad();
if(!ME.option.can_proceed){
return false;
}

var data = event.dataTransfer;

/*
for (var i = 0; i < data.files.length; i++) {
$('#image_list').prepend($('<img src="img/spinner.gif" width="16" height="16" />').css("padding", "33px"));
}
*/

var boundary = '------multipartformboundary' + (new Date).getTime();
var dashdash = '--';
var crlf = '\r\n';

/* Build RFC2388 string. */
var builder = '';

builder += dashdash;
builder += boundary;
builder += crlf;

var xhr = new XMLHttpRequest()


xhr.upload.addEventListener("progress", ME.option.onProgress, false);

xhr.open("POST", ME.option.gate, true);

if($.browser.safari){
var formData = new FormData();
formData.append("file", data.files.item());
xhr.send(formData);
}
else{


for (var i = 0; i < data.files.length; i++) {
var file = data.files[i];

/* Generate headers. */
builder += 'Content-Disposition: form-data; name="file[]"';
if (file.fileName) {
builder += '; filename="' + file.fileName + '"';
}
builder += crlf;

builder += 'Content-Type: application/octet-stream';
builder += crlf;
builder += crlf;

/* Append binary data. */
builder += file.getAsBinary();
builder += crlf;

/* Write boundary. */
builder += dashdash;
builder += boundary;
builder += crlf;
}


/* Mark end of the request. */
builder += dashdash;
builder += boundary;
builder += dashdash;
builder += crlf;

xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' + boundary);

try{
xhr.sendAsBinary(builder);
}
catch(err){
Note.set('error',t("Upload failed - use only latin-encoded filenames"),13);
return false;
}
}

xhr.onload = function(event){
ME.option.onComplete(event,xhr.responseText);
};
}

$(input).get(0).addEventListener('drop', ME.upload, false);

$(input).get(0).addEventListener('dragenter', function(event) {
$(input).css("background-color", ME.option.dragEnterColor);
event.stopPropagation();
event.preventDefault();
}, false);

$(input).get(0).addEventListener('dragover', function(event) {
$(input).css("background-color", ME.option.dragEnterColor);
event.stopPropagation();
event.preventDefault();
}, false);

$(input).get(0).addEventListener('dragexit', function(event) { $(input).css("background-color", ME.option.dragExitColor); }, false);

return ME;
}

$.fn.upload5 = function upload5(options){
this.each(function() {
var input = this;
new jQuery.upload5(input, options);
});

return this;
};

Post form с window.open

· 1 мин. чтения

четверг, 19 июня 2008 г. в 17:13:56

Я сам не люблю popup-окна, но иногда клиенту очень уж хочется. Публиковать же форму в такое окошко имеет смысл например при работе с отчётами - в основном окошке выделяются данные, а в открывающемся показывается список готовый для распечатки c window.print()

Решение простое:

<form method='post' action='' target="foo"   onSubmit="window.open('', 'foo', 'width=850,height=400,status=yes,resizable=yes,scrollbars=yes')">

Если же подобное открытие формы должно зависеть от других элементов, например checkbox'а, то на jQuery это выглядит так:

$('#report_form').submit(function(){           if ($('#csv_radio').attr('checked')!=true){               this.target="foo";               window.open('', 'foo', 'width=950,height=400,status=yes,resizable=yes,scrollbars=yes');           }           else this.target="_self";       });

Видео и mp3 в своём блоге

· 3 мин. чтения

Чем профессиональней становится web-ресурс тем больше необходимость использовать собственных технологий, или по крайней мере своего стиля. Применимо к видео и аудио это значит что внешний вид, функциональность и хостинг - не от youtube/rutube/vimeo а свой. Особенно это заметно когда у вас 500 статей и у большинства - ролики с ютуба, и можно с высокой долей вероятности утверждать что несколько из них уже не работают потому что автор или ютуб их удалил.

Поэтому преимущества держать файл у себя и показывать в своём плеере:

  • правовую независимость и надёжность неудаляемости роликов
  • инновативность, например показ только собственных связанных видео или HD-канал
  • удержание аудитории от публичного сервиса
  • свой дизайн и стиль плеера

В качестве примера таких решений в рунете можно привести ТНТ и Absolute Games.


Аудио-плеер

Для mp3 есть JW MP3 Player и Macloo player, оба на flash естественно. Второй мне особенно показался симпатичным, отчасти из-за его схожести с используемом в сервисе boomp3. Код достаточно простой - в качестве параметра передаётся URL источника. В своём блоге я уже прикрутил - смотрите ниже, как и поиск по эстонско-русскому словарику, но это не в тему. Так что подумайте о ведении подкастинга или видео-кастинга. Уникальность содержимого очень ценится. Есть ещё dewplayer.

Для того что-бы встроить такой элемент в rss 2.0 надо вставить внутрь item-блока примерно такой код

<enclosure url="http://kurapov.name/pathkrasivosleva.mp3" length="7332316" type="audio/mpeg" />

Видео-плеер

Появление flv-видео значительно укрепило перспективы flash. И хотя для этого надо конвертировать стандартные форматы (avi, mov, mpg) с помощью ffmpeg в качестве консоли на стороне сервера и могут возникнуть проблемы с кодеками, размер и удобство стало очевидным.

Некоторые из плееров
Spring playerHD, поточность, есть поддержка MOV и MP4
flv-mp3.comНикита для wordpress советует
Flowplayerнемножко игрушечный
JW FLV Playerдостаточно заезженный
FLV-playerслишком примитивный
uppodочень схож с flv-mp3
xmoovс лишними иконками
Sonneticc HD, немного игрушечный
Agriyaплатный
AS-Flash Media playerплатный, отличный скин
Proxus FLV component

Сложность возникает с перемоткой, буфферизацией и тп. Здесь нельзя обойтись без возможности сервера в поточной раздаче (streaming), т.е. с подстраиванием под возможности клиента. Для этого нужны специальные серверные программы типа xmoovStream и mammoth server. По этой теме советую послушать Андрея Смирнова на РИТ 2007, где он советует модуль для nginx lighttpd. Теоретически конвертация из командной строки должна выглядеть так..

ffmpeg -i movie.[avi] -s 320x240 -ar 44100 -r 12 movie.flv cat movie.flv | flvtool2 -U stdin movie.flv

Подробней о конверторах смотрите:

Будущее

Если основная функциональность имеется, то во многих видео-сайтах уже прослеживаются инновации типа

  • HD-разрешение
  • субтитров
  • слоёв, зависящих от времени
  • комментариев, зависящих от времени
  • geo-tagging
  • поиск лиц
  • полный 360° обзор со специальной камерой

По теме читайте также:

AddHandler в Visual Basic 2005

· 2 мин. чтения

С Visual Basic мне пришлось столкнуться по учёбе, и именно по невизуальной его части - создание элементов форм динамически (в цикле) с частично ограниченным числом оных элементов. Задание в общем звучит невинно - создать программу (автомобильного) теста.

Для упрощённого решения, все данные лежат в открытом виде в текстовом файле. Пользователю надо показать форму с вопросами. По умолчанию пользователь вводит сам ответ в TextBox компоненты, что даёт ему 3 очка за правильное решение. Если нажать на вопрос, то TextBox прячется и пользователь выбирает из предлагаемых вариантов.

Честно говоря самое сложное это вовсе не чтение из файла, а привязка создаваемых объектов с событиями (Events)

Dim resFile As IO.StreamReader Dim Row As String Dim line As Integer resFile = New IO.StreamReader("Valuutad.txt") Do line = line + 1 If resFile.EndOfStream Then Exit Do Row = resFile.ReadLine Me.AddQ(line, Row) Loop resFile.Close()

Сначала я нашёл такой кусок кода как AddHandler objLabel.Click, AddressOf Labelx_Click и решил что неплохо бы передать вместе с этим параметры EventArgs. Не тут то было, оказалось что VB делали такие перцы, логика которых с моей не совпадала вовсе, и которые наделали в VB тучу возможностей которыми я даже и не задумывался пользоваться..

Как оказалось, третьи примеры кода следовали всё тому же AddHandler, но уникальность того, что за кнопка из сотни кнопок была нажата брали из ActiveControl.Text! Это всё равно что баловаться фокусом в среде где фокус легко может отбираться или вообще не присваиваться, как это у меня с Label и происходило. Просто взять и добавить в объект новый параметр ID и по нему найти объект обратно, подобно getElementById в Javascript, тоже задача не из лёгких - надо тогда делать новый класс и наследовать контрол.. дебри.

Программу спас параметр Tag и занесение объектов в массив.

Rounded corners

· 2 мин. чтения

Закруглённые углы - элемент дизайна, подражения природе. В природе не существует углов - всё разрушается, выветривается, стачивается и в результате - закругляется и как правило - тоже не идеально.

Не известно какой гений додумался закруглять уголки блоков в дизайне страничек, но пальму первенства можно отдать Apple и MacOS.

Возможные решения

  • решение в-лоб - делаем картинки, выпиливаем в photoshop набор уголков и настраиваем в css свойства нашего блока (div или table), см. screencast
  • CurvyCorners решение наиболее подходящее. Всё далает javascript, достаточно указать id элемента и радиусы закругления. Можно устроить и разные настройки для разных элементов. Поддерживается прозрачность и сглаживание.
  • NiftyCorners чуть посложнее - кроме javascript функций и их инициализаций надо указывать и css стили. Поддерживает прозрачность, но настроить немного сложнее. Генерирует лишние inline-тэги b для создания углов без картинок.
  • Roundozer так же при помощи JS закругляет уголки, правда немного тормознут, зато отечественного производства!
  • ShaderBorder кроме уголков ещё предлагает делать тень
  • Планируется что в CSS3 будет поддержка закруглённых углов, а пока gecko-engine браузеры могут использовать параметр
    -moz-border-radius: 4px; /* Firefox */ -khtml-border-radius: 4px; -webkit-border-radius: 4px; /* Safari, Chrome */ border-radius:4px; /* CSS3*/ behavior: url(border-radius.htc); /*IE*/

Читайте также:

Типография в Web

· 2 мин. чтения

Кодировки как глобальная проблема невольно зацепили и проблему со шрифтами. На данный момент очень сложно сделать на странице текст своим шрифтом так, что-бы шрифт был виден таким же и на других компьютерах.

Согласно CSS2, шрифты можно подгружать в качестве ресурса, впрочем большинство браузеров это игнорирует.

@font-face { font-family: Broken15; font-style: normal; font-weight: normal; src: url('/fonts/BROKEN0.eot'); @font-face { font-family: "Kimberley"; src: url(http://www.princexml.com/fonts/larabie/kimberle.ttf) format("truetype");} h1 { font-family: "Kimberley", sans-serif }

Браузерная поддержка

Решение Microsoft & IE

Использовать шрифты уже предустановленные в C:WidnowsFonts . Обычные посетители остаются обычными, система у всех только от Microsoft, больше никого не волнует.
Для advanced-вебмастеров специально выпускается WEFT3 конвертор True Type шрифтов (.ttf) в Embedded Open Type (.eot). Впрочем сама программа 1997 года, поэтому прийдётся потрудиться закачивать через ftp созданный eot файл, который к тому же привязан к домену сайта.

Решение от Mozilla ещё в buglist-е.

Впрочем некогда в далёком 1998, делалась поддержка Portable Font Resource (pfr) от Bitstream который можно было бы использовать так же как и .eot, вот только создать pfr можно лишь купив TrueDoc, увы найти истину - есть поддержка или нет я не смог.

Замена на Flash

В народе это называется SIFR (Scalable Inman Flash Replacement). Принцип немного закручен - Javascript ищет элементы по тэгам и выставляет поверх flash-слой. Есть упрощённые версии для jquery, sifr lite и тп.

В минусы

  • пропадают свойства элемента типа href, title;
  • Также невозможно отобразить картинку попавшую в такой элемент,
  • Нельзя вызвать javascript event на таком элементе
  • Проблемы со всплывающими adblock опциями в Firefox+plugins

Также как и в предыдуших решениях надо каким-то образом шрифт впихнуть во flash и при необходимости защитить.

Замена на VML/SVG

Есть несколько библиотек - cufon (поддерживаются .otf-шрифты тоже), typeface (только .ttf)

Серверные решения

Почему бы не сделать картинку с нужным текстом шрифтом? Берём php с GD-библиотечкой и кидаем динамично в этот скрипт нужную нам строчку, он сам будет читать нужный файл шрифтов и рисовать что нам нужно. Но зачем изобретать велосипед

В минусы

  • неизвестный фон заголовков
  • wrapping-проблемы
  • проблемы с отключенными в браузере картинками
  • простая замена картинками означает отсутсвие аналогов h1 тэго, снижение SEO

PS. Интересные шрифты можно найти на fuelfonts, вот только кириллица редкая.

Читайте также: