Ian Hickson 449f4a6673
License update (#45373)
* Update project.pbxproj files to say Flutter rather than Chromium

Also, the templates now have an empty organization so that we don't cause people to give their apps a Flutter copyright.

* Update the copyright notice checker to require a standard notice on all files

* Update copyrights on Dart files. (This was a mechanical commit.)

* Fix weird license headers on Dart files that deviate from our conventions; relicense Shrine.

Some were already marked "The Flutter Authors", not clear why. Their
dates have been normalized. Some were missing the blank line after the
license. Some were randomly different in trivial ways for no apparent
reason (e.g. missing the trailing period).

* Clean up the copyrights in non-Dart files. (Manual edits.)

Also, make sure templates don't have copyrights.

* Fix some more ORGANIZATIONNAMEs
2019-11-27 15:04:02 -08:00

388 lines
9.9 KiB
Dart

// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math';
import 'package:flutter/widgets.dart';
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
// Based on https://github.com/eseidelGoogle/bezier_perf/blob/master/lib/main.dart
class CubicBezierPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Bezier(Colors.amber, 1.0),
],
),
);
}
}
class Bezier extends StatelessWidget {
const Bezier(this.color, this.scale, {this.blur = 0.0, this.delay = 0.0});
final Color color;
final double scale;
final double blur;
final double delay;
List<PathDetail> _getLogoPath() {
final List<PathDetail> paths = <PathDetail>[];
final Path path = Path();
path.moveTo(100.0, 97.0);
path.cubicTo(100.0, 97.0, 142.0, 59.0, 169.91, 41.22);
path.cubicTo(197.82, 23.44, 249.24, 5.52, 204.67, 85.84);
paths.add(PathDetail(path));
// Path 2
final Path bezier2Path = Path();
bezier2Path.moveTo(0.0, 70.55);
bezier2Path.cubicTo(0.0, 70.55, 42.0, 31.55, 69.91, 14.77);
bezier2Path.cubicTo(97.82, -2.01, 149.24, -20.93, 104.37, 59.39);
paths.add(PathDetail(bezier2Path,
translate: <double>[29.45, 151.0], rotation: -1.5708));
// Path 3
final Path bezier3Path = Path();
bezier3Path.moveTo(0.0, 69.48);
bezier3Path.cubicTo(0.0, 69.48, 44.82, 27.92, 69.91, 13.7);
bezier3Path.cubicTo(95.0, -0.52, 149.24, -22.0, 104.37, 58.32);
paths.add(PathDetail(bezier3Path,
translate: <double>[53.0, 200.48], rotation: -3.14159));
// Path 4
final Path bezier4Path = Path();
bezier4Path.moveTo(0.0, 69.48);
bezier4Path.cubicTo(0.0, 69.48, 43.82, 27.92, 69.91, 13.7);
bezier4Path.cubicTo(96.0, -0.52, 149.24, -22.0, 104.37, 58.32);
paths.add(PathDetail(bezier4Path,
translate: <double>[122.48, 77.0], rotation: -4.71239));
return paths;
}
@override
Widget build(BuildContext context) {
return Stack(children: <Widget>[
CustomPaint(
foregroundPainter:
BezierPainter(Colors.grey, 0.0, _getLogoPath(), false),
size: const Size(100.0, 100.0),
),
AnimatedBezier(color, scale, blur: blur, delay: delay),
]);
}
}
class PathDetail {
PathDetail(this.path, {this.translate, this.rotation});
Path path;
List<double> translate = <double>[];
double rotation;
}
class AnimatedBezier extends StatefulWidget {
const AnimatedBezier(this.color, this.scale, {this.blur = 0.0, this.delay});
final Color color;
final double scale;
final double blur;
final double delay;
@override
State createState() => AnimatedBezierState();
}
class Point {
Point(this.x, this.y);
double x;
double y;
}
class AnimatedBezierState extends State<AnimatedBezier>
with SingleTickerProviderStateMixin {
double scale;
AnimationController controller;
CurvedAnimation curve;
bool isPlaying = false;
List<List<Point>> pointList = <List<Point>>[
<Point>[],
<Point>[],
<Point>[],
<Point>[],
];
bool isReversed = false;
List<PathDetail> _playForward() {
final List<PathDetail> paths = <PathDetail>[];
final double t = curve.value;
final double b = controller.upperBound;
double pX;
double pY;
final Path path = Path();
if (t < b / 2) {
pX = _getCubicPoint(t * 2, 100.0, 100.0, 142.0, 169.91);
pY = _getCubicPoint(t * 2, 97.0, 97.0, 59.0, 41.22);
pointList[0].add(Point(pX, pY));
} else {
pX = _getCubicPoint(t * 2 - b, 169.91, 197.80, 249.24, 204.67);
pY = _getCubicPoint(t * 2 - b, 41.22, 23.44, 5.52, 85.84);
pointList[0].add(Point(pX, pY));
}
path.moveTo(100.0, 97.0);
for (Point p in pointList[0]) {
path.lineTo(p.x, p.y);
}
paths.add(PathDetail(path));
// Path 2
final Path bezier2Path = Path();
if (t <= b / 2) {
final double pX = _getCubicPoint(t * 2, 0.0, 0.0, 42.0, 69.91);
final double pY = _getCubicPoint(t * 2, 70.55, 70.55, 31.55, 14.77);
pointList[1].add(Point(pX, pY));
} else {
final double pX = _getCubicPoint(t * 2 - b, 69.91, 97.82, 149.24, 104.37);
final double pY = _getCubicPoint(t * 2 - b, 14.77, -2.01, -20.93, 59.39);
pointList[1].add(Point(pX, pY));
}
bezier2Path.moveTo(0.0, 70.55);
for (Point p in pointList[1]) {
bezier2Path.lineTo(p.x, p.y);
}
paths.add(PathDetail(bezier2Path,
translate: <double>[29.45, 151.0], rotation: -1.5708));
// Path 3
final Path bezier3Path = Path();
if (t <= b / 2) {
pX = _getCubicPoint(t * 2, 0.0, 0.0, 44.82, 69.91);
pY = _getCubicPoint(t * 2, 69.48, 69.48, 27.92, 13.7);
pointList[2].add(Point(pX, pY));
} else {
pX = _getCubicPoint(t * 2 - b, 69.91, 95.0, 149.24, 104.37);
pY = _getCubicPoint(t * 2 - b, 13.7, -0.52, -22.0, 58.32);
pointList[2].add(Point(pX, pY));
}
bezier3Path.moveTo(0.0, 69.48);
for (Point p in pointList[2]) {
bezier3Path.lineTo(p.x, p.y);
}
paths.add(PathDetail(bezier3Path,
translate: <double>[53.0, 200.48], rotation: -3.14159));
// Path 4
final Path bezier4Path = Path();
if (t < b / 2) {
final double pX = _getCubicPoint(t * 2, 0.0, 0.0, 43.82, 69.91);
final double pY = _getCubicPoint(t * 2, 69.48, 69.48, 27.92, 13.7);
pointList[3].add(Point(pX, pY));
} else {
final double pX = _getCubicPoint(t * 2 - b, 69.91, 96.0, 149.24, 104.37);
final double pY = _getCubicPoint(t * 2 - b, 13.7, -0.52, -22.0, 58.32);
pointList[3].add(Point(pX, pY));
}
bezier4Path.moveTo(0.0, 69.48);
for (Point p in pointList[3]) {
bezier4Path.lineTo(p.x, p.y);
}
paths.add(PathDetail(bezier4Path,
translate: <double>[122.48, 77.0], rotation: -4.71239));
return paths;
}
List<PathDetail> _playReversed() {
for (List<Point> list in pointList) {
if (list.isNotEmpty) {
list.removeLast();
}
}
final List<Point> points = pointList[0];
final Path path = Path();
path.moveTo(100.0, 97.0);
for (Point point in points) {
path.lineTo(point.x, point.y);
}
final Path bezier2Path = Path();
bezier2Path.moveTo(0.0, 70.55);
for (Point p in pointList[1]) {
bezier2Path.lineTo(p.x, p.y);
}
final Path bezier3Path = Path();
bezier3Path.moveTo(0.0, 69.48);
for (Point p in pointList[2]) {
bezier3Path.lineTo(p.x, p.y);
}
final Path bezier4Path = Path();
bezier4Path.moveTo(0.0, 69.48);
for (Point p in pointList[3]) {
bezier4Path.lineTo(p.x, p.y);
}
return <PathDetail>[
PathDetail(path),
PathDetail(bezier2Path, translate: <double>[29.45, 151.0], rotation: -1.5708),
PathDetail(bezier3Path,
translate: <double>[53.0, 200.48], rotation: -3.14159),
PathDetail(bezier4Path, translate: <double>[122.48, 77.0], rotation: -4.71239),
];
}
List<PathDetail> _getLogoPath() {
if (!isReversed) {
return _playForward();
}
return _playReversed();
}
//From http://wiki.roblox.com/index.php?title=File:Beziereq4.png
double _getCubicPoint(double t, double p0, double p1, double p2, double p3) {
return pow(1 - t, 3) * p0 +
3 * pow(1 - t, 2) * t * p1 +
3 * (1 - t) * pow(t, 2) * p2 +
pow(t, 3) * p3;
}
void playAnimation() {
isPlaying = true;
isReversed = false;
for (List<Point> list in pointList) {
list.clear();
}
controller.reset();
controller.forward();
}
void stopAnimation() {
isPlaying = false;
controller.stop();
for (List<Point> list in pointList) {
list.clear();
}
}
void reverseAnimation() {
isReversed = true;
controller.reverse();
}
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 1000));
curve = CurvedAnimation(parent: controller, curve: Curves.linear)
..addListener(() {
setState(() {});
})
..addStatusListener((AnimationStatus state) {
if (state == AnimationStatus.completed) {
reverseAnimation();
} else if (state == AnimationStatus.dismissed) {
playAnimation();
}
});
playAnimation();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return CustomPaint(
foregroundPainter: BezierPainter(widget.color,
curve.value * widget.blur, _getLogoPath(), isPlaying),
size: const Size(100.0, 100.0));
}
}
class BezierPainter extends CustomPainter {
BezierPainter(this.color, this.blur, this.path, this.isPlaying);
Color color;
final double blur;
List<PathDetail> path;
bool isPlaying;
@override
void paint(Canvas canvas, Size size) {
final Paint paint = Paint();
paint.strokeWidth = 18.0;
paint.style = PaintingStyle.stroke;
paint.strokeCap = StrokeCap.round;
paint.color = color;
canvas.scale(0.5, 0.5);
for (int i = 0; i < path.length; i++) {
if (path[i].translate != null) {
canvas.translate(path[i].translate[0], path[i].translate[1]);
}
if (path[i].rotation != null) {
canvas.rotate(path[i].rotation);
}
if (blur > 0) {
final MaskFilter blur = MaskFilter.blur(BlurStyle.normal, this.blur);
paint.maskFilter = blur;
canvas.drawPath(path[i].path, paint);
}
paint.maskFilter = null;
canvas.drawPath(path[i].path, paint);
}
}
@override
bool shouldRepaint(BezierPainter oldDelegate) => true;
@override
bool shouldRebuildSemantics(BezierPainter oldDelegate) => false;
}