fix(h5): reduce report swipe conflicts and limit edge overscroll
This commit is contained in:
+6
-1
@@ -1513,12 +1513,17 @@
|
||||
panels[i].setAttribute('role', 'tabpanel'); panels[i].setAttribute('aria-labelledby', tab.id);
|
||||
});
|
||||
function height(at) {
|
||||
// Fonts/images may resize while dragging. Keep the current card height
|
||||
// until release so the rest of the report does not move under the finger.
|
||||
if (swipe && swipe.isDragging()) return;
|
||||
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)); } });
|
||||
gestureSelector: '.viz-frame', startThreshold: 12, directionRatio: 1.5, maxOverscroll: 16,
|
||||
onStart: function () { viewport.style.height = viewport.getBoundingClientRect().height + 'px'; },
|
||||
onCancel: height, onSelect: function (i) { setDetailTab(card, String(i)); } });
|
||||
detailSwipes.set(card, { swipe: swipe, height: height });
|
||||
setDetailTab(card, '0', true);
|
||||
if (typeof ResizeObserver !== 'undefined') {
|
||||
|
||||
+12
-5
@@ -21,6 +21,7 @@ window.SwipeTrack = (function () {
|
||||
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.
|
||||
if (options.gestureSelector && (!e.target.closest || !e.target.closest(options.gestureSelector))) return;
|
||||
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;
|
||||
@@ -31,9 +32,14 @@ window.SwipeTrack = (function () {
|
||||
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');
|
||||
var threshold = options.startThreshold || 8, ratio = options.directionRatio || 1;
|
||||
if (Math.max(Math.abs(dx), Math.abs(dy)) <= threshold) return;
|
||||
// Ambiguous diagonal movement belongs to page scrolling. Once locked,
|
||||
// neither direction can steal this gesture before the finger lifts.
|
||||
if (Math.abs(dy) * ratio >= Math.abs(dx)) { drag.vertical = true; return; }
|
||||
drag.horizontal = true;
|
||||
if (options.onStart) options.onStart(index);
|
||||
viewport.classList.add('dragging'); track.classList.add('dragging');
|
||||
try { target.setPointerCapture(e.pointerId); } catch (_) {}
|
||||
}
|
||||
if (e.cancelable) e.preventDefault();
|
||||
@@ -41,8 +47,9 @@ window.SwipeTrack = (function () {
|
||||
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;
|
||||
var edgeLimit = options.maxOverscroll == null ? Infinity : options.maxOverscroll;
|
||||
if (offset > 0) offset = Math.min(edgeLimit, offset * .25);
|
||||
else if (offset < min) offset = min - Math.min(edgeLimit, (min - offset) * .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)));
|
||||
});
|
||||
|
||||
@@ -65,18 +65,30 @@ try {
|
||||
const touch = (type, x, y, timestamp) => cdp.send('Input.dispatchTouchEvent', {
|
||||
type, ...(timestamp ? { timestamp } : {}), touchPoints: /touchEnd|touchCancel/.test(type) ? [] : [{ x, y, id: 1 }]
|
||||
});
|
||||
async function drag(card, dx, { dy = 0, cancel = false, pause = 0, flick = false, verify = false } = {}) {
|
||||
const frame = card.locator('.dpanel.active .viz-frame');
|
||||
async function drag(card, dx, { dy = 0, cancel = false, pause = 0, flick = false, verify = false, text = false } = {}) {
|
||||
const frame = card.locator('.dpanel.active ' + (text ? '.ditem-text' : '.viz-frame'));
|
||||
await frame.scrollIntoViewIfNeeded(); await page.waitForTimeout(400);
|
||||
const box = await frame.boundingBox(), x = box.x + box.width / 2, y = Math.min(500, box.y + box.height / 2);
|
||||
const box = await frame.boundingBox(), x = box.x + box.width / 2, y = box.y + box.height / 2;
|
||||
const before = await offset(card), previous = await selected(card), now = flick ? Date.now() / 1000 : null;
|
||||
const scrollBefore = await page.locator('#s-report').evaluate(el => el.scrollTop);
|
||||
const beforeHeight = await card.locator('.detail-viewport').evaluate(el => el.getBoundingClientRect().height);
|
||||
await touch('touchStart', x, y, now);
|
||||
await touch('touchMove', x + dx / 2, y + dy / 2, now && now + .02);
|
||||
await touch('touchMove', x + dx, y + dy, now && now + .04);
|
||||
const liveOffset = await offset(card), limit = await card.locator('.detail-viewport').evaluate(el => el.clientWidth * (el.querySelectorAll('.dpanel').length - 1));
|
||||
assert(liveOffset <= 17 && liveOffset >= -limit - 17, 'edge resistance caps overscroll at 16px');
|
||||
if (verify) {
|
||||
assert(Math.abs(await offset(card) - before - dx) < 3, 'report follows finger before release');
|
||||
assert.equal(await selected(card), previous, 'selection commits only on release');
|
||||
assert.equal(await card.locator('.detail-panels.dragging').count(), 1);
|
||||
assert(Math.abs(await card.locator('.detail-viewport').evaluate(el => el.getBoundingClientRect().height) - beforeHeight) < 1, 'card height stays fixed during horizontal dragging');
|
||||
await card.locator('.dpanel.active .ditem-text').evaluate(el => {
|
||||
const growth = document.createElement('span');
|
||||
growth.dataset.testGrowth = ''; growth.style.cssText = 'display:block;height:80px'; el.append(growth);
|
||||
});
|
||||
await page.waitForTimeout(100);
|
||||
assert(Math.abs(await card.locator('.detail-viewport').evaluate(el => el.getBoundingClientRect().height) - beforeHeight) < 1, 'height stays fixed even when panel content resizes while held');
|
||||
await card.locator('[data-test-growth]').evaluate(el => el.remove());
|
||||
if (process.env.H5_ARTIFACT_DIR) {
|
||||
fs.mkdirSync(process.env.H5_ARTIFACT_DIR, { recursive: true });
|
||||
await page.screenshot({ path: path.join(process.env.H5_ARTIFACT_DIR, 'report-drag-' + await card.locator('.dtab').first().textContent() + '.png') });
|
||||
@@ -85,6 +97,9 @@ try {
|
||||
if (pause) await page.waitForTimeout(pause);
|
||||
await touch(cancel ? 'touchCancel' : 'touchEnd', 0, 0, now && now + .05);
|
||||
await page.waitForTimeout(400);
|
||||
if (dy <= -80 && Math.abs(dx) < 10) {
|
||||
assert(await page.locator('#s-report').evaluate(el => el.scrollTop) > scrollBefore + 10, 'vertical gesture actually scrolls the report');
|
||||
}
|
||||
assert.equal(await card.locator('.detail-panels.dragging').count(), 0);
|
||||
assert.equal(await page.locator('#p-zoom.show').count(), 0, 'drag never opens zoom');
|
||||
}
|
||||
@@ -105,6 +120,9 @@ try {
|
||||
await drag(card, 25, { pause: 160 }); assert.equal(await selected(card), '1', 'short slow drag snaps back');
|
||||
await drag(card, 100, { cancel: true }); assert.equal(await selected(card), '1', 'cancellation restores selection');
|
||||
await drag(card, 2, { dy: -90 }); assert.equal(await selected(card), '1', 'vertical scrolling preserves tab');
|
||||
await drag(card, 65, { dy: -60 }); assert.equal(await selected(card), '1', 'ambiguous diagonal scrolling does not switch tabs');
|
||||
await drag(card, 100, { text: true }); assert.equal(await selected(card), '1', 'horizontal dragging on analysis text does not switch tabs');
|
||||
await drag(card, 2, { dy: -90, text: true }); assert.equal(await selected(card), '1', 'reading text keeps vertical scrolling and current tab');
|
||||
await drag(card, 40, { flick: true }); assert.equal(await selected(card), '0', 'short fast flick goes back');
|
||||
await drag(card, 100); assert.equal(await selected(card), '0', 'first edge cannot overshoot');
|
||||
await card.locator('.dtab').last().click(); await page.waitForTimeout(400); await aligned(card);
|
||||
|
||||
Reference in New Issue
Block a user