Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[完成翻译] src/content/cookbook/effects/parallax-scrolling.md #1487

Merged
merged 12 commits into from
Aug 31, 2024
Merged
162 changes: 160 additions & 2 deletions src/content/cookbook/effects/parallax-scrolling.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
title: Create a scrolling parallax effect
description: How to implement a scrolling parallax effect.
# title: Create a scrolling parallax effect
title: 创建一个视差滚动效果
# description: How to implement a scrolling parallax effect.
description: 如何实现视差滚动效果
js:
- defer: true
url: /assets/js/inject_dartpad.js
Expand All @@ -16,26 +18,50 @@ are in the foreground, but the images themselves sit
far off in the distant background. This effect is
known as parallax.

当你在应用程序中滚动一个包含图片的卡片列表时,
你可能会注意到那些图片的滚动速度比屏幕的其余部分要慢一些。
看起来似乎列表中的卡片位于前面,
而图片本身则像是远远地在背景中。
这种效果被称为视差。

In this recipe, you create the parallax effect by building
a list of cards (with rounded corners containing some text).
Each card also contains an image.
As the cards slide up the screen,
the images within each card slide down.

在这个教程中,
你通过构建一个卡片列表来创建视差效果
(这些卡片具有圆角并包含一些文本)。
每个卡片还包含一张图片。
当卡片向上滑动时,卡片中的图片会向下滑动。

The following animation shows the app's behavior:

下面的动画展示了应用程序的行为:

![Parallax scrolling](/assets/images/docs/cookbook/effects/ParallaxScrolling.gif){:.site-mobile-screenshot}

## Create a list to hold the parallax items

## 创建一个列表来保存视差项

To display a list of parallax scrolling images,
you must first display a list.

要实现一个带有视差滚动效果的图片列表,首先必须显示一个列表。

Create a new stateless widget called `ParallaxRecipe`.
Within `ParallaxRecipe`, build a widget tree with a
`SingleChildScrollView` and a `Column`, which forms
a list.

创建一个新的 stateless widget,
名为 `ParallaxRecipe`。
在 `ParallaxRecipe` 中,
构建一个包含 `SingleChildScrollView` 和 `Column` 的 widget 树,
这样就形成了一个列表。

<?code-excerpt "lib/excerpt1.dart (ParallaxRecipe)"?>
```dart
class ParallaxRecipe extends StatelessWidget {
Expand All @@ -54,6 +80,8 @@ class ParallaxRecipe extends StatelessWidget {

## Display items with text and a static image

## 显示带有文本和静态图片的列表项

Each list item displays a rounded-rectangle background
image, representing one of seven locations in the world.
Stacked on top of that background image is the
Expand All @@ -63,11 +91,22 @@ background image and the text is a dark gradient,
which improves the legibility
of the text against the background.

每个列表项显示一个圆角矩形背景图片,
代表世界上的七个地点之一。
在该背景图片的左下角叠加了地点名称及其所在国家。
在背景图片和文字之间有一个深色渐变层,
以提高文字在背景上的可读性。

Implement a stateless widget called `LocationListItem`
that consists of the previously mentioned visuals.
For now, use a static `Image` widget for the background.
Later, you'll replace that widget with a parallax version.

实现一个名为 `LocationListItem` 的 stateless widget,
该 widget 包含之前提到的视觉效果。
现在,使用一个静态的 `Image` widget 作为背景。
稍后,你将用视差版本的 widget 替换这个静态图片 widget。

<?code-excerpt "lib/excerpt2.dart (LocationListItem)"?>
```dart
@immutable
Expand Down Expand Up @@ -159,6 +198,8 @@ class LocationListItem extends StatelessWidget {

Next, add the list items to your `ParallaxRecipe` widget.

然后,将列表项添加到你的 `ParallaxRecipe` widget 中。

<?code-excerpt "lib/excerpt3.dart (ParallaxRecipeItems)"?>
```dart
class ParallaxRecipe extends StatelessWidget {
Expand Down Expand Up @@ -187,8 +228,14 @@ that displays seven unique locations in the world.
In the next step, you add a parallax effect to the
background image.

现在,你有了一个常见的可滚动卡片列表,
展示了世界上七个独特的地点。
在下一步中,你将为背景图片添加视差效果。

## Implement the parallax effect

## 实现视差滚动效果

A parallax scrolling effect is achieved by slightly
pushing the background image in the opposite direction
of the rest of the list. As the list items slide up
Expand All @@ -197,6 +244,13 @@ Conversely, as the list items slide down the screen,
each background image slides slightly upward.
Visually, this results in parallax.

视差滚动效果通过轻微地将背景图片推向
与列表其余部分相反的方向来实现。
当列表项向上滑动时,每个背景图片会轻微向下滑动。
相反,当列表项向下滑动时,
每个背景图片会轻微向上滑动。
从视觉上看,这就产生了视差效果。

The parallax effect depends on the list item's
current position within its ancestor `Scrollable`.
As the list item's scroll position changes, the position
Expand All @@ -213,26 +267,54 @@ before the widget is painted. In other words,
you can intercept the painting phase and take control
to reposition your child widgets however you want.

视差效果依赖于列表项在其祖先 `Scrollable` 中的当前位置。
随着列表项的滚动位置变化,
列表项的背景图片位置也必须随之变化。
这是一个有趣的问题,
因为在 Flutter 的布局阶段完成之前,
无法获取列表项在 `Scrollable` 中的位置。
这意味着背景图片的位置必须在绘制阶段确定,
而绘制阶段在布局阶段之后进行。
幸运的是,Flutter 提供了一个名为 `Flow` 的 widget,
专门设计用于在 widget 被绘制之前立即控制子 widget 的变换。
换句话说,你可以拦截绘制阶段并控制子 widget 的位置,
以便按照你的需求重新定位。

:::note

To learn more, check out this short
Widget of the Week video on the `Flow` widget:

要了解更多信息,
请观看这段关于 `Flow` widget (Flutter widget of the week) 的简短视频:

{% ytEmbed 'NG6pvXpnIso', 'Flow | Flutter widget of the week' %}
:::

:::note

In cases where you need control over what a child paints,
rather than where a child is painted,
consider using a [`CustomPaint`][] widget.

如果你需要控制子 widget 绘制内容,
而不是子 widget 的绘制位置时,
可以考虑使用 [`CustomPaint`][] widget。

In cases where you need control over the layout,
painting, and hit testing, consider defining a
custom [`RenderBox`][].

如果你需要控制布局、绘制和点击测试时,
可以考虑自定义一个 [`RenderBox`][]。

:::

Wrap your background `Image` widget with a
[`Flow`][] widget.

用 [`Flow`][] widget 包裹你的背景 `Image` widget。

<?code-excerpt "lib/excerpt4.dart (BuildParallaxBackground)" replace="/\n delegate: ParallaxFlowDelegate\(\),//g"?>
```dart
Widget _buildParallaxBackground(BuildContext context) {
Expand All @@ -249,6 +331,8 @@ Widget _buildParallaxBackground(BuildContext context) {

Introduce a new `FlowDelegate` called `ParallaxFlowDelegate`.

引入一个新的 `FlowDelegate`,名为 `ParallaxFlowDelegate`。

<?code-excerpt "lib/excerpt4.dart (BuildParallaxBackground)"?>
```dart
Widget _buildParallaxBackground(BuildContext context) {
Expand Down Expand Up @@ -292,8 +376,15 @@ and where those children are painted. In this case,
your `Flow` widget has only one child: the background image.
That image must be exactly as wide as the `Flow` widget.

`FlowDelegate` 控制其子 widget 的大小和绘制位置。
在本教程中,
你的 `Flow` widget 只有一个子 widget:背景图片。
该图片的宽度必须与 `Flow` widget 的宽度完全一致。

Return tight width constraints for your background image child.

为你的背景图片子 widget 返回严格的宽度约束。

<?code-excerpt "lib/main.dart (TightWidth)"?>
```dart
@override
Expand All @@ -309,28 +400,54 @@ but you still need to calculate the vertical position
of each background image based on its scroll
position, and then paint it.

现在你的背景图片大小已经合适,
但你仍然需要根据每个背景图片的滚动位置计算其垂直位置,
然后进行绘制。

There are three critical pieces of information that
you need to compute the desired position of a
background image:

计算背景图片所需位置的三个关键要素是:

* The bounds of the ancestor `Scrollable`

祖先 `Scrollable` 的边界

* The bounds of the individual list item

单个列表项的边界

* The size of the image after it's scaled down
to fit in the list item

图片缩放后的尺寸(为了适应列表项)

To look up the bounds of the `Scrollable`,
you pass a `ScrollableState` into your `FlowDelegate`.

要获取 `Scrollable` 的边界,
可以将 `ScrollableState` 传递给你的 `FlowDelegate`。

To look up the bounds of your individual list item,
pass your list item's `BuildContext` into your `FlowDelegate`.

要获取单个列表项的边界,
可以将列表项的 `BuildContext` 传递给你的 `FlowDelegate`。

To look up the final size of your background image,
assign a `GlobalKey` to your `Image` widget,
and then you pass that `GlobalKey` into your
`FlowDelegate`.

要获取背景图片最终的尺寸,
可以为 `Image` widget 分配一个 `GlobalKey`,
然后将该 `GlobalKey` 传递给你的 `FlowDelegate`。

Make this information available to `ParallaxFlowDelegate`.

将这些信息提供给 `ParallaxFlowDelegate`。

<?code-excerpt "lib/excerpt5.dart (global-key)" plaster="none"?>
```dart
@immutable
Expand Down Expand Up @@ -374,6 +491,9 @@ class ParallaxFlowDelegate extends FlowDelegate {
Having all the information needed to implement
parallax scrolling, implement the `shouldRepaint()` method.

在拥有实现视差滚动所需的所有信息之后,
实现 `shouldRepaint()` 方法。

<?code-excerpt "lib/main.dart (ShouldRepaint)"?>
```dart
@override
Expand All @@ -386,9 +506,13 @@ bool shouldRepaint(ParallaxFlowDelegate oldDelegate) {

Now, implement the layout calculations for the parallax effect.

现在,实施视差效果的布局计算。

First, calculate the pixel position of a list
item within its ancestor `Scrollable`.

首先,计算列表项在其祖先 `Scrollable` 中的像素位置。

<?code-excerpt "lib/excerpt5.dart (paint-children)" plaster="none"?>
```dart
@override
Expand All @@ -408,6 +532,10 @@ A list item at the top of the scrollable area should
produce 0%, and a list item at the bottom of the
scrollable area should produce 100%.

使用列表项的像素位置来计算它距离 `Scrollable` 顶部的百分比。
位于可滚动区域顶部的列表项应产生 0%,
而位于可滚动区域底部的列表项应产生 100%。

<?code-excerpt "lib/excerpt5.dart (paint-children-2)"?>
```dart
@override
Expand All @@ -434,6 +562,11 @@ and at 100%, you want `Alignment(0.0, 1.0)`.
These coordinates correspond to top and bottom
alignment, respectively.

使用滚动百分比来计算 `Alignment`。
在 0% 时,你需要 `Alignment(0.0, -1.0)`,
在 100% 时,你需要 `Alignment(0.0, 1.0)`。
这些坐标分别对应于顶部对齐和底部对齐。

<?code-excerpt "lib/excerpt5.dart (paint-children-3)" plaster="none"?>
```dart
@override
Expand Down Expand Up @@ -462,6 +595,10 @@ list item and the size of the background image,
to produce a `Rect` that determines where the
background image should be positioned.

使用 `verticalAlignment`,
结合列表项的大小和背景图片的大小,
生成一个 `Rect`,以确定背景图片的位置。

<?code-excerpt "lib/excerpt5.dart (paint-children-4)" plaster="none"?>
```dart
@override
Expand Down Expand Up @@ -499,6 +636,9 @@ the desired translation transformation.
It's this transformation over time that gives you the
parallax effect.

使用 `childRect`,根据所需的平移变换绘制背景图片。
正是这种随时间推移的变换效果产生了视差效果。

<?code-excerpt "lib/excerpt5.dart (paint-children-5)" plaster="none" ?>
```dart
@override
Expand Down Expand Up @@ -543,10 +683,19 @@ The `ParallaxFlowDelegate` repaints when the inputs change,
but the `ParallaxFlowDelegate` doesn't repaint every time
the scroll position changes.

你还需要一个最后的细节来实现视差效果。
`ParallaxFlowDelegate` 在参数发生变化时会重新绘制,
但它不会在每次滚动位置变化时都重新绘制。

Pass the `ScrollableState`'s `ScrollPosition` to
the `FlowDelegate` superclass so that the `FlowDelegate`
repaints every time the `ScrollPosition` changes.

将 `ScrollableState` 的 `ScrollPosition`
传递给 `FlowDelegate` 的父类,
以便在 `ScrollPosition` 每次变化时,
`FlowDelegate` 都会重新绘制。

<?code-excerpt "lib/main.dart (SuperScrollPosition)" replace="/;\n/;\n}/g"?>
```dart
class ParallaxFlowDelegate extends FlowDelegate {
Expand All @@ -562,12 +711,21 @@ Congratulations!
You now have a list of cards with parallax,
scrolling background images.

恭喜!
现在你拥有了一个带有视差滚动背景图片的卡片列表。

## Interactive example

## 交互示例

Run the app:

运行应用程序:

* Scroll up and down to observe the parallax effect.

向上和向下滚动,以观察视差效果。

<?code-excerpt "lib/main.dart"?>
```dartpad title="Flutter parallax scrolling hands-on example in DartPad" run="true"
import 'package:flutter/material.dart';
Expand Down