Commit 8be6ed58 authored by Viktor Lidholt's avatar Viktor Lidholt

Fitness demo, initial version

parent 7a2c38ab
......@@ -20,7 +20,10 @@ assets:
- packages/flutter_gallery_assets/icon-snow.png
- packages/flutter_gallery_assets/kangaroo_valley_safari.png
- packages/flutter_gallery_assets/top_10_australian_beaches.png
- packages/flutter_gallery_assets/jumpingjack.json
- packages/flutter_gallery_assets/jumpingjack.png
material-design-icons:
- name: action/accessibility
- name: action/account_circle
- name: action/alarm
- name: action/android
......@@ -29,6 +32,8 @@ material-design-icons:
- name: action/home
- name: action/hourglass_empty
- name: action/language
- name: av/play_arrow
- name: av/stop
- name: communication/call
- name: communication/email
- name: communication/location_on
......@@ -39,6 +44,8 @@ material-design-icons:
- name: content/create
- name: image/brightness_5
- name: image/brightness_7
- name: image/flash_on
- name: image/timer
- name: navigation/arrow_back
- name: navigation/arrow_drop_down
- name: navigation/arrow_forward
......
// Copyright 2015 The Chromium 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:async';
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_sprites/flutter_sprites.dart';
ImageMap _images;
SpriteSheet _sprites;
class FitnessDemo extends StatelessComponent {
FitnessDemo({ Key key }) : super(key: key);
Widget build(BuildContext context) {
return new Scaffold(
toolBar: new ToolBar(
center: new Text("Fitness")
),
body: new _FitnessDemoContents()
);
}
}
class _FitnessDemoContents extends StatefulComponent {
_FitnessDemoContents({ Key key }) : super(key: key);
_FitnessDemoContentsState createState() => new _FitnessDemoContentsState();
}
class _FitnessDemoContentsState extends State<_FitnessDemoContents> {
Future _loadAssets(AssetBundle bundle) async {
_images = new ImageMap(bundle);
await _images.load(<String>[
'packages/flutter_gallery_assets/jumpingjack.png',
]);
String json = await DefaultAssetBundle.of(context).loadString('packages/flutter_gallery_assets/jumpingjack.json');
_sprites = new SpriteSheet(_images['packages/flutter_gallery_assets/jumpingjack.png'], json);
}
void initState() {
super.initState();
AssetBundle bundle = DefaultAssetBundle.of(context);
_loadAssets(bundle).then((_) {
setState(() {
assetsLoaded = true;
workoutAnimation = new _WorkoutAnimationNode(
onPerformedJumpingJack: () {
setState(() {
count += 1;
});
},
onSecondPassed: (int seconds) {
setState(() {
time = seconds;
});
}
);
});
});
}
bool assetsLoaded = false;
int count = 0;
int time = 0;
int get kcal => (count * 0.2).toInt();
_WorkoutAnimationNode workoutAnimation;
Widget build(BuildContext context) {
if (!assetsLoaded)
return new Container();
Color buttonColor;
String buttonText;
VoidCallback onButtonPressed;
if (workoutAnimation.workingOut) {
buttonColor = Colors.red[500];
buttonText = "STOP WORKOUT";
onButtonPressed = endWorkout;
} else {
buttonColor = Theme.of(context).primaryColor;
buttonText = "START WORKOUT";
onButtonPressed = startWorkout;
}
return new Material(
child: new Column(
justifyContent: FlexJustifyContent.center,
children: <Widget>[
new Flexible(
child: new Container(
decoration: new BoxDecoration(backgroundColor: Colors.grey[800]),
child: new SpriteWidget(workoutAnimation, SpriteBoxTransformMode.scaleToFit)
)
),
new Padding(
padding: new EdgeDims.only(top: 20.0),
child: new Text("JUMPING JACKS", style: Theme.of(context).text.title)
),
new Padding(
padding: new EdgeDims.only(top: 20.0, bottom: 20.0),
child: new Row(
justifyContent: FlexJustifyContent.center,
children: <Widget>[
_createInfoPanelCell("action/accessibility", "$count", "COUNT"),
_createInfoPanelCell("image/timer", _formatSeconds(time), "TIME"),
_createInfoPanelCell("image/flash_on", "$kcal", "KCAL")
]
)
),
new Padding(
padding: new EdgeDims.only(bottom: 16.0),
child: new SizedBox(
width: 300.0,
height: 72.0,
child: new RaisedButton (
onPressed: onButtonPressed,
color: buttonColor,
child: new Text(
buttonText,
style: new TextStyle(color: Colors.white, fontSize: 20.0)
)
)
)
)
]
)
);
}
Widget _createInfoPanelCell(String icon, String value, String description) {
Color color;
if (workoutAnimation.workingOut)
color = Colors.black87;
else
color = Theme.of(context).disabledColor;
return new Container(
width: 100.0,
child: new Center(
child: new Column(
children: <Widget>[
new Icon(icon: icon, size: IconSize.s48, color: color),
new Text(value, style: new TextStyle(fontSize: 24.0, color: color)),
new Text(description, style: new TextStyle(color: color))
]
)
)
);
}
String _formatSeconds(int seconds) {
int minutes = seconds ~/ 60;
String secondsStr = "${seconds % 60}".padLeft(2, "0");
return "$minutes:$secondsStr";
}
void startWorkout() {
setState(() {
count = 0;
time = 0;
workoutAnimation.start();
});
}
void endWorkout() {
setState(() {
workoutAnimation.stop();
});
}
}
typedef void _SecondPassedCallback(int seconds);
class _WorkoutAnimationNode extends NodeWithSize {
_WorkoutAnimationNode({
this.onPerformedJumpingJack,
this.onSecondPassed
}) : super(const Size(1024.0, 1024.0)) {
reset();
_progress = new _ProgressCircle(const Size(800.0, 800.0));
_progress.pivot = const Point(0.5, 0.5);
_progress.position = const Point(512.0, 512.0);
addChild(_progress);
_jumpingJack = new _JumpingJack((){
onPerformedJumpingJack();
});
_jumpingJack.scale = 0.5;
_jumpingJack.position = const Point(512.0, 550.0);
addChild(_jumpingJack);
}
final VoidCallback onPerformedJumpingJack;
final _SecondPassedCallback onSecondPassed;
int seconds;
bool workingOut;
static const int _kTargetMillis = 1000 * 30;
int _startTimeMillis;
_ProgressCircle _progress;
_JumpingJack _jumpingJack;
void reset() {
seconds = 0;
workingOut = false;
}
void start() {
reset();
_startTimeMillis = new DateTime.now().millisecondsSinceEpoch;
workingOut = true;
_jumpingJack.animateJumping();
}
void stop() {
workingOut = false;
_jumpingJack.neutralPose();
}
void update(double dt) {
if (workingOut) {
int millis = new DateTime.now().millisecondsSinceEpoch - _startTimeMillis;
int newSeconds = (millis) ~/ 1000;
if (newSeconds != seconds) {
seconds = newSeconds;
onSecondPassed(seconds);
}
_progress.value = millis / _kTargetMillis;
} else {
_progress.value = 0.0;
}
}
}
class _ProgressCircle extends NodeWithSize {
_ProgressCircle(Size size, [this.value = 0.0]) : super(size);
static const _kTwoPI = math.PI * 2.0;
static const _kEpsilon = .0000001;
static const _kSweep = _kTwoPI - _kEpsilon;
double value;
void paint(Canvas canvas) {
applyTransformForPivot(canvas);
Paint circlePaint = new Paint()
..color = Colors.white30
..strokeWidth = 24.0
..style = ui.PaintingStyle.stroke;
canvas.drawCircle(
new Point(size.width / 2.0, size.height / 2.0),
size.width / 2.0,
circlePaint
);
Paint pathPaint = new Paint()
..color = Colors.purple[500]
..strokeWidth = 25.0
..style = ui.PaintingStyle.stroke;
double angle = value.clamp(0.0, 1.0) * _kSweep;
Path path = new Path()
..arcTo(Point.origin & size, -math.PI / 2.0, angle, false);
canvas.drawPath(path, pathPaint);
}
}
class _JumpingJack extends Node {
_JumpingJack(VoidCallback onPerformedJumpingJack) {
left = new _JumpingJackSide(false, onPerformedJumpingJack);
right = new _JumpingJackSide(true, null);
addChild(left);
addChild(right);
}
void animateJumping() {
left.animateJumping();
right.animateJumping();
}
void neutralPose() {
left.neutralPosition(true);
right.neutralPosition(true);
}
_JumpingJackSide left;
_JumpingJackSide right;
}
class _JumpingJackSide extends Node {
_JumpingJackSide(bool right, this.onPerformedJumpingJack) {
// Torso and head
torso = _createPart('torso.png', const Point(512.0, 512.0));
addChild(torso);
head = _createPart('head.png', const Point(512.0, 160.0));
torso.addChild(head);
if (right) {
torso.opacity = 0.0;
head.opacity = 0.0;
torso.scaleX = -1.0;
}
// Left side movable parts
upperArm = _createPart('upper-arm.png', const Point(445.0, 220.0));
torso.addChild(upperArm);
lowerArm = _createPart('lower-arm.png', const Point(306.0, 200.0));
upperArm.addChild(lowerArm);
hand = _createPart('hand.png', const Point(215.0, 127.0));
lowerArm.addChild(hand);
upperLeg = _createPart('upper-leg.png', const Point(467.0, 492.0));
torso.addChild(upperLeg);
lowerLeg = _createPart('lower-leg.png', const Point(404.0, 660.0));
upperLeg.addChild(lowerLeg);
foot = _createPart('foot.png', const Point(380.0, 835.0));
lowerLeg.addChild(foot);
torso.setPivotAndPosition(Point.origin);
neutralPosition(false);
}
_JumpingJackPart torso;
_JumpingJackPart head;
_JumpingJackPart upperArm;
_JumpingJackPart lowerArm;
_JumpingJackPart hand;
_JumpingJackPart lowerLeg;
_JumpingJackPart upperLeg;
_JumpingJackPart foot;
final VoidCallback onPerformedJumpingJack;
_JumpingJackPart _createPart(String textureName, Point pivotPosition) {
return new _JumpingJackPart(_sprites[textureName], pivotPosition);
}
void animateJumping() {
actions.stopAll();
actions.run(new ActionSequence([
_createPoseAction(null, 0, 0.5),
new ActionCallFunction(_animateJumpingLoop)
]));
}
void _animateJumpingLoop() {
actions.run(new ActionRepeatForever(
new ActionSequence(<Action>[
_createPoseAction(0, 1, 0.30),
_createPoseAction(1, 2, 0.30),
_createPoseAction(2, 1, 0.30),
_createPoseAction(1, 0, 0.30),
new ActionCallFunction(() {
if (onPerformedJumpingJack != null)
onPerformedJumpingJack();
})
])
));
}
void neutralPosition(bool animate) {
actions.stopAll();
if (animate) {
actions.run(_createPoseAction(null, 1, 0.5));
} else {
List<double> d = _dataForPose(1);
upperArm.rotation = d[0];
lowerArm.rotation = d[1];
hand.rotation = d[2];
upperLeg.rotation = d[3];
lowerLeg.rotation = d[4];
foot.rotation = d[5];
torso.position = new Point(0.0, d[6]);
}
}
ActionInterval _createPoseAction(int startPose, int endPose, double duration) {
List<double> d0 = _dataForPose(startPose);
List<double> d1 = _dataForPose(endPose);
List<ActionTween> tweens = <ActionTween>[
_tweenRotation(upperArm, d0[0], d1[0], duration),
_tweenRotation(lowerArm, d0[1], d1[1], duration),
_tweenRotation(hand, d0[2], d1[2], duration),
_tweenRotation(upperLeg, d0[3], d1[3], duration),
_tweenRotation(lowerLeg, d0[4], d1[4], duration),
_tweenRotation(foot, d0[5], d1[5], duration),
new ActionTween(
(Point a) => torso.position = a,
new Point(0.0, d0[6]),
new Point(0.0, d1[6]),
duration
)
];
return new ActionGroup(tweens);
}
ActionTween _tweenRotation(_JumpingJackPart part, double r0, double r1, double duration) {
return new ActionTween(
(double a) => part.rotation = a,
r0,
r1,
duration
);
}
List<double> _dataForPose(int pose) {
if (pose == null)
return _dataForCurrentPose();
if (pose == 0) {
return <double>[
-80.0, // Upper arm rotation
-30.0, // Lower arm rotation
-10.0, // Hand rotation
-15.0, // Upper leg rotation
5.0, // Lower leg rotation
15.0, // Foot rotation
0.0 // Torso y offset
];
} else if (pose == 1) {
return <double>[
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
-70.0
];
} else {
return <double>[
40.0,
30.0,
10.0,
20.0,
-20.0,
15.0,
40.0
];
}
}
List<double> _dataForCurrentPose() {
return <double>[
upperArm.rotation,
lowerArm.rotation,
hand.rotation,
upperLeg.rotation,
lowerLeg.rotation,
foot.rotation,
torso.position.y
];
}
}
class _JumpingJackPart extends Sprite {
_JumpingJackPart(Texture texture, this.pivotPosition) : super(texture);
final Point pivotPosition;
void setPivotAndPosition(Point newPosition) {
pivot = new Point(pivotPosition.x / 1024.0, pivotPosition.y / 1024.0);
position = newPosition;
for (Node child in children) {
_JumpingJackPart subPart = child;
subPart.setPivotAndPosition(
new Point(
subPart.pivotPosition.x - pivotPosition.x,
subPart.pivotPosition.y - pivotPosition.y
)
);
}
}
}
......@@ -30,6 +30,7 @@ import '../demo/time_picker_demo.dart';
import '../demo/two_level_list_demo.dart';
import '../demo/typography_demo.dart';
import '../demo/weathers_demo.dart';
import '../demo/fitness_demo.dart';
class GalleryHome extends StatefulComponent {
GalleryHome({ Key key }) : super(key: key);
......@@ -65,7 +66,8 @@ class GalleryHomeState extends State<GalleryHome> {
image: 'assets/section_animation.png',
colors: Colors.purple,
demos: <GalleryDemo>[
new GalleryDemo(title: 'Weathers', builder: () => new WeathersDemo())
new GalleryDemo(title: 'Weathers', builder: () => new WeathersDemo()),
new GalleryDemo(title: 'Fitness', builder: () => new FitnessDemo())
]
),
new GallerySection(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment