Dokunmatik ekranlar, telefonlardan masaüstü ekranlarına kadar giderek daha fazla cihazda kullanılıyor. Uygulamanız, kullanıcıların dokunmalarına sezgisel ve güzel bir şekilde yanıt vermelidir.
Dokunmatik ekranlar, telefonlardan masaüstü ekranlarına kadar giderek daha fazla cihazda kullanılıyor. Kullanıcılarınız kullanıcı arayüzünüzle etkileşime geçmeyi seçtiğinde uygulamanız, dokunmalarına sezgisel yollarla yanıt vermelidir.
Öğe durumlarını yanıtlama
Bir web sayfasındaki bir öğeye dokunduğunuzda veya öğeyi tıkladığınızda sitenin bunu gerçekten algılayıp algılamadığını merak ettiniz mi?
Kullanıcılar kullanıcı arayüzünüzün bölümlerine dokunurken veya bu bölümlerle etkileşime geçerken bir öğenin rengini değiştirmek, sitenizin çalıştığına dair temel bir güvence verir. Bu, kullanıcıların canını sıkmanın yanı sıra hızlı ve duyarlı bir deneyim sunabilir.
DOM öğeleri aşağıdaki durumlardan herhangi birini devralabilir: varsayılan, odak, fareyle üzerine gelme ve etkin. Bu durumların her biri için kullanıcı arayüzünü değiştirmek üzere aşağıdaki gibi aşağıdaki sözde sınıflara :hover
, :focus
ve :active
stiller 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 fareyle üzerine gelme ve/veya odak durumları uygulanır.
Hangi stilleri ayarlayacağınızı ve kullanıcı dokunuşunu tamamladıktan sonra bu stillerin kullanıcıya nasıl görüneceğini dikkatlice düşünün.
Varsayılan tarayıcı stillerini engelleme
Farklı durumlar için stiller ekledikten sonra, çoğu tarayıcının bir kullanıcının dokunmasına yanıt olarak kendi stilini uyguladığını fark edeceksiniz. Bunun başlıca nedeni, mobil cihazlar ilk kullanıma sunulduğunda birçok sitenin :active
durumu için stilinin olmamasıdır. Sonuç olarak, birçok tarayıcı kullanıcıya geri bildirim vermek için ek vurgu rengi veya stili ekledi.
Çoğu tarayıcı, bir öğe odaklanıldığında öğenin etrafında bir halka görüntülemek için outline
CSS özelliğini kullanır. Şunlarla engelleyebilirsiniz:
.btn:focus {
outline: 0;
/* Add replacement focus styling here (i.e. border) */
}
Safari ve Chrome, dokunma vurgu rengi ekler. Bu renk, -webkit-tap-highlight-color
CSS özelliğiyle önlenebilir:
/* Webkit / Chrome Specific CSS to remove tap
highlight color */
.btn {
-webkit-tap-highlight-color: transparent;
}
Windows Phone'daki Internet Explorer'da da benzer bir davranış vardır ancak bu davranış bir meta etiketiyle engellenir:
<meta name="msapplication-tap-highlight" content="no">
Firefox'un ilgilenmesi gereken iki yan etkisi vardır.
Dokunabilir öğelere bir dış çizgi ekleyen -moz-focus-inner
sözde sınıfını border: 0
ayarlayarak kaldırabilirsiniz.
Firefox'ta <button>
öğesi kullanıyorsanız bir gradyan uygulanır. Bu gradyanı 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şime geçmesini istediğiniz ancak uzun basma veya fareyle kullanıcı arayüzünüzün üzerine gelme gibi durumlarda metin seçmeyi engellemek istediğiniz senaryolar olabilir.
Bunu user-select
CSS mülküyle yapabilirsiniz, ancak içerik üzerinde bunu yapmanın, öğedeki metni seçmek isteyen kullanıcılar için çok can sıkıcı olabileceğini unutmayın.
Bu nedenle, bu özelliği dikkatli ve ölçülü 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 hareketler hakkında bir fikriniz varsa göz önünde bulundurmanız gereken iki konu vardır:
- Tüm tarayıcılar nasıl desteklenir?
- Kare hızınızı yüksek tutma
Bu makalede, tüm tarayıcılara ulaşmak için desteklememiz gereken API'leri kapsayan tam olarak bu konulara değineceğiz ve ardından bu etkinlikleri nasıl verimli bir şekilde kullandığımızı ele alacağız.
Hareketinizin ne yapmasını istediğinize bağlı olarak, kullanıcının aynı anda bir öğeyle veya birden fazla öğeyle etkileşim kurmasını isteyebilirsiniz.
Bu makalede, tüm tarayıcılar için destek sunan ve kare hızının nasıl yüksek tutulduğunu gösteren iki örneğe göz atacağız.
İlk örnekte, kullanıcının bir öğeyle etkileşime geçmesine izin verilir. Bu durumda, hareket başlangıçta öğenin kendisinde başladığı sürece tüm dokunma etkinliklerinin bu öğeye verilmesini isteyebilirsiniz. Örneğin, kaydırılabilir öğeden bir parmağınızı kaldırsanız bile öğeyi kontrol edebilirsiniz.
Bu, kullanıcıya büyük esneklik sağladığı ancak kullanıcının kullanıcı arayüzünüzle etkileşim kurma biçimini kısıtladığı için kullanışlıdır.
Ancak kullanıcıların aynı anda birden fazla öğeyle etkileşime geçmesini (çoklu dokunma özelliğini kullanarak) istiyorsanız dokunmayı belirli bir öğeyle kısıtlamanız gerekir.
Bu, kullanıcılar için daha esnektir ancak kullanıcı arayüzünü değiştirme mantığını karmaşıklaştırır ve kullanıcı hatalarına karşı daha az dirençlidir.
Etkinlik işleyici ekleme
Chrome (55 ve sonraki sürümler), Internet Explorer ve Edge'de özel hareketleri uygulamak için önerilen yaklaşım PointerEvents
'tür.
Diğer tarayıcılarda TouchEvents
ve MouseEvents
doğru yaklaşımdır.
PointerEvents
'ün en önemli özelliği, fare, dokunma ve kalem etkinlikleri dahil olmak üzere birden fazla giriş türünü tek bir geri çağırma grubuna birleştirmesidir. Dinlenecek etkinlikler pointerdown
, pointermove
, pointerup
ve pointercancel
'dır.
Diğer tarayıcılarda dokunma etkinlikleri için touchstart
, touchmove
, touchend
ve touchcancel
değerleri kullanılır. Fare girişi için aynı hareketi uygulamak isterseniz mousedown
, mousemove
ve mouseup
değerlerini uygulamanız gerekir.
Hangi etkinliklerin kullanılacağı hakkında sorularınız varsa dokunma, fare ve işaretçi etkinlikleri tablosuna göz atın.
Bu etkinlikleri kullanmak için bir DOM öğesinde addEventListener()
yönteminin yanı sıra bir etkinliğin adı, geri çağırma işlevi ve bir boole değeri çağrılmalıdır.
Bu boole değeri, etkinliği, diğer öğeler etkinlikleri yakalayıp yorumlama fırsatı yakaladıktan sonra mı yoksa önce mi yakalamanız gerektiğini belirler. (true
, etkinliğin diğer öğelerden önce gelmesini istediğiniz anlamına gelir.)
Bir etkileşimin başlangıcını dinleme örneğini aşağıda bulabilirsiniz.
// 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 öğe etkileşimini işleme
Yukarıdaki kısa kod snippet'inde, yalnızca fare etkinlikleri için başlangıç etkinliği işleyicisini ekledik. Bunun nedeni, fare etkinliklerinin yalnızca imleç, etkinlik işleyicinin eklendiği öğenin üzerindeyken tetiklenmesidir.
TouchEvents
, dokunmanın nerede gerçekleştiğine bakılmaksızın başlatıldıktan sonra hareketi izler. PointerEvents
, bir DOM öğesinde setPointerCapture
çağrısından sonra dokunmanın nerede gerçekleştiğine bakılmaksızın etkinlikleri izler.
Fare hareket ettirme ve sonlandırma etkinlikleri için hareket başlangıç yöntemine etkinlik işleyicileri ekleriz ve işleyicileri dokümana ekleriz. Böylece hareket tamamlanana kadar imleci izleyebiliriz.
Bunu uygulamak için gereken adımlar şunlardır:
- Tüm TouchEvent ve PointerEvent dinleyicilerini ekleyin. MouseEvents için yalnızca başlangıç etkinliğini ekleyin.
- Başlangıç hareketi geri çağırma işlevinin içinde, fare hareketi ve bitiş etkinliklerini dokümana bağlayın. Bu sayede, etkinliğin orijinal öğede gerçekleşip gerçekleşmediğinden bağımsız olarak tüm fare etkinlikleri alınır. PointerEvents için diğer tüm etkinlikleri almak üzere orijinal öğemizde
setPointerCapture()
çağrısı yapmamız gerekir. Ardından hareketin başlangıcını ele alın. - Taşıma etkinliklerini işleyin.
- Son etkinlikte, fare hareketini ve son işleyicileri dokümandan kaldırıp hareketi sonlandırın.
Aşağıda, taşıma ve bitiş etkinliklerini dokümana ekleyen handleGestureStart()
yöntemimizin snippet'ini görebilirsiniz:
// 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 bitiş geri çağırma işlevi handleGestureEnd()
'tür. Bu işlev, hareket sona erdiğinde hareket ve bitiş etkinliği işleyicilerini belgeden kaldırır ve işaretçi yakalamayı şu şekilde 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ğini dokümana eklemeyle ilgili bu kalıbı uygularsak kullanıcı bir öğeyle etkileşime geçmeye başlar ve hareketini öğenin dışına taşırsa etkinlikler dokümandan alındığı için sayfanın neresinde olduklarına bakılmaksızın fare hareketlerini almaya devam ederiz.
Bu diyagramda, bir hareket başladıktan sonra hareket ve bitiş etkinliklerini dokümana eklerken dokunma etkinliklerinin ne yaptığı gösterilmektedir.
Dokunmaya verimli bir şekilde yanıt verme
Başlangıç ve bitiş etkinliklerini hallettiğimize göre dokunma etkinliklerine yanıt verebiliriz.
Başlangıç ve taşıma etkinliklerinden herhangi biri için bir etkinlikten x
ve y
değerlerini kolayca ayıklayabilirsiniz.
Aşağıdaki örnekte, targetTouches
değerinin olup olmadığını kontrol ederek etkinliğin TouchEvent
kaynağından gelip gelmediğini kontrol edilir. Bu durumda, ilk dokunuştan clientX
ve clientY
değerlerini çıkarır.
Etkinlik bir PointerEvent
veya MouseEvent
ise clientX
ve clientY
verilerini 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;
}
TouchEvent
, dokunma verilerini içeren üç liste içerir:
touches
: DOM öğesinden bağımsız olarak ekrandaki tüm dokunuşların listesi.targetTouches
: Etkinliğin bağlı olduğu DOM öğesinde bulunan dokunma noktalarının listesi.changedTouches
: Etkinliğin tetiklenmesiyle sonuçlanan dokunmaların listesi.
Çoğu durumda targetTouches
, ihtiyacınız olan ve istediğiniz her şeyi size sunar. (Bu listeler hakkında daha fazla bilgi için Dokunma listeleri bölümüne bakın).
requestAnimationFrame işlevini kullanma
Etkinlik geri çağırma işlevleri ana iş parçacığında tetiklendiğinden, etkinlik geri çağırma işlevlerinde mümkün olduğunca az kod çalıştırarak kare hızımızı yüksek tutmak ve takılmaları önlemek istiyoruz.
requestAnimationFrame()
kullanarak, tarayıcı bir kare çizmeden hemen önce kullanıcı arayüzünü güncelleme fırsatı elde ederiz. Bu da bazı işleri etkinlik geri çağırmalarımızdan kaldırmamıza yardımcı olur.
requestAnimationFrame()
hakkında bilginiz yoksa buradan daha fazla bilgi edinebilirsiniz.
Tipik bir uygulama, başlangıç ve taşıma etkinliklerinden x
ve y
koordinatlarını kaydetmektir ve taşıma etkinliği geri çağırma işlevi içinde bir animasyon karesi istemektir.
Demo'da ilk dokunma konumunu handleGestureStart()
içinde depolarız (initialTouchPos
'u bulun):
// 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, gerekirse animasyon karesi istemeden önce etkinliğinin konumunu depolar ve geri çağırma işlevi 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ü taşımak için değiştiren bir işlevdir. Bu işlevi requestAnimationFrame()
içine aktararak tarayıcıya sayfayı güncellemeden (ör. sayfadaki değişiklikleri boyamadan) hemen önce bu işlevi çağırmasını söyleriz.
handleGestureMove()
geri çağırma işlevinde ilk olarak rafPending
değerinin yanlış olup olmadığını kontrol ederiz. Bu değer, son hareket etkinliğinden bu yana onAnimFrame()
'nin requestAnimationFrame()
tarafından çağrılıp çağrılmadığını belirtir. Bu, aynı anda çalıştırılmayı bekleyen yalnızca bir requestAnimationFrame()
hesabı olduğu anlamına gelir.
onAnimFrame()
geri çağırma işlemimiz yürütüldüğünde, rafPending
öğesini false
olarak güncellemeden önce taşımak istediğimiz tüm öğelerde dönüşümü ayarlarız. Böylece, bir sonraki dokunma etkinliğinin yeni bir animasyon çerçevesi istemesi sağlanır.
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 hareketlerini kullanarak hareketleri kontrol etme
CSS özelliği touch-action
, bir öğenin varsayılan dokunma davranışını kontrol etmenize olanak tanır. Örneklerimizde, tarayıcının kullanıcının dokunuşuyla herhangi bir işlem yapmasını engellemek için touch-action: none
kullanırız. Bu sayede tüm dokunma etkinliklerini durdurabiliriz.
/* Pass all touches to javascript: */
button.custom-touch-logic {
touch-action: none;
}
Varsayılan tarayıcı davranışlarının tümünü engellediği için touch-action: none
kullanmak bir tür nükleer seçenektir. Çoğu durumda, aşağıdaki seçeneklerden
biri daha iyi bir çözümdür.
touch-action
, bir tarayıcı tarafından uygulanan hareketleri devre dışı bırakmanıza olanak tanır.
Örneğin, IE10 ve sonraki sürümler yakınlaştırmak için iki kez dokunma hareketini destekler. touch-action
değerini manipulation
olarak ayarlayarak varsayılan çifte dokunma davranışını engellersiniz.
Bu sayede, çift dokunma hareketini kendiniz uygulayabilirsiniz.
Sık kullanılan touch-action
değerlerinin listesi aşağıda verilmiştir:
IE'nin eski sürümlerini destekleme
IE10'u desteklemek istiyorsanız PointerEvents
'ün tedarikçi firma ön ekleriyle başlayan sürümlerini ele almanız gerekir.
PointerEvents
desteğini kontrol etmek için genellikle window.PointerEvent
'ı ararsınız ancak IE10'da window.navigator.msPointerEnabled
'yi ararsınız.
Tedarikçi firma ön eklerine sahip etkinlik adları: 'MSPointerDown'
, 'MSPointerUp'
ve 'MSPointerMove'
.
Aşağıdaki örnekte, desteği nasıl kontrol edeceğinizi ve etkinlik adlarını nasıl değiştireceğinizi görebilirsiniz.
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 güncelleme makalesine göz atın.
Referans
Dokunma durumları için sözde sınıflar
Dokunma etkinlikleriyle ilgili kesin referansı W3C Dokunma Etkinlikleri sayfasında bulabilirsiniz.
Dokunma, fare ve işaretçi etkinlikleri
Aşağıdaki etkinlikler, uygulamanıza yeni hareketler eklemenin 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'teki Safari, etkin durumunu varsayılan olarak uygulamaz. Bu durumun çalışması için doküman gövdesine veya her öğeye bir touchstart
etkinlik dinleyicisi eklemeniz gerekir.
Bunu yalnızca iOS cihazlarda çalışacak şekilde bir kullanıcı aracısı testinin arkasından yapmanız gerekir.
Body'ye dokunma başlangıcı eklemek, DOM'daki tüm öğelere uygulanması avantajına sahiptir ancak sayfayı kaydırırken performans sorunları yaşanabilir.
window.onload = function() {
if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
document.body.addEventListener('touchstart', function() {}, false);
}
};
Alternatif olarak, dokunma başlangıcı dinleyicilerini sayfadaki tüm etkileşimli öğelere ekleyerek performansla ilgili endişelerin bir kısmını azaltabilirsiniz.
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);
}
}
};