feat(h5): refine report layout, add finger-following tabs and optimize images
This commit is contained in:
+47
-130
@@ -126,39 +126,29 @@
|
||||
});
|
||||
|
||||
/* ─── 报告页状态重置(各维度卡回到第一个子 Tab、产品横滑归零) ─── */
|
||||
function setDetailTab(card, idx) {
|
||||
var detailSwipes = new WeakMap();
|
||||
function setDetailTab(card, idx, instant) {
|
||||
card.querySelectorAll('.dtab').forEach(function (t) {
|
||||
var selected = t.dataset.tab === idx;
|
||||
t.classList.toggle('active', selected);
|
||||
t.setAttribute('aria-selected', String(selected));
|
||||
t.setAttribute('tabindex', selected ? '0' : '-1');
|
||||
});
|
||||
card.querySelectorAll('.dpanel').forEach(function (p) { p.classList.toggle('active', p.dataset.panel === idx); });
|
||||
card.querySelectorAll('.dpanel').forEach(function (p) {
|
||||
var selected = p.dataset.panel === idx;
|
||||
p.classList.toggle('active', selected); p.setAttribute('aria-hidden', String(!selected)); p.inert = !selected;
|
||||
});
|
||||
var detail = detailSwipes.get(card);
|
||||
if (detail) { detail.swipe.go(Number(idx), instant); detail.height(Number(idx)); }
|
||||
}
|
||||
function resetReport() {
|
||||
document.querySelectorAll('.card-detail').forEach(function (c) { setDetailTab(c, '0'); });
|
||||
document.querySelectorAll('.card-detail').forEach(function (c) { setDetailTab(c, '0', true); });
|
||||
var ps = $('#prod-scroll');
|
||||
if (ps) ps.scrollLeft = 0;
|
||||
}
|
||||
|
||||
/* ─── 弹窗管理 ─── */
|
||||
var SCAN_CYCLE_MS = 1000, scanCycle = null, scanMask = null;
|
||||
function setScanPhoto(source, mirrored) {
|
||||
var image = $('#scan-photo');
|
||||
image.classList.toggle('mirror', !!mirrored);
|
||||
if (image.getAttribute('src') === source) return;
|
||||
image.onload = function () {
|
||||
// A delayed JPEG decode must still get one complete visible scan cycle.
|
||||
if ($('#p-analyzing').classList.contains('show')) restartScanCycle();
|
||||
};
|
||||
image.onerror = function () { image.removeAttribute('src'); };
|
||||
image.src = source;
|
||||
}
|
||||
function clearScanPhoto() {
|
||||
var image = $('#scan-photo');
|
||||
image.onload = image.onerror = null;
|
||||
image.removeAttribute('src'); image.classList.remove('mirror');
|
||||
}
|
||||
function restartScanCycle() {
|
||||
if (!scanCycle || scanCycle.done) return;
|
||||
var overlay = $('#p-analyzing'), cycle = scanCycle;
|
||||
@@ -222,7 +212,7 @@
|
||||
}
|
||||
function closeOverlay(id) {
|
||||
$('#' + id).classList.remove('show');
|
||||
if (id === 'p-analyzing') { stopScanCycle(); clearScanPhoto(); }
|
||||
if (id === 'p-analyzing') stopScanCycle();
|
||||
}
|
||||
|
||||
// 点遮罩空白处关闭(与各自关闭按钮同行为:隐私→回启动页、拍照引导→留在档案页)
|
||||
@@ -278,7 +268,7 @@
|
||||
{ img: 'assets/img/ui-20261009/skin-3-oily.png', name: '油性肌肤', desc: '全脸易出油,容易泛油光、毛孔明显' },
|
||||
{ img: 'assets/img/ui-20261009/skin-4-mixedoily.png', name: '混合偏油肌肤', desc: 'T区易出油,两颊相对清爽或轻微干燥' }
|
||||
];
|
||||
var skinIndex = 0;
|
||||
var skinIndex = 0, skinSwipe;
|
||||
var track = $('#skin-track'), dotsBox = $('#skin-dots');
|
||||
|
||||
SKINS.forEach(function (s, i) {
|
||||
@@ -297,8 +287,8 @@
|
||||
|
||||
function goSkin(i, persist) {
|
||||
skinIndex = Math.max(0, Math.min(SKINS.length - 1, i));
|
||||
track.classList.remove('dragging');
|
||||
track.style.transform = 'translateX(-' + skinIndex * 100 + '%)';
|
||||
if (skinSwipe) skinSwipe.go(skinIndex);
|
||||
else track.style.transform = 'translateX(-' + skinIndex * 100 + '%)';
|
||||
dotsBox.querySelectorAll('i').forEach(function (d, k) {
|
||||
d.classList.toggle('on', k === skinIndex);
|
||||
});
|
||||
@@ -312,74 +302,12 @@
|
||||
});
|
||||
goSkin(0);
|
||||
|
||||
// Follow the finger immediately, then snap with distance/flick intent.
|
||||
// pan-y and direction locking preserve normal vertical page scrolling.
|
||||
var drag = { id: null, horizontal: false, vertical: false, suppressUntil: 0 };
|
||||
var carousel = $('#skin-carousel');
|
||||
function cancelSkinDrag() {
|
||||
var id = drag.id;
|
||||
drag.id = null; drag.horizontal = false; drag.vertical = false;
|
||||
try { if (id != null && carousel.hasPointerCapture(id)) carousel.releasePointerCapture(id); } catch (_) {}
|
||||
goSkin(skinIndex, false);
|
||||
}
|
||||
carousel.addEventListener('pointerdown', function (e) {
|
||||
if (drag.id != null || e.isPrimary === false || e.button !== 0 || !carousel.clientWidth) return;
|
||||
try { carousel.setPointerCapture(e.pointerId); } catch (err) { /* 非活动指针时忽略,退化为普通监听 */ }
|
||||
drag.id = e.pointerId;
|
||||
drag.startX = e.clientX;
|
||||
drag.startY = e.clientY;
|
||||
drag.offset = track.getBoundingClientRect().left - carousel.getBoundingClientRect().left;
|
||||
drag.width = carousel.clientWidth;
|
||||
drag.lastX = e.clientX; drag.lastAt = e.timeStamp; drag.velocity = 0;
|
||||
drag.horizontal = false; drag.vertical = false;
|
||||
});
|
||||
carousel.addEventListener('pointermove', function (e) {
|
||||
if (e.pointerId !== drag.id || drag.vertical) return;
|
||||
var dx = e.clientX - drag.startX, dy = e.clientY - drag.startY;
|
||||
if (!drag.horizontal) {
|
||||
if (Math.abs(dy) > 8 && Math.abs(dy) >= Math.abs(dx)) { drag.vertical = true; return; }
|
||||
if (Math.abs(dx) <= 8 || Math.abs(dx) <= Math.abs(dy)) return;
|
||||
drag.horizontal = true; track.classList.add('dragging');
|
||||
}
|
||||
if (e.cancelable) e.preventDefault();
|
||||
var elapsed = e.timeStamp - drag.lastAt;
|
||||
if (elapsed > 0) drag.velocity = (e.clientX - drag.lastX) / elapsed;
|
||||
drag.lastX = e.clientX; drag.lastAt = e.timeStamp;
|
||||
var offset = drag.offset + dx, min = -(SKINS.length - 1) * drag.width;
|
||||
if (offset > 0) offset *= .25;
|
||||
else if (offset < min) offset = min + (offset - min) * .25;
|
||||
track.style.transform = 'translate3d(' + offset + 'px,0,0)';
|
||||
});
|
||||
carousel.addEventListener('pointerup', function (e) {
|
||||
if (e.pointerId !== drag.id) return;
|
||||
var dx = e.clientX - drag.startX;
|
||||
if (drag.horizontal) {
|
||||
var start = Math.round(-drag.offset / drag.width), target = start;
|
||||
var speed = e.timeStamp - drag.lastAt > 100 ? 0 : drag.velocity;
|
||||
if (Math.abs(dx) >= Math.min(50, drag.width * .2) || (Math.abs(dx) > 12 && Math.abs(speed) > .4)) {
|
||||
target = Math.round(-(drag.offset + dx) / drag.width);
|
||||
if (target === start) target += (Math.abs(speed) > .4 ? speed : dx) < 0 ? 1 : -1;
|
||||
}
|
||||
goSkin(target);
|
||||
drag.suppressUntil = Date.now() + 350;
|
||||
}
|
||||
drag.id = null;
|
||||
drag.horizontal = false; drag.vertical = false;
|
||||
});
|
||||
carousel.addEventListener('pointercancel', function (e) {
|
||||
if (e.pointerId === drag.id) cancelSkinDrag();
|
||||
});
|
||||
carousel.addEventListener('lostpointercapture', function (e) {
|
||||
if (e.pointerId === drag.id) cancelSkinDrag();
|
||||
});
|
||||
carousel.addEventListener('dragstart', function (e) { e.preventDefault(); });
|
||||
carousel.addEventListener('click', function (e) {
|
||||
if (Date.now() < drag.suppressUntil) { e.preventDefault(); e.stopPropagation(); }
|
||||
}, true);
|
||||
skinSwipe = SwipeTrack.create(carousel, track, { count: SKINS.length, onSelect: goSkin });
|
||||
function cancelSkinDrag() { skinSwipe.cancel(); }
|
||||
carousel.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') { e.preventDefault(); goSkin(skinIndex + (e.key === 'ArrowRight' ? 1 : -1)); }
|
||||
});
|
||||
window.addEventListener('resize', cancelSkinDrag);
|
||||
|
||||
/* ─── 档案选择控件:出生年月 / 性别 / 省份城市,统一用 mobile-select 底部滚轮(选中值写入同名 hidden input) ─── */
|
||||
var NOW_Y = new Date().getFullYear();
|
||||
@@ -461,7 +389,7 @@
|
||||
var IS_MOCK = /[?&]mock=1/.test(location.search); // mock:不加载 face-api、不跑检测,analyze 直返内置样本
|
||||
var camVideo = $('#cam-video'), camFile = $('#cam-file'), albumFile = $('#album-file');
|
||||
var camStream = null, camLive = false, camFallback = false, camSeqCam = 0; // camSeqCam:相机启动令牌,防止迟到的授权回调泄漏摄像头
|
||||
var photos = [], scanPhotoMirrored = false; // 三连拍(dataURL),拍摄中与 CAM_STEPS 同序;送检前经 photosByView 重排为 [正脸, 左脸, 右脸]
|
||||
var photos = []; // 三连拍(dataURL),拍摄中与 CAM_STEPS 同序;送检前经 photosByView 重排为 [正脸, 左脸, 右脸]
|
||||
var captureDiagnostics = [], holdState = null;
|
||||
|
||||
// 约束逐级放宽:facingMode 裸值在部分 Android 抛 OverconstrainedError,先 ideal 再退 video:true,最后才降级
|
||||
@@ -587,7 +515,7 @@
|
||||
hideCamStatus();
|
||||
shutterLock = false;
|
||||
if (err) { toast(err); voice.play(VOICE.fail, { cut: true }); return; } // 不合格:停在本步重拍
|
||||
onPhotoCaptured(url, false); // Files already have their own camera orientation.
|
||||
onPhotoCaptured(url);
|
||||
});
|
||||
};
|
||||
img.onerror = function () { toast('照片读取失败,请重新选择'); };
|
||||
@@ -1028,13 +956,9 @@
|
||||
var shutterLock = false; // 快门防连点锁:流程进行中直接忽略
|
||||
|
||||
// 照片就绪(已通过校验)后推进流程:存图→「拍摄完成」→300ms 过渡→下一步;三步严格按顺序,本步合格才进下一步
|
||||
function onPhotoCaptured(dataUrl, mirrored) {
|
||||
function onPhotoCaptured(dataUrl) {
|
||||
shutterLock = true; // 推进窗口上锁(停顿期间不检测、不接受快门),直到下一步/送检
|
||||
photos[camStep] = dataUrl;
|
||||
if (camStep === 0) {
|
||||
scanPhotoMirrored = !!mirrored;
|
||||
setScanPhoto(dataUrl, scanPhotoMirrored); // Decode the front photo before the waiting screen opens.
|
||||
}
|
||||
voice.play(VOICE.shot, { cut: true }); // 「拍摄完成」:已拍下,本步未播完的引导作废
|
||||
var seq = camSeq, hasNext = camStep < CAM_STEPS.length - 1;
|
||||
if (hasNext) {
|
||||
@@ -1072,7 +996,7 @@
|
||||
var fill = $('#cam-fill');
|
||||
fill.classList.add('on'); // .on 无过渡,瞬间点亮
|
||||
setTimeout(function () {
|
||||
var shot = captureFromVideo(), mirrored = effMirror();
|
||||
var shot = captureFromVideo();
|
||||
setTimeout(function () {
|
||||
fill.classList.remove('on'); // 移除 .on 恢复过渡,淡出
|
||||
capturing = false;
|
||||
@@ -1085,7 +1009,7 @@
|
||||
shutterLock = false; holdState = null; okSince = 0;
|
||||
setHint(error, false); armNudge(); return;
|
||||
}
|
||||
onPhotoCaptured(shot, mirrored);
|
||||
onPhotoCaptured(shot);
|
||||
});
|
||||
}, 300);
|
||||
}, 300);
|
||||
@@ -1095,7 +1019,6 @@
|
||||
function submitPhotos(seq) {
|
||||
camStep = 0;
|
||||
photos = photosByView(photos);
|
||||
setScanPhoto(photos[0], scanPhotoMirrored);
|
||||
openOverlay('p-analyzing');
|
||||
voice.play(VOICE.analyzing); // 「正在生成您的肌肤报告」
|
||||
// 档案与检测一起上报(后台记录用):年龄非法值服务端按 null 处理
|
||||
@@ -1567,38 +1490,34 @@
|
||||
});
|
||||
|
||||
/* ─── 报告页:每张维度卡各自的子 Tab;点检测图放大,再点返回 ─── */
|
||||
document.querySelectorAll('.card-detail').forEach(function (card) {
|
||||
document.querySelectorAll('.card-detail').forEach(function (card, group) {
|
||||
card.querySelector('.detail-tabs').setAttribute('role', 'tablist');
|
||||
card.querySelectorAll('.dtab').forEach(function (tab) { tab.setAttribute('role', 'tab'); });
|
||||
setDetailTab(card, '0');
|
||||
var gesture = null, suppressImageClick = false;
|
||||
card.addEventListener('pointerdown', function (e) {
|
||||
if (e.isPrimary === false || (e.pointerType === 'mouse' && e.button !== 0)) return;
|
||||
gesture = { id: e.pointerId, x: e.clientX, y: e.clientY, horizontal: false };
|
||||
suppressImageClick = false;
|
||||
var tabs = Array.from(card.querySelectorAll('.dtab')), panels = Array.from(card.querySelectorAll('.dpanel'));
|
||||
var viewport = card.querySelector('.detail-viewport'), track = card.querySelector('.detail-panels'), position = 0;
|
||||
tabs.forEach(function (tab, i) {
|
||||
tab.setAttribute('role', 'tab'); tab.id = 'detail-tab-' + group + '-' + i;
|
||||
tab.setAttribute('aria-controls', 'detail-panel-' + group + '-' + i);
|
||||
panels[i].id = 'detail-panel-' + group + '-' + i;
|
||||
panels[i].setAttribute('role', 'tabpanel'); panels[i].setAttribute('aria-labelledby', tab.id);
|
||||
});
|
||||
card.addEventListener('pointermove', function (e) {
|
||||
if (!gesture || gesture.id !== e.pointerId) return;
|
||||
var dx = e.clientX - gesture.x, dy = e.clientY - gesture.y;
|
||||
if (Math.abs(dx) > 12 && Math.abs(dx) > Math.abs(dy) * 1.4) {
|
||||
gesture.horizontal = true;
|
||||
if (e.cancelable) e.preventDefault();
|
||||
}
|
||||
});
|
||||
card.addEventListener('pointerup', function (e) {
|
||||
if (!gesture || gesture.id !== e.pointerId) return;
|
||||
var dx = e.clientX - gesture.x, dy = e.clientY - gesture.y;
|
||||
if (gesture.horizontal && Math.abs(dx) >= 45 && Math.abs(dx) > Math.abs(dy) * 1.4) {
|
||||
var tabs = Array.from(card.querySelectorAll('.dtab'));
|
||||
var active = tabs.findIndex(function (tab) { return tab.classList.contains('active'); });
|
||||
var next = Math.max(0, Math.min(tabs.length - 1, active + (dx < 0 ? 1 : -1)));
|
||||
setDetailTab(card, tabs[next].dataset.tab);
|
||||
suppressImageClick = true;
|
||||
}
|
||||
gesture = null;
|
||||
});
|
||||
card.addEventListener('pointercancel', function () { gesture = null; });
|
||||
card.addEventListener('dragstart', function (e) { if (e.target.closest('.ditem-img')) e.preventDefault(); });
|
||||
function height(at) {
|
||||
position = at;
|
||||
var lower = Math.floor(at), upper = Math.min(panels.length - 1, Math.ceil(at)), ratio = at - lower;
|
||||
viewport.style.height = (panels[lower].offsetHeight * (1 - ratio) + panels[upper].offsetHeight * ratio) + 'px';
|
||||
}
|
||||
var swipe = SwipeTrack.create(viewport, track, { count: panels.length, gestureTarget: card,
|
||||
onMove: height, onCancel: height, onSelect: function (i) { setDetailTab(card, String(i)); } });
|
||||
detailSwipes.set(card, { swipe: swipe, height: height });
|
||||
setDetailTab(card, '0', true);
|
||||
if (typeof ResizeObserver !== 'undefined') {
|
||||
var observer = new ResizeObserver(function () { height(position); });
|
||||
panels.forEach(function (panel) { observer.observe(panel); });
|
||||
} else {
|
||||
window.addEventListener('resize', function () { height(position); });
|
||||
card.addEventListener('load', function () { height(position); }, true);
|
||||
if (document.fonts) document.fonts.ready.then(function () { height(position); });
|
||||
}
|
||||
card.querySelectorAll('.ditem-img').forEach(function (image) { image.draggable = false; });
|
||||
card.addEventListener('keydown', function (e) {
|
||||
var tab = e.target.closest('.dtab');
|
||||
if (!tab || !['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) return;
|
||||
@@ -1608,11 +1527,10 @@
|
||||
setDetailTab(card, tabs[index].dataset.tab); tabs[index].focus({ preventScroll: true });
|
||||
});
|
||||
card.addEventListener('click', function (e) {
|
||||
if (suppressImageClick) { suppressImageClick = false; e.preventDefault(); return; }
|
||||
var btn = e.target.closest('.dtab');
|
||||
if (btn) { setDetailTab(card, btn.dataset.tab); return; }
|
||||
var img = e.target.closest('.ditem-img');
|
||||
if (img && img.src && !img.hidden) {
|
||||
if (img && img.src && !img.hidden && img.closest('.dpanel').classList.contains('active')) {
|
||||
$('#zoom-img').src = img.src;
|
||||
openOverlay('p-zoom');
|
||||
history.pushState({ screen: currentScreen, overlay: 'p-zoom' }, '');
|
||||
@@ -1653,7 +1571,6 @@
|
||||
camSeq++; // 使旧的快门/分析回调失效
|
||||
camStep = 0;
|
||||
photos = []; // 新一轮三连拍(从 startCamera 挪出:后台恢复续拍时保留已拍)
|
||||
scanPhotoMirrored = false; clearScanPhoto();
|
||||
captureDiagnostics = [];
|
||||
shutterLock = false; // 重新进入流程,保险解锁
|
||||
showScreen('s-cam', 'push');
|
||||
|
||||
+3
-3
@@ -10,9 +10,9 @@
|
||||
['sens', '泛红', 'stab', 'sens'], ['acne', '痘痘', 'stab', 'acne']
|
||||
];
|
||||
var products = [
|
||||
{ tier: 1, match: '25+束紧精华', title: "1'me 主场精华 I", name: '以我本然有光蓄活', image: 'assets/img/ui-20261012/product-25.webp' },
|
||||
{ tier: 2, match: '35+支撑精华', title: "1'me 主场精华 II", name: '以我自在有势挺括', image: 'assets/img/ui-20261012/product-35.webp' },
|
||||
{ tier: 3, match: '50+重塑精华', title: "1'me 主场精华 III", name: '以我从容有态构塑', image: 'assets/img/ui-20261012/product-50.webp' }
|
||||
{ tier: 1, match: '25+束紧精华', title: "1'me 主场精华 I", name: '以我本然有光蓄活', image: 'assets/img/ui-20261012/product-25.jpg' },
|
||||
{ tier: 2, match: '35+支撑精华', title: "1'me 主场精华 II", name: '以我自在有势挺括', image: 'assets/img/ui-20261012/product-35.jpg' },
|
||||
{ tier: 3, match: '50+重塑精华', title: "1'me 主场精华 III", name: '以我从容有态构塑', image: 'assets/img/ui-20261012/product-50.jpg' }
|
||||
];
|
||||
var levelMap = { '预警': '改善', '改善': '良好', '良好': '优秀' };
|
||||
var dimNames = { firm: '紧塑力', vita: '透亮感', stab: '稳韧力' };
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/* Shared finger-following track for skin choices and report detail panels. */
|
||||
window.SwipeTrack = (function () {
|
||||
'use strict';
|
||||
function create(viewport, track, options) {
|
||||
var target = options.gestureTarget || viewport, index = 0;
|
||||
var drag = { id: null, horizontal: false, vertical: false }, suppressUntil = 0;
|
||||
function release() {
|
||||
var id = drag.id;
|
||||
drag.id = null; drag.horizontal = drag.vertical = false;
|
||||
viewport.classList.remove('dragging'); track.classList.remove('dragging');
|
||||
try { if (id != null && target.hasPointerCapture(id)) target.releasePointerCapture(id); } catch (_) {}
|
||||
}
|
||||
function go(next, instant) {
|
||||
index = Math.max(0, Math.min(options.count - 1, next));
|
||||
release();
|
||||
if (instant) track.style.transition = 'none';
|
||||
track.style.transform = 'translateX(-' + index * 100 + '%)';
|
||||
if (instant) { void track.offsetWidth; track.style.transition = ''; }
|
||||
}
|
||||
function cancel() { go(index, true); if (options.onCancel) options.onCancel(index); }
|
||||
target.addEventListener('pointerdown', function (e) {
|
||||
if (drag.id != null || e.isPrimary === false || e.button !== 0 || !viewport.clientWidth) return;
|
||||
suppressUntil = 0; // A fresh tap is intentional, not the preceding drag's click.
|
||||
drag.id = e.pointerId; drag.startX = e.clientX; drag.startY = e.clientY;
|
||||
drag.offset = track.getBoundingClientRect().left - viewport.getBoundingClientRect().left;
|
||||
drag.width = viewport.clientWidth; drag.lastX = e.clientX; drag.lastAt = e.timeStamp; drag.velocity = 0;
|
||||
drag.horizontal = drag.vertical = false;
|
||||
// Capture only after direction lock: an ordinary image tap must still zoom.
|
||||
});
|
||||
target.addEventListener('pointermove', function (e) {
|
||||
if (e.pointerId !== drag.id || drag.vertical) return;
|
||||
var dx = e.clientX - drag.startX, dy = e.clientY - drag.startY;
|
||||
if (!drag.horizontal) {
|
||||
if (Math.abs(dy) > 8 && Math.abs(dy) >= Math.abs(dx)) { drag.vertical = true; return; }
|
||||
if (Math.abs(dx) <= 8 || Math.abs(dx) <= Math.abs(dy)) return;
|
||||
drag.horizontal = true; viewport.classList.add('dragging'); track.classList.add('dragging');
|
||||
try { target.setPointerCapture(e.pointerId); } catch (_) {}
|
||||
}
|
||||
if (e.cancelable) e.preventDefault();
|
||||
var elapsed = e.timeStamp - drag.lastAt;
|
||||
if (elapsed > 0) drag.velocity = (e.clientX - drag.lastX) / elapsed;
|
||||
drag.lastX = e.clientX; drag.lastAt = e.timeStamp;
|
||||
var offset = drag.offset + dx, min = -(options.count - 1) * drag.width;
|
||||
if (offset > 0) offset *= .25;
|
||||
else if (offset < min) offset = min + (offset - min) * .25;
|
||||
track.style.transform = 'translate3d(' + offset + 'px,0,0)';
|
||||
if (options.onMove) options.onMove(Math.max(0, Math.min(options.count - 1, -offset / drag.width)));
|
||||
});
|
||||
window.addEventListener('pointerup', function (e) {
|
||||
if (e.pointerId !== drag.id) return;
|
||||
var horizontal = drag.horizontal, dx = e.clientX - drag.startX, next = index;
|
||||
if (horizontal) {
|
||||
var start = Math.round(-drag.offset / drag.width);
|
||||
next = start;
|
||||
var speed = e.timeStamp - drag.lastAt > 100 ? 0 : drag.velocity;
|
||||
if (Math.abs(dx) >= Math.min(50, drag.width * .2) || (Math.abs(dx) > 12 && Math.abs(speed) > .4)) {
|
||||
next = Math.round(-(drag.offset + dx) / drag.width);
|
||||
if (next === start) next += (Math.abs(speed) > .4 ? speed : dx) < 0 ? 1 : -1;
|
||||
}
|
||||
suppressUntil = Date.now() + 350;
|
||||
}
|
||||
go(next);
|
||||
if (horizontal && options.onSelect) options.onSelect(index);
|
||||
});
|
||||
window.addEventListener('pointercancel', function (e) { if (e.pointerId === drag.id) cancel(); });
|
||||
target.addEventListener('lostpointercapture', function (e) {
|
||||
// Touch starts with implicit capture on an image/button. Its bubbled loss
|
||||
// when we take horizontal capture is not a cancellation of this track.
|
||||
if (e.target === target && e.pointerId === drag.id) cancel();
|
||||
});
|
||||
target.addEventListener('dragstart', function (e) { e.preventDefault(); });
|
||||
target.addEventListener('click', function (e) {
|
||||
if (Date.now() < suppressUntil) { e.preventDefault(); e.stopPropagation(); }
|
||||
}, true);
|
||||
window.addEventListener('resize', cancel);
|
||||
go(0, true);
|
||||
return { go: go, cancel: cancel, isDragging: function () { return drag.horizontal; } };
|
||||
}
|
||||
return { create: create };
|
||||
})();
|
||||
Reference in New Issue
Block a user