Skip to content

Instantly share code, notes, and snippets.

@dartlab-user
Forked from anonymous/.metadata.json
Last active August 29, 2015 14:14
Show Gist options
  • Save dartlab-user/dbc2a50c7c8ac37544ab to your computer and use it in GitHub Desktop.
Save dartlab-user/dbc2a50c7c8ac37544ab to your computer and use it in GitHub Desktop.
Clock
{
"origin": "dartlab.org",
"url": "http://dartlab.org/#:gistId",
"history": [
"bf1af9b3c187997b573c"
]
}
<h1>Clock</h1>
<p>An html5 clock using absolutely positioned elements.</p>
<div id="canvas-content"></div>
<footer>
<p id="summary"> </p>
<p id="notes"> </p>
</footer>
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library clock;
import 'dart:async';
import 'dart:html';
import 'dart:math';
void main() {
new CountDownClock();
}
num _fpsAverage;
/**
* Display the animation's FPS in a div.
*/
void showFps(num fps) {
if (_fpsAverage == null) {
_fpsAverage = fps;
} else {
_fpsAverage = fps * 0.05 + _fpsAverage * 0.95;
querySelector("#notes").text = "${_fpsAverage.round().toInt()} fps";
}
}
class CountDownClock {
static const double NUMBER_SPACING = 19.0;
static const double BALL_WIDTH = 19.0;
static const double BALL_HEIGHT = 19.0;
List<ClockNumber> hours = new List<ClockNumber>(2);
List<ClockNumber> minutes = new List<ClockNumber>(2);
List<ClockNumber> seconds = new List<ClockNumber>(2);
int displayedHour = -1;
int displayedMinute = -1;
int displayedSecond = -1;
Balls balls = new Balls();
CountDownClock() {
var parent = querySelector("#canvas-content");
createNumbers(parent, parent.client.width, parent.client.height);
updateTime(new DateTime.now());
window.requestAnimationFrame(tick);
}
void tick(num time) {
updateTime(new DateTime.now());
balls.tick(time);
window.requestAnimationFrame(tick);
}
void updateTime(DateTime now) {
if (now.hour != displayedHour) {
setDigits(pad2(now.hour), hours);
displayedHour = now.hour;
}
if (now.minute != displayedMinute) {
setDigits(pad2(now.minute), minutes);
displayedMinute = now.minute;
}
if (now.second != displayedSecond) {
setDigits(pad2(now.second), seconds);
displayedSecond = now.second;
}
}
void setDigits(String digits, List<ClockNumber> numbers) {
for (int i = 0; i < numbers.length; ++i) {
int digit = digits.codeUnitAt(i) - '0'.codeUnitAt(0);
numbers[i].setPixels(ClockNumbers.PIXELS[digit]);
}
}
String pad3(int number) {
if (number < 10) {
return "00${number}";
}
if (number < 100) {
return "0${number}";
}
return "${number}";
}
String pad2(int number) => number < 10 ? "0${number}" : "${number}";
void createNumbers(Element parent, num width, num height) {
DivElement root = new DivElement();
makeRelative(root);
root.style.textAlign = 'center';
querySelector("#canvas-content").nodes.add(root);
double hSize = (BALL_WIDTH * ClockNumber.WIDTH + NUMBER_SPACING) * 6
+ (BALL_WIDTH + NUMBER_SPACING) * 2;
hSize -= NUMBER_SPACING;
double vSize = BALL_HEIGHT * ClockNumber.HEIGHT;
double x = (width - hSize) / 2;
double y = (height - vSize) / 3;
for (int i = 0; i < hours.length; ++i) {
hours[i] = new ClockNumber(this, x, Balls.BLUE_BALL_INDEX);
root.nodes.add(hours[i].root);
setElementPosition(hours[i].root, x, y);
x += BALL_WIDTH * ClockNumber.WIDTH + NUMBER_SPACING;
}
root.nodes.add(new Colon(x, y).root);
x += BALL_WIDTH + NUMBER_SPACING;
for (int i = 0; i < minutes.length; ++i) {
minutes[i] = new ClockNumber(this, x, Balls.RED_BALL_INDEX);
root.nodes.add(minutes[i].root);
setElementPosition(minutes[i].root, x, y);
x += BALL_WIDTH * ClockNumber.WIDTH + NUMBER_SPACING;
}
root.nodes.add(new Colon(x, y).root);
x += BALL_WIDTH + NUMBER_SPACING;
for (int i = 0; i < seconds.length; ++i) {
seconds[i] = new ClockNumber(this, x, Balls.GREEN_BALL_INDEX);
root.nodes.add(seconds[i].root);
setElementPosition(seconds[i].root, x, y);
x += BALL_WIDTH * ClockNumber.WIDTH + NUMBER_SPACING;
}
}
}
void makeAbsolute(Element elem) {
elem.style.left = '0px';
elem.style.top = '0px';
elem.style.position = 'absolute';
}
void makeRelative(Element elem) {
elem.style.position = 'relative';
}
void setElementPosition(Element elem, double x, double y) {
elem.style.transform = 'translate(${x}px, ${y}px)';
}
void setElementSize(Element elem, double l, double t, double r, double b) {
setElementPosition(elem, l, t);
elem.style.right = "${r}px";
elem.style.bottom = "${b}px";
}
int get clientWidth => window.innerWidth;
int get clientHeight => window.innerHeight;
class Balls {
static const double RADIUS2 = Ball.RADIUS * Ball.RADIUS;
static const int LT_GRAY_BALL_INDEX = 0;
static const int GREEN_BALL_INDEX = 1;
static const int BLUE_BALL_INDEX = 2;
static const int DK_GRAY_BALL_INDEX = 4;
static const int RED_BALL_INDEX = 5;
static const int MD_GRAY_BALL_INDEX = 6;
static const List<String> PNGS = const [
"https://cdn.rawgit.com/dart-lang/sample-clock/master/web/images/ball-d9d9d9.png",
"https://cdn.rawgit.com/dart-lang/sample-clock/master/web/images/ball-009a49.png",
"https://cdn.rawgit.com/dart-lang/sample-clock/master/web/images/ball-13acfa.png",
"https://cdn.rawgit.com/dart-lang/sample-clock/master/web/images/ball-265897.png",
"https://cdn.rawgit.com/dart-lang/sample-clock/master/web/images/ball-b6b4b5.png",
"https://cdn.rawgit.com/dart-lang/sample-clock/master/web/images/ball-c0000b.png",
"https://cdn.rawgit.com/dart-lang/sample-clock/master/web/images/ball-c9c9c9.png"
];
DivElement root;
num lastTime;
List<Ball> balls;
Balls() :
lastTime = new DateTime.now().millisecondsSinceEpoch,
balls = new List<Ball>() {
root = new DivElement();
document.body.nodes.add(root);
makeAbsolute(root);
setElementSize(root, 0.0, 0.0, 0.0, 0.0);
}
void tick(num now) {
showFps(1000.0 / (now - lastTime + 0.01));
double delta = min((now - lastTime) / 1000.0, 0.1);
lastTime = now;
// incrementally move each ball, removing balls that are offscreen
balls = balls.where((ball) => ball.tick(delta)).toList();
collideBalls(delta);
}
void collideBalls(double delta) {
balls.forEach((b0) {
balls.forEach((b1) {
// See if the two balls are intersecting.
double dx = (b0.x - b1.x).abs();
double dy = (b0.y - b1.y).abs();
double d2 = dx * dx + dy * dy;
if (d2 < RADIUS2) {
// Make sure they're actually on a collision path
// (not intersecting while moving apart).
// This keeps balls that end up intersecting from getting stuck
// without all the complexity of keeping them strictly separated.
if (newDistanceSquared(delta, b0, b1) > d2) {
return;
}
// They've collided. Normalize the collision vector.
double d = sqrt(d2);
if (d == 0) {
return;
}
dx /= d;
dy /= d;
// Calculate the impact velocity and speed along the collision vector.
double impactx = b0.vx - b1.vx;
double impacty = b0.vy - b1.vy;
double impactSpeed = impactx * dx + impacty * dy;
// Bump.
b0.vx -= dx * impactSpeed;
b0.vy -= dy * impactSpeed;
b1.vx += dx * impactSpeed;
b1.vy += dy * impactSpeed;
}
});
});
}
double newDistanceSquared(double delta, Ball b0, Ball b1) {
double nb0x = b0.x + b0.vx * delta;
double nb0y = b0.y + b0.vy * delta;
double nb1x = b1.x + b1.vx * delta;
double nb1y = b1.y + b1.vy * delta;
double ndx = (nb0x - nb1x).abs();
double ndy = (nb0y - nb1y).abs();
double nd2 = ndx * ndx + ndy * ndy;
return nd2;
}
void add(double x, double y, int color) {
balls.add(new Ball(root, x, y, color));
}
}
class Ball {
static const double GRAVITY = 400.0;
static const double RESTITUTION = 0.8;
static const double MIN_VELOCITY = 100.0;
static const double INIT_VELOCITY = 800.0;
static const double RADIUS = 14.0;
static Random random;
static double randomVelocity() {
if (random == null) {
random = new Random();
}
return (random.nextDouble() - 0.5) * INIT_VELOCITY;
}
Element root;
ImageElement elem;
double x, y;
double vx, vy;
double ax, ay;
double age;
Ball(this.root, this.x, this.y, int color) {
elem = new ImageElement(src: Balls.PNGS[color]);
makeAbsolute(elem);
setElementPosition(elem, x, y);
root.nodes.add(elem);
ax = 0.0;
ay = GRAVITY;
vx = randomVelocity();
vy = randomVelocity();
}
// return false => remove me
bool tick(double delta) {
// Update velocity and position.
vx += ax * delta;
vy += ay * delta;
x += vx * delta;
y += vy * delta;
// Handle falling off the edge.
if ((x < RADIUS) || (x > clientWidth)) {
elem.remove();
return false;
}
// Handle ground collisions.
if (y > clientHeight) {
y = clientHeight.toDouble();
vy *= -RESTITUTION;
}
// Position the element.
setElementPosition(elem, x - RADIUS, y - RADIUS);
return true;
}
}
class ClockNumber {
static const int WIDTH = 4;
static const int HEIGHT = 7;
CountDownClock app;
Element root;
List<List<ImageElement>> imgs;
List<List<int>> pixels;
int ballColor;
ClockNumber(this.app, double pos, this.ballColor) {
imgs = new List<List<ImageElement>>(HEIGHT);
root = new DivElement();
makeAbsolute(root);
setElementPosition(root, pos, 0.0);
for (int y = 0; y < HEIGHT; ++y) {
imgs[y] = new List<ImageElement>(WIDTH);
}
for (int y = 0; y < HEIGHT; ++y) {
for (int x = 0; x < WIDTH; ++x) {
imgs[y][x] = new ImageElement();
root.nodes.add(imgs[y][x]);
makeAbsolute(imgs[y][x]);
setElementPosition(imgs[y][x],
x * CountDownClock.BALL_WIDTH, y * CountDownClock.BALL_HEIGHT);
}
}
}
void setPixels(List<List<int>> px) {
for (int y = 0; y < HEIGHT; ++y) {
for (int x = 0; x < WIDTH; ++x) {
ImageElement img = imgs[y][x];
if (pixels != null) {
if ((pixels[y][x] != 0) && (px[y][x] == 0)) {
scheduleMicrotask(() {
var r = img.getBoundingClientRect();
double absx = r.left;
double absy = r.top;
app.balls.add(absx, absy, ballColor);
});
}
}
img.src = px[y][x] != 0 ? Balls.PNGS[ballColor] : Balls.PNGS[6];
}
}
pixels = px;
}
}
class Colon {
Element root;
Colon(double xpos, double ypos) {
root = new DivElement();
makeAbsolute(root);
setElementPosition(root, xpos, ypos);
ImageElement dot = new ImageElement(src: Balls.PNGS[Balls.DK_GRAY_BALL_INDEX]);
root.nodes.add(dot);
makeAbsolute(dot);
setElementPosition(dot, 0.0, 2.0 * CountDownClock.BALL_HEIGHT);
dot = new ImageElement(src: Balls.PNGS[Balls.DK_GRAY_BALL_INDEX]);
root.nodes.add(dot);
makeAbsolute(dot);
setElementPosition(dot, 0.0, 4.0 * CountDownClock.BALL_HEIGHT);
}
}
class ClockNumbers {
static const PIXELS = const [
const [
const[ 1, 1, 1, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 1, 1, 1 ]
], const [
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ]
], const [
const[ 1, 1, 1, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 1, 1, 1, 1 ],
const[ 1, 0, 0, 0 ],
const[ 1, 0, 0, 0 ],
const[ 1, 1, 1, 1 ]
], const [
const[ 1, 1, 1, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 1, 1, 1, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 1, 1, 1, 1 ]
], const [
const[ 1, 0, 0, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 1, 1, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ]
], const [
const[ 1, 1, 1, 1 ],
const[ 1, 0, 0, 0 ],
const[ 1, 0, 0, 0 ],
const[ 1, 1, 1, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 1, 1, 1, 1 ]
], const [
const[ 1, 1, 1, 1 ],
const[ 1, 0, 0, 0 ],
const[ 1, 0, 0, 0 ],
const[ 1, 1, 1, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 1, 1, 1 ]
], const [
const[ 1, 1, 1, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ]
], const [
const[ 1, 1, 1, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 1, 1, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 1, 1, 1 ]
], const [
const[ 1, 1, 1, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 0, 0, 1 ],
const[ 1, 1, 1, 1 ],
const[ 0, 0, 0, 1 ],
const[ 0, 0, 0, 1 ],
const[ 1, 1, 1, 1 ]
]
];
}
/* Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file */
/* for details. All rights reserved. Use of this source code is governed by a */
/* BSD-style license that can be found in the LICENSE file. */
body {
background-color: #F8F8F8;
font-family: 'Open Sans', sans-serif;
font-size: 14px;
font-weight: normal;
line-height: 1.2em;
margin: 15px;
}
p {
color: #333;
}
#canvas-content {
width: 100%;
height: 400px;
position: relative;
border: 1px solid #ccc;
background-color: #fff;
}
#summary {
float: left;
}
#notes {
float: right;
width: 120px;
text-align: right;
}
.error {
font-style: italic;
color: red;
}
#container2 {
width: 100px;
height: 100px;
position: relative;
margin: auto;
}
#target {
width: 100%;
height: 100%;
position: absolute;
}
#target figure {
display: block;
position: absolute;
width: 150px;
height: 80px;
border: 1px solid #333;
font-size: 36px;
color: #222;
background: #3291d8;
text-align: center;
line-height: 2em;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment