Skip to content

Commit

Permalink
[完成翻译] src/content/cookbook/effects/shimmer-loading.md
Browse files Browse the repository at this point in the history
  • Loading branch information
Pleasurecruise committed Sep 3, 2024
1 parent c41b836 commit ab0fe1c
Showing 1 changed file with 142 additions and 0 deletions.
142 changes: 142 additions & 0 deletions src/content/cookbook/effects/shimmer-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,54 @@ to communicate to users that data is loading is to
display a chrome color with a shimmer animation over
the shapes that approximate the type of content that is loading.

在应用开发中,加载时间是不可避免的。
从用户体验 (UX) 的角度来看,
最重要的是向用户展示正在进行加载操作。
一种常见的方式是通过显示带有微光动画的镀铬颜色覆盖在近似于正在加载内容形状的区域上,
以此来告知用户数据正在加载。

The following animation shows the app's behavior:

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

![Gif showing the UI loading](/assets/images/docs/cookbook/effects/UILoadingAnimation.gif){:.site-mobile-screenshot}

This recipe begins with the content widgets defined and positioned.
There is also a Floating Action Button (FAB) in the bottom-right
corner that toggles between a loading mode and a loaded mode
so that you can easily validate your implementation.

该示例从定义和定位内容 widgets 开始。
界面右下角还放置了一个浮动操作按钮 (FAB),
用于在加载模式和已加载模式之间切换,
以便您可以轻松验证您的实现。

## Draw the shimmer shapes

## 绘制微光效果的形状

The shapes that shimmer in this effect are independent
from the actual content that eventually loads.

在这个效果中闪烁的形状是独立于最终加载的实际内容的。

Therefore, the goal is to display shapes that represent
the eventual content as accurately as possible.

因此,目标是尽可能准确地显示代表最终内容的形状。

Displaying accurate shapes is easy in situations where the
content has a clear boundary. For example, in this recipe,
there are some circular images and some rounded rectangle images.
You can draw shapes that precisely match the outlines
of those images.

在内容有明确边界的情况下,
显示准确的形状很容易。
例如,在这个示例中,
有一些圆形图片和一些圆角矩形图片。
你可以绘制与这些图片轮廓完全匹配的形状。

On the other hand, consider the text that appears beneath the
rounded rectangle images. You won't know how many lines of
text exist until the text loads.
Expand All @@ -50,10 +75,21 @@ you draw a couple of very thin rounded rectangles that
represent the text that will appear. The shape and size
doesn't quite match, but that is OK.

另一方面,考虑显示在圆角矩形图片下方的文本。
在文本加载之前,你不会知道有多少行文本。
因此,尝试为每行文本绘制一个矩形是没有意义的。
相反,当数据加载时,
你可以绘制几个非常细的圆角矩形来代表即将出现的文本。
形状和大小可能不完全匹配,
但这样做是可以的。

Start with the circular list items at the top of the screen.
Ensure that each `CircleListItem` widget displays a circle
with a color while the image is loading.

从屏幕顶部的圆形列表项开始。确
保每个 `CircleListItem` widget 在图片加载时显示一个有颜色的圆形。

<?code-excerpt "lib/main.dart (CircleListItem)"?>
```dart
class CircleListItem extends StatelessWidget {
Expand Down Expand Up @@ -86,13 +122,21 @@ class CircleListItem extends StatelessWidget {
As long as your widgets display some kind of shape,
you can apply the shimmer effect in this recipe.

只要你的 widgets 显示某种形状,
你就可以在这个示例中应用微光效果。

Similar to the `CircleListItem` widgets,
ensure that the `CardListItem` widgets
display a color where the image will appear.
Also, in the `CardListItem` widget,
switch between the display of the text and
the rectangles based on the current loading status.

类似于 `CircleListItem` widget,
确保 `CardListItem` widget 件在图片将出现的地方显示颜色。
同时,在 `CardListItem` widget 中,
根据当前的加载状态在文本和矩形的显示之间切换。

<?code-excerpt "lib/main.dart (CardListItem)"?>
```dart
class CardListItem extends StatelessWidget {
Expand Down Expand Up @@ -181,24 +225,40 @@ whether it's loading or loaded.
By temporarily commenting out the image URLs,
you can see the two ways your UI renders.

现在,您的UI会根据是否正在加载或已加载来以不同方式呈现。
通过暂时注释掉图像URL,
您可以看到UI呈现的两种方式。


![Gif showing the shimmer animation](/assets/images/docs/cookbook/effects/LoadingShimmer.gif){:.site-mobile-screenshot}

The next goal is to paint all of the colored areas
with a single gradient that looks like a shimmer.

接下来的目标是将所有着色区域涂上一个看起来像微光效果的渐变。

## Paint the shimmer gradient

## 绘制微光渐变效果

The key to the effect achieved in this recipe is to use a widget
called [`ShaderMask`][]. The `ShaderMask` widget, as the name suggests,
applies a shader to its child, but only in the areas where
the child already painted something. For example,
you'll apply a shader to only the black shapes that you
configured earlier.

实现这一效果的关键是使用一个名为 [`ShaderMask`][] 的 widget。
顾名思义, `ShaderMask` widget 将着色器应用于其子 widget,
但仅在子小部件已经绘制了内容的区域。
例如,你将把着色器应用于之前配置的黑色形状上。

Define a chrome-colored, linear gradient that gets applied to the
shimmer shapes.

定义一个铬色的线性渐变,
将其应用于闪烁的形状。

<?code-excerpt "lib/main.dart (shimmerGradient)"?>
```dart
const _shimmerGradient = LinearGradient(
Expand All @@ -225,6 +285,12 @@ gradient as a shader with a `blendMode` of `srcATop`.
The `srcATop` blend mode replaces any color that your
`child` widget painted with the shader color.

定义一个新的叫做 `ShimmerLoading` 的 stateful widget,
它将给定的 `child` widget 包裹在 `ShaderMask` 中。
配置 `ShaderMask` widget 以将微光渐变作为着色器应用,
并使用 `srcATop` 的混合模式。
`srcATop` 混合模式会将 `child` widget 绘制的任何颜色替换为着色器颜色。

<?code-excerpt "lib/main.dart (ShimmerLoading)"?>
```dart
class ShimmerLoading extends StatefulWidget {
Expand Down Expand Up @@ -261,6 +327,8 @@ class _ShimmerLoadingState extends State<ShimmerLoading> {

Wrap your `CircleListItem` widgets with a `ShimmerLoading` widget.

将您的 `CircleListItem` widgets 用 `ShimmerLoading` widget 进行包裹。

<?code-excerpt "lib/shimmer_loading_items.dart (buildTopRowItem)"?>
```dart
Widget _buildTopRowItem() {
Expand All @@ -273,6 +341,8 @@ Widget _buildTopRowItem() {

Wrap your `CardListItem` widgets with a `ShimmerLoading` widget.

将您的 `CardListItem` widgets 用 `ShimmerLoading` widget 进行包裹。

<?code-excerpt "lib/shimmer_loading_items.dart (buildListItem)"?>
```dart
Widget _buildListItem() {
Expand All @@ -289,6 +359,9 @@ When your shapes are loading, they now display
the shimmer gradient that is
returned from the `shaderCallback`.

当您的形状正在加载时,
它们现在显示了从 `shaderCallback` 返回的微光加载。

This is a big step in the right direction,
but there's a problem with this gradient display.
Each `CircleListItem` widget and each `CardListItem` widget
Expand All @@ -297,14 +370,27 @@ For this recipe, the entire screen should
look like one, big shimmering surface.
You solve this problem in the next step.

这是朝着正确方向迈出的重要一步,
但这个渐变显示存在一个问题。
每个 `CircleListItem` widget 和每个 `CardListItem` widget
都显示了渐变的一个新版本。
对于这个示例,整个屏幕应该看起来像一个大的微光表面。
您将在下一步解决这个问题。

## Paint one big shimmer

## 绘制一个大面积的微光效果

To paint one big shimmer across the screen,
each `ShimmerLoading` widget needs
to paint the same full-screen gradient based
on the position of that `ShimmerLoading`
widget on the screen.

为了在整个屏幕上绘制一个大面积的微光效果,
每个 `ShimmerLoading` widget 需要根据
`ShimmerLoading` widget 在屏幕上的位置绘制相同的全屏渐变。

To be more precise, rather than assume that the shimmer
should take up the entire screen,
there should be some area that shares the shimmer.
Expand All @@ -317,10 +403,22 @@ Then, each `ShimmerLoading` widget gets a reference
to the `Shimmer` ancestor
and requests the desired size and gradient to display.

更准确地说,不应该假设微光效果会占据整个屏幕,
而是应该有一个区域共享微光效果。
这个区域可能会占据整个屏幕,也可能不会。
解决这个问题的方式是在所有的 `ShimmerLoading` widget 上方
定义另一个 widget ,称为 `Shimmer`
然后,每个 `ShimmerLoading` widget 可以引用 `Shimmer` 祖先,
并请求显示所需的大小和渐变。

Define a new stateful widget called `Shimmer` that
takes in a [`LinearGradient`][] and provides descendants
with access to its `State` object.

定义一个新的 stateful widget ,命名为 `Shimmer`
它接受一个 [`LinearGradient`][] 并为
后代 widget 提供访问其 `State` 对象的权限。

<?code-excerpt "lib/main.dart (Shimmer)"?>
```dart
class Shimmer extends StatefulWidget {
Expand Down Expand Up @@ -355,6 +453,10 @@ the size of the `ShimmerState`'s `RenderBox`,
and look up the position of a descendant within the
`ShimmerState`'s `RenderBox`.

`ShimmerState` 类中添加方法,
以提供对 `linearGradient``ShimmerState``RenderBox` 的大小
以及查找 `ShimmerState``RenderBox` 中某个后代位置的访问权限。

<?code-excerpt "lib/shimmer_state.dart (ShimmerState)"?>
```dart
class ShimmerState extends State<Shimmer> {
Expand Down Expand Up @@ -387,6 +489,8 @@ class ShimmerState extends State<Shimmer> {

Wrap all of your screen's content with the `Shimmer` widget.

将屏幕上的所有内容都用 `Shimmer` widget 包裹起来。

<?code-excerpt "lib/main.dart (ExampleUiAnimationState)"?>
```dart
class _ExampleUiLoadingAnimationState extends State<ExampleUiLoadingAnimation> {
Expand All @@ -407,6 +511,8 @@ class _ExampleUiLoadingAnimationState extends State<ExampleUiLoadingAnimation> {
Use the `Shimmer` widget within your
`ShimmerLoading` widget to paint the shared gradient.

在你的 `ShimmerLoading` widget 中使用 `Shimmer` widget 来绘制共享的渐变效果。

<?code-excerpt "lib/shimmer_loading_state_pt2.dart (ShimmerLoadingStatePt2)"?>
```dart
class _ShimmerLoadingState extends State<ShimmerLoading> {
Expand Down Expand Up @@ -451,19 +557,33 @@ Your `ShimmerLoading` widgets now display a shared
gradient that takes up all of the space within the
`Shimmer` widget.

你的 `ShimmerLoading` widget 现在显示了一个共享的渐变效果,
这个效果覆盖了 `Shimmer` widget 内部的所有空间。

## Animate the shimmer

## 给微光效果添加动画

The shimmer gradient needs to move in order to
give the appearance of a shimmering shine.

微光渐变需要移动以产生微光的效果。

The `LinearGradient` has a property called `transform`
that can be used to transform the appearance of the gradient,
for example, to move it horizontally.
The `transform` property accepts a `GradientTransform` instance.

`LinearGradient` 有一个名为 `transform` 的属性,
可以用来改变渐变的外观,例如,使其水平移动。
`transform` 属性接受一个 `GradientTransform` 实例。

Define a class called `_SlidingGradientTransform` that implements
`GradientTransform` to achieve the appearance of horizontal sliding.

定义一个名为 `_SlidingGradientTransform` 的类,
来实现 `GradientTransform` 接口,以实现水平滑动的效果。

<?code-excerpt "lib/original_example.dart (sliding-gradient-transform)"?>
```dart
class _SlidingGradientTransform extends GradientTransform {
Expand All @@ -485,6 +605,9 @@ in order to create the appearance of motion.
To change the percentage, configure an
[`AnimationController`][] in the `ShimmerState` class.

渐变滑动百分比随时间变化,以创造运动的效果。
要改变百分比,请在 `ShimmerState` 类中配置一个 [`AnimationController`][]

<?code-excerpt "lib/original_example.dart (shimmer-state-animation)" replace="/\/\/ code-excerpt-closing-bracket/}/g"?>
```dart
class ShimmerState extends State<Shimmer> with SingleTickerProviderStateMixin {
Expand All @@ -509,6 +632,9 @@ class ShimmerState extends State<Shimmer> with SingleTickerProviderStateMixin {
Apply the `_SlidingGradientTransform` to the `gradient`
by using the `_shimmerController`'s `value` as the `slidePercent`.

通过使用 `_shimmerController``value` 作为 `slidePercent`
`_SlidingGradientTransform` 应用到 `gradient` 上。

<?code-excerpt "lib/original_example.dart (linear-gradient)"?>
```dart
LinearGradient get gradient => LinearGradient(
Expand All @@ -526,9 +652,15 @@ The gradient now animates, but your individual
as the gradient changes. Therefore, it looks like nothing
is happening.

渐变现在已经动画化了,
但你的单个 `ShimmerLoading` widget 在渐变变化时没有重新绘制自己。
因此,看起来什么也没有发生。

Expose the `_shimmerController` from `ShimmerState`
as a [`Listenable`][].

`ShimmerState` 中将 `_shimmerController` 暴露为一个 [`Listenable`][]

<?code-excerpt "lib/original_example.dart (shimmer-changes)"?>
```dart
Listenable get shimmerChanges => _shimmerController;
Expand All @@ -538,6 +670,10 @@ In `ShimmerLoading`, listen for changes to the ancestor
`ShimmerState`'s `shimmerChanges` property,
and repaint the shimmer gradient.

`ShimmerLoading` 中,
监听祖先 `ShimmerState``shimmerChanges` 属性的变化,
并重新绘制微光渐变效果。

<?code-excerpt "lib/original_example.dart (shimmer-loading-state)" replace="/\/\/ code-excerpt-closing-bracket/}/g"?>
```dart
class _ShimmerLoadingState extends State<ShimmerLoading> {
Expand Down Expand Up @@ -576,8 +712,14 @@ You now have a full-screen,
animated shimmer effect that turns
on and off as the content loads.

恭喜!
你现在拥有了一个全屏的动画微光效果,
它在内容加载时会打开和关闭。

## Interactive example

## 交互示例

<?code-excerpt "lib/original_example.dart" remove="code-excerpt-closing-bracket"?>
```dartpad title="Flutter shimmer loading hands-on example in DartPad" run="true"
import 'package:flutter/material.dart';
Expand Down

0 comments on commit ab0fe1c

Please sign in to comment.