Giriş
Dönen yenilemeler, kesintili sayfa geçişleri ve dokunma etkinliklerinde yaşanan aralıklı gecikmeler, günümüzün mobil web ortamlarında karşılaşılan sorunlardan sadece birkaçıdır. Geliştiriciler, mümkün olduğunca yerel uygulamalara yakın bir deneyim sunmaya çalışsa da genellikle saldırılar, sıfırlamalar ve katı çerçeveler nedeniyle bu hedeflerinden saparlar.
Bu makalede, mobil HTML5 web uygulaması oluşturmak için gereken minimum düzeydeki bilgileri ele alacağız. Buradaki temel amaç, günümüzün mobil çerçevelerinin gizlemeye çalıştığı karmaşıklıkları ortaya çıkarmaktır. Minimalist bir yaklaşım (temel HTML5 API'lerini kullanarak) ve kendi çerçevenizi yazmanıza ya da şu anda kullandığınız çerçeveye katkıda bulunmanıza olanak tanıyan temel bilgiler göreceksiniz.
Donanım hızlandırma
Normalde GPU'lar ayrıntılı 3D modelleme veya CAD diyagramlarını işler ancak bu durumda, temel çizimlerimizin (div'ler, arka planlar, gölge efektli metinler, resimler vb.) GPU aracılığıyla sorunsuz görünmesini ve sorunsuz şekilde animasyonlu olmasını istiyoruz. Maalesef çoğu ön uç geliştirici, semantiklerle ilgilenmeden bu animasyon sürecini üçüncü taraf bir çerçeveye aktarıyor. Ancak bu temel CSS3 özellikleri maskelenmeli mi? Bu konularla ilgilenmenin neden önemli olduğuna dair birkaç neden sunmak istiyorum:
Bellek ayırma ve hesaplama yükü: Yalnızca donanım hızlandırması için DOM'daki her öğeyi birleştirirseniz kodunuz üzerinde çalışan bir sonraki kişi sizi kovalayıp ciddi şekilde dövebilir.
Güç tüketimi: Donanım devreye girdiğinde pil de devreye girer. Mobil için geliştirme yaparken geliştiriciler, mobil web uygulamaları yazarken çok çeşitli cihaz kısıtlamalarını dikkate almak zorunda kalır. Tarayıcı üreticileri, giderek daha fazla cihaz donanımına erişimi etkinleştirmeye başladıkça bu durum daha da yaygınlaşacaktır.
Çakışmalar: Donanım hızlandırma, sayfanın zaten hızlandırılmış olan kısımlarına uygulandığında hatalı davranışlarla karşılaşıldı. Bu nedenle, çakışan hızlandırmanız olup olmadığını bilmek çok önemlidir.
Kullanıcı etkileşimini sorunsuz ve mümkün olduğunca yerel hale getirmek için tarayıcıyı bizim için çalışır hale getirmemiz gerekir. İdeal olarak, mobil cihazın CPU'sunun ilk animasyonu oluşturmasını ve ardından GPU'nun animasyon işlemi sırasında yalnızca farklı katmanları birleştirmesini isteriz. translate3d, scale3d ve translateZ işlevleri bunu yapar. Animasyonlu öğelere kendi katmanlarını verirler ve böylece cihazın her şeyi sorunsuz bir şekilde birlikte oluşturmasına olanak tanırlar. Hızlandırılmış birleştirme ve WebKit'in işleyiş şekli hakkında daha fazla bilgi edinmek için Ariya Hidayat'ın blogunda çok sayıda faydalı bilgi bulabilirsiniz.
Sayfa geçişleri
Mobil web uygulaması geliştirirken en sık kullanılan üç kullanıcı etkileşimi yaklaşımına (kaydırma, çevirme ve döndürme efektleri) göz atalım.
Bu kodu çalışırken görmek için http://slidfast.appspot.com/slide-flip-rotate.html adresini ziyaret edin (Not: Bu demo, mobil cihazlar için oluşturulmuştur. Bu nedenle, bir emülatör başlatın, telefonunuzu veya tabletinizi kullanın ya da tarayıcı pencerenizin boyutunu yaklaşık 1.024 piksel veya daha küçük olacak şekilde küçültün).
İlk olarak, kaydırma, çevirme ve döndürme geçişlerini ve bunların nasıl hızlandırıldığını inceleyeceğiz. Her animasyonun yalnızca üç veya dört satır CSS ve JavaScript kodu içerdiğine dikkat edin.
Sürgülü
Üç geçiş yaklaşımı arasında en yaygın olanı olan kayan sayfa geçişleri, mobil uygulamaların doğal görünümünü taklit eder. Slayt geçişi, yeni bir içerik alanını görünüm alanına getirmek için çağrılır.
Slayt efekti için önce işaretlememizi bildiririz:
<div id="home-page" class="page">
<h1>Home Page</h1>
</div>
<div id="products-page" class="page stage-right">
<h1>Products Page</h1>
</div>
<div id="about-page" class="page stage-left">
<h1>About Page</h1>
</div>
Sayfaları sola veya sağa yerleştirme kavramına dikkat edin. Temelde herhangi bir yönde olabilir ancak en yaygın olanı budur.
Artık yalnızca birkaç satır CSS ile animasyonun yanı sıra donanım hızlandırma da kullanabilirsiniz. Gerçek animasyon, sayfadaki div öğelerinde sınıfları değiştirdiğimizde gerçekleşir.
.page {
position: absolute;
width: 100%;
height: 100%;
/*activate the GPU for compositing each page */
-webkit-transform: translate3d(0, 0, 0);
}
translate3d(0,0,0), "sihirli değnek" yaklaşımı olarak bilinir.
Kullanıcı bir gezinme öğesini tıkladığında sınıfları değiştirmek için aşağıdaki JavaScript'i yürütüyoruz. Üçüncü taraf çerçeveleri kullanılmıyor. Bu, doğrudan JavaScript! ;)
function getElement(id) {
return document.getElementById(id);
}
function slideTo(id) {
//1.) the page we are bringing into focus dictates how
// the current page will exit. So let's see what classes
// our incoming page is using. We know it will have stage[right|left|etc...]
var classes = getElement(id).className.split(' ');
//2.) decide if the incoming page is assigned to right or left
// (-1 if no match)
var stageType = classes.indexOf('stage-left');
//3.) on initial page load focusPage is null, so we need
// to set the default page which we're currently seeing.
if (FOCUS_PAGE == null) {
// use home page
FOCUS_PAGE = getElement('home-page');
}
//4.) decide how this focused page should exit.
if (stageType > 0) {
FOCUS_PAGE.className = 'page transition stage-right';
} else {
FOCUS_PAGE.className = 'page transition stage-left';
}
//5. refresh/set the global variable
FOCUS_PAGE = getElement(id);
//6. Bring in the new page.
FOCUS_PAGE.className = 'page transition stage-center';
}
stage-left veya stage-right, stage-center olur ve sayfanın merkez görünüm bağlantı noktasına kaymasını sağlar. CSS3'ü kullanarak tüm ağır işleri yapıyoruz.
.stage-left {
left: -480px;
}
.stage-right {
left: 480px;
}
.stage-center {
top: 0;
left: 0;
}
Şimdi de mobil cihaz algılama ve yönlendirmeyi işleyen CSS'ye göz atalım. Her cihazı ve her çözünürlüğü ele alabiliriz (bkz. medya sorgusu çözünürlüğü). Bu demoda, mobil cihazlardaki çoğu dikey ve yatay görünümü kapsamak için yalnızca birkaç basit örnek kullandım. Bu, donanım hızlandırmayı cihaz başına uygulamak için de kullanışlıdır. Örneğin, WebKit'in masaüstü sürümü tüm dönüştürülmüş öğeleri (2 boyutlu veya 3 boyutlu olmasına bakılmaksızın) hızlandırdığından bir medya sorgusu oluşturup bu düzeyde hızlandırmayı hariç tutmak mantıklıdır. Donanım hızlandırma hilelerinin Android Froyo 2.2+ sürümlerinde herhangi bir hız artışı sağlamadığını unutmayın. Tüm birleştirme işlemleri yazılım içinde yapılır.
/* iOS/android phone landscape screen width*/
@media screen and (max-device-width: 480px) and (orientation:landscape) {
.stage-left {
left: -480px;
}
.stage-right {
left: 480px;
}
.page {
width: 480px;
}
}
Çevirme
Mobil cihazlarda sayfayı çevirme işlemi, sayfayı kaydırma olarak bilinir. Burada, iOS ve Android (WebKit tabanlı) cihazlarda bu etkinliği işlemek için basit bir JavaScript kullanıyoruz.
Nasıl çalıştığını görmek için http://slidfast.appspot.com/slide-flip-rotate.html adresini ziyaret edin.
Dokunma etkinlikleri ve geçişlerle uğraşırken ilk olarak öğenin mevcut konumunu belirlemek istersiniz. WebKitCSSMatrix hakkında daha fazla bilgi için bu dokümana bakın.
function pageMove(event) {
// get position after transform
var curTransform = new WebKitCSSMatrix(window.getComputedStyle(page).webkitTransform);
var pagePosition = curTransform.m41;
}
Sayfa çevirme işlemi için CSS3 ease-out geçişi kullandığımızdan normal element.offsetLeft işe yaramaz.
Ardından, kullanıcının hangi yöne çevirdiğini bulmak ve bir etkinliğin (sayfa gezinme) gerçekleşmesi için bir eşik belirlemek istiyoruz.
if (pagePosition >= 0) {
//moving current page to the right
//so means we're flipping backwards
if ((pagePosition > pageFlipThreshold) || (swipeTime < swipeThreshold)) {
//user wants to go backward
slideDirection = 'right';
} else {
slideDirection = null;
}
} else {
//current page is sliding to the left
if ((swipeTime < swipeThreshold) || (pagePosition < pageFlipThreshold)) {
//user wants to go forward
slideDirection = 'left';
} else {
slideDirection = null;
}
}
Ayrıca swipeTime değerini de milisaniye cinsinden ölçtüğümüzü fark edeceksiniz. Bu sayede, kullanıcı sayfayı çevirmek için ekranı hızlıca kaydırdığında gezinme etkinliği tetiklenir.
Sayfayı konumlandırmak ve bir parmak ekrana dokunurken animasyonların doğal görünmesini sağlamak için her etkinlik tetiklendikten sonra CSS3 geçişlerini kullanırız.
function positionPage(end) {
page.style.webkitTransform = 'translate3d('+ currentPos + 'px, 0, 0)';
if (end) {
page.style.WebkitTransition = 'all .4s ease-out';
//page.style.WebkitTransition = 'all .4s cubic-bezier(0,.58,.58,1)'
} else {
page.style.WebkitTransition = 'all .2s ease-out';
}
page.style.WebkitUserSelect = 'none';
}
Geçişlere en iyi doğal görünümü vermek için cubic-bezier ile oynamaya çalıştım ancak ease-out işe yaradı.
Son olarak, gezinmenin gerçekleşmesi için önceki demoda kullandığımız, daha önce tanımladığımız slideTo() yöntemlerini çağırmamız gerekir.
track.ontouchend = function(event) {
pageMove(event);
if (slideDirection == 'left') {
slideTo('products-page');
} else if (slideDirection == 'right') {
slideTo('home-page');
}
}
Döndürme
Şimdi de bu demoda kullanılan döndürme animasyonuna göz atalım. İstediğiniz zaman "İletişim" menü seçeneğine dokunarak görüntülediğiniz sayfayı 180 derece döndürüp arka yüzünü gösterebilirsiniz. Yine, geçiş sınıfı atamak için yalnızca birkaç satır CSS ve biraz JavaScript gerekir onclick.
NOT: Döndürme geçişi, 3D CSS dönüştürme özellikleri eksik olduğundan Android'in çoğu sürümünde doğru şekilde oluşturulmaz. Android, çevirme işlemini yoksaymak yerine sayfayı döndürerek "takla attırıyor". Destek iyileşene kadar bu geçişi dikkatli bir şekilde kullanmanızı öneririz.
Biçimlendirme (ön ve arka yüzün temel kavramı):
<div id="front" class="normal">
...
</div>
<div id="back" class="flipped">
<div id="contact-page" class="page">
<h1>Contact Page</h1>
</div>
</div>
JavaScript:
function flip(id) {
// get a handle on the flippable region
var front = getElement('front');
var back = getElement('back');
// again, just a simple way to see what the state is
var classes = front.className.split(' ');
var flipped = classes.indexOf('flipped');
if (flipped >= 0) {
// already flipped, so return to original
front.className = 'normal';
back.className = 'flipped';
FLIPPED = false;
} else {
// do the flip
front.className = 'flipped';
back.className = 'normal';
FLIPPED = true;
}
}
CSS:
/*----------------------------flip transition */
#back,
#front {
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden;
-webkit-transition-duration: .5s;
-webkit-transform-style: preserve-3d;
}
.normal {
-webkit-transform: rotateY(0deg);
}
.flipped {
-webkit-user-select: element;
-webkit-transform: rotateY(180deg);
}
Donanım hızlandırmayı hata ayıklama
Temel geçişleri ele aldığımıza göre şimdi bunların nasıl çalıştığına ve birleştirildiğine dair mekanizmalara göz atalım.
Bu sihirli hata ayıklama oturumunu başlatmak için birkaç tarayıcıyı ve seçtiğiniz IDE'yi açalım. Bazı hata ayıklama ortamı değişkenlerinden yararlanmak için önce komut satırından Safari'yi başlatın. Mac kullandığım için komutlar işletim sisteminize göre farklılık gösterebilir. Terminal'i açıp aşağıdakileri yazın:
- $> export CA_COLOR_OPAQUE=1
- $> export CA_LOG_MEMORY_USAGE=1
- $> /Applications/Safari.app/Contents/MacOS/Safari
Bu komut, Safari'yi birkaç hata ayıklama yardımcısıyla başlatır. CA_COLOR_OPAQUE, hangi öğelerin gerçekten birleştirildiğini veya hızlandırıldığını gösterir. CA_LOG_MEMORY_USAGE, çizim işlemlerimizi backing store'a gönderirken ne kadar bellek kullandığımızı gösterir. Bu, mobil cihaza tam olarak ne kadar yük bindiğini gösterir ve GPU kullanımınızın hedef cihazın pilini nasıl tükettiği hakkında ipuçları verebilir.
Şimdi saniyedeki kare sayısı (FPS) ile ilgili bazı iyi bilgileri görebilmek için Chrome'u açalım:
- Google Chrome web tarayıcısını açın.
- URL çubuğuna about:flags yazın.
- Birkaç öğe aşağı kaydırın ve FPS sayacı için "Etkinleştir"i tıklayın.
Bu sayfayı Chrome'un geliştirilmiş sürümünde görüntülerseniz sol üst köşede kırmızı FPS sayacını görürsünüz.
Donanım hızlandırmanın etkin olup olmadığını bu şekilde anlayabilirsiniz. Ayrıca, animasyonun nasıl çalıştığı ve sızıntı olup olmadığı (durdurulması gereken sürekli çalışan animasyonlar) hakkında da fikir verir.
Donanım hızlandırmayı görselleştirmenin bir diğer yolu da aynı sayfayı Safari'de (yukarıda bahsettiğim ortam değişkenleriyle) açmaktır. Hızlandırılmış her DOM öğesi kırmızı renktedir. Bu, katmanlara göre tam olarak neyin birleştirildiğini gösterir. Beyaz gezinme hızlandırılmadığı için kırmızı renkte değildir.
Chrome için benzer bir ayar, about:flags sayfasındaki "Composited render layer borders" (Birleştirilmiş oluşturma katmanı kenarlıkları) bölümünde de mevcuttur.
Birleştirilmiş katmanları görmenin bir diğer harika yolu da bu mod uygulanırken WebKit yaprak dökümü demosunu görüntülemektir.
Son olarak, uygulamamızın grafik donanımı performansını tam olarak anlamak için belleğin nasıl kullanıldığına bakalım. Burada, Mac OS'teki CoreAnimation arabelleklerine 1,38 MB çizim talimatı gönderdiğimizi görüyoruz. Core Animation bellek arabellekleri, ekranda gördüğünüz son pikselleri oluşturmak için OpenGL ES ile GPU arasında paylaşılır.
Tarayıcı penceresini yeniden boyutlandırdığımızda veya ekranı kaplayacak şekilde büyüttüğümüzde belleğin de genişlediğini görüyoruz.
Bu, yalnızca tarayıcıyı doğru boyutlara yeniden boyutlandırırsanız mobil cihazınızda belleğin nasıl kullanıldığı hakkında fikir verir. iPhone ortamları için hata ayıklama veya test yapıyorsanız boyutu 480 x 320 piksel olarak değiştirin. Artık donanım hızlandırmanın tam olarak nasıl çalıştığını ve hata ayıklama için ne gerektiğini biliyoruz. GPU bellek arabelleklerinin görsel olarak çalıştığını görmek, bu konuyu okumaktan çok daha farklı bir bakış açısı sunuyor.
Kamera Arkası: Getirme ve Önbelleğe Alma
Şimdi sayfa ve kaynak önbelleğe alma işlemlerimizi bir üst seviyeye taşıma zamanı. JQuery Mobile ve benzer çerçevelerin kullandığı yaklaşıma benzer şekilde, eşzamanlı AJAX çağrılarıyla sayfalarımızı önceden getirip önbelleğe alacağız.
Mobil web ile ilgili birkaç temel sorunu ve bunları çözmemizin nedenlerini ele alalım:
- Getirme: Sayfalarımızı önceden getirme, kullanıcıların uygulamayı çevrimdışı kullanmasına ve gezinme işlemleri arasında bekleme olmamasına olanak tanır. Elbette cihaz internete bağlandığında bant genişliğini kısıtlamak istemeyiz. Bu nedenle, bu özelliği dikkatli bir şekilde kullanmamız gerekir.
- Önbelleğe alma: Ardından, bu sayfaları getirirken ve önbelleğe alırken eşzamanlı veya eşzamansız bir yaklaşım istiyoruz. Ayrıca, cihazlar arasında iyi desteklendiği için localStorage'ı kullanmamız gerekiyor. Ancak bu özellik maalesef eşzamansız değil.
- AJAX ve yanıtı ayrıştırma: AJAX yanıtını DOM'a eklemek için innerHTML() kullanmak tehlikelidir (ve güvenilmez?). Bunun yerine, AJAX yanıtı ekleme ve eşzamanlı çağrıları işleme için güvenilir bir mekanizma kullanıyoruz. Ayrıca,
xhr.responseTextayrıştırmak için HTML5'in bazı yeni özelliklerinden de yararlanıyoruz.
Kaydırma, çevirme ve döndürme demosundaki kodu temel alarak bazı ikincil sayfalar ekleyip bunlara bağlantı vererek başlıyoruz. Bağlantıları ayrıştırıp anında geçişler oluştururuz.
Getirme ve önbelleğe alma demosunu buradan izleyebilirsiniz.
Gördüğünüz gibi, burada semantik işaretlemeyi kullanıyoruz. Başka bir sayfanın bağlantısı. Alt sayfa, üst sayfasıyla aynı düğüm/sınıf yapısını kullanır. Bir adım daha ileri giderek "sayfa" düğümleri vb. için data-* özelliğini kullanabiliriz. Aşağıda, uygulama yüklenirken geçiş için yüklenecek, önbelleğe alınacak ve ayarlanacak ayrı bir HTML dosyasında (/demo2/home-detail.html) bulunan ayrıntılar sayfası (alt) yer almaktadır.
<div id="home-page" class="page">
<h1>Home Page</h1>
<a href="demo2/home-detail.html" class="fetch">Find out more about the home page!</a>
</div>
Şimdi JavaScript'e göz atalım. Basitlik adına, yardımcıları veya optimizasyonları kodun dışında bırakıyorum. Burada yaptığımız tek şey, getirilecek ve önbelleğe alınacak bağlantıları bulmak için belirtilen bir DOM düğümleri dizisinde döngü oluşturmaktır.
Not: Bu demo için bu yöntem fetchAndCache() sayfa yükleme sırasında çağrılıyor. Ağ bağlantısı algılandığında ve ne zaman çağrılması gerektiği belirlendiğinde bir sonraki bölümde bu işlevi yeniden düzenleriz.
var fetchAndCache = function() {
// iterate through all nodes in this DOM to find all mobile pages we care about
var pages = document.getElementsByClassName('page');
for (var i = 0; i < pages.length; i++) {
// find all links
var pageLinks = pages[i].getElementsByTagName('a');
for (var j = 0; j < pageLinks.length; j++) {
var link = pageLinks[j];
if (link.hasAttribute('href') &&
//'#' in the href tells us that this page is already loaded in the DOM - and
// that it links to a mobile transition/page
!(/[\#]/g).test(link.href) &&
//check for an explicit class name setting to fetch this link
(link.className.indexOf('fetch') >= 0)) {
//fetch each url concurrently
var ai = new ajax(link,function(text,url){
//insert the new mobile page into the DOM
insertPages(text,url);
});
ai.doGet();
}
}
}
};
"AJAX" nesnesi kullanılarak uygun eşzamansız son işlem gerçekleştirilir. localStorage'ın AJAX çağrısında kullanılmasıyla ilgili daha gelişmiş bir açıklamayı Working Off the Grid with HTML5 Offline (HTML5 Çevrimdışı ile Şebeke Dışı Çalışma) başlıklı makalede bulabilirsiniz. Bu örnekte, her istekte önbelleğe almanın temel kullanımını ve sunucu başarılı (200) yanıt dışında bir yanıt döndürdüğünde önbelleğe alınmış nesnelerin sağlanmasını görüyorsunuz.
function processRequest () {
if (req.readyState == 4) {
if (req.status == 200) {
if (supports_local_storage()) {
localStorage[url] = req.responseText;
}
if (callback) callback(req.responseText,url);
} else {
// There is an error of some kind, use our cached copy (if available).
if (!!localStorage[url]) {
// We have some data cached, return that to the callback.
callback(localStorage[url],url);
return;
}
}
}
}
Ne yazık ki localStorage, karakter kodlaması için UTF-16 kullandığından her tek bayt 2 bayt olarak depolanır.Bu da depolama sınırımızı 5 MB'tan toplam 2, 6 MB'a düşürür. Bu sayfaların/işaretlemelerin uygulama önbelleği kapsamı dışında getirilip önbelleğe alınmasının asıl nedeni sonraki bölümde açıklanmaktadır.
HTML5 ile iframe öğesinde son zamanlarda yapılan geliştirmeler sayesinde, AJAX çağrımızdan aldığımız responseText öğesini ayrıştırmak için artık basit ve etkili bir yöntemimiz var. 3.000 satırlık pek çok JavaScript ayrıştırıcısı ve komut dosyası etiketlerini vb. kaldıran normal ifadeler vardır. Ancak tarayıcının en iyi yaptığı işi yapmasına neden izin vermiyorsunuz? Bu örnekte, responseText öğesini geçici bir gizli iFrame'e yazacağız. Komut dosyalarını devre dışı bırakan ve birçok güvenlik özelliği sunan HTML5 "sandbox" özelliğini kullanıyoruz…
Spesifikasyondan: Belirtildiğinde sandbox özelliği, iframe tarafından barındırılan tüm içeriklerde bir dizi ek kısıtlama sağlar. Değeri, ASCII'de büyük/küçük harfe duyarlı olmayan, benzersiz ve boşlukla ayrılmış jetonlardan oluşan sırasız bir küme olmalıdır. İzin verilen değerler allow-forms, allow-same-origin, allow-scripts ve allow-top-navigation'dır. Özellik ayarlandığında içerik benzersiz bir kaynaktan geliyormuş gibi değerlendirilir, formlar ve komut dosyaları devre dışı bırakılır, bağlantıların diğer tarama bağlamlarını hedeflemesi engellenir ve eklentiler devre dışı bırakılır.
var insertPages = function(text, originalLink) {
var frame = getFrame();
//write the ajax response text to the frame and let
//the browser do the work
frame.write(text);
//now we have a DOM to work with
var incomingPages = frame.getElementsByClassName('page');
var pageCount = incomingPages.length;
for (var i = 0; i < pageCount; i++) {
//the new page will always be at index 0 because
//the last one just got popped off the stack with appendChild (below)
var newPage = incomingPages[0];
//stage the new pages to the left by default
newPage.className = 'page stage-left';
//find out where to insert
var location = newPage.parentNode.id == 'back' ? 'back' : 'front';
try {
// mobile safari will not allow nodes to be transferred from one DOM to another so
// we must use adoptNode()
document.getElementById(location).appendChild(document.adoptNode(newPage));
} catch(e) {
// todo graceful degradation?
}
}
};
Safari, bir düğümü bir dokümandan diğerine örtülü olarak taşımayı doğru şekilde reddediyor. Yeni alt düğüm farklı bir dokümanda oluşturulursa hata oluşur. Bu nedenle burada adoptNode kullanıyoruz ve her şey yolunda.
Peki neden iframe? Neden sadece innerHTML kullanılmıyor? innerHTML artık HTML5 spesifikasyonunun bir parçası olsa da bir sunucudan (kötü veya iyi) gelen yanıtı kontrol edilmemiş bir alana eklemek tehlikeli bir uygulamadır. Bu makalenin yazıldığı sırada, innerHTML dışında bir şey kullanan kimseyi bulamadım. JQuery'nin yalnızca istisna durumunda ekleme geri dönüşüyle temelinde kullandığını biliyorum. JQuery Mobile da bunu kullanır. Ancak innerHTML'nin "rastgele çalışmayı durdurması" ile ilgili kapsamlı bir test yapmadım. Bununla birlikte, bu durumdan etkilenen tüm platformları görmek çok ilginç olacaktır. Hangi yaklaşımın daha iyi performans gösterdiğini görmek de ilginç olacaktır. Bu konuda her iki taraftan da iddialar duyuyorum.
Ağ türü algılama, işleme ve profilleme
Web uygulamamızı arabelleğe alma (veya tahmini önbelleğe alma) özelliğine sahip olduğumuza göre, uygulamamızı daha akıllı hale getiren uygun bağlantı algılama özelliklerini sağlamamız gerekir. Bu noktada mobil uygulama geliştirme, çevrimiçi/çevrimdışı modlara ve bağlantı hızına karşı son derece hassas hale gelir. The Network Information API'yi girin. Bu özelliği bir sunumda her gösterdiğimde, izleyicilerden biri elini kaldırıp "Bunu ne için kullanırım?" diye soruyor. Bu nedenle, son derece akıllı bir mobil web uygulaması oluşturmanın olası bir yolunu burada açıklıyoruz.
Önce sıkıcı olan, herkesin bildiği senaryo... Yüksek hızlı trenle seyahat ederken mobil cihazdan web ile etkileşimde bulunurken ağın çeşitli anlarda kesilmesi ve farklı coğrafyalarda farklı iletim hızlarının desteklenmesi (ör. HSPA veya 3G bazı şehirlerde kullanılabilir ancak uzak bölgelerde çok daha yavaş olan 2G teknolojileri destekleniyor olabilir. Aşağıdaki kod, bağlantı senaryolarının çoğunu ele alır.
Aşağıdaki kod şunları sağlar:
applicationCachetarihine kadar çevrimdışı erişim.- Yer işaretli ve çevrimdışı olup olmadığını algılar.
- Çevrimdışı moddan çevrimiçi moda ve tam tersi geçişleri algılar.
- Yavaş bağlantıları algılar ve ağ türüne göre içerik getirir.
Bu özelliklerin tümü için çok az kod gerekir. Öncelikle etkinliklerimizi ve yükleme senaryolarımızı algılarız:
window.addEventListener('load', function(e) {
if (navigator.onLine) {
// new page load
processOnline();
} else {
// the app is probably already cached and (maybe) bookmarked...
processOffline();
}
}, false);
window.addEventListener("offline", function(e) {
// we just lost our connection and entered offline mode, disable eternal link
processOffline(e.type);
}, false);
window.addEventListener("online", function(e) {
// just came back online, enable links
processOnline(e.type);
}, false);
Yukarıdaki EventListener'larda, kodumuzun bir etkinlikten mi yoksa gerçek bir sayfa isteğinden mi ya da yenilemeden mi çağrıldığını belirtmemiz gerekir. Bunun temel nedeni, online ve çevrimdışı modlar arasında geçiş yapıldığında body onload etkinliğinin tetiklenmemesidir.
Ardından, ononline veya onload etkinliği için basit bir kontrolümüz var. Bu kod, çevrimdışı moddan çevrimiçi moda geçiş yapıldığında devre dışı bırakılan bağlantıları sıfırlar. Ancak bu uygulama daha gelişmiş olsaydı içerik getirme işlemini devam ettirecek veya aralıklı bağlantılar için kullanıcı deneyimini yönetecek bir mantık ekleyebilirsiniz.
function processOnline(eventType) {
setupApp();
checkAppCache();
// reset our once disabled offline links
if (eventType) {
for (var i = 0; i < disabledLinks.length; i++) {
disabledLinks[i].onclick = null;
}
}
}
Aynı durum processOffline() için de geçerlidir. Burada, uygulamanızı çevrimdışı mod için düzenleyebilir ve arka planda devam eden işlemleri kurtarmaya çalışabilirsiniz. Aşağıdaki kod, tüm harici bağlantılarımızı bulup devre dışı bırakarak kullanıcıları çevrimdışı uygulamamıza sonsuza dek hapseder.
function processOffline() {
setupApp();
// disable external links until we come back - setting the bounds of app
disabledLinks = getUnconvertedLinks(document);
// helper for onlcick below
var onclickHelper = function(e) {
return function(f) {
alert('This app is currently offline and cannot access the hotness');return false;
}
};
for (var i = 0; i < disabledLinks.length; i++) {
if (disabledLinks[i].onclick == null) {
//alert user we're not online
disabledLinks[i].onclick = onclickHelper(disabledLinks[i].href);
}
}
}
Şimdi de iyi haberlere geçelim. Uygulamamız artık hangi bağlantı durumunda olduğunu bildiğine göre, çevrimiçi olduğunda bağlantı türünü de kontrol edebilir ve buna göre ayarlama yapabiliriz. Her bağlantı için yorumlarda tipik Kuzey Amerika sağlayıcılarının indirme ve gecikme sürelerini listeledim.
function setupApp(){
// create a custom object if navigator.connection isn't available
var connection = navigator.connection || {'type':'0'};
if (connection.type == 2 || connection.type == 1) {
//wifi/ethernet
//Coffee Wifi latency: ~75ms-200ms
//Home Wifi latency: ~25-35ms
//Coffee Wifi DL speed: ~550kbps-650kbps
//Home Wifi DL speed: ~1000kbps-2000kbps
fetchAndCache(true);
} else if (connection.type == 3) {
//edge
//ATT Edge latency: ~400-600ms
//ATT Edge DL speed: ~2-10kbps
fetchAndCache(false);
} else if (connection.type == 2) {
//3g
//ATT 3G latency: ~400ms
//Verizon 3G latency: ~150-250ms
//ATT 3G DL speed: ~60-100kbps
//Verizon 3G DL speed: ~20-70kbps
fetchAndCache(false);
} else {
//unknown
fetchAndCache(true);
}
}
FetchAndCache sürecimizde yapabileceğimiz çok sayıda düzenleme var ancak burada yaptığım tek şey, belirli bir bağlantı için kaynakları eşzamansız (true) veya eşzamanlı (false) olarak getirmesini söylemekti.
Edge (Eşzamanlı) İstek Zaman Çizelgesi
Kablosuz (Asenkron) İstek Zaman Çizelgesi
Bu sayede, yavaş veya hızlı bağlantılara göre kullanıcı deneyiminde en azından bazı ayarlamalar yapılabilir. Bu çözüm, her sorunu çözebilecek bir çözüm değildir. Yapılacak bir diğer şey de bağlantı tıklandığında (yavaş bağlantılarda) yükleme modalı göstermektir. Bu sırada uygulama, bağlantının sayfasını arka planda getirmeye devam edebilir. Buradaki önemli nokta, kullanıcının bağlantısının tüm özelliklerinden yararlanırken gecikmeleri azaltmaktır. Bu amaçla, HTML5'in sunduğu en yeni ve en iyi özelliklerden yararlanılır. Ağ algılama demosunu buradan izleyin.
Sonuç
Mobil HTML5 uygulamaları yolculuğu daha yeni başlıyor. Şimdi, yalnızca HTML5 ve destekleyici teknolojiler üzerine kurulu bir mobil "çerçevenin" çok basit ve temel yapısını görüyorsunuz. Geliştiricilerin bu özelliklerle çalışması ve bunları temel düzeyde ele alması, sarmalayıcıyla gizlememesi gerektiğini düşünüyorum.