Swipe between items like turning pages (#52)
On a phone the reader is fixed over the item list, and the swipe from #49 moved it alone and faded it to 40%, so the list showed through it and in the strip it uncovered, and again as the next item slid in from the far side. Now a layer beside the reader, #dpeek, holds what is really there: the next or previous item, drawn by the same detailHtml the reader uses, with 16px of the page's background between them; "No more items" past the last; or, swiping right from the first, a dimmer over the list that lifts as the reader, shadowed along its edge, is drawn off. On release the swipe carries on from where the finger left it, over 120-250ms by how far is left, and the neighbour becomes the reader in place; with reduced motion it switches at once. A touch starting within 14px of the left edge is kept from Safari, which otherwise takes it as Back and leaves the page mid-swipe. 14px because the back button starts at 16. Offsets are rounded to whole pixels: at a fractional offset the seam between the reader and its neighbour drew a stray light line. The design had the list shift a third of the way across as it is uncovered, as iOS does. #detail lives inside #shell, and a transform there makes it the containing block for the fixed reader, so that part is left out; the dimmer and shadow carry it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1211,14 +1211,15 @@ test.describe('touch gestures on a phone', () => {
|
||||
test.use({ viewport: { width: 390, height: 844 }, hasTouch: true, isMobile: true });
|
||||
|
||||
// Playwright's touchscreen only taps; a drag goes through the DevTools protocol.
|
||||
async function drag(page, from, to) {
|
||||
async function drag(page, from, to, lift = true) {
|
||||
const cdp = await page.context().newCDPSession(page);
|
||||
const steps = 8;
|
||||
await cdp.send('Input.dispatchTouchEvent', { type: 'touchStart', touchPoints: [from] });
|
||||
for (let i = 1; i <= steps; i++)
|
||||
await cdp.send('Input.dispatchTouchEvent', { type: 'touchMove', touchPoints: [{
|
||||
x: from.x + (to.x - from.x) * i / steps, y: from.y + (to.y - from.y) * i / steps }] });
|
||||
await cdp.send('Input.dispatchTouchEvent', { type: 'touchEnd', touchPoints: [] });
|
||||
if (lift) await cdp.send('Input.dispatchTouchEvent', { type: 'touchEnd', touchPoints: [] });
|
||||
return cdp;
|
||||
}
|
||||
|
||||
test('a swipe moves between items, and right from the first goes back to the list', async ({ page }) => {
|
||||
@@ -1234,6 +1235,14 @@ test.describe('touch gestures on a phone', () => {
|
||||
await drag(page, { x: 300, y: 400 }, { x: 230, y: 410 }); // too short: stays (#49)
|
||||
await expect(shown).toHaveText(titles[0]);
|
||||
await expect(page.locator('#detail')).not.toHaveAttribute('style', /translate/); // springs back
|
||||
// Held partway: the next item is beside the reader, not the list under it (#52), and
|
||||
// let go short of a swipe, it goes again.
|
||||
const held = await drag(page, { x: 300, y: 400 }, { x: 230, y: 405 }, false);
|
||||
await expect(page.locator('#dpeek.next .dt')).toHaveText(titles[1]);
|
||||
await expect(page.locator('#detail')).toHaveCSS('opacity', '1');
|
||||
await held.send('Input.dispatchTouchEvent', { type: 'touchEnd', touchPoints: [] });
|
||||
await expect(page.locator('#dpeek')).toHaveCount(0);
|
||||
await expect(shown).toHaveText(titles[0]);
|
||||
await drag(page, { x: 300, y: 400 }, { x: 80, y: 410 }); // left: the next item
|
||||
await expect(shown).toHaveText(titles[1]);
|
||||
await drag(page, { x: 300, y: 400 }, { x: 80, y: 410 }); // left on the last: stays
|
||||
|
||||
Reference in New Issue
Block a user