代码之家  ›  专栏  ›  技术社区  ›  soupjake

如何为tween<offset>转换renderbox全局位置坐标?

  •  0
  • soupjake  · 技术社区  · 6 年前

    我正在尝试创建自己的 Hero 样式转换使用 SlideTransition 有了这个职位 Offset 从用户点击上一屏幕上的项目的位置开始。

    这是我当前用于接收用户点击屏幕位置的坐标值(我只需要 dy 价值):

    GestureDetector(
        child: //stuff
        onTapDown: (TapDownDetails details) async {
            RenderBox box = context.findRenderObject();
            double position = box.localToGlobal(details.globalPosition).dy;
                await Navigator.push(context, MaterialPageRoute(builder: (context) {
            return SecondPage(startPosition: position);
            }));
        },
    )
    

    然后我把这个传给 SecondPage 并将其用作 Animation<Offset> 在我的 initState :

    @override
    void initState() {
        controller = new AnimationController(
            duration: const Duration(milliseconds: 2000), vsync: this);
        curve = CurvedAnimation(parent: controller, curve: Curves.easeInOut);
        offset = new Tween<Offset>(begin: Offset(0.0, widget.startPosition), end: Offset.zero).animate(curve);
        controller.forward();
        super.initState();
    }
    

    我面临的问题是找到一种方法来正确地转换 值与 Tween<Offset> 用作 值与say值一起出现 250-300 在屏幕的一半,但同样适用于 Offset(0.0, widget.startPosition) 将在附近 2.0 以匹配相同的位置。我尝试过各种数学方法来匹配这些(例如 但是我没有找到与它完全匹配的东西。

    如果有人知道正确的方法/精确的数学,我必须执行匹配这些值,我会永远爱你。

    编辑:自我包含的例子,我正在努力实现什么,你可以玩。我目前正在使用 double position = (box.globalToLocal(details.globalPosition).dy) / box.size.height * 3; 因为这是我找到的最接近的匹配。

    import 'package:flutter/material.dart';
    
    class HomePage extends StatefulWidget {
      @override
      State createState() => HomePageState();
    }
    
    class HomePageState extends State<HomePage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            backgroundColor: Colors.indigo.shade200,
            floatingActionButton: Padding(
                padding: EdgeInsets.only(
                    bottom: MediaQuery.of(context).size.height / 35),
                child: FloatingActionButton(
                  child: Icon(Icons.add),
                  backgroundColor: Colors.grey.shade200,
                  foregroundColor: Colors.black,
                  onPressed: () {},
                )),
            body: Stack(children: <Widget>[
              SingleChildScrollView(
                  child: Padding(
                      padding: EdgeInsets.only(
                          bottom: MediaQuery.of(context).size.height / 11),
                      child:
                          Column(children: <Widget>[Stack(children: getCards())]))),
              Container(
                  height: MediaQuery.of(context).size.height / 5,
                  width: MediaQuery.of(context).size.width,
                  decoration: BoxDecoration(
                      color: Colors.grey.shade200,
                      borderRadius:
                          BorderRadius.only(bottomLeft: Radius.circular(100.0))),
                  child: Center(
                      child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: <Widget>[
                        Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            InkWell(
                                child: Padding(
                              padding: EdgeInsets.all(6.0),
                              child: Container(
                                height: 40.0,
                                width: 40.0,
                                decoration: BoxDecoration(
                                    shape: BoxShape.circle,
                                    border: Border.all(
                                        width: 2.0, color: Colors.pink.shade300)),
                                child: Icon(Icons.sentiment_satisfied),
                              ),
                            )),
                            Text('Tab1',
                                style: TextStyle(
                                    fontSize: 10.0,
                                    fontWeight: FontWeight.bold,
                                    color: Colors.black38))
                          ],
                        ),
                        Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            InkWell(
                                child: Padding(
                              padding: EdgeInsets.all(6.0),
                              child: Container(
                                height: 40.0,
                                width: 40.0,
                                decoration: BoxDecoration(
                                    shape: BoxShape.circle,
                                    border: Border.all(
                                        width: 2.0, color: Colors.pink.shade300)),
                                child: Icon(Icons.trending_up),
                              ),
                            )),
                            Text('Tab2',
                                style: TextStyle(
                                    fontSize: 10.0,
                                    fontWeight: FontWeight.bold,
                                    color: Colors.black38))
                          ],
                        ),
                        Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            InkWell(
                                child: Padding(
                              padding: EdgeInsets.all(6.0),
                              child: Container(
                                height: 40.0,
                                width: 40.0,
                                decoration: BoxDecoration(
                                    shape: BoxShape.circle,
                                    border: Border.all(
                                        width: 2.0, color: Colors.pink.shade300)),
                                child: Icon(Icons.favorite_border),
                              ),
                            )),
                            Text('Tab3',
                                style: TextStyle(
                                    fontSize: 10.0,
                                    fontWeight: FontWeight.bold,
                                    color: Colors.black38))
                          ],
                        )
                      ])))
            ]));
      }
    
      List<Widget> getCards() {
        List<Widget> widgets = new List<Widget>();
        for (int i = 0; i < 5; i++) {
          widgets.add(GestureDetector(
            child: Container(
              margin: EdgeInsets.only(top: (i * 175).toDouble()),
              padding: EdgeInsets.all(60.0),
              height: 300.0,
              decoration: BoxDecoration(
                color: getColor(i),
                borderRadius: BorderRadius.only(bottomLeft: Radius.circular(100.0)),
              ),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.end,
                children: <Widget>[
                  Center(
                          child: Text('Text ' + (i + 1).toString(),
                              maxLines: 2,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                                  color: Colors.white,
                                  fontSize: 26.0,
                                  fontWeight: FontWeight.bold)))
                ],
              ),
            ),
            onTapDown: (TapDownDetails details) async {
              RenderBox box = context.findRenderObject();
              double position = (box.globalToLocal(details.globalPosition).dy) / box.size.height * 3;
              await Navigator.push(context, CustomPageRoute(builder: (context) {
                return SecondPage(index: i, startPosition: position);
              }));
            },
          ));
        }
        return widgets.reversed.toList();
      }
    }
    
    class SecondPage extends StatefulWidget {
      final int index;
      final double startPosition;
    
      SecondPage({this.index, this.startPosition});
    
      @override
      State createState() => SecondPageState();
    }
    
    class SecondPageState extends State<SecondPage> with TickerProviderStateMixin {
      AnimationController controller;
      Animation curve;
      Animation<Offset> offset;
    
      @override
      void initState() {
        controller = new AnimationController(
            duration: const Duration(milliseconds: 2000), vsync: this);
        curve = CurvedAnimation(parent: controller, curve: Curves.easeInOut);
        offset = new Tween<Offset>(
                begin: Offset(0.0, widget.startPosition), end: Offset.zero)
            .animate(curve);
        controller.forward();
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            backgroundColor: Colors.indigo,
            body: Material(
                color: Colors.transparent,
                child: Stack(children: <Widget>[
                  SlideTransition(
                      position: offset,
                      child: Container(
                          height: 200.0,
                          decoration: BoxDecoration(
                              color: getColor(widget.index),
                              borderRadius: BorderRadius.only(
                                  bottomLeft: Radius.circular(100.0))),
                          child: Padding(
                              padding: EdgeInsets.only(top: 28.0),
                              child: Stack(
                                children: <Widget>[
                                  Align(
                                      alignment: Alignment.topLeft,
                                      child: IconButton(
                                        icon: Icon(Icons.arrow_back,
                                            color: Colors.white),
                                        onPressed: () {
                                          Navigator.of(context).pop(true);
                                        },
                                      )),
                                  Align(
                                      alignment: Alignment.topRight,
                                      child: IconButton(
                                        icon:
                                            Icon(Icons.launch, color: Colors.white),
                                        onPressed: () {
                                          print("launch website");
                                        },
                                      )),
                                      Align(
                                      alignment: Alignment.center,
                                      child: Padding(
                                          padding: EdgeInsets.symmetric(
                                              horizontal: MediaQuery.of(context)
                                                      .size
                                                      .width /
                                                  5),
                                          child: Material(
                                                color: Colors.transparent,
                                                child: Text('Text ' + (widget.index + 1).toString(),
                                                  maxLines: 2,
                                                  overflow: TextOverflow.ellipsis,
                                                  style: TextStyle(
                                                      color: Colors.white,
                                                      fontSize: 26.0,
                                                      fontWeight:
                                                          FontWeight.bold)))))
                                ],
                              ))))
                ])));
      }
    }
    
    class CustomPageRoute<T> extends MaterialPageRoute<T> {
      CustomPageRoute({WidgetBuilder builder, RouteSettings settings})
          : super(builder: builder, settings: settings);
    
      @override
      Widget buildTransitions(BuildContext context, Animation<double> animation,
          Animation<double> secondaryAnimation, Widget child) {
        return child;
      }
    }
    
    Color getColor(int index) {
      switch (index) {
        case 0:
          return Colors.pink.shade300;
        case 1:
          return Colors.purple.shade300;
        case 2:
          return Colors.deepPurple.shade400;
        case 3:
          return Colors.deepPurple.shade900;
        case 4:
          return Colors.indigo.shade900;
        default:
          return Colors.red;
      }
    }
    
    1 回复  |  直到 6 年前
        1
  •  3
  •   Mertus    6 年前

    在这种情况下,查找renderbox并不有用,因为所有卡片都放在一个堆栈的上下文中。findrenderObject查找最上面的堆栈。该堆栈覆盖整个屏幕,因此当您从全局位置转换为本地位置时,您将得到相同的位置。

    您可以这样计算第二个屏幕的正确偏移量:

      var scrollOffset = controller.position.pixels;
      double position =  ((i * 175).toDouble() + 100 - scrollOffset) / 200;
    
    1. (i*175):每张卡从顶部偏移。
    2. 100:第一屏卡片高度差 第二屏(第一屏300,第二屏200)。我们把这个添加到 公司为差异,所以卡的位置将是 同样。
    3. 200:第二个屏幕中卡的高度。我们除以这个 因为

    翻译表示为按子级大小缩放的偏移量。

    最后,您需要给您的singleChildScrollView一个ScrollController来获取滚动偏移量。如果没有滚动偏移,则无法计算滚动卡时的正确位置(即卡4或卡5)。

    以下是主页状态的外观。

    class HomePageState extends State<HomePage> {
    
      ScrollController controller = new ScrollController();
    
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            backgroundColor: Colors.indigo.shade200,
            floatingActionButton: Padding(
                padding: EdgeInsets.only(
                    bottom: MediaQuery.of(context).size.height / 35),
                child: FloatingActionButton(
                  child: Icon(Icons.add),
                  backgroundColor: Colors.grey.shade200,
                  foregroundColor: Colors.black,
                  onPressed: () {},
                )),
            body: Stack(children: <Widget>[
              SingleChildScrollView(
                controller: controller,
                  child: Padding(
                      padding: EdgeInsets.only(
                          bottom: MediaQuery.of(context).size.height / 11),
                      child:
                          Column(children: <Widget>[Stack(children: getCards())]))),
              Container(
                  height: MediaQuery.of(context).size.height / 5,
                  width: MediaQuery.of(context).size.width,
                  decoration: BoxDecoration(
                      color: Colors.grey.shade200,
                      borderRadius:
                          BorderRadius.only(bottomLeft: Radius.circular(100.0))),
                  child: Center(
                      child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: <Widget>[
                        Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            InkWell(
                                child: Padding(
                              padding: EdgeInsets.all(6.0),
                              child: Container(
                                height: 40.0,
                                width: 40.0,
                                decoration: BoxDecoration(
                                    shape: BoxShape.circle,
                                    border: Border.all(
                                        width: 2.0, color: Colors.pink.shade300)),
                                child: Icon(Icons.sentiment_satisfied),
                              ),
                            )),
                            Text('Tab1',
                                style: TextStyle(
                                    fontSize: 10.0,
                                    fontWeight: FontWeight.bold,
                                    color: Colors.black38))
                          ],
                        ),
                        Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            InkWell(
                                child: Padding(
                              padding: EdgeInsets.all(6.0),
                              child: Container(
                                height: 40.0,
                                width: 40.0,
                                decoration: BoxDecoration(
                                    shape: BoxShape.circle,
                                    border: Border.all(
                                        width: 2.0, color: Colors.pink.shade300)),
                                child: Icon(Icons.trending_up),
                              ),
                            )),
                            Text('Tab2',
                                style: TextStyle(
                                    fontSize: 10.0,
                                    fontWeight: FontWeight.bold,
                                    color: Colors.black38))
                          ],
                        ),
                        Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            InkWell(
                                child: Padding(
                              padding: EdgeInsets.all(6.0),
                              child: Container(
                                height: 40.0,
                                width: 40.0,
                                decoration: BoxDecoration(
                                    shape: BoxShape.circle,
                                    border: Border.all(
                                        width: 2.0, color: Colors.pink.shade300)),
                                child: Icon(Icons.favorite_border),
                              ),
                            )),
                            Text('Tab3',
                                style: TextStyle(
                                    fontSize: 10.0,
                                    fontWeight: FontWeight.bold,
                                    color: Colors.black38))
                          ],
                        )
                      ])))
            ]));
      }
    
      List<Widget> getCards() {
        List<Widget> widgets = new List<Widget>();
        for (int i = 0; i < 5; i++) {
          widgets.add(GestureDetector(
            child: Container(
              margin: EdgeInsets.only(top: (i * 175).toDouble()),
              padding: EdgeInsets.all(60.0),
              height: 300.0,
              decoration: BoxDecoration(
                color: getColor(i),
                borderRadius: BorderRadius.only(bottomLeft: Radius.circular(100.0)),
              ),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.end,
                children: <Widget>[
                  Center(
                          child: Text('Text ' + (i + 1).toString(),
                              maxLines: 2,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                                  color: Colors.white,
                                  fontSize: 26.0,
                                  fontWeight: FontWeight.bold)))
                ],
              ),
            ),
            onTapDown: (TapDownDetails details) async {
              var scrollOffset = controller.position.pixels;
              double position =  ((i * 175).toDouble() + 100 - scrollOffset) / 200;
    
              await Navigator.push(context, CustomPageRoute(builder: (context) {
                return SecondPage(index: i, startPosition: position);
              }));
            },
          ));
        }
        return widgets.reversed.toList();
      }
    }