Tutoriel : même largeur à chaque étape ; règle ajoutée à la recette
Les pages sont des éléments de la colonne flexible #screen ; leurs marges automatiques annulaient l'étirement, et une page au contenu court se réduisait à la largeur de ce contenu : l'étape 6 « à vous » (consigne courte, pas d'explication) avait un plateau de 300 px au lieu de 396 en 412 × 915. Les pages prennent toute la largeur, bornée par max-width. La recette mesure le plateau et le texte à chaque écran du tutoriel, dans les trois tailles, et refuse un plateau de largeur différente ou une page plus large que l'écran (mesures dans tutoriel-largeurs.txt). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
4e74bab3ac
commit
b2263c7e3d
@@ -16,7 +16,7 @@
|
||||
|
||||
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
||||
import { chromium, firefox } from "playwright";
|
||||
import { hiddenUnderActionBar } from "./recette.mjs";
|
||||
import { hiddenUnderActionBar, tutorialWidthProblems, tutorialWidths } from "./recette.mjs";
|
||||
|
||||
const [base, browserName, shots, updateBase, updateDir] = process.argv.slice(2);
|
||||
const browserType = { chromium, firefox }[browserName];
|
||||
@@ -171,6 +171,7 @@ async function checkTutorialView(page, name) {
|
||||
return b.scrollHeight - b.clientHeight;
|
||||
})(),
|
||||
height: innerHeight,
|
||||
width: innerWidth,
|
||||
buttons,
|
||||
};
|
||||
});
|
||||
@@ -189,8 +190,13 @@ async function checkTutorialView(page, name) {
|
||||
}
|
||||
tutorialViews++;
|
||||
tutorialMinNumber = Math.min(tutorialMinNumber, view.minNumber);
|
||||
// Largeurs du plateau et du texte, comparées d'une étape à l'autre à la
|
||||
// fin du tutoriel (tutorialWidthProblems).
|
||||
const widths = await tutorialWidths(page);
|
||||
tutorialWidthViews.push({ name, size: `${view.width}x${view.height}`, ...widths });
|
||||
}
|
||||
let tutorialViews = 0;
|
||||
const tutorialWidthViews = [];
|
||||
let tutorialBodyScroll = { px: 0, where: "" };
|
||||
let tutorialMinNumber = Infinity;
|
||||
|
||||
@@ -331,6 +337,18 @@ async function playTouches(page, touches) {
|
||||
await until(page, "fin", () => location.hash === "#/tutoriel/fin");
|
||||
await page.click("#tutorial-next");
|
||||
await until(page, "première partie", () => location.hash === "#/" && window.__shown("game"));
|
||||
if (full) {
|
||||
// Les mesures, écran par écran, à côté des captures.
|
||||
writeFileSync(`${dir}/tutoriel-largeurs.txt`, tutorialWidthViews.map((v) =>
|
||||
`${v.size}\t${v.name}\tplateau ${v.board.toFixed(1)}\ttexte ${v.text.toFixed(1)}\tpage ${v.page}`)
|
||||
.join("\n") + "\n");
|
||||
const widthProblems = tutorialWidthProblems(tutorialWidthViews);
|
||||
check(widthProblems.length === 0, `largeurs du tutoriel :\n ${widthProblems.join("\n ")}`);
|
||||
const sizes = [...new Set(tutorialWidthViews.map((v) => v.size))];
|
||||
log(`tutoriel : plateau de même largeur à chaque étape (${sizes.map((s) =>
|
||||
`${s} : ${tutorialWidthViews.find((v) => v.size === s).board.toFixed(1)} px`).join(", ")}), ` +
|
||||
"aucune page plus large que l'écran");
|
||||
}
|
||||
log(`tutoriel : ${screens} écrans, gestes refusés puis justes sur les 4 écrans « à vous », fin` +
|
||||
(full ? ` ; recette sur ${tutorialViews} vues, numéros de ${tutorialMinNumber.toFixed(1)} px au moins, ` +
|
||||
`texte défilant au plus de ${tutorialBodyScroll.px} px${tutorialBodyScroll.px > 0 ? ` (${tutorialBodyScroll.where})` : ""}` : ""));
|
||||
|
||||
@@ -98,3 +98,72 @@ export async function hiddenUnderActionBar(target) {
|
||||
return hidden;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Les largeurs d'un écran du tutoriel, en px CSS : le plateau, la colonne
|
||||
* de texte, et la largeur de la page (scrollWidth) face à celle de l'écran.
|
||||
* Une page plus large que l'écran défile de côté ; sur un téléphone, Chrome
|
||||
* la dézoome pour la montrer en entier, et tout l'écran paraît rétréci.
|
||||
*/
|
||||
export async function tutorialWidths(target) {
|
||||
return target.evaluate(() => {
|
||||
const width = (selector) => {
|
||||
const el = document.querySelector(selector);
|
||||
return el === null ? 0 : el.getBoundingClientRect().width;
|
||||
};
|
||||
const page = document.scrollingElement;
|
||||
// Le plus à droite des descendants du tutoriel, pour nommer le
|
||||
// coupable d'un débordement.
|
||||
let widest = { what: "", right: 0 };
|
||||
for (const el of document.querySelectorAll("#tutorial *")) {
|
||||
const r = el.getBoundingClientRect();
|
||||
if (r.width > 0 && r.right > widest.right) {
|
||||
const name = el.id
|
||||
? `#${el.id}`
|
||||
: `${el.tagName.toLowerCase()}${[...el.classList].map((c) => `.${c}`).join("")}`;
|
||||
widest = { what: name, right: r.right };
|
||||
}
|
||||
}
|
||||
return {
|
||||
board: width("#tutorial-board"),
|
||||
text: width("#tutorial .tutorial-body"),
|
||||
page: Math.max(page.scrollWidth, document.body.scrollWidth),
|
||||
screen: innerWidth,
|
||||
widest,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Règle de la recette : pour une taille d'écran donnée, le plateau du
|
||||
* tutoriel a la même largeur à toutes les étapes, et aucune page n'est plus
|
||||
* large que l'écran. views : [{ name, size, board, text, page, screen,
|
||||
* widest }]. Renvoie la liste des écarts, décrits (vide si tout va bien).
|
||||
*/
|
||||
export function tutorialWidthProblems(views) {
|
||||
const problems = [];
|
||||
const bySize = new Map();
|
||||
for (const v of views) {
|
||||
bySize.set(v.size, [...(bySize.get(v.size) ?? []), v]);
|
||||
if (v.page > v.screen + 0.5) {
|
||||
problems.push(
|
||||
`${v.name} en ${v.size} : page de ${v.page} px pour ${v.screen} px ` +
|
||||
`(le plus à droite : ${v.widest.what}, ${Math.round(v.widest.right)} px)`,
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const [size, list] of bySize) {
|
||||
const widths = list.map((v) => v.board);
|
||||
const min = Math.min(...widths);
|
||||
const max = Math.max(...widths);
|
||||
if (max - min > 0.5) {
|
||||
const odd = list
|
||||
.filter((v) => Math.abs(v.board - max) > 0.5)
|
||||
.map((v) => `${v.name} ${v.board.toFixed(1)}`);
|
||||
problems.push(
|
||||
`${size} : plateau de ${min.toFixed(1)} à ${max.toFixed(1)} px (${odd.join(", ")})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return problems;
|
||||
}
|
||||
|
||||
@@ -295,6 +295,15 @@ a {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Les pages sont des éléments de la colonne flexible #screen. Leurs marges
|
||||
* automatiques les centrent, mais annulent leur étirement en largeur : sans
|
||||
* largeur explicite, une page au contenu court se réduit à la largeur de ce
|
||||
* contenu (le plateau de l'étape 6 « à vous » du tutoriel passait de 396 à
|
||||
* 300 px en 412 × 915). Leur max-width les borne toujours. */
|
||||
.screen > .page {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user