Repository: flutter
Issue: [web] Frame drops playing animation
Labels: engine,a: animation,c: performance,platform-web,perf: speed,has reproducible steps,P2,found in release: 2.2,found in release: 2.6,team-web,triaged-web

Hi,
I work on animations and can't figure out why this code is working perfectly well on iOS and Android, but lagging when building for web.
When I inspect performances in Chrome DevTools, one frame out of two is dropped. I don't understand why. Is that a flutter web bug?
Thanks for your answers.

```
import 'dart:math';
import 'package:flutter/material.dart';

class TestAnimationBody extends StatefulWidget {
  @override
  _TestAnimationBodyState createState() => _TestAnimationBodyState();
}

class _TestAnimationBodyState extends State<TestAnimationBody>
    with TickerProviderStateMixin {
  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    final circles = [
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
      Circle(size: size),
    ];
    return Stack(children: circles);
  }
}

class Circle extends StatefulWidget {
  final Size size;
  final int index;
  Circle({
    Key? key,
    required this.size,
    this.index = 0,
  }) : super(key: key);

  @override
  _CircleState createState() => _CircleState();
}

class _CircleState extends State<Circle> with SingleTickerProviderStateMixin {
  bool xForward = Random().nextBool();
  bool yForward = Random().nextBool();
  late double direction;
  late double x;
  late double y;
  double speed = 10;

  late final AnimationController _controller;

  @override
  void initState() {
    super.initState();
    direction = Random().nextDouble() * (2 * pi);
    xForward = direction < pi / 2 || direction > (pi + pi / 2);
    yForward = direction > pi;
    x = Random().nextDouble() * 500 + (widget.size.width / 2) - 250;
    y = Random().nextDouble() * 500 + (widget.size.height / 2) - 250;
    speed = max(1, Random().nextDouble() * 10);
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));
    _controller.repeat();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) => Positioned(
        child: Container(
          width: 80,
          height: 80,
          decoration: BoxDecoration(
              color: widget.index == 1 ? Colors.red : Colors.blue,
              borderRadius: BorderRadius.circular(80 / 2),
              boxShadow: [
                BoxShadow(
                  blurRadius: 5,
                  spreadRadius: 3,
                  offset: Offset(2, 2),
                  color: Colors.black26,
                ),
              ]),
        ),
        top: _getY(),
        left: _getX(),
      ),
    );
  }

  _getX() {
    if (x > widget.size.width - 80 || x < 0) {
      xForward = !xForward;
    }
    x = xForward
        ? x + cos(direction).abs() * speed
        : x - cos(direction).abs() * speed;
    return x;
  }

  _getY() {
    if (y > widget.size.height - 80 || y < 0) {
      yForward = !yForward;
    }
    y = yForward
        ? y + sin(direction).abs() * speed
        : y - sin(direction).abs() * speed;
    return y;
  }
}
```