vuetify
Version:
Vue Material Component Framework
144 lines (142 loc) • 6.14 kB
JavaScript
// Composables
import { useToggleScope } from "../../composables/toggleScope.js";
import { useVelocity } from "../../composables/touch.js"; // Utilities
import { computed, onBeforeUnmount, onMounted, onScopeDispose, shallowRef, watchEffect } from 'vue';
// Types
export function useTouch(_ref) {
let {
el,
isActive,
isTemporary,
width,
touchless,
position
} = _ref;
onMounted(() => {
window.addEventListener('touchstart', onTouchstart, {
passive: true
});
window.addEventListener('touchmove', onTouchmove, {
passive: false
});
window.addEventListener('touchend', onTouchend, {
passive: true
});
});
onBeforeUnmount(() => {
window.removeEventListener('touchstart', onTouchstart);
window.removeEventListener('touchmove', onTouchmove);
window.removeEventListener('touchend', onTouchend);
});
const isHorizontal = computed(() => ['left', 'right'].includes(position.value));
const {
addMovement,
endTouch,
getVelocity
} = useVelocity();
let maybeDragging = false;
const isDragging = shallowRef(false);
const dragProgress = shallowRef(0);
const offset = shallowRef(0);
let start;
function getOffset(pos, active) {
return (position.value === 'left' ? pos : position.value === 'right' ? document.documentElement.clientWidth - pos : position.value === 'top' ? pos : position.value === 'bottom' ? document.documentElement.clientHeight - pos : oops()) - (active ? width.value : 0);
}
function getProgress(pos) {
let limit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
const progress = position.value === 'left' ? (pos - offset.value) / width.value : position.value === 'right' ? (document.documentElement.clientWidth - pos - offset.value) / width.value : position.value === 'top' ? (pos - offset.value) / width.value : position.value === 'bottom' ? (document.documentElement.clientHeight - pos - offset.value) / width.value : oops();
return limit ? Math.max(0, Math.min(1, progress)) : progress;
}
function onTouchstart(e) {
if (touchless.value) return;
const touchX = e.changedTouches[0].clientX;
const touchY = e.changedTouches[0].clientY;
const touchZone = 25;
const inTouchZone = position.value === 'left' ? touchX < touchZone : position.value === 'right' ? touchX > document.documentElement.clientWidth - touchZone : position.value === 'top' ? touchY < touchZone : position.value === 'bottom' ? touchY > document.documentElement.clientHeight - touchZone : oops();
const inElement = isActive.value && (position.value === 'left' ? touchX < width.value : position.value === 'right' ? touchX > document.documentElement.clientWidth - width.value : position.value === 'top' ? touchY < width.value : position.value === 'bottom' ? touchY > document.documentElement.clientHeight - width.value : oops());
if (inTouchZone || inElement || isActive.value && isTemporary.value) {
start = [touchX, touchY];
offset.value = getOffset(isHorizontal.value ? touchX : touchY, isActive.value);
dragProgress.value = getProgress(isHorizontal.value ? touchX : touchY);
maybeDragging = offset.value > -20 && offset.value < 80;
endTouch(e);
addMovement(e);
}
}
function onTouchmove(e) {
const touchX = e.changedTouches[0].clientX;
const touchY = e.changedTouches[0].clientY;
if (maybeDragging) {
if (!e.cancelable) {
maybeDragging = false;
return;
}
const dx = Math.abs(touchX - start[0]);
const dy = Math.abs(touchY - start[1]);
const thresholdMet = isHorizontal.value ? dx > dy && dx > 3 : dy > dx && dy > 3;
if (thresholdMet) {
isDragging.value = true;
maybeDragging = false;
} else if ((isHorizontal.value ? dy : dx) > 3) {
maybeDragging = false;
}
}
if (!isDragging.value) return;
e.preventDefault();
addMovement(e);
const progress = getProgress(isHorizontal.value ? touchX : touchY, false);
dragProgress.value = Math.max(0, Math.min(1, progress));
if (progress > 1) {
offset.value = getOffset(isHorizontal.value ? touchX : touchY, true);
} else if (progress < 0) {
offset.value = getOffset(isHorizontal.value ? touchX : touchY, false);
}
}
function onTouchend(e) {
maybeDragging = false;
if (!isDragging.value) return;
addMovement(e);
isDragging.value = false;
const velocity = getVelocity(e.changedTouches[0].identifier);
const vx = Math.abs(velocity.x);
const vy = Math.abs(velocity.y);
const thresholdMet = isHorizontal.value ? vx > vy && vx > 400 : vy > vx && vy > 3;
if (thresholdMet) {
isActive.value = velocity.direction === ({
left: 'right',
right: 'left',
top: 'down',
bottom: 'up'
}[position.value] || oops());
} else {
isActive.value = dragProgress.value > 0.5;
}
}
const dragStyles = computed(() => {
return isDragging.value ? {
transform: position.value === 'left' ? `translateX(calc(-100% + ${dragProgress.value * width.value}px))` : position.value === 'right' ? `translateX(calc(100% - ${dragProgress.value * width.value}px))` : position.value === 'top' ? `translateY(calc(-100% + ${dragProgress.value * width.value}px))` : position.value === 'bottom' ? `translateY(calc(100% - ${dragProgress.value * width.value}px))` : oops(),
transition: 'none'
} : undefined;
});
useToggleScope(isDragging, () => {
const transform = el.value?.style.transform ?? null;
const transition = el.value?.style.transition ?? null;
watchEffect(() => {
el.value?.style.setProperty('transform', dragStyles.value?.transform || 'none');
el.value?.style.setProperty('transition', dragStyles.value?.transition || null);
});
onScopeDispose(() => {
el.value?.style.setProperty('transform', transform);
el.value?.style.setProperty('transition', transition);
});
});
return {
isDragging,
dragProgress,
dragStyles
};
}
function oops() {
throw new Error();
}
//# sourceMappingURL=touch.js.map