Zac*_*Zac 3 dart flutter flutter-layout flutter-animation flutter-web
我正在尝试构建 Threejs 元素周期表螺旋视图的 Flutter Web 版本(请参见此处:https: //mrdoob.com/lab/javascript/thirdjs/css3d/periodictable/并单击“Helix”)
目前,我将所有元素图块放在一个堆栈中,并通过矩阵变换来定位和旋转它们。见下面的代码:
@override
Widget build(BuildContext context) {
double initialRotateX = (widget.atomicNumber - 1) *
(math.pi / Constants.numberOfElementsPerHalfCircle);
return Transform(
transform: Matrix4.identity()
..translate(
math.sin((widget.atomicNumber - 1) *
(math.pi / Constants.numberOfElementsPerHalfCircle)) *
Constants.radius,
widget.atomicNumber * 4,
-math.cos((widget.atomicNumber - 1) *
(math.pi / Constants.numberOfElementsPerHalfCircle)) *
Constants.radius,
)
..translate(Constants.elementCardHalfSize)
..rotateY(-initialRotateX)
..translate(-Constants.elementCardHalfSize),
child: Container(
decoration: BoxDecoration(
color: Constants.elementCardColor,
border: Border.all(width: 1, color: Colors.white.withOpacity(0.3))),
child: SizedBox(
width: Constants.elementCardWidth.toDouble(),
height: Constants.elementCardHeight.toDouble(),
child: ElementText()
),
),
);
Run Code Online (Sandbox Code Playgroud)
然后,所有这些卡片都被放置在另一个类的堆栈小部件内,并且该小部件被旋转,如下所示:
return AnimatedBuilder(
animation: widget.animationControllerX,
builder: (context, _) {
double rotateX =
widget.animationControllerX.value / Constants.turnResolution;
double rotateY =
widget.animationControllerY.value / Constants.turnResolution;
return Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(-rotateX)
..rotateX(rotateY),
alignment: Alignment.center,
child: Container(
child: Stack(
children: List<Widget>.generate(
Constants.numberOfElements,
(index) => ElementCard(
atomicNumber:
Constants.elements[(index + 1).toString()].atomicNumber,
),
),
),
),
);
},
);
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,应用了“..rotateY”变换,这使得当堆叠旋转时卡片看起来向前移动。
然而,旋转时,应该位于后面的卡片较小,但要涂在应该位于前面的卡片上。(毫无疑问,卡片处于正确的位置,因为我可以沿着 x 轴旋转它们并亲眼看到这一点,但是在绘画时,背面的卡片会画在正面卡片上的文本上。我相信这是因为堆栈总是根据它们提供的顺序来定位其小部件的高度,并且顺序在列表中是固定的。有什么方法可以解决这个问题吗?
截图来解释我的意思:
我可以更改图块的不透明度以使问题更加明显:
正如您所看到的,较大的卡片更靠近屏幕,因为这就是它们的变形方式,但它们在较小的卡片后面进行绘制。关于如何实现这一点有什么想法吗?
所以我找到了解决方案。它并不完美,但到目前为止它是有效的。
问题是 Flutter 按照 widget 出现的顺序绘制它们。由于较大的卡片在列表中的位置较高,因此即使它们的 z 坐标更近,也会先绘制它们,然后将较小的卡片绘制在它们之上。
解决方案是使用 Flow() 小部件动态更改卡片绘制的顺序。在我通常构建它们的子窗口小部件中,我将它们的转换存储在地图中。然后,在流委托函数中,访问这些转换,计算转换后每个小部件的 z 坐标(转换矩阵中的第 3 行,第 3 列(1 索引))。按 z 坐标对小部件进行排序,然后按该顺序将它们绘制在屏幕上。
这是相同的代码:
带有根 Flow 小部件的部分:
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: widget.animationControllerX,
builder: (context, _) {
double rotateX =
widget.animationControllerX.value / Constants.turnResolution;
double rotateY =
widget.animationControllerY.value / Constants.turnResolution;
return Container(
transform: Matrix4.identity()
..translate(
MediaQuery.of(context).size.width / 2,
),
child: Flow(
clipBehavior: Clip.none,
delegate: CustomFlowDelegate(rotateY: -rotateX, rotateX: rotateY),
children: List<Widget>.generate(
Constants.numberOfElements,
(index) => ElementCard(
atomicNumber:
Constants.elements[(index + 1).toString()].atomicNumber,
),
),
),
);
},
);
}
Run Code Online (Sandbox Code Playgroud)
小部件的 FlowDelegate:
class CustomFlowDelegate extends FlowDelegate {
CustomFlowDelegate({this.rotateX, this.rotateY});
final rotateY, rotateX;
@override
void paintChildren(FlowPaintingContext context) {
Map<int, double> zValue = {};
for (int i = 0; i < context.childCount; i++) {
var map = Constants.transformationsMap[i + 1];
Matrix4 transformedMatrix = Matrix4.identity()
..setEntry(3, 2, 0.001)
..setEntry(3, 1, 0.001)
..rotateY(this.rotateY)
..rotateX(this.rotateX)
..translate(
map['translateOneX'], map['translateOneY'], map['translateOneZ'])
..translate(map['translateTwoX'])
..rotateY(map['rotateThreeY'])
..translate(map['translateFourX']);
zValue[i + 1] = transformedMatrix.getRow(2)[2];
}
var sortedKeys = zValue.keys.toList(growable: false)
..sort((k1, k2) => zValue[k1].compareTo(zValue[k2]));
LinkedHashMap sortedMap = new LinkedHashMap.fromIterable(sortedKeys,
key: (k) => k, value: (k) => zValue[k]);
for (int key in sortedMap.keys) {
var map = Constants.transformationsMap[key];
context.paintChild(
key - 1,
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..setEntry(3, 1, 0.001)
..rotateY(this.rotateY)
..rotateX(this.rotateX)
..translate(
map['translateOneX'], map['translateOneY'], map['translateOneZ'])
..translate(map['translateTwoX'])
..rotateY(map['rotateThreeY'])
..translate(map['translateFourX']),
);
}
}
@override
bool shouldRepaint(covariant FlowDelegate oldDelegate) {
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
结果如下:
并且将不透明度调高一点,这样您就可以真正看到修复的效果:
是的,我知道将转换存储在地图中然后从另一个页面对它们进行排序并不是最佳实践。
希望这对某人有帮助。
| 归档时间: |
|
| 查看次数: |
620 次 |
| 最近记录: |