blob: 2d3679d5e65271e1a5a5e7d9e049d10323a9cedb [file] [log] [blame]
// Copyright 2019 The Flutter team. 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/material.dart';
// Color gradients.
const pinkLeft = Color(0xFFFF5983);
const pinkRight = Color(0xFFFF8383);
const tealLeft = Color(0xFF1CDDC8);
const tealRight = Color(0xFF00A5B3);
// Dimensions.
const unitHeight = 1;
const unitWidth = 1;
const stickLength = 5 / 9;
const stickWidth = 5 / 36;
const stickRadius = stickWidth / 2;
const knobDiameter = 5 / 54;
const knobRadius = knobDiameter / 2;
const stickGap = 5 / 54;
// Locations.
const knobDistanceFromCenter = stickGap / 2 + stickWidth / 2;
const lowerKnobCenter = Offset(0, knobDistanceFromCenter);
const upperKnobCenter = Offset(0, -knobDistanceFromCenter);
const knobDeviation = stickLength / 2 - stickRadius;
// Key moments in animation.
const _colorKnobContractionBegins = 1 / 23;
const _monoKnobExpansionEnds = 11 / 23;
const _colorKnobContractionEnds = 14 / 23;
// Stages.
bool isTransitionPhase(double time) => time < _colorKnobContractionEnds;
// Curve easing.
const _curve = Curves.easeInOutCubic;
double _progress(
double time, {
@required double begin,
@required double end,
}) =>
_curve.transform(((time - begin) / (end - begin)).clamp(0, 1).toDouble());
double _monoKnobProgress(double time) => _progress(
time,
begin: 0,
end: _monoKnobExpansionEnds,
);
double _colorKnobProgress(double time) => _progress(
time,
begin: _colorKnobContractionBegins,
end: _colorKnobContractionEnds,
);
double _rotationProgress(double time) => _progress(
time,
begin: _colorKnobContractionEnds,
end: 1,
);
// Changing lengths: mono.
double monoLength(double time) =>
_monoKnobProgress(time) * (stickLength - knobDiameter) + knobDiameter;
double _monoLengthLeft(double time) =>
min(monoLength(time) - knobRadius, stickRadius);
double _monoLengthRight(double time) =>
monoLength(time) - _monoLengthLeft(time);
double _monoHorizontalOffset(double time) =>
(_monoLengthRight(time) - _monoLengthLeft(time)) / 2 - knobDeviation;
Offset upperMonoOffset(double time) =>
upperKnobCenter + Offset(_monoHorizontalOffset(time), 0);
Offset lowerMonoOffset(double time) =>
lowerKnobCenter + Offset(-_monoHorizontalOffset(time), 0);
// Changing lengths: color.
double colorLength(double time) => (1 - _colorKnobProgress(time)) * stickLength;
Offset upperColorOffset(double time) =>
upperKnobCenter + Offset(stickLength / 2 - colorLength(time) / 2, 0);
Offset lowerColorOffset(double time) =>
lowerKnobCenter + Offset(-stickLength / 2 + colorLength(time) / 2, 0);
// Moving objects.
double knobRotation(double time) => _rotationProgress(time) * pi / 4;
Offset knobCenter(double time) {
final progress = _rotationProgress(time);
if (progress == 0) {
return lowerKnobCenter;
} else if (progress == 1) {
return upperKnobCenter;
} else {
// Calculates the current location.
final center = Offset(knobDistanceFromCenter / tan(pi / 8), 0);
final radius = (lowerKnobCenter - center).distance;
final angle = pi + (progress - 1 / 2) * pi / 4;
return center + Offset.fromDirection(angle, radius);
}
}