在實現拖放功能時,我考慮了兩種主要方法:一是使用原生的 HTML5 拖放 API,另一是利用 touchstart 觸碰事件。綜合考慮後,我選擇了使用觸碰事件,主要因為 HTML5 的拖放 API 直到近年才獲得較為廣泛的支援。
但我這裡仍想對補充一下 拖放 API 的寫法。
方法:
這個程式碼示例提供了一個基本但完整的拖放互動的模型,包括從一個列表拖動項目並將其放置到另一個列表中。
與拖放 API 的差異是我將 touchstart 跟 touchend 事件綁在同一個元素上。然後用 touchmove 的事件來抓取移動位置是否是在放置的範圍裡,或說是來決定放置位置。
拖曳開始
const handleClueCardTouch = (cardIndex, ev) => {
//為防止 iOS safari 觸發重新整理頁面
ev.preventDefault();
//當開始拖曳就把一開始教學動畫取消
isShowTip.value = false;
//抓取最上層的作答卡片並初始化卡片跟手指的位置
currentClueCardEl.value = clueCardEl.value[cardIndex];
document.body.append(currentClueCardEl.value);
setCurrentClueCardMove(ev.touches[0].pageX, ev.touches[0].pageY - currentClueCardEl.value.offsetHeight / 2);
//開始跟著手指移動
document.addEventListener('touchmove', handleClueCardMove);
};
拖曳進行中
const handleClueCardMove = (ev) => {
ev.preventDefault();
//純錯的更新移動位置
if (isMobile()) {
setCurrentClueCardMove(ev.touches ? ev.touches[0]?.pageX : ev.pageX, (ev.touches ? ev.touches[0]?.pageY : ev.pageY) - currentClueCardEl.value.offsetHeight / 2);
} else {
setCurrentClueCardMove(ev.pageX, ev.pageY - currentClueCardEl.value.offsetHeight / 2);
}
//另外拆出判斷作答位置的邏輯
handleAnswerProcess();
};
放掉拖曳
const handleClueCardTouchOff = async (cardIndex, ev) => {
ev.preventDefault();
let isCurrentAnswerCorrect = false;
if (isShowHint.value) {
// 邏輯 略 如果有在作答區域內就進一步判斷卡片放置作答位置
} else {
// 如果作答卡片不在作答位置,就將卡片放回原位
setCurrentClueCardMove(0, 0);
clueCardContainerEl.value.append(currentClueCardEl.value);
}
// 無論如何收合拉伸的時間軸
handleTimelineContainerExtend(false);
// 移除監聽事件
document.removeEventListener('touchmove', handleClueCardMove);
};