跳到主要内容
食谱编号174

排版食谱 · 第12章 · 教科书

答案倒印的益智游戏页

儿童杂志的益智游戏页:填字游戏、找字游戏、谜语、故事排序等各占一张卡片,每张卡片底部的答案都倒着印。

本页内容
输出
Canvas · PDF
难度
中级
Postext
已用Postext 1.59.0测试
需要≥ 1.59.0 · postext-pdf ≥ 1.59.0
许可证
更新于2026年10月24日
代码MIT · 文本CC BY 4.0
  • 英文样例:尚无中文版本
  • 成品尺寸210 × 280 mm
  • 2栏, 栏间距7 mm
  • Nunito 10.5/15
  • Baloo 2
  • Fredoka
  • 2页
  • 难度
  • Postext 1.59.0
  • 排版用时71 ms
  • 129行代码

简单来说

一本讲海洋的儿童杂志里的益智游戏页。每个游戏占一张卡片,答案倒着印在下面,读者把杂志转过来就能对答案。

成品一览

海洋主题儿童杂志《Splash!》的益智游戏页,共两页,A4大小,双栏。第一页上部是一幅珊瑚礁的画,标题落在开阔的海面上。下面每个游戏各占一张米色卡片,编号是珊瑚色的:一个用六种海洋动物和提示组成的填字游戏,一个把八个单词按横向、纵向、斜向和倒序藏起来的找字游戏,一个有三个选项可圈的谜语,一段要排好顺序的海龟一生,一句打乱了词序的冷知识,还有要和名字连起来的动物宝宝。每个游戏下面、卡片之内,答案用灰色小字倒着印,益智杂志一向这样把答案藏起来。

这道食谱解答

  • 怎样用一组词语生成找词游戏或填字游戏?
  • 怎样把答案倒印在题目下方?

简短回答

script.js · 第33–59行在完整代码中
// 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.jpg
  • reef-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 · 珊瑚礁横跨两栏

script.js · 第63–82行在完整代码中
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)
沙盒