صفحههای لمسی در دستگاههای بیشتری از تلفنها گرفته تا صفحههای رومیزی در دسترس هستند. برنامه شما باید به روش های شهودی و زیبا به لمس آنها پاسخ دهد.
صفحههای لمسی در دستگاههای بیشتری از تلفنها گرفته تا صفحههای رومیزی در دسترس هستند. هنگامی که کاربران شما تصمیم می گیرند با رابط کاربری شما تعامل داشته باشند، برنامه شما باید به روش های شهودی به لمس آنها پاسخ دهد.
به حالت های عنصر پاسخ دهید
آیا تا به حال روی یک عنصر در یک صفحه وب کلیک یا لمس کرده اید و سوال کرده اید که آیا سایت واقعاً آن را شناسایی کرده است؟
صرفاً تغییر رنگ یک عنصر در حین لمس یا تعامل کاربران با بخشهایی از رابط کاربری شما، اطمینان اولیه ای از کارکرد سایت شما میدهد. این نه تنها ناامیدی را کاهش میدهد، بلکه میتواند حسی سریع و پاسخگو به شما بدهد.
عناصر DOM می توانند هر یک از حالت های زیر را به ارث ببرند: پیش فرض، فوکوس، شناور و فعال. برای تغییر رابط کاربری خود برای هر یک از این حالتها، باید استایلها را برای کلاسهای شبه زیر اعمال کنیم :hover
، :focus
و :active
مانند شکل زیر:
.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;
}
در اکثر مرورگرهای تلفن همراه، حالت های ماوس و/یا فوکوس روی یک عنصر پس از ضربه زدن اعمال می شود.
به دقت در نظر بگیرید که چه سبک هایی را تنظیم می کنید و پس از اتمام لمس کاربر چگونه به نظر می رسد.
سرکوب سبک های پیش فرض مرورگر
هنگامی که استایل ها را برای حالت های مختلف اضافه می کنید، متوجه خواهید شد که اکثر مرورگرها سبک های خود را در پاسخ به لمس کاربر پیاده سازی می کنند. این بیشتر به این دلیل است که وقتی دستگاه های تلفن همراه برای اولین بار راه اندازی شدند، تعدادی از سایت ها حالت :active
را نداشتند. در نتیجه، بسیاری از مرورگرها رنگ یا سبک هایلایت اضافی را برای ارائه بازخورد به کاربر اضافه کردند.
اکثر مرورگرها از ویژگی outline
CSS برای نمایش یک حلقه در اطراف یک عنصر زمانی که یک عنصر متمرکز است استفاده می کنند. شما می توانید آن را با:
.btn:focus {
outline: 0;
/* Add replacement focus styling here (i.e. border) */
}
سافاری و کروم یک رنگ برجسته ضربه اضافه می کنند که می توان با ویژگی -webkit-tap-highlight-color
CSS از آن جلوگیری کرد:
/* Webkit / Chrome Specific CSS to remove tap
highlight color */
.btn {
-webkit-tap-highlight-color: transparent;
}
اینترنت اکسپلورر در ویندوز فون رفتار مشابهی دارد، اما از طریق یک متا تگ سرکوب می شود:
<meta name="msapplication-tap-highlight" content="no">
فایرفاکس دو عارضه جانبی دارد.
کلاس شبه -moz-focus-inner
، که یک طرح کلی روی عناصر قابل لمس اضافه می کند، می توانید با تنظیم border: 0
حذف کنید.
اگر از عنصر <button>
در فایرفاکس استفاده میکنید، یک گرادیان اعمال میشود که میتوانید با تنظیم background-image: none
.
/* Firefox Specific CSS to remove button
differences and focus ring */
.btn {
background-image: none;
}
.btn::-moz-focus-inner {
border: 0;
}
غیرفعال کردن انتخاب کاربر
هنگامی که رابط کاربری خود را ایجاد می کنید، ممکن است سناریوهایی وجود داشته باشد که بخواهید کاربران با عناصر شما تعامل داشته باشند، اما می خواهید رفتار پیش فرض انتخاب متن با فشار طولانی یا کشیدن ماوس روی رابط کاربری خود را سرکوب کنید.
شما می توانید این کار را با ویژگی CSS user-select
انجام دهید، اما مراقب باشید که انجام این کار در محتوا می تواند برای کاربران بسیار آزاردهنده باشد اگر بخواهند متن موجود در عنصر را انتخاب کنند. بنابراین مطمئن شوید که آن را با احتیاط و کم استفاده کنید.
/* Example: Disable selecting text on a paragraph element: */
p.disable-text-selection {
user-select: none;
}
ژست های سفارشی را پیاده سازی کنید
اگر ایده ای برای تعاملات و حرکات سفارشی برای سایت خود دارید، دو موضوع وجود دارد که باید در نظر داشته باشید:
- نحوه پشتیبانی از همه مرورگرها
- چگونه نرخ فریم خود را بالا نگه دارید
در این مقاله، دقیقاً به این موضوعات مربوط به APIهایی که برای ضربه زدن به همه مرورگرها باید از آنها پشتیبانی کنیم و سپس نحوه استفاده کارآمد از این رویدادها را توضیح خواهیم داد.
بسته به کاری که میخواهید ژست شما انجام دهد، احتمالاً میخواهید کاربر در یک زمان با یک عنصر تعامل داشته باشد یا میخواهید که بتواند همزمان با چندین عنصر تعامل داشته باشد.
ما قصد داریم در این مقاله دو نمونه را بررسی کنیم که هر دو نشان دهنده پشتیبانی از همه مرورگرها و نحوه بالا نگه داشتن نرخ فریم هستند.
مثال اول به کاربر اجازه می دهد تا با یک عنصر تعامل داشته باشد. در این مورد ممکن است بخواهید همه رویدادهای لمسی به آن یک عنصر داده شود، تا زمانی که ژست در ابتدا روی خود عنصر شروع شده باشد. به عنوان مثال، حرکت انگشت از عنصر قابل کشیدن انگشت میتواند همچنان عنصر را کنترل کند.
این مفید است زیرا انعطاف پذیری زیادی را برای کاربر فراهم می کند، اما محدودیتی را در مورد نحوه تعامل کاربر با رابط کاربری شما اعمال می کند.
با این حال، اگر از کاربران انتظار دارید که همزمان با چندین عنصر تعامل داشته باشند (با استفاده از لمس چندگانه)، باید لمس را به عنصر خاصی محدود کنید.
این برای کاربران انعطافپذیرتر است، اما منطق دستکاری UI را پیچیدهتر میکند و در برابر خطاهای کاربر مقاومت کمتری دارد.
شنوندگان رویداد را اضافه کنید
در کروم (نسخه 55 و جدیدتر)، Internet Explorer و Edge، PointerEvents
رویکرد توصیه شده برای اجرای حرکات سفارشی هستند.
در سایر مرورگرها، TouchEvents
و MouseEvents
رویکرد صحیحی هستند.
ویژگی عالی PointerEvents
این است که چندین نوع ورودی، از جمله رویدادهای ماوس، لمسی و قلم را در یک مجموعه از تماسها ادغام میکند. رویدادهایی که باید به آنها گوش دهید عبارتند از: pointerdown
, pointermove
, pointerup
و pointercancel
.
معادلهای موجود در سایر مرورگرها عبارتند از: touchstart
، touchmove
، touchend
و touchcancel
برای رویدادهای لمسی و اگر میخواهید همان حرکت را برای ورودی ماوس پیادهسازی کنید، باید mousedown
، mousemove
و mouseup
پیادهسازی کنید.
اگر درباره رویدادهایی که باید استفاده کنید سؤالی دارید، این جدول رویدادهای لمس، ماوس و اشاره گر را بررسی کنید.
استفاده از این رویدادها مستلزم فراخوانی متد addEventListener()
بر روی یک عنصر DOM، همراه با نام رویداد، یک تابع callback و یک بولی است. بولی تعیین می کند که آیا شما باید رویداد را قبل یا بعد از اینکه دیگر عناصر فرصتی برای گرفتن و تفسیر رویدادها داشته باشند، دریافت کنید. ( true
به این معنی است که شما رویداد را قبل از سایر عناصر می خواهید.)
در اینجا یک مثال از گوش دادن برای شروع یک تعامل است.
// 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);
}
تعامل تک عنصری را مدیریت کنید
در قطعه کوتاه کد بالا، ما فقط شنونده رویداد شروع را برای رویدادهای ماوس اضافه کردیم. دلیل این امر این است که رویدادهای ماوس تنها زمانی فعال می شوند که مکان نما روی عنصری که شنونده رویداد به آن اضافه شده است، حرکت کند.
TouchEvents
یک حرکت را پس از شروع آن بدون توجه به جایی که لمس انجام میشود ردیابی میکند و PointerEvents
رویدادها را بدون توجه به جایی که لمس رخ میدهد پس از فراخوانی setPointerCapture
در عنصر DOM ردیابی میکند.
برای رویدادهای حرکت و پایان ماوس، شنوندگان رویداد را به روش شروع حرکت اضافه می کنیم و شنوندگان را به سند اضافه می کنیم، به این معنی که می تواند مکان نما را تا زمانی که ژست کامل شود ردیابی کند.
اقدامات انجام شده برای اجرای این امر عبارتند از:
- همه شنوندگان TouchEvent و PointerEvent را اضافه کنید. برای MouseEvents فقط رویداد شروع را اضافه کنید.
- در داخل بازگشت به تماس با اشاره شروع، رویدادهای حرکت و پایان ماوس را به سند متصل کنید. به این ترتیب تمام رویدادهای ماوس صرف نظر از اینکه رویداد روی عنصر اصلی رخ می دهد یا خیر دریافت می شود. برای PointerEvents باید
setPointerCapture()
روی عنصر اصلی خود فراخوانی کنیم تا همه رویدادهای بعدی را دریافت کنیم. سپس شروع ژست را کنترل کنید. - رویدادهای حرکت را مدیریت کنید.
- در رویداد پایانی، حرکت ماوس و پایان شنوندگان را از سند بردارید و ژست را پایان دهید.
در زیر قطعه ای از متد handleGestureStart()
ما آمده است که رویدادهای حرکت و پایان را به سند اضافه می کند:
// 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);
فراخوان پایانی که اضافه میکنیم handleGestureEnd()
است که شنوندههای انتقال و رویداد پایانی را از سند حذف میکند و وقتی ژست به این صورت تمام شد، نشانگر را آزاد میکند:
// 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);
با پیروی از این الگوی افزودن رویداد حرکت به سند، اگر کاربر شروع به تعامل با یک عنصر کرد و ژست خود را به خارج از عنصر حرکت داد، بدون توجه به اینکه در کجای صفحه هستند، به حرکت ماوس ادامه خواهیم داد، زیرا رویدادها از سند دریافت می شود.
این نمودار نشان میدهد که رویدادهای لمسی چه میکنند، زمانی که رویدادهای انتقال و پایان را به سند اضافه میکنیم، پس از شروع حرکت.
به لمس موثر پاسخ می دهد
اکنون که رویدادهای شروع و پایان را در نظر گرفته ایم، در واقع می توانیم به رویدادهای لمسی پاسخ دهیم.
برای هر یک از رویدادهای شروع و حرکت، می توانید به راحتی x
و y
از یک رویداد استخراج کنید.
مثال زیر با بررسی وجود targetTouches
بررسی میکند که آیا رویداد مربوط به TouchEvent
است یا خیر. اگر این کار را انجام داد، پس از اولین لمس، clientX
و clientY
را استخراج می کند. اگر رویداد یک PointerEvent
یا MouseEvent
باشد، clientX
و clientY
مستقیماً از خود رویداد استخراج میکند.
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
دارای سه لیست حاوی داده های لمسی است:
-
touches
: فهرستی از تمام لمسهای فعلی روی صفحه، صرفنظر از عنصر DOM که روی آن هستند. -
targetTouches
: فهرست لمسهایی که در حال حاضر روی عنصر DOM که رویداد به آن محدود شده است. -
changedTouches
: فهرست لمسهایی که تغییر کرده و منجر به اجرا شدن رویداد میشود.
در بیشتر موارد، targetTouches
هر آنچه را که نیاز دارید و می خواهید در اختیار شما قرار می دهد. (برای اطلاعات بیشتر در مورد این لیست ها به لیست های لمسی مراجعه کنید).
از requestAnimationFrame استفاده کنید
از آنجایی که تماسهای رویداد در رشته اصلی فعال میشوند، ما میخواهیم تا حد امکان کد کمتری را در تماسهای رویدادهایمان اجرا کنیم، نرخ فریم را بالا نگه داریم و از jank جلوگیری کنیم.
با استفاده از requestAnimationFrame()
فرصتی برای به روز رسانی رابط کاربری داریم درست قبل از اینکه مرورگر قصد ترسیم یک فریم را داشته باشد و به ما کمک می کند تا برخی از کارها را از تماس های رویداد خود خارج کنیم.
اگر با requestAnimationFrame()
آشنا نیستید، می توانید در اینجا اطلاعات بیشتری کسب کنید .
یک پیاده سازی معمولی ذخیره مختصات x
و y
از شروع و جابجایی رویدادها و درخواست یک فریم انیمیشن در داخل callback رویداد move است.
در نسخه ی نمایشی خود، موقعیت لمس اولیه را در handleGestureStart()
ذخیره می کنیم (به دنبال initialTouchPos
بگردید):
// 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()
موقعیت رویداد خود را قبل از درخواست یک فریم انیمیشن در صورت نیاز ذخیره میکند و تابع onAnimFrame()
ما را به عنوان callback ارسال میکند:
this.handleGestureMove = function (evt) {
evt.preventDefault();
if (!initialTouchPos) {
return;
}
lastTouchPos = getGesturePointFromEvent(evt);
if (rafPending) {
return;
}
rafPending = true;
window.requestAnimFrame(onAnimFrame);
}.bind(this);
مقدار onAnimFrame
تابعی است که با فراخوانی آن، رابط کاربری ما را تغییر میدهد تا آن را جابهجا کند. با ارسال این تابع به requestAnimationFrame()
، به مرورگر می گوییم که درست قبل از اینکه صفحه را به روز کند آن را فراخوانی کند (یعنی هر تغییری را در صفحه نقاشی کند).
در فراخوانی handleGestureMove()
ابتدا بررسی میکنیم که آیا rafPending
نادرست است، که نشان میدهد onAnimFrame()
توسط requestAnimationFrame()
از آخرین رویداد حرکت فراخوانی شده است یا خیر. این بدان معناست که ما فقط یک requestAnimationFrame()
داریم که در هر زمان منتظر اجراست.
وقتی فراخوانی onAnimFrame()
ما اجرا میشود، تبدیل را روی هر عنصری که میخواهیم منتقل کنیم، قبل از بهروزرسانی rafPending
روی false
قرار میدهیم، و به رویداد لمسی بعدی اجازه میدهیم یک فریم انیمیشن جدید درخواست کند.
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;
}
حرکات را با استفاده از اقدامات لمسی کنترل کنید
ویژگی CSS touch-action
به شما امکان می دهد رفتار لمس پیش فرض یک عنصر را کنترل کنید. در مثالهایمان، ما از touch-action: none
برای جلوگیری از انجام کاری توسط مرورگر با لمس کاربر، به ما امکان میدهد همه رویدادهای لمسی را رهگیری کنیم.
/* Pass all touches to javascript: */
button.custom-touch-logic {
touch-action: none;
}
استفاده از touch-action: none
تا حدی یک گزینه هسته ای نیستند زیرا از همه رفتارهای پیش فرض مرورگر جلوگیری می کنند. در بسیاری از موارد یکی از گزینه های زیر راه حل بهتری است.
touch-action
به شما امکان می دهد ژست های پیاده سازی شده توسط مرورگر را غیرفعال کنید. به عنوان مثال، IE10+ از حرکت دوبار ضربه زدن برای بزرگنمایی پشتیبانی می کند. با تنظیم یک touch-action
manipulation
از رفتار پیش فرض دو ضربه زدن جلوگیری می کنید.
این به شما این امکان را می دهد که خودتان یک ژست دوبار ضربه بزنید.
در زیر لیستی از مقادیر متداول touch-action
وجود دارد:
پشتیبانی از نسخه های قدیمی IE
اگر میخواهید از IE10 پشتیبانی کنید، باید نسخههای پیشوند فروشنده PointerEvents
را مدیریت کنید.
برای بررسی پشتیبانی از PointerEvents
معمولاً به دنبال window.PointerEvent
میگردید، اما در IE10، به دنبال window.navigator.msPointerEnabled
میگردید.
نام رویدادها با پیشوندهای فروشنده عبارتند از: 'MSPointerDown'
، 'MSPointerUp'
و 'MSPointerMove'
.
مثال زیر نحوه بررسی پشتیبانی و تغییر نام رویداد را به شما نشان می دهد.
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;
}
برای اطلاعات بیشتر، این مقاله بهروزرسانیهای مایکروسافت را بررسی کنید.
مرجع
کلاس های شبه برای حالت های لمسی
مرجع رویدادهای لمسی قطعی را میتوانید در اینجا پیدا کنید: رویدادهای لمسی W3C .
رویدادهای لمس، ماوس و اشاره گر
این رویدادها بلوک های ساختمانی برای اضافه کردن ژست های جدید به برنامه شما هستند:
لیست ها را لمس کنید
هر رویداد لمسی شامل سه ویژگی لیست است:
فعال کردن پشتیبانی حالت فعال در iOS
متأسفانه، Safari در iOS به طور پیشفرض حالت فعال را اعمال نمیکند، برای اینکه آن را به کار ببندید، باید یک شنونده رویداد touchstart
به بدنه سند یا به هر عنصر اضافه کنید.
شما باید این کار را پشت تست عامل کاربر انجام دهید تا فقط روی دستگاههای iOS اجرا شود.
افزودن یک شروع لمسی به بدنه این مزیت را دارد که برای همه عناصر موجود در DOM اعمال می شود، با این حال ممکن است هنگام پیمایش صفحه مشکلات عملکردی داشته باشد.
window.onload = function() {
if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
document.body.addEventListener('touchstart', function() {}, false);
}
};
جایگزین این است که شنوندگان شروع لمسی را به تمام عناصر قابل تعامل در صفحه اضافه کنید و برخی از نگرانی های عملکرد را کاهش دهید.
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);
}
}
};