如何用 React、Tailwind 和 Framer-motion 复制 Zelda BOTW 界面:第一部分 为什么要这样做?关于这个界面 关于格式 Zelda BOTW 入门 从布局开始 添加项目网格 项目选择 键盘导航 动画 下一步是什么

2025-05-24

如何使用 React、Tailwind 和 Framer-motion 复制 Zelda BOTW 界面:第 1 部分

为什么要这么做?

关于此界面

关于格式

塞尔达传说 BOTW 入门版

从布局开始

添加项目网格

项目选择

键盘导航

动画片

下一步

在本系列文章中,我们将学习如何在网络上复制《塞尔达传说:荒野之息》的菜单库存界面!

点击下面的图片查看最终的演示:
塞尔达传说 BOTW 试玩版

文章结构

这将是一系列 3 篇文章:

  • 第一部分:设置项目网格 + 添加导航 + 选择动画
  • PART2:添加分页+页面过渡动画+显示附加项目数据
  • 第三部分:使用模态框处理操作 + 使用奖励处理物品 + 新物品动画 + 添加声音

目标受众:

中级前端开发人员。
如果你经验更丰富,可能学不到太多东西(至少对于第一部分来说),但这仍然是一个有趣的练习。

为什么要这么做?

我发现 GameUI 非常鼓舞人心,它们与 Web 界面存在一些共同的问题,也存在一些类似的问题。GameUI 的设计通常不会妨碍玩家的游戏体验(《塞尔达传说:终极版》就是一个很好的例子)。Web 在这方面也没什么不同。我们设计网站实际上是在构建一种体验,界面只是与这种体验互动的方式。
此外,GameUI 通常充满了许多小细节,这些细节并非总是应该一眼就能注意到,但它们却是构成一个令人愉悦的界面的重要组成部分。

关于此界面

塞尔达库存界面

截图来自interfaceingame.com

该界面基本上是两列布局

  • 第一列包含按类别分组的项目网格。
  • 第二列用于显示有关所选项目的信息。

界面上有很多动画,它们都有各自的用途:

  • 箭头在所选项目的边缘移动
  • 网格水平边缘的箭头表示您可以在类别之间导航
  • 当你改变所选项目时,项目描述会逐步输入,以提醒你这些数据已经改变
  • 全新(耐久度最高)的物品会有一个闪亮的小星星。这个动画与其他动画相比非常微妙,因为它关注的是物品的细节。

前 3 个动画最为明显,起到了指示作用(这是一个 UX 术语,来自《日常设计》的作者唐纳德·诺曼),可以帮助玩家了解他可以在界面上执行的操作。

关于格式

起初我只想提供一些带有解释的代码示例,但我认为这没什么价值。
你可能已经意识到,作为开发者,我们通过积极实践才能学得更好。这就是为什么本系列会积极鼓励你编写你自己版本的 Zelda BOTW 界面。
我会在每个步骤中设定一个目标,并提供实现目标的线索。
如果你遇到困难或者只是想继续阅读,每个步骤的解决方案都会在单独的要点中提供。
我已经建立了一个包含所有资源的基础存储库,以便你立即开始:

GitHub 徽标 flagrede / zelda-botw-starter

用于重现 Zelda BOTW 库存界面的模板




该存储库是用 Typescript 设置的,但这里没有义务,您可以根据需要使用 ts 或 js。

有关所用技术的先前要求和说明

本系列文章基于以下技术栈:

  • ReactJS 并利用其生态系统的一些库
    • 需要一些先验知识,但不需要高级知识
  • TailwindCSS
    • 无需任何先验知识,如果您不熟悉,我们将简要介绍。
  • 成帧运动:
    • 这是用于动画的主要库,无需任何先验知识

Tailwind 快速介绍

如果你已经熟悉 Tailwind,可以跳过此部分

Tailwind 徽标

w-fullTailwind 是一个 CSS 实用框架,它提供了一组类似forwidth: 100%flexfor 的低责任的 css 类display: flex

这些类是基于 JS 配置文件生成的。在这个文件中,你可以定义一些设计标记,例如颜色、间距等等,然后 Tailwind 会根据你定义的设计约束生成所有 CSS 类。

您可以在此处找到此文件的结构:默认配置

我选择 Tailwind 作为本系列文章的框架,因为它允许我提前准备好设计配置,以便您专注于最有趣的部分。
此外,我还可以将所需的 Tailwind 类直接添加到文章提供的架构中,从而简化实现过程。

如果这是您第一次使用 Tailwind,您可以使用此备忘单来帮助您在开始时找到课程:https
://nerdcave.com/tailwind-cheat-sheet 我还强烈建议安装一个插件来实现 Tailwind 课程的自动完成功能(请参阅入门自述文件中的链接)。

从布局开始

我们将从创建界面的基础布局开始。
我们想要的是一个响应式布局,并遵循以下约束:

  • 超大屏幕(XL):两列布局列,每列占据屏幕的 1/2。
  • 在超大屏幕(<XL)以下:应该只有一列占据全宽。
  • 布局应包含在居中的响应容器内。

一旦你的布局完成,你可以将bg-zelda-darkGreen类添加到你的主 div 中,这对该部分很有帮助。

布局

资源:

解决方案:

https://gist.github.com/flagrede/e440ac5c56f21ef9b9db37d1c780ad20

添加项目网格

我们现在将创建ItemsGridItem组件。

替代文本

我们希望创建一个 5 列的网格,使用 css-grid 实现,在移动设备上缩减为 3 列,并在每个单元格中显示一个 Item 组件。
要创建 Item 组件,我们需要查看 中可用的数据src/data/items.js。Item 数据如下所示:

    {
      name: "Tree Branch",
      category: "weapon",
      icon: "/zelda-botw/items/weapons/BotW_Tree_Branch_Icon.png",
      value: "2",
      isNew: true,
      description:
        "Wooden branches such as this are pretty common, but it's surprisingly well-balanced. It doesn't do much damage but can serve as a weapon in a pinch.",
    }
Enter fullscreen mode Exit fullscreen mode

现在,我们的组件只需显示其图标、值以及图像标题的名称。
我们的项目应该如下所示:

替代文本

数据集中的项目主要分为weaponsshields和三个类别armors。目前我们只需要显示一个类别,因此我们可以先以weapons项目为例。我们可以创建一个getItems函数来检索该类别,并用一个空的项目对象填充数组的其余部分,以达到所需的单元格数量。

资源:

解决方案:

项目选择

现在,我们希望能够通过应用以下类来单击来选择一个项目。

已选择项目

此时我们的应用程序结构应如下所示:

<App>
  <ItemsGrid>
    <Item>
Enter fullscreen mode Exit fullscreen mode

我们需要: - 使用组件
存储所选项目的索引位置 - 创建一个上下文来存储位置和前一个-使用钩子 检索组件内的上下文值。 - 使用实用程序和上下文中的值 从上面激活选定的类。React.useSateApp
React.createContextselectedItemsetSelectedItemuseState
ItemuseContext
classnames

资源:

解决方案:

https://gist.github.com/flagrede/d0627032cb459166dd454110337a1ab1

键盘导航

在这一部分中,我们需要将元素的索引位置转换为 5x4 矩阵位置,然后再转换为另一个矩阵位置来还原它。
网格位置应该如下所示:

网格位置

因此,我们需要一个转换为的方法,例如6,另一个转换{x:1 ,y:1}
的方法, 第一个方法签名应该是:{x:1 ,y:1}6

  • (index: number) => {x:number, y:number}

第二种方法签名应该是:

  • ({x:number, y:number}) => number

然后,我们将需要四种方法来根据方向计算新的位置,goUp, goRight, goLeft, goBottom
我强烈建议对这些方法进行测试,但这里没有义务。

测试示例:

it("should convert 6 to x:1, y:1 position", () => {
  // Given
  const index = 6;

  // When
  const result = convertIndexToPosition(index);

  // Then
  expect(result).toEqual({ x: 1, y: 1 });
});
Enter fullscreen mode Exit fullscreen mode

最后,组件handleKeyPressed内部有一个函数App来处理根据按下的键执行的行为。

注意:键盘导航预计仅适用于 5 列网格(因为移动布局有 3 列)。

资源:

解决方案:

动画片

项目选择动画架构

为了结束第一部分,我们将使用 framer-motion 添加一些动画。
现在,我们将在所选项目的边缘添加三角形并为其添加动画。
这些三角形将使用 CSS 完成。创建三角形的 CSS 类已经可用,您可以访问zelda-botw-triangle-upzelda-botw-triangle-bottom

我们需要创建两个组件:

Triangle具有以下属性的组件

type Props {
  animateParams: {
    rotate: string, 
    x: [number, number, number],
    y: [number, number, number]
  },
  className: string
}
Enter fullscreen mode Exit fullscreen mode
  • rotate应该是这样的"-10deg"
  • x应该类似于[8, 2, 4],它将通过数组中提供的值来使三角形在 x 轴上移动。这里给出的值是随机的,我让你自己找到正确的值。
  • y相同,x但用于y
  • className将用于应用 CSS 类列表

一个TrianglesBox没有 props 但显示 4 个Triangle组件的组件。

  • 为了定位三角形,我们可以absolute在它们上面应用一个位置,并relative在父级上应用一个位置Item。要为triangle组件添加动画效果,我们必须使用motionframer-motion 的 API。
import { motion } from "framer-motion";

const DummyComponent ({ animateParams }) => (
  <motion.div
    animate={animateParams}
  />
);
Enter fullscreen mode Exit fullscreen mode

我们还需要应用过渡参数来motion.div获得所需的效果。

资源:

解决方案:

  • 三角形分量:

https://gist.github.com/flagrede/8143023838936fb4b7cf81eae1c52d54

  • TrianglesBox 组件:

https://gist.github.com/flagrede/9587f518c4cf7f8ca991c63a8144b75b

下一步

恭喜您完成第一部分,希望您喜欢!
在第二部分中,我们将实现项目类别之间的分页、页面过渡动画以及显示有关项目的附加数据。如果您遇到任何问题或有任何反馈,请在这里或在Twitter
上告诉我,我会很乐意尽力解决!

笔记

所有图谱均使用 Excalidraw 绘制。感谢团队免费提供如此优秀的工具。

文章来源:https://dev.to/flagrede/how-to-replicate-the-zelda-botw-interface-with-react-tailwind-and-framer-motion-part-1-298g
PREV
适用于管理面板的 7 款最佳 Node.js React 模板和主题
NEXT
游戏编程基础 硬件编程语言 API