
* 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
388 lines
9.9 KiB
Dart
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;
|
|
}
|