isometric-animation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Isometric (2.5D) Animation

等距(2.5D)动画

Isometric scenes give explainers, UI walkthroughs, and infographics a built, dimensional feel without true perspective: equal-foreshortened axes, stacked Z layers, extruded blocks, exploded diagrams. The look is geometric and deliberate — depth comes from consistent axes and per-face shading, motion from revealing structure one layer at a time.
等距场景无需真实透视,就能为讲解动画、UI引导和信息图带来立体的构建感:等比例缩短的坐标轴、堆叠的Z轴图层、挤出块、爆炸图。这种风格几何感强且设计严谨——深度来自一致的坐标轴和逐面着色,动态效果则通过逐层展示结构来呈现。

When to use

适用场景

  • 2.5D scenes for explainers, product UI, or "how it works" infographics.
  • Stacked layers / floors / a tech-stack tower that builds bottom-up.
  • Extruded blocks, isometric grids, isometric city/server-rack scenes.
  • Exploded-view diagrams that pull apart along Z and reassemble.
  • A static iso illustration that needs gentle life: camera drift, parallax, hover lift.
  • 用于讲解动画、产品UI或「工作原理」信息图的2.5D场景。
  • 自下而上构建的堆叠图层/楼层/技术栈塔。
  • 挤出块、等距网格、等距城市/服务器机架场景。
  • 沿Z轴拆分后重新组装的爆炸视图图。
  • 需要增添细微生机的静态等距插画:相机漂移、视差效果、悬停抬升。

Projection: get the axes right first

投影:先确定正确的坐标轴

Two conventions — pick one and never mix:
  • True isometric — all three axes 120° apart; on screen the X/Y ground axes run at ±30° from horizontal. This is the math-correct iso. Use for CSS/Three.js.
  • 2:1 dimetric ("pixel-art iso") — tiles drawn 2 wide : 1 tall, so axes sit at ~26.57° (
    atan(0.5)
    ). Cleaner pixels, the game-art default. Use for tile/sprite scenes.
For DOM/SVG the fastest true-iso plane is a CSS 3D transform on a
perspective: none
(orthographic-feeling) container:
css
.scene { transform-style: preserve-3d; }
/* rotate a flat plane onto the iso ground */
.iso-plane { transform: rotateX(60deg) rotateZ(45deg); }
rotateX(60deg) rotateZ(45deg)
tips a flat element back 60° then spins it 45° — the standard "true iso" pair that lands the ground-plane axes at ±30° on screen. Any flat SVG/DOM (a floorplan, a UI mock, a grid) placed on
.iso-plane
is now an iso surface; lift children toward the camera with
translateZ()
.
Equivalent grid-to-screen math when you place tiles by code (2:1 dimetric):
js
// tile (col, row) → screen pixels, TILE = full tile width
const screenX = (col - row) * (TILE / 2);
const screenY = (col + row) * (TILE / 4);
Keep
perspective
OFF (or very large) — iso is parallel projection, so far objects must NOT shrink. A small
perspective
reads as a tilted 3D card, not iso.
两种约定——选其一且切勿混用:
  • 真等距——三个坐标轴彼此呈120°;在屏幕上X/Y地面坐标轴与水平线呈**±30°**。这是数学上准确的等距方式,适用于CSS/Three.js。
  • 2:1斜二测(「像素艺术等距」)—— tiles绘制比例为2宽:1高,因此坐标轴与水平线呈~26.57°
    atan(0.5)
    )。像素效果更清晰,是游戏美术的默认选择,适用于 tile/精灵场景。
对于DOM/SVG,最快实现真等距平面的方式是在
perspective: none
(正交视觉)容器上应用CSS 3D变换:
css
.scene { transform-style: preserve-3d; }
/* 将平面旋转至等距地面 */
.iso-plane { transform: rotateX(60deg) rotateZ(45deg); }
rotateX(60deg) rotateZ(45deg)
先将平面向后倾斜60°,再旋转45°——这组标准的「真等距」变换会让地面坐标轴在屏幕上呈现±30°。放置在
.iso-plane
上的任何平面SVG/DOM(平面图、UI原型、网格)都会成为等距表面;通过
translateZ()
可将子元素向相机方向抬升。
通过代码放置tiles时(2:1斜二测),对应的网格转屏幕坐标公式:
js
// tile (列, 行) → 屏幕像素,TILE = tile完整宽度
const screenX = (col - row) * (TILE / 2);
const screenY = (col + row) * (TILE / 4);
务必关闭
perspective
(或设置为极大值)——等距是平行投影,因此远处物体绝对不能缩小。较小的
perspective
会让场景看起来像倾斜的3D卡片,而非等距效果。

Building scenes

场景构建

Stacked Z layers (cards / floors)

堆叠Z轴图层(卡片/楼层)

Each layer is the same flat shape on the iso plane, separated along Z. Lift later layers higher; stagger reveals bottom-up.
html
<div class="scene"><div class="iso-plane">
  <div class="layer" style="--z:0">  <!-- base --></div>
  <div class="layer" style="--z:40"> <!-- mid  --></div>
  <div class="layer" style="--z:80"> <!-- top  --></div>
</div></div>
css
.layer { transform: translateZ(calc(var(--z) * 1px)); }
每个图层都是等距平面上的相同平面形状,沿Z轴分隔。将上层图层抬升更高;交错展示实现自下而上构建的效果。
html
<div class="scene"><div class="iso-plane">
  <div class="layer" style="--z:0">  <!-- 底层 --></div>
  <div class="layer" style="--z:40"> <!-- 中层 --></div>
  <div class="layer" style="--z:80"> <!-- 顶层 --></div>
</div></div>
css
.layer { transform: translateZ(calc(var(--z) * 1px)); }

Extruded block (faces + per-face shading = depth)

挤出块(面+逐面着色=深度)

A solid block is a top + two visible side faces. Shade by face so the eye reads volume — top lightest, left mid, right darkest (a fixed light direction). This is the single most important trick for believable iso depth.
css
.block .top   { background:#3a9bff; }              /* lit   */
.block .left  { background:#2c78c8; transform: rotateY(-90deg); transform-origin:left; }
.block .right { background:#1f5896; transform: rotateX(90deg);  transform-origin:bottom; } /* shade */
Same shading rule in SVG: draw three polygons (top rhombus, left + right parallelograms) and fill them light/mid/dark. SVG is often simpler than 3D-CSS for many small blocks (see references).
实心块由顶面+两个可见侧面组成。通过逐面着色让眼睛感知体积——顶面最亮,左侧中等亮度,右侧最暗(固定光源方向)。这是实现逼真等距深度最重要的技巧。
css
.block .top   { background:#3a9bff; }              /* 受光面 */
.block .left  { background:#2c78c8; transform: rotateY(-90deg); transform-origin:left; }
.block .right { background:#1f5896; transform: rotateX(90deg);  transform-origin:bottom; } /* 阴影面 */
SVG中遵循相同的着色规则:绘制三个多边形(顶面菱形、左侧+右侧平行四边形)并填充亮/中等/暗色调。对于许多小方块,SVG通常比3D-CSS更简单(参见参考资料)。

Isometric grid

等距网格

A floor grid is just two sets of parallel lines on
.iso-plane
(drawn flat, the rotation makes them iso). Author it flat in SVG, drop it on the plane, animate lines drawing on with
stroke-dashoffset
.
地面网格只是
.iso-plane
上的两组平行线(平面绘制,旋转后形成等距效果)。在SVG中平面绘制网格,将其放置在平面上,通过
stroke-dashoffset
实现线条绘制动画。

Exploded-view diagram

爆炸视图图

Same as stacked layers but the resting state is pulled apart along Z; assemble = animate every layer's
translateZ
toward its seated value. Explode = reverse. Keep one shared
--gap
variable so the whole stack opens/closes together.
与堆叠图层原理相同,但静止状态是沿Z轴拆分;组装动画就是将每个图层的
translateZ
动画到其就位值。拆分则是反向动画。使用一个共享的
--gap
变量,让整个堆叠同步展开/闭合。

Motion

动效实现

GoalMechanismEasing
Layer reveal (build)per-layer
opacity
+
translateZ
from below, staggered
back.out(1.4)
, stagger 0.12s
Explode / assembletween shared
--gap
(or each
translateZ
)
power3.inOut
, 0.7–1s
Tower / stack buildlayers drop in bottom→top
back.out(1.5)
, stagger 0.15s
Camera driftslow
rotateZ
/
translate
on
.scene
, looped yoyo
sine.inOut
, 6–12s
Parallaxlayers shift by depth on pointer/scroll (
--z
∝ shift)
power2.out
Hover liftone block
translateZ(+12px)
+ soft shadow
power2.out
, 0.25s
Rules that keep it reading as iso:
  • Lift along Z, never scale to fake height — scaling breaks parallel projection.
  • Camera drift must be tiny and slow (a few degrees / pixels). Big moves expose that it's a flat plane.
  • Stagger builds by physical position (bottom layer first), not DOM order, so the structure assembles logically.
  • Keep the light direction fixed for the whole scene — every block shades the same way.
目标实现方式缓动函数
图层展示(构建)逐图层从下方应用
opacity
+
translateZ
,交错执行
back.out(1.4)
,交错间隔0.12s
拆分/组装缓动共享变量
--gap
(或每个图层的
translateZ
power3.inOut
,时长0.7–1s
塔/堆叠构建图层自下而上落下
back.out(1.5)
,交错间隔0.15s
相机漂移
.scene
上缓慢执行
rotateZ
/
translate
,循环往复
sine.inOut
,时长6–12s
视差效果图层随指针/滚动按深度偏移(
--z
与偏移量成正比)
power2.out
悬停抬升单个方块应用
translateZ(+12px)
+ 柔和阴影
power2.out
,时长0.25s
保持等距视觉效果的规则:
  • 沿Z轴抬升,切勿缩放来模拟高度——缩放会破坏平行投影。
  • 相机漂移必须细微且缓慢(几度/几像素)。大幅移动会暴露场景是平面。
  • 物理位置(底层优先)交错构建,而非DOM顺序,确保结构组装逻辑合理。
  • 整个场景保持固定光源方向——所有方块的着色方式一致。

Three.js OrthographicCamera (true 3D iso) — alternative

Three.js OrthographicCamera(真3D等距)——替代方案

When blocks must occlude correctly, cast real shadows, or rotate in 3D, use real geometry under an orthographic camera (parallel projection = genuine iso; a
PerspectiveCamera
is NOT iso).
js
const aspect = innerWidth / innerHeight, d = 8;
const cam = new THREE.OrthographicCamera(-d*aspect, d*aspect, d, -d, 0.1, 100);
cam.position.set(10, 10, 10);   // equal X,Y,Z → classic iso vantage
cam.lookAt(0, 0, 0);
Equal
position
components give the 30° iso view;
OrthographicCamera
keeps far objects the same size. Light from one direction, enable shadows, and stagger
mesh.position.y
/ scale-in for the build. Full setup in references.
当方块需要正确遮挡、投射真实阴影或进行3D旋转时,可在正交相机下使用真实几何体(平行投影=真正的等距效果;
PerspectiveCamera
并非等距相机)。
js
const aspect = innerWidth / innerHeight, d = 8;
const cam = new THREE.OrthographicCamera(-d*aspect, d*aspect, d, -d, 0.1, 100);
cam.position.set(10, 10, 10);   // X,Y,Z值相等 → 经典等距视角
cam.lookAt(0, 0, 0);
position
分量相等可实现30°等距视图;
OrthographicCamera
确保远处物体尺寸不变。从单一方向打光,启用阴影,通过交错设置
mesh.position.y
或缩放实现构建动画。完整设置请参见参考资料。

Build-tool choice

构建工具选择

  • CSS 3D transforms — best for a handful of blocks/layers with crisp DOM/text on faces; one HTML file.
  • Inline SVG (iso polygons) — best for many small blocks, grids, and infographics; easiest to animate with GSAP/CSS, no 3D-transform z-fighting.
  • Three.js + OrthographicCamera — when you need real occlusion, shadows, or 3D rotation; still shippable as one HTML file via CDN.
  • Pixel/tile (2:1) — game-art or map scenes; use the
    (col-row, col+row)
    projection.
  • CSS 3D transforms——最适合少量带清晰DOM/text的方块/图层;单HTML文件即可实现。
  • 内联SVG(等距多边形)——最适合大量小方块、网格和信息图;使用GSAP/CSS动画最简单,无3D变换的Z轴冲突问题。
  • Three.js + OrthographicCamera——当需要真实遮挡、阴影或3D旋转时使用;仍可通过CDN打包为单HTML文件交付。
  • 像素/tile(2:1)——游戏美术或地图场景;使用
    (col-row, col+row)
    投影公式。

Output checklist

输出检查清单

  • Axes land at the right angle (±30° true-iso, ~26.57° for 2:1); no accidental perspective shrink.
  • Every block shaded top-light / side-mid / side-dark from one fixed light.
  • Layers stack with believable depth — no z-fighting, no two faces flickering, no overlap errors.
  • Build staggers by physical position (bottom-up), one idea per beat.
  • Camera drift is small and slow; explode/assemble seats every layer exactly.
  • prefers-reduced-motion
    shows the final assembled scene with no looping drift.
  • 坐标轴角度正确(真等距±30°,2:1斜二测~26.57°);无意外透视缩小。
  • 每个方块都从固定光源出发,遵循顶面亮/侧面中等/侧面暗的着色规则。
  • 图层堆叠具有逼真深度——无Z轴冲突、无两面闪烁、无重叠错误。
  • 按物理位置(自下而上)交错构建,每一步展示一个元素。
  • 相机漂移细微且缓慢;拆分/组装时每个图层都精准就位。
  • 当开启
    prefers-reduced-motion
    时,展示最终组装完成的场景,无循环漂移。

Deliver & verify (standalone HTML)

交付与验证(独立HTML)

Packaged helper (
scripts/
):
scripts/seek-shot.sh anim.html 0 1.5 3
freezes the
?t=N
harness and screenshots each moment;
scripts/contact-sheet.sh sheet.png frame-*.png
tiles them for one-glance review. See
scripts/README.md
.
For a self-contained iso scene (stack build, exploded diagram, drifting infographic) the deliverable is one HTML file that opens directly in a browser — no build step, no framework, no render pipeline. A single file is the right tier for web motion; reach for a bundler only when one file genuinely can't carry it. (If the scene is part of a rendered video, build it as a Remotion composition and verify with
remotion still
instead.)
Output contract:
  • One
    .html
    file: CSS 3D transforms or inline SVG (or Three.js from CDN), markup, and all motion in one inline
    <script>
    on one master timeline (
    const tl = gsap.timeline()
    ) — a single playhead you can seek.
  • Include the seek harness so any moment can be frozen for a screenshot.
Seek harness — freeze an exact moment for screenshots. The web parallel of a frame-pin:
?t=N
seeks the master timeline to
N
seconds and pauses, so a screenshot lands on a deterministic still.
html
<script>
  // ... build your master timeline as `tl` (the layer build / explode / drift) ...
  const t = new URLSearchParams(location.search).get("t");
  if (t !== null) { tl.pause(); tl.seek(parseFloat(t)); }  // frozen at t seconds
  // no ?t → plays normally
  window.__ready = true;            // ready signal for headless wait
  console.log("duration", tl.duration());
</script>
For a Three.js scene with no GSAP timeline, drive the build from a single
clock
/progress value and set it from
?t=N
, then render one frame — same idea, one deterministic still.
Verify loop — render → freeze → screenshot → check:
  1. Open the file at three moments — start, mid, end:
    …/iso.html?t=0
    ,
    ?t=<dur/2>
    ,
    ?t=<dur>
    (read
    tl.duration()
    from the console).
  2. Headless-screenshot each frozen frame:
    bash
    npx playwright screenshot --wait-for-timeout=500 "file://$PWD/iso.html?t=1.2" frame-mid.png
  3. INSPECT the projection, not just "did it animate":
    • Axes — ground lines run at the intended angle (±30° true-iso / ~26.57° dimetric); verticals stay vertical; nothing is foreshortened like perspective.
    • Depth — layers stack in the right Z order, blocks occlude correctly, per-face shading reads volume; no z-fighting (flickering coincident faces) and no overlap errors (a back block punching through a front one).
    • Build — at mid-frame the stack is partway assembled in physical order; at end every layer is exactly seated (explode fully closed), no drift left mid-tween.
    • Artifacts — clipped/skewed text on faces, off-canvas blocks, FOUC before fonts, jagged grid seams.
  4. Iterate: fix angle/shading/Z-order, re-freeze the same
    ?t
    values, re-screenshot until it reads as solid iso.
Before you finish:
  1. Opens standalone in a browser — no console errors, no missing CDN.
  2. One master timeline (or one Three.js progress value);
    ?t=N
    freezes on a deterministic still.
  3. Screenshotted at start / mid / end — axes correct, depth/Z-order correct, no z-fighting, build seats exactly.
  4. Per-face shading consistent from one fixed light; no accidental perspective shrink.
  5. prefers-reduced-motion
    shows the final assembled scene without looping drift.
打包工具
scripts/
目录):
scripts/seek-shot.sh anim.html 0 1.5 3
可冻结
?t=N
工具并截取每个时刻的截图;
scripts/contact-sheet.sh sheet.png frame-*.png
将截图拼接为一张预览图。详见
scripts/README.md
对于独立的等距场景(堆叠构建、爆炸图、漂移信息图),交付物应为可直接在浏览器中打开的单HTML文件——无需构建步骤、无需框架、无需渲染流水线。单文件是Web动效的理想交付形式;仅当单文件确实无法承载内容时才使用打包工具。(如果场景是渲染视频的一部分,可将其构建为Remotion合成,并使用
remotion still
进行验证。)
输出规范:
  • 单个
    .html
    文件:包含CSS 3D transforms或内联SVG(或通过CDN引入Three.js)、标记语言,以及所有动效都在一个内联
    <script>
    中的主时间线
    const tl = gsap.timeline()
    )——一个可定位的单一播放头。
  • 包含定位工具,以便冻结任意时刻进行截图。
定位工具——冻结精确时刻进行截图。相当于Web版的帧锁定:
?t=N
会将主时间线定位到第N秒并暂停,因此截图会得到确定的静态画面。
html
<script>
  // ... 将主时间线构建为`tl`(图层构建/拆分/漂移) ...
  const t = new URLSearchParams(location.search).get("t");
  if (t !== null) { tl.pause(); tl.seek(parseFloat(t)); }  // 在t秒处冻结
  // 无?t参数 → 正常播放
  window.__ready = true;            // 无头模式就绪信号
  console.log("duration", tl.duration());
</script>
对于没有GSAP时间线的Three.js场景,可通过单个
clock
/进度值驱动构建,并从
?t=N
设置该值,然后渲染一帧——原理相同,得到确定的静态画面。
验证流程——渲染→冻结→截图→检查:
  1. 在三个时刻打开文件——开始、中间、结束:
    …/iso.html?t=0
    ?t=<dur/2>
    ?t=<dur>
    (从控制台读取
    tl.duration()
    )。
  2. 无头模式截取每个冻结帧的截图:
    bash
    npx playwright screenshot --wait-for-timeout=500 "file://$PWD/iso.html?t=1.2" frame-mid.png
  3. 检查投影效果,而非仅看「是否动起来」:
    • 坐标轴——地面线条呈预期角度(真等距±30° / 斜二测~26.57°);垂直线保持垂直;无透视缩短效果。
    • 深度——图层按正确Z轴顺序堆叠,方块正确遮挡,逐面着色体现体积;无Z轴冲突(重合面闪烁)和无重叠错误(后方方块穿透前方方块)。
    • 构建过程——中间帧时堆叠按物理顺序部分组装;结束帧时每个图层都精准就位(拆分完全闭合),无中途漂移。
    • ** artifacts**——面上的文本被裁剪/扭曲,方块超出画布,字体加载前的FOUC,网格接缝锯齿状。
  4. 迭代:修正角度/着色/Z轴顺序,重新冻结相同
    ?t
    值,重新截图直到呈现稳定的等距效果。
完成前检查:
  1. 可在浏览器中独立打开——无控制台错误,无CDN缺失。
  2. 单个主时间线(或单个Three.js进度值);
    ?t=N
    可冻结在确定的静态画面。
  3. 已在开始/中间/结束时刻截图——坐标轴正确,深度/Z轴顺序正确,无Z轴冲突,构建精准就位。
  4. 逐面着色从固定光源出发保持一致;无意外透视缩小。
  5. prefers-reduced-motion
    模式下展示最终组装完成的场景,无循环漂移。

Reference files

参考文件

  • references/isometric-recipes.md
    — fuller runnable code: the projection math (true-iso vs 2:1 dimetric, transform vs matrix), flat-SVG/DOM → iso-plane conversion, extruded-block faces with per-face shading (CSS and SVG), stacked-layer and exploded-view builds, isometric grid, camera drift / parallax / hover, and a complete Three.js
    OrthographicCamera
    iso scene — with easing notes and reduced-motion handling.
  • references/isometric-recipes.md
    ——更完整的可运行代码:投影数学(真等距vs2:1斜二测,变换vs矩阵)、平面SVG/DOM→等距平面转换、带逐面着色的挤出块面(CSS和SVG)、堆叠图层和爆炸视图构建、等距网格、相机漂移/视差/悬停效果,以及完整的Three.js
    OrthographicCamera
    等距场景——包含缓动说明和减少动效处理。