Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
de499c2a
Unverified
Commit
de499c2a
authored
Oct 11, 2019
by
Dan Field
Committed by
GitHub
Oct 11, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Gradient transform (#42484)
parent
e7616070
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
192 additions
and
8 deletions
+192
-8
goldens.version
bin/internal/goldens.version
+1
-1
gradient.dart
packages/flutter/lib/src/painting/gradient.dart
+119
-7
gradient_test.dart
packages/flutter/test/painting/gradient_test.dart
+72
-0
No files found.
bin/internal/goldens.version
View file @
de499c2a
7efcec3e8b0bbb6748a992b23a0a89300aa323c7
fa13c1b039e693123888e434e4ee1f9ff79d3b6e
packages/flutter/lib/src/painting/gradient.dart
View file @
de499c2a
...
@@ -4,9 +4,11 @@
...
@@ -4,9 +4,11 @@
import
'dart:collection'
;
import
'dart:collection'
;
import
'dart:math'
as
math
;
import
'dart:math'
as
math
;
import
'dart:typed_data'
;
import
'dart:ui'
as
ui
show
Gradient
,
lerpDouble
;
import
'dart:ui'
as
ui
show
Gradient
,
lerpDouble
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'alignment.dart'
;
import
'alignment.dart'
;
import
'basic_types.dart'
;
import
'basic_types.dart'
;
...
@@ -57,6 +59,64 @@ _ColorsAndStops _interpolateColorsAndStops(
...
@@ -57,6 +59,64 @@ _ColorsAndStops _interpolateColorsAndStops(
return
_ColorsAndStops
(
interpolatedColors
,
interpolatedStops
);
return
_ColorsAndStops
(
interpolatedColors
,
interpolatedStops
);
}
}
/// Base class for transforming gradient shaders without applying the same
/// transform to the entire canvas.
///
/// For example, a [SweepGradient] normally starts its gradation at 3 o'clock
/// and draws clockwise. To have the sweep appear to start at 6 o'clock, supply
/// a [GradientRotation] of `pi/4` radians (i.e. 45 degrees).
@immutable
abstract
class
GradientTransform
{
/// A const constructor so that subclasses may be const.
const
GradientTransform
();
/// When a [Gradient] creates its [Shader], it will call this method to
/// determine what transform to apply to the shader for the given [Rect] and
/// [TextDirection].
///
/// Implementers may return null from this method, which achieves the same
/// final effect as returning [Matrix4.identity].
Matrix4
transform
(
Rect
bounds
,
{
TextDirection
textDirection
});
}
/// A [GradientTransform] that rotates the gradient around the center-point of
/// its bounding box.
///
/// {@tool sample}
///
/// This sample would rotate a sweep gradient by a quarter turn clockwise:
///
/// ```dart
/// const SweepGradient gradient = SweepGradient(
/// colors: <Color>[Color(0xFFFFFFFF), Color(0xFF009900)],
/// transform: GradientRotation(math.pi/4),
/// );
/// ```
@immutable
class
GradientRotation
extends
GradientTransform
{
/// Constructs a [GradientRotation] for the specified angle.
///
/// The angle is in radians in the clockwise direction.
const
GradientRotation
(
this
.
radians
);
/// The angle of rotation in radians in the clockwise direction.
final
double
radians
;
@override
Matrix4
transform
(
Rect
bounds
,
{
TextDirection
textDirection
})
{
assert
(
bounds
!=
null
);
final
double
sinRadians
=
math
.
sin
(
radians
);
final
double
oneMinusCosRadians
=
1
-
math
.
cos
(
radians
);
final
Offset
center
=
bounds
.
center
;
final
double
originX
=
sinRadians
*
center
.
dy
+
oneMinusCosRadians
*
center
.
dx
;
final
double
originY
=
-
sinRadians
*
center
.
dx
+
oneMinusCosRadians
*
center
.
dy
;
return
Matrix4
.
identity
()
..
translate
(
originX
,
originY
)
..
rotateZ
(
radians
);
}
}
/// A 2D gradient.
/// A 2D gradient.
///
///
/// This is an interface that allows [LinearGradient], [RadialGradient], and
/// This is an interface that allows [LinearGradient], [RadialGradient], and
...
@@ -76,9 +136,17 @@ abstract class Gradient {
...
@@ -76,9 +136,17 @@ abstract class Gradient {
/// If specified, the [stops] argument must have the same number of entries as
/// If specified, the [stops] argument must have the same number of entries as
/// [colors] (this is also not verified until the [createShader] method is
/// [colors] (this is also not verified until the [createShader] method is
/// called).
/// called).
///
/// The [transform] argument can be applied to transform _only_ the gradient,
/// without rotating the canvas itself or other geometry on the canvas. For
/// example, a `GradientRotation(math.pi/4)` will result in a [SweepGradient]
/// that starts from a position of 6 o'clock instead of 3 o'clock, assuming
/// no other rotation or perspective transformations have been applied to the
/// [Canvas]. If null, no transformation is applied.
const
Gradient
({
const
Gradient
({
@required
this
.
colors
,
@required
this
.
colors
,
this
.
stops
,
this
.
stops
,
this
.
transform
,
})
:
assert
(
colors
!=
null
);
})
:
assert
(
colors
!=
null
);
/// The colors the gradient should obtain at each of the stops.
/// The colors the gradient should obtain at each of the stops.
...
@@ -107,6 +175,12 @@ abstract class Gradient {
...
@@ -107,6 +175,12 @@ abstract class Gradient {
/// with the first stop at 0.0 and the last stop at 1.0.
/// with the first stop at 0.0 and the last stop at 1.0.
final
List
<
double
>
stops
;
final
List
<
double
>
stops
;
/// The transform, if any, to apply to the gradient.
///
/// This transform is in addition to any other transformations applied to the
/// canvas, but does not add any transformations to the canvas.
final
GradientTransform
transform
;
List
<
double
>
_impliedStops
()
{
List
<
double
>
_impliedStops
()
{
if
(
stops
!=
null
)
if
(
stops
!=
null
)
return
stops
;
return
stops
;
...
@@ -124,6 +198,9 @@ abstract class Gradient {
...
@@ -124,6 +198,9 @@ abstract class Gradient {
/// If the gradient's configuration is text-direction-dependent, for example
/// If the gradient's configuration is text-direction-dependent, for example
/// it uses [AlignmentDirectional] objects instead of [Alignment]
/// it uses [AlignmentDirectional] objects instead of [Alignment]
/// objects, then the `textDirection` argument must not be null.
/// objects, then the `textDirection` argument must not be null.
///
/// The shader's transform will be resolved from the [transform] of this
/// gradient.
Shader
createShader
(
Rect
rect
,
{
TextDirection
textDirection
});
Shader
createShader
(
Rect
rect
,
{
TextDirection
textDirection
});
/// Returns a new gradient with its properties scaled by the given factor.
/// Returns a new gradient with its properties scaled by the given factor.
...
@@ -220,6 +297,10 @@ abstract class Gradient {
...
@@ -220,6 +297,10 @@ abstract class Gradient {
assert
(
a
!=
null
&&
b
!=
null
);
assert
(
a
!=
null
&&
b
!=
null
);
return
t
<
0.5
?
a
.
scale
(
1.0
-
(
t
*
2.0
))
:
b
.
scale
((
t
-
0.5
)
*
2.0
);
return
t
<
0.5
?
a
.
scale
(
1.0
-
(
t
*
2.0
))
:
b
.
scale
((
t
-
0.5
)
*
2.0
);
}
}
Float64List
_resolveTransform
(
Rect
bounds
,
TextDirection
textDirection
)
{
return
transform
?.
transform
(
bounds
,
textDirection:
textDirection
)?.
storage
;
}
}
}
/// A 2D linear gradient.
/// A 2D linear gradient.
...
@@ -284,10 +365,11 @@ class LinearGradient extends Gradient {
...
@@ -284,10 +365,11 @@ class LinearGradient extends Gradient {
@required
List
<
Color
>
colors
,
@required
List
<
Color
>
colors
,
List
<
double
>
stops
,
List
<
double
>
stops
,
this
.
tileMode
=
TileMode
.
clamp
,
this
.
tileMode
=
TileMode
.
clamp
,
GradientTransform
transform
,
})
:
assert
(
begin
!=
null
),
})
:
assert
(
begin
!=
null
),
assert
(
end
!=
null
),
assert
(
end
!=
null
),
assert
(
tileMode
!=
null
),
assert
(
tileMode
!=
null
),
super
(
colors:
colors
,
stops:
stops
);
super
(
colors:
colors
,
stops:
stops
,
transform:
transform
);
/// The offset at which stop 0.0 of the gradient is placed.
/// The offset at which stop 0.0 of the gradient is placed.
///
///
...
@@ -334,7 +416,7 @@ class LinearGradient extends Gradient {
...
@@ -334,7 +416,7 @@ class LinearGradient extends Gradient {
return
ui
.
Gradient
.
linear
(
return
ui
.
Gradient
.
linear
(
begin
.
resolve
(
textDirection
).
withinRect
(
rect
),
begin
.
resolve
(
textDirection
).
withinRect
(
rect
),
end
.
resolve
(
textDirection
).
withinRect
(
rect
),
end
.
resolve
(
textDirection
).
withinRect
(
rect
),
colors
,
_impliedStops
(),
tileMode
,
colors
,
_impliedStops
(),
tileMode
,
_resolveTransform
(
rect
,
textDirection
),
);
);
}
}
...
@@ -533,11 +615,12 @@ class RadialGradient extends Gradient {
...
@@ -533,11 +615,12 @@ class RadialGradient extends Gradient {
this
.
tileMode
=
TileMode
.
clamp
,
this
.
tileMode
=
TileMode
.
clamp
,
this
.
focal
,
this
.
focal
,
this
.
focalRadius
=
0.0
,
this
.
focalRadius
=
0.0
,
GradientTransform
transform
,
})
:
assert
(
center
!=
null
),
})
:
assert
(
center
!=
null
),
assert
(
radius
!=
null
),
assert
(
radius
!=
null
),
assert
(
tileMode
!=
null
),
assert
(
tileMode
!=
null
),
assert
(
focalRadius
!=
null
),
assert
(
focalRadius
!=
null
),
super
(
colors:
colors
,
stops:
stops
);
super
(
colors:
colors
,
stops:
stops
,
transform:
transform
);
/// The center of the gradient, as an offset into the (-1.0, -1.0) x (1.0, 1.0)
/// The center of the gradient, as an offset into the (-1.0, -1.0) x (1.0, 1.0)
/// square describing the gradient which will be mapped onto the paint box.
/// square describing the gradient which will be mapped onto the paint box.
...
@@ -605,7 +688,7 @@ class RadialGradient extends Gradient {
...
@@ -605,7 +688,7 @@ class RadialGradient extends Gradient {
center
.
resolve
(
textDirection
).
withinRect
(
rect
),
center
.
resolve
(
textDirection
).
withinRect
(
rect
),
radius
*
rect
.
shortestSide
,
radius
*
rect
.
shortestSide
,
colors
,
_impliedStops
(),
tileMode
,
colors
,
_impliedStops
(),
tileMode
,
null
,
// transform
_resolveTransform
(
rect
,
textDirection
),
focal
==
null
?
null
:
focal
.
resolve
(
textDirection
).
withinRect
(
rect
),
focal
==
null
?
null
:
focal
.
resolve
(
textDirection
).
withinRect
(
rect
),
focalRadius
*
rect
.
shortestSide
,
focalRadius
*
rect
.
shortestSide
,
);
);
...
@@ -771,9 +854,36 @@ class RadialGradient extends Gradient {
...
@@ -771,9 +854,36 @@ class RadialGradient extends Gradient {
/// Color(0xFF4285F4), // blue again to seamlessly transition to the start
/// Color(0xFF4285F4), // blue again to seamlessly transition to the start
/// ],
/// ],
/// stops: const <double>[0.0, 0.25, 0.5, 0.75, 1.0],
/// stops: const <double>[0.0, 0.25, 0.5, 0.75, 1.0],
/// ),
/// ),
/// )
/// )
/// ```
/// {@end-tool}
///
/// {@tool sample}
///
/// This sample takes the above gradient and rotates it by `math.pi/4` radians,
/// i.e. 45 degrees.
///
/// ```dart
/// Container(
/// decoration: BoxDecoration(
/// gradient: SweepGradient(
/// center: FractionalOffset.center,
/// startAngle: 0.0,
/// endAngle: math.pi * 2,
/// colors: const <Color>[
/// Color(0xFF4285F4), // blue
/// Color(0xFF34A853), // green
/// Color(0xFFFBBC05), // yellow
/// Color(0xFFEA4335), // red
/// Color(0xFF4285F4), // blue again to seamlessly transition to the start
/// ],
/// stops: const <double>[0.0, 0.25, 0.5, 0.75, 1.0],
/// transform: GradientRotation(math.pi/4),
/// ),
/// ),
/// ),
///
)
/// )
/// ```
/// ```
/// {@end-tool}
/// {@end-tool}
///
///
...
@@ -797,11 +907,12 @@ class SweepGradient extends Gradient {
...
@@ -797,11 +907,12 @@ class SweepGradient extends Gradient {
@required
List
<
Color
>
colors
,
@required
List
<
Color
>
colors
,
List
<
double
>
stops
,
List
<
double
>
stops
,
this
.
tileMode
=
TileMode
.
clamp
,
this
.
tileMode
=
TileMode
.
clamp
,
GradientTransform
transform
,
})
:
assert
(
center
!=
null
),
})
:
assert
(
center
!=
null
),
assert
(
startAngle
!=
null
),
assert
(
startAngle
!=
null
),
assert
(
endAngle
!=
null
),
assert
(
endAngle
!=
null
),
assert
(
tileMode
!=
null
),
assert
(
tileMode
!=
null
),
super
(
colors:
colors
,
stops:
stops
);
super
(
colors:
colors
,
stops:
stops
,
transform:
transform
);
/// The center of the gradient, as an offset into the (-1.0, -1.0) x (1.0, 1.0)
/// The center of the gradient, as an offset into the (-1.0, -1.0) x (1.0, 1.0)
/// square describing the gradient which will be mapped onto the paint box.
/// square describing the gradient which will be mapped onto the paint box.
...
@@ -846,6 +957,7 @@ class SweepGradient extends Gradient {
...
@@ -846,6 +957,7 @@ class SweepGradient extends Gradient {
colors
,
_impliedStops
(),
tileMode
,
colors
,
_impliedStops
(),
tileMode
,
startAngle
,
startAngle
,
endAngle
,
endAngle
,
_resolveTransform
(
rect
,
textDirection
),
);
);
}
}
...
...
packages/flutter/test/painting/gradient_test.dart
View file @
de499c2a
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:math'
as
math
;
import
'dart:math'
as
math
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/painting.dart'
;
import
'package:flutter/painting.dart'
;
...
@@ -765,4 +766,75 @@ void main() {
...
@@ -765,4 +766,75 @@ void main() {
expect
(()
{
test2a
.
createShader
(
rect
);
},
throwsArgumentError
);
expect
(()
{
test2a
.
createShader
(
rect
);
},
throwsArgumentError
);
expect
(()
{
test2b
.
createShader
(
rect
);
},
throwsArgumentError
);
expect
(()
{
test2b
.
createShader
(
rect
);
},
throwsArgumentError
);
});
});
group
(
'Transforms'
,
()
{
const
List
<
Color
>
colors
=
<
Color
>[
Color
(
0xFFFFFFFF
),
Color
(
0xFF000088
)];
const
Rect
rect
=
Rect
.
fromLTWH
(
0.0
,
0.0
,
300.0
,
400.0
);
const
List
<
Gradient
>
gradients45
=
<
Gradient
>[
LinearGradient
(
colors:
colors
,
transform:
GradientRotation
(
math
.
pi
/
4
)),
// A radial gradient won't be interesting to rotate unless the center is changed.
RadialGradient
(
colors:
colors
,
center:
Alignment
.
topCenter
,
transform:
GradientRotation
(
math
.
pi
/
4
)),
SweepGradient
(
colors:
colors
,
transform:
GradientRotation
(
math
.
pi
/
4
)),
];
const
List
<
Gradient
>
gradients90
=
<
Gradient
>[
LinearGradient
(
colors:
colors
,
transform:
GradientRotation
(
math
.
pi
/
2
)),
// A radial gradient won't be interesting to rotate unless the center is changed.
RadialGradient
(
colors:
colors
,
center:
Alignment
.
topCenter
,
transform:
GradientRotation
(
math
.
pi
/
2
)),
SweepGradient
(
colors:
colors
,
transform:
GradientRotation
(
math
.
pi
/
2
)),
];
const
Map
<
Type
,
String
>
gradientSnakeCase
=
<
Type
,
String
>
{
LinearGradient:
'linear_gradient'
,
RadialGradient:
'radial_gradient'
,
SweepGradient:
'sweep_gradient'
,
};
Future
<
void
>
runTest
(
WidgetTester
tester
,
Gradient
gradient
,
double
degrees
)
async
{
final
String
goldenName
=
'
${gradientSnakeCase[gradient.runtimeType]}
_
$degrees
.png'
;
final
Shader
shader
=
gradient
.
createShader
(
rect
,
);
final
Key
painterKey
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
SizedBox
.
fromSize
(
size:
rect
.
size
,
child:
RepaintBoundary
(
key:
painterKey
,
child:
CustomPaint
(
painter:
GradientPainter
(
shader
,
rect
)
),
),
),
));
await
expectLater
(
find
.
byKey
(
painterKey
),
matchesGoldenFile
(
goldenName
));
}
testWidgets
(
'Gradients - 45 degrees'
,
(
WidgetTester
tester
)
async
{
for
(
Gradient
gradient
in
gradients45
)
{
await
runTest
(
tester
,
gradient
,
45
);
}
});
testWidgets
(
'Gradients - 90 degrees'
,
(
WidgetTester
tester
)
async
{
for
(
Gradient
gradient
in
gradients90
)
{
await
runTest
(
tester
,
gradient
,
90
);
}
});
});
}
class
GradientPainter
extends
CustomPainter
{
const
GradientPainter
(
this
.
shader
,
this
.
rect
);
final
Shader
shader
;
final
Rect
rect
;
@override
void
paint
(
Canvas
canvas
,
Size
size
)
{
canvas
.
drawRect
(
rect
,
Paint
()..
shader
=
shader
);
}
@override
bool
shouldRepaint
(
CustomPainter
oldDelegate
)
=>
true
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment