Dokunmatik ekranlar, telefonlardan masaüstü ekranlara kadar her geçen gün daha fazla cihazda kullanılabilmektedir. Uygulamanız, kullanıcıların dokunuşlarına sezgisel ve güzel bir şekilde yanıt vermelidir.
Dokunmatik ekranlar, telefonlardan masaüstü ekranlara kadar her türlü cihazda kullanılmaktadır. Kullanıcılarınız kullanıcı arayüzünüzle etkileşimde bulunmayı seçtiğinde uygulamanız, kullanıcıların dokunmalarına sezgisel şekillerde yanıt vermelidir.
Öğe durumlarına yanıt verme
Bir web sayfasındaki herhangi bir öğeye dokunduğunuz veya tıkladığınız ve sitenin bu öğeyi gerçekten algılayıp algılamadığını sorguladığınız oldu mu?
Kullanıcılar, kullanıcı arayüzünüzün belirli kısımlarına dokunurken veya bu kısımlarla etkileşimde bulunurken bir öğenin rengini değiştirmek, sitenizin çalıştığına dair temel bir güvence verir. Bu hem hayal kırıklığını azaltmakla kalmaz, hem de hızlı ve duyarlı bir his verir.
DOM öğeleri şu durumlardan herhangi birini devralabilir: varsayılan, odak, fareyle üzerine gelme ve etkin. Kullanıcı arayüzümüzü bu durumların her birinde değiştirmek için stilleri aşağıda gösterildiği gibi aşağıdaki sözde sınıflara (:hover
, :focus
ve :active
) uygulamamız gerekir:
.btn {
background-color: #4285f4;
}
.btn:hover {
background-color: #296cdb;
}
.btn:focus {
background-color: #0f52c1;
/* The outline parameter suppresses the border
color / outline when focused */
outline: 0;
}
.btn:active {
background-color: #0039a8;
}
Çoğu mobil tarayıcıda, bir öğeye dokunulduktan sonra hover ve/veya hover durumları uygulanır.
Hangi stilleri ayarladığınızı ve bunların dokunmayı bitirdikten sonra kullanıcıya nasıl görüneceğini dikkatlice düşünün.
Varsayılan tarayıcı stillerini engelleme
Farklı durumlar için stiller eklediğinizde, çoğu tarayıcının kullanıcının dokunmasına yanıt olarak kendi stillerini uyguladığını görürsünüz. Bunun nedeni büyük ölçüde, mobil cihazlar ilk kez kullanıma sunulduğunda bazı sitelerin :active
durumu için stil kullanmamasıdır. Sonuç olarak, birçok tarayıcı kullanıcıya geri bildirim vermek için
ek bir vurgu rengi veya stili ekledi.
Çoğu tarayıcı, bir öğeye odaklanıldığında öğenin etrafında bir halka görüntülemek için outline
CSS özelliğini kullanır. Şu seçenekleri kullanarak gizleyebilirsiniz:
.btn:focus {
outline: 0;
/* Add replacement focus styling here (i.e. border) */
}
Safari ve Chrome, -webkit-tap-highlight-color
CSS özelliğiyle engellenebilecek bir dokunma vurgulama rengi ekler:
/* Webkit / Chrome Specific CSS to remove tap
highlight color */
.btn {
-webkit-tap-highlight-color: transparent;
}
Windows Phone'daki Internet Explorer da benzer bir davranışa sahiptir, ancak bir meta etiket ile engellenir:
<meta name="msapplication-tap-highlight" content="no">
Firefox'un işlemesi gereken iki yan etkisi vardır.
Dokunulabilir öğelere bir dış çizgi ekleyen -moz-focus-inner
sözde sınıfı, border: 0
ayarlayarak kaldırabilirsiniz.
Firefox'ta bir <button>
öğesi kullanıyorsanız bir renk geçişi uygulanır. Bunu background-image: none
ayarlayarak kaldırabilirsiniz.
/* Firefox Specific CSS to remove button
differences and focus ring */
.btn {
background-image: none;
}
.btn::-moz-focus-inner {
border: 0;
}
Kullanıcı seçimini devre dışı bırakma
Kullanıcı arayüzünüzü oluştururken, kullanıcıların öğelerinizle etkileşim kurmasını istediğiniz, ancak uzun basıldığında veya fareyi kullanıcı arayüzünüz üzerine sürüklediğinde metin seçme varsayılan davranışını gizlemek istediğiniz durumlar olabilir.
Bunu user-select
CSS mülkü ile yapabilirsiniz ancak içerikte bunu yapmanın, öğedeki metni seçmek isteyen kullanıcılar için extremely sinir bozucu olabileceğini unutmayın.
Bu nedenle, bu aracı dikkatli bir şekilde ve tutumlu bir şekilde kullandığınızdan emin olun.
/* Example: Disable selecting text on a paragraph element: */
p.disable-text-selection {
user-select: none;
}
Özel hareketler uygulama
Siteniz için özel etkileşimler ve hareketlerle ilgili bir fikriniz varsa aklınızda bulundurmanız gereken iki konu vardır:
- Tüm tarayıcılar nasıl desteklenir?
- Kare hızınızı nasıl yüksek tutabilirsiniz?
Bu makalede, tüm tarayıcılarda etkili olması için desteklememiz gereken API'leri kapsayan bu konulara ve bu etkinlikleri verimli bir şekilde nasıl kullandığımıza değineceğiz.
Hareketinizin ne yapmasını istediğinize bağlı olarak kullanıcının aynı anda bir öğeyle etkileşimde bulunmasını veya aynı anda birden çok öğeyle etkileşimde bulunabilmesini istersiniz.
Bu makalede, tüm tarayıcılar için destek sağlayan ve kare hızını nasıl yüksek tutacağınızı gösteren iki örneğe bakacağız.
İlk örnek, kullanıcının bir öğe ile etkileşim kurmasına olanak tanır. Bu durumda, hareket başlangıçta öğenin kendisinde başladığı sürece tüm dokunma etkinliklerinin söz konusu öğeye verilmesini isteyebilirsiniz. Örneğin, bir parmağınızı kaydırılabilir öğeden uzaklaştırdığınızda öğeyi kontrol etmeye devam edebilirsiniz.
Bu, kullanıcıya büyük ölçüde esneklik sağladığı ancak kullanıcının kullanıcı arayüzüyle etkileşim kurma biçimini kısıtladığı için faydalıdır.
Ancak, kullanıcıların aynı anda birden çok öğeyle etkileşimde bulunmasını bekliyorsanız (çoklu dokunma özelliğini kullanarak) dokunmayı belirli öğeyle sınırlamanız gerekir.
Bu, kullanıcılar için daha esnektir ancak kullanıcı arayüzü işleme mantığını karmaşık hale getirir ve kullanıcı hatalarına karşı daha az dirençlidir.
Etkinlik işleyici ekle
Chrome'da (55 ve sonraki sürümler), Internet Explorer ve Edge'de PointerEvents
, özel hareketleri uygulamak için önerilen yaklaşımdır.
Diğer tarayıcılarda TouchEvents
ve MouseEvents
doğru yaklaşımdır.
PointerEvents
ürününün en iyi özelliği, fare, dokunma ve kalem etkinlikleri de dahil olmak üzere birden fazla giriş türünü tek bir geri çağırma grubunda birleştirmesidir. Dinlenecek etkinlikler: pointerdown
, pointermove
,
pointerup
ve pointercancel
.
Diğer tarayıcılardaki eşdeğerleri, dokunma etkinlikleri için touchstart
, touchmove
, touchend
ve touchcancel
şeklindedir. Fare girişi için aynı hareketi uygulamak isterseniz mousedown
, mousemove
ve mouseup
öğelerini uygulamanız gerekir.
Hangi etkinliklerin kullanılacağıyla ilgili sorularınız varsa Dokunma, fare ve işaretçi etkinlikleri tablosunu inceleyin.
Bu etkinlikleri kullanmak için etkinliğin adı, geri çağırma işlevi ve boole ile birlikte bir DOM öğesinde addEventListener()
yönteminin çağrılması gerekir.
Bu boole, diğer öğeler etkinlikleri yakalama ve yorumlama fırsatı yakaladıktan önce mi yoksa sonra mı yakalamanız gerektiğini belirler. (true
, etkinliğin diğer öğelerden önce olmasını istediğiniz anlamına gelir.)
Burada, etkileşimin başlangıcında dinlemeyle ilgili bir örnek verilmiştir.
// Check if pointer events are supported.
if (window.PointerEvent) {
// Add Pointer Event Listener
swipeFrontElement.addEventListener('pointerdown', this.handleGestureStart, true);
swipeFrontElement.addEventListener('pointermove', this.handleGestureMove, true);
swipeFrontElement.addEventListener('pointerup', this.handleGestureEnd, true);
swipeFrontElement.addEventListener('pointercancel', this.handleGestureEnd, true);
} else {
// Add Touch Listener
swipeFrontElement.addEventListener('touchstart', this.handleGestureStart, true);
swipeFrontElement.addEventListener('touchmove', this.handleGestureMove, true);
swipeFrontElement.addEventListener('touchend', this.handleGestureEnd, true);
swipeFrontElement.addEventListener('touchcancel', this.handleGestureEnd, true);
// Add Mouse Listener
swipeFrontElement.addEventListener('mousedown', this.handleGestureStart, true);
}
Tek öğeli etkileşimi işleme
Yukarıdaki kod snippet'ine yalnızca fare etkinlikleri için başlangıç etkinlik işleyicisini ekledik. Bunun nedeni, fare etkinliklerinin yalnızca imleç etkinlik işleyicinin eklendiği öğenin üzerine geldiğinde tetiklenmesidir.
TouchEvents
, dokunma işleminin nerede gerçekleştiğinden bağımsız olarak hareket başlatıldıktan sonra izler. PointerEvents
, bir DOM öğesinde setPointerCapture
çağrıldıktan sonra dokunmanın nerede gerçekleştiğinden bağımsız olarak etkinlikleri izler.
Fare hareketi ve bitiş etkinlikleri için hareket başlatma yönteminin içindeki etkinlik işleyicilerini ekleriz ve işleyicileri dokümana ekleriz. Böylece doküman, hareket tamamlanana kadar imleci izleyebilir.
Bunu uygulamak için atılan adımlar şunlardır:
- Tüm TouchEvent ve PointerEvent işleyicilerini ekleyin. MouseEvents için yalnızca başlangıç etkinliğini ekleyin.
- Başlatma hareketiyle geri çağırmanın içinde, fare hareketini ve bitiş etkinliklerini dokümana bağlayın. Bu şekilde, etkinliğin orijinal öğede gerçekleşip gerçekleşmediğinden bağımsız olarak tüm fare etkinlikleri alınır. PointerEvents söz konusu olduğunda, diğer tüm etkinlikleri almak için orijinal öğemizde
setPointerCapture()
çağırmamız gerekir. Ardından hareketin başlangıcını tutun. - Taşıma etkinliklerini işleme.
- Sonlandırma etkinliğinde, fare hareketini ve bitiş işleyicilerini dokümandan kaldırıp hareketi sonlandırın.
Aşağıda, taşıma ve bitiş etkinliklerini belgeye ekleyen handleGestureStart()
yöntemimizin bir snippet'i bulunmaktadır:
// Handle the start of gestures
this.handleGestureStart = function(evt) {
evt.preventDefault();
if(evt.touches && evt.touches.length > 1) {
return;
}
// Add the move and end listeners
if (window.PointerEvent) {
evt.target.setPointerCapture(evt.pointerId);
} else {
// Add Mouse Listeners
document.addEventListener('mousemove', this.handleGestureMove, true);
document.addEventListener('mouseup', this.handleGestureEnd, true);
}
initialTouchPos = getGesturePointFromEvent(evt);
swipeFrontElement.style.transition = 'initial';
}.bind(this);
Eklediğimiz son geri çağırma handleGestureEnd()
öğesidir. Bu geri çağırma, taşıma ve bitiş etkinliği işleyicilerini dokümandan kaldırır ve hareket şu şekilde tamamlandığında işaretçi yakalamayı serbest bırakır:
// Handle end gestures
this.handleGestureEnd = function(evt) {
evt.preventDefault();
if (evt.touches && evt.touches.length > 0) {
return;
}
rafPending = false;
// Remove Event Listeners
if (window.PointerEvent) {
evt.target.releasePointerCapture(evt.pointerId);
} else {
// Remove Mouse Listeners
document.removeEventListener('mousemove', this.handleGestureMove, true);
document.removeEventListener('mouseup', this.handleGestureEnd, true);
}
updateSwipeRestPosition();
initialTouchPos = null;
}.bind(this);
Taşıma etkinliğinin dokümana eklenmesiyle ilgili bu kalıbı izleyerek kullanıcı bir öğeyle etkileşimde bulunmaya başlarsa ve hareketini öğenin dışına taşırsa, etkinlikler dokümandan alındığı için sayfanın neresinde olurlarsa olsunlar fare hareketlerini almaya devam ederiz.
Bu şema, bir hareket başladığında taşıma ve sonlandırma etkinlikleri dokümana eklenirken dokunma etkinliklerinin ne yaptığı gösterilmektedir.
Dokunmaya etkili şekilde yanıt verme
Artık başlangıç ve bitiş etkinliklerini ele aldığımıza göre dokunma etkinliklerine yanıt verebiliyoruz.
Başlangıç ve taşınma etkinliklerinin herhangi biri için x
ve y
değerlerini bir etkinlikten kolayca ayıklayabilirsiniz.
Aşağıdaki örnekte, targetTouches
var olup olmadığını kontrol ederek etkinliğin bir TouchEvent
öğesinden gelip gelmediği kontrol edilir. İçeriyorsa ilk dokunuştan clientX
ve clientY
öğelerini çıkarır.
Etkinlik PointerEvent
veya MouseEvent
ise clientX
ve clientY
öğelerini doğrudan etkinliğin kendisinden ayıklar.
function getGesturePointFromEvent(evt) {
var point = {};
if (evt.targetTouches) {
// Prefer Touch Events
point.x = evt.targetTouches[0].clientX;
point.y = evt.targetTouches[0].clientY;
} else {
// Either Mouse event or Pointer Event
point.x = evt.clientX;
point.y = evt.clientY;
}
return point;
}
Bir TouchEvent
, dokunma verileri içeren üç listeye sahiptir:
touches
: DOM öğesi ne olursa olsun ekrandaki tüm geçerli dokunmaların listesi.targetTouches
: Etkinliğin bağlı olduğu DOM öğesinde yapılan dokunmaların listesi.changedTouches
: Etkinliğin tetiklenmesiyle sonuçlanan değişikliklerin listesi.
Çoğu durumda, targetTouches
ihtiyacınız olan ve istediğiniz her şeyi sağlar. (Bu listelerle ilgili daha fazla bilgi için Dokunma listeleri konusuna bakın).
requestAnimationFrame Yöntemini Kullanma
Etkinlik geri çağırmaları ana iş parçacığında tetiklendiğinden, kare hızımızı yüksek tutarak ve olumsuzlukları önleyerek etkinliklerimizin geri çağırmalarında mümkün olduğunca küçük bir kod çalıştırmak isteriz.
requestAnimationFrame()
sayesinde, tarayıcı bir çerçeve çizmeyi planmadan hemen önce kullanıcı arayüzünü güncelleme fırsatı yakalarız ve böylece etkinlik geri çağırma işlemlerimizden bazı işleri halletmemize yardımcı oluruz.
requestAnimationFrame()
hakkında bilginiz yoksa buradan daha fazla bilgi edinebilirsiniz.
Tipik bir uygulama, etkinlikleri başlangıçtan itibaren x
ve y
koordinatlarını kaydetmek ve taşımak ve taşıma etkinliği geri çağırması için bir animasyon çerçevesi istemektir.
Demomuzda, ilk dokunma konumunu handleGestureStart()
olarak saklarız (initialTouchPos
arayın):
// Handle the start of gestures
this.handleGestureStart = function(evt) {
evt.preventDefault();
if (evt.touches && evt.touches.length > 1) {
return;
}
// Add the move and end listeners
if (window.PointerEvent) {
evt.target.setPointerCapture(evt.pointerId);
} else {
// Add Mouse Listeners
document.addEventListener('mousemove', this.handleGestureMove, true);
document.addEventListener('mouseup', this.handleGestureEnd, true);
}
initialTouchPos = getGesturePointFromEvent(evt);
swipeFrontElement.style.transition = 'initial';
}.bind(this);
handleGestureMove()
yöntemi, gerektiğinde bir animasyon çerçevesi istemeden önce etkinliğinin konumunu depolar ve geri çağırma olarak onAnimFrame()
işlevimizi iletir:
this.handleGestureMove = function (evt) {
evt.preventDefault();
if (!initialTouchPos) {
return;
}
lastTouchPos = getGesturePointFromEvent(evt);
if (rafPending) {
return;
}
rafPending = true;
window.requestAnimFrame(onAnimFrame);
}.bind(this);
onAnimFrame
değeri, çağrıldığında kullanıcı arayüzümüzü hareket ettirmek için değiştiren bir işlevdir. Bu işlevi requestAnimationFrame()
işlevine geçirerek tarayıcıya, sayfayı güncellemeden hemen önce çağrı yapmasını söyleriz (diğer bir deyişle, sayfadaki değişiklikleri boyayabilirsiniz).
handleGestureMove()
geri çağırmasında ilk olarak rafPending
öğesinin false (yanlış) olup olmadığını kontrol ederiz. Bu, son taşıma etkinliğinden bu yana requestAnimationFrame()
tarafından onAnimFrame()
çağrılıp çağrılmadığını gösterir. Bu, aynı anda çalıştırılmayı bekleyen yalnızca bir requestAnimationFrame()
olduğu anlamına gelir.
onAnimFrame()
geri çağırmamız yürütüldüğünde, rafPending
öğesini false
değerine güncellemeden önce taşımak istediğimiz tüm öğelerdeki dönüşümü ayarlarız. Böylece, bir sonraki dokunma etkinliği yeni bir animasyon çerçevesi isteğinde bulunabilir.
function onAnimFrame() {
if (!rafPending) {
return;
}
var differenceInX = initialTouchPos.x - lastTouchPos.x;
var newXTransform = (currentXPosition - differenceInX)+'px';
var transformStyle = 'translateX('+newXTransform+')';
swipeFrontElement.style.webkitTransform = transformStyle;
swipeFrontElement.style.MozTransform = transformStyle;
swipeFrontElement.style.msTransform = transformStyle;
swipeFrontElement.style.transform = transformStyle;
rafPending = false;
}
Dokunma işlemlerini kullanarak hareketleri kontrol etme
touch-action
CSS özelliği, bir öğenin varsayılan dokunma davranışını kontrol etmenizi sağlar. Örneklerimizde, tarayıcının kullanıcı dokunuşuyla herhangi bir işlem yapmasını engellemek ve tüm dokunma olaylarını engellememize olanak tanımak için touch-action: none
kullanırız.
/* Pass all touches to javascript: */
button.custom-touch-logic {
touch-action: none;
}
touch-action: none
kullanımı, tüm varsayılan tarayıcı davranışlarını engellediği için
biraz nükleer bir seçenektir. Çoğu durumda aşağıdaki seçeneklerden
biri daha iyi bir çözümdür.
touch-action
, tarayıcının uyguladığı hareketleri devre dışı bırakmanıza olanak tanır.
Örneğin, IE10+ zum yapmak için iki kez dokunma hareketini destekler. manipulation
değerini touch-action
olarak ayarlayarak varsayılan iki kez dokunma davranışını önlersiniz.
Bu şekilde, iki kez dokunma hareketini kendiniz uygulayabilirsiniz.
Aşağıda, yaygın olarak kullanılan touch-action
değerlerinin listesi verilmiştir:
IE'nin eski sürümlerini destekleme
IE10'u desteklemek istiyorsanız PointerEvents
için tedarikçi önek içeren sürümleri yönetmeniz gerekir.
PointerEvents
desteğini kontrol etmek için normalde window.PointerEvent
ifadesini ararsınız, ancak IE10'da window.navigator.msPointerEnabled
ifadesini kullanmanız gerekir.
Tedarikçi firma öneklerine sahip etkinlik adları şunlardır: 'MSPointerDown'
, 'MSPointerUp'
ve
'MSPointerMove'
.
Aşağıdaki örnekte, desteğin nasıl kontrol edileceği ve etkinlik adlarını nasıl değiştireceğiniz gösterilmektedir.
var pointerDownName = 'pointerdown';
var pointerUpName = 'pointerup';
var pointerMoveName = 'pointermove';
if (window.navigator.msPointerEnabled) {
pointerDownName = 'MSPointerDown';
pointerUpName = 'MSPointerUp';
pointerMoveName = 'MSPointerMove';
}
// Simple way to check if some form of pointerevents is enabled or not
window.PointerEventsSupport = false;
if (window.PointerEvent || window.navigator.msPointerEnabled) {
window.PointerEventsSupport = true;
}
Daha fazla bilgi için Microsoft'un bu güncelleme makalesine göz atın.
Referans
Dokunma durumları hakkında sahte sınıflar
Kesin dokunma etkinlikleri referansını burada bulabilirsiniz: W3C Touch Etkinlikleri.
Dokunma, fare ve işaretçi etkinlikleri
Aşağıdaki etkinlikler, uygulamanıza yeni hareketler eklemek için yapı taşlarıdır:
Dokunma listeleri
Her dokunma etkinliği üç liste özelliği içerir:
iOS'te etkin durum desteğini etkinleştirme
Maalesef iOS'te Safari, varsayılan olarak etkin durumunu uygulamaz. Bu durumun çalışması için doküman gövdesine veya her bir öğeye bir touchstart
etkinlik işleyici eklemeniz gerekir.
Yalnızca iOS cihazlarda çalıştırılması için bunu bir kullanıcı aracısı testinin arkasında yapmanız gerekir.
Gövdeye bir dokunma başlangıcı eklemek, DOM'daki tüm öğeleri uygulama avantajına sahiptir ancak bu durumda sayfa kaydırılırken performans sorunları yaşanabilir.
window.onload = function() {
if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
document.body.addEventListener('touchstart', function() {}, false);
}
};
Bunun alternatifi, sayfadaki etkileşimde bulunulabilir tüm öğelere dokunma başlatma işleyicileri ekleyerek performans endişelerinin bir kısmını hafifletmektir.
window.onload = function() {
if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
var elements = document.querySelectorAll('button');
var emptyFunction = function() {};
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('touchstart', emptyFunction, false);
}
}
};