简单来说
一本讲海洋的儿童杂志里的益智游戏页。每个游戏占一张卡片,答案倒着印在下面,读者把杂志转过来就能对答案。
成品一览
海洋主题儿童杂志《Splash!》的益智游戏页,共两页,A4大小,双栏。第一页上部是一幅珊瑚礁的画,标题落在开阔的海面上。下面每个游戏各占一张米色卡片,编号是珊瑚色的:一个用六种海洋动物和提示组成的填字游戏,一个把八个单词按横向、纵向、斜向和倒序藏起来的找字游戏,一个有三个选项可圈的谜语,一段要排好顺序的海龟一生,一句打乱了词序的冷知识,还有要和名字连起来的动物宝宝。每个游戏下面、卡片之内,答案用灰色小字倒着印,益智杂志一向这样把答案藏起来。
这道食谱解答
- 怎样用一组词语生成找词游戏或填字游戏?
- 怎样把答案倒印在题目下方?
简短回答
// placement 'inverted' sets each puzzle's key after it in a box turned 180°, inside the
// puzzle's card, so a page break never parts a puzzle from its answers. The key is made
// from the puzzle: the crossword's words by number, the ringed option, the right order.
const puzzle = {
id: 'puzzle',
box: 'card', // the card each puzzle sits on (a calloutStyles id)
number: { shape: 'disc', fontFamily: DISPLAY, background: col('coral') },
instruction: { fontFamily: TEXT, bold: false },
options: { marker: 'circle', labels: 'none' }, // a ring to colour in, no letters
grid: { fontFamily: LABEL, rule: col('sea'), cell: em(1.45) },
match: { color: col('sea') },
area: { kind: 'lines', color: col('sea') },
answers: { color: col('coral'), fontFamily: LABEL },
solutions: { placement: 'inverted', box: 'turned' },
marginTop: mm(0), marginBottom: mm(0),
};
const card = { id: 'card', title: '', background: col('card'), borderRadius: mm(4),
padding: { top: mm(3.5), right: mm(4), bottom: mm(3), left: mm(4) },
titleStyle: { fontFamily: DISPLAY, fontSize: pt(15), fontWeight: 700, color: col('ink') },
body: { fontFamily: TEXT, fontSize: pt(10.5), lineHeight: pt(15), color: col('ink'),
firstLineIndent: pt(0) },
marginTop: mm(0), marginBottom: mm(5), keepTogether: true };
const turned = { id: 'turned', title: '', backgroundEnabled: false, border: { enabled: false },
padding: { top: pt(0), right: pt(0), bottom: pt(0), left: pt(0) },
body: { fontFamily: LABEL, fontSize: pt(8.5), lineHeight: pt(11), color: col('muted'),
firstLineIndent: pt(0) },
marginTop: mm(2), marginBottom: mm(0) };
用料
- 类型
- Baloo 2, Nunito, Fredoka(SIL OFL 1.1)
- 素材
hatchling-600.jpgreef-1600x792.jpg- A coral reef with a smiling sea turtle, an octopus, a clownfish, a pufferfish and a seahorse under sunbeams. (Generated With Diffusion Models, 原创)
- A baby sea turtle hatching from its egg on the sand. (Generated With Diffusion Models, 原创)
做法
#1 · 每个游戏一张卡片,答案倒过来
代码见上文的简短回答。练习样式的box指定一个框样式,所以每个游戏都排在一张卡片上,编号在圆片里,标题作为题目说明的第一行。placement: 'inverted'把答案放在每个游戏后面、卡片之内一个旋转180°的框里,所以题目和答案永远不会分在两页;格子和书写线都留在卡片的内边距之内。
#2 · 让引擎生成格子
Markdown里只有单词:填字游戏写- OCTOPUS: Eight arms and three hearts,找字游戏写- SHELL。引擎排填字游戏时让每个词都和另一个词交叉,并按阅读顺序给起点编号。找字游戏的单词按指令里写的方向藏进去(directions="across down diagonal backwards"),其余格子用这些单词自己的字母填满。两种格子都用固定的随机种子,所以每次排版、每种输出里的格子都一样,西班牙文版则用自己的单词生成自己的格子。
#3 · 珊瑚礁横跨两栏
const BAND = 104; // mm from the top edge: the painting's height at the page's width
const opener = { enabled: true, minHeight: mm(BAND - 16 + 6), slot: { elements: [
{ kind: 'image', id: 'reef', resourceId: 'reef', decorative: true, reserve: false,
placement: { anchor: { to: 'page', edge: 'top-left' },
size: { width: mm(210), height: mm(BAND) } } },
{ kind: 'text', id: 'kicker', content: '{attr.kicker}', fontFamily: LABEL, fontSize: pt(11),
fontWeight: 600, color: col('paper'), textTransform: 'uppercase', letterSpacing: pt(2),
box: { backgroundColor: col('coral'), borderRadius: mm(2),
padding: { top: mm(0.8), bottom: mm(0.8), left: mm(2.4), right: mm(2.2) } },
placement: { anchor: { to: 'page', edge: 'top-left' }, offset: { x: mm(16), y: mm(14) } } },
{ kind: 'text', id: 'title', content: '{titleText}', fontFamily: DISPLAY, fontSize: pt(56),
fontWeight: 800, lineHeight: 1, color: col('paper'),
placement: { anchor: { to: '#kicker', edge: 'below' }, offset: { x: mm(0), y: mm(3) } } },
{ kind: 'text', id: 'sub', content: '{attr.sub}', fontFamily: TEXT, fontSize: pt(12),
fontWeight: 700, color: col('paper'), overflow: 'wrap', align: 'left',
box: { backgroundColor: col('ink'), borderRadius: mm(2),
padding: { top: mm(1.5), bottom: mm(1.5), left: mm(3), right: mm(3) } },
placement: { anchor: { to: '#title', edge: 'below' }, offset: { x: mm(0), y: mm(2) },
size: { width: mm(110) } } },
] } };
开篇横跨两栏(span: 'page'),所以每一栏的游戏都从画的下方开始。图片按开篇区的比例裁切,并锚定在页面上,因此顶边和两侧都出血;导语放在深色框里,压在海水上也看得清。
完整食谱
// ═══ Postext Cookbook · Nº 174 · A puzzle page with the answers upside down ═══════
// https://postext.dev/en/cookbook/puzzle-page-upside-down-answers
// Code: MIT · Text: original (CC BY 4.0) · Pictures: generated with diffusion models
// Fonts: Baloo 2, Nunito, Fredoka (SIL OFL 1.1) · Needs postext ≥ 1.59.0
import {
buildDocumentWithFonts, renderPageToCanvas, registerResourceImage,
} from 'https://esm.sh/postext';
import { renderToPdf, decompressWoff2 } from 'https://esm.sh/postext-pdf';
const LANG = 'en'; // @lang: the language of the sample document ('en' | 'es')
const RECIPE = 'puzzle-page-upside-down-answers';
// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: reef colours: deep blue ink, coral, sea green, a sandy paper
const palette = {
ink: '#16324a', // text: a deep sea blue
coral: '#e8573f', // the accent: puzzle numbers, the title kicker
sea: '#0f8b8d', // the second: crossword and word-search rules, the riddle's rings
sun: '#ffc93c', // the turtle card's badge
card: '#fff4e0', // the puzzle cards
muted: '#5d7180', // answers, colophon, folio
paper: '#ffffff',
};
const col = (id) => ({ hex: palette[id], model: 'hex', paletteId: id });
const colorPalette = [
...Object.entries(palette).map(([id, hex]) => ({ id, name: id, value: { hex, model: 'hex' } })),
{ id: 'main-color', name: 'accent (defaults)', value: { hex: palette.coral, model: 'hex' } },
];
// #endregion
const [DISPLAY, TEXT, LABEL] = ['Baloo 2', 'Nunito', 'Fredoka'];
// #region answer: every puzzle on a card, its answers turned upside down under it
// placement 'inverted' sets each puzzle's key after it in a box turned 180°, inside the
// puzzle's card, so a page break never parts a puzzle from its answers. The key is made
// from the puzzle: the crossword's words by number, the ringed option, the right order.
const puzzle = {
id: 'puzzle',
box: 'card', // the card each puzzle sits on (a calloutStyles id)
number: { shape: 'disc', fontFamily: DISPLAY, background: col('coral') },
instruction: { fontFamily: TEXT, bold: false },
options: { marker: 'circle', labels: 'none' }, // a ring to colour in, no letters
grid: { fontFamily: LABEL, rule: col('sea'), cell: em(1.45) },
match: { color: col('sea') },
area: { kind: 'lines', color: col('sea') },
answers: { color: col('coral'), fontFamily: LABEL },
solutions: { placement: 'inverted', box: 'turned' },
marginTop: mm(0), marginBottom: mm(0),
};
组合好的script.js可以直接运行:把它粘贴到任何页面的模块脚本中,或在CodePen上打开这道食谱。 GitHub上的食谱文件夹 ↗ (在新标签页中打开)
变化
#隐藏单词表
难一点的找字游戏只印格子:在指令里加上hidden。
-:::exercise{type=wordsearch size=9 directions="across down diagonal backwards"}
+:::exercise{type=wordsearch size=9 directions="across down diagonal backwards" hidden}#把答案印在杂志末尾
chapterEnd把所有答案集中到末尾,不再逐个倒印。
- solutions: { placement: 'inverted', box: 'turned' },
+ solutions: { placement: 'chapterEnd' },常见问题
- 填字游戏里不和任何词交叉的词会被舍弃,并报告
exerciseWordUnplaced。选有相同字母的词,或者加一个把两组词连起来的词。 - 游戏卡片不会拆开,所以较高的填字游戏放不下时会移到下一栏。检查一下西班牙文版,那里的单词更长。
致谢
- 文本
- 原创文字, CC BY 4.0
- 图片
- A coral reef with a smiling sea turtle, an octopus, a clownfish, a pufferfish and a seahorse under sunbeams. · Generated With Diffusion Models · 原创
- A baby sea turtle hatching from its egg on the sand. · Generated With Diffusion Models · 原创
- 字体
- Baloo 2 (SIL OFL 1.1) · Nunito (SIL OFL 1.1) · Fredoka (SIL OFL 1.1)


