<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<canvas id="canvas"></canvas>
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<canvas id="canvas"></canvas>
<section class="round">
<article class="game">
<div class="team team-winner team-florida" data-team="florida">
<span class="team-seed">1</span>
<span class="team-name">Florida</span>
<span class="team-score">61</span>
</div>
<div class="team team-loser team-pitt" data-team="pitt">
<span class="team-seed">9</span>
<span class="team-name">Pitt</span>
<span class="team-score">45</span>
</div>
</article>
<article class="game">
<div class="team team-winner team-ucla" data-team="ucla">
<span class="team-seed">4</span>
<span class="team-name">UCLA</span>
<span class="team-score">77</span>
</div>
<div class="team team-loser team-stephen-f-austin" data-team="stephen-f-austin">
<span class="team-seed">12</span>
<span class="team-name">Stephen F. Austin</span>
<span class="team-score">60</span>
</div>
</article>
<article class="game">
<div class="team team-loser team-kansas" data-team="kansas">
<span class="team-seed">2</span>
<span class="team-name">Kansas</span>
<span class="team-score">57</span>
</div>
<div class="team team-winner team-stanford" data-team="stanford">
<span class="team-seed">10</span>
<span class="team-name">Stanford</span>
<span class="team-score">60</span>
</div>
</article>
<article class="game">
<div class="team team-loser team-syracuse" data-team="syracuse">
<span class="team-seed">3</span>
<span class="team-name">Syracuse</span>
<span class="team-score">53</span>
</div>
<div class="team team-winner team-dayton" data-team="dayton">
<span class="team-seed">11</span>
<span class="team-name">Dayton</span>
<span class="team-score">55</span>
</div>
</article>
<article class="game">
<div class="team team-loser team-kansas" data-team="kansas">
<span class="team-seed">2</span>
<span class="team-name">Kansas</span>
<span class="team-score">57</span>
</div>
<div class="team team-winner team-stanford" data-team="stanford">
<span class="team-seed">10</span>
<span class="team-name">Stanford</span>
<span class="team-score">60</span>
</div>
</article>
<article class="game">
<div class="team team-loser team-syracuse" data-team="syracuse">
<span class="team-seed">3</span>
<span class="team-name">Syracuse</span>
<span class="team-score">53</span>
</div>
</article>
</section>
body {
background: #F2F2F2;
margin: 0;
}
* {
box-sizing: border-box;
}
body, html {
height: 100%;
width: 100%;
}
#canvas {
position: absolute;
top: 0;
left: 0;
}
.region {
height: 100%;
position: relative;
z-index: 1;
}
.region .region-name {
position: absolute;
top: 50%;
left: 0;
width: 100%;
text-align: center;
line-height: 0;
margin: -18px 0 0;
padding: 0;
font: 600 30px proxima-nova, proxima-nova-1, proxima-nova-2, "Proxima Nova", arial, helvetica, sans-serif;
}
.round {
font: 300 15px proxima-nova, proxima-nova-1, proxima-nova-2, "Proxima Nova", arial, helvetica, sans-serif;
display: flex;
flex-direction: column;
justify-content: space-around;
height: 100%;
padding: 0 20px;
float: left;
width: 33.3333333333%;
position: relative;
z-index: 2;
pointer-events: none;
}
.round.round-collapse {
margin-left: -16.666666666%;
}
.round .game {
background: #FFF;
border-radius: 3px;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.05);
overflow: hidden;
display: flex;
flex-direction: column;
max-height: 80px;
pointer-events: auto;
}
.round .game .team {
display: flex;
align-items: center;
flex: 1 1 40px;
cursor: pointer;
border-left: 10px solid #DDD;
transition: .2s;
/* keep this for smoother transitions: */
box-shadow: inset 0 40px 0 rgba(255, 255, 255, 0);
}
.round .game .team .team-seed {
font-size: 10px;
color: #999;
flex: 0 1 30px;
text-align: center;
transition: .2s;
}
.round .game .team .team-name {
flex: 1 1 auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.round .game .team .team-score {
font-weight: 600;
flex: 0 1 30px;
}
.round .game .team .team-score:empty {
display: none;
}
.round .game .team.team-hover {
background: #CFC59A;
color: #FFF !important;
box-shadow: inset 0 40px 0 rgba(255, 255, 255, 0.2);
}
.round .game .team.team-hover .team-seed {
color: #FFF;
}
.round .game .team:active {
box-shadow: inset 0 40px 0 rgba(255, 255, 255, 0);
}
.round .game .team.team-loser {
background: #F7F7F7;
color: #BBB;
}
.region-right .round {
float: right;
}
.region-right .round.round-collapse {
margin-left: 0;
margin-right: -16.666666666%;
}
.region-right .round .team {
border-left: 0;
border-right: 10px solid #DDD;
flex-direction: row-reverse;
text-align: right;
}
.round .game .team-florida {
border-color: #0020A5;
}
.round .game .team-ua-ms-mary {
border-color: #908458;
}
.round .game .team-colorado {
border-color: #090907;
}
.round .game .team-pitt {
border-color: #CFC59A;
}
.round .game .team-ucla {
border-color: #0073CF;
}
.round .game .team-tulsa {
border-color: #064A99;
}
.round .game .team-vcu {
border-color: #000000;
}
.round .game .team-stephen-f-austin {
border-color: #330066;
}
.round .game .team-kansas {
border-color: #0018A8;
}
.round .game .team-eastern-kentucky {
border-color: #760018;
}
.round .game .team-new-mexico {
border-color: #CC003D;
}
.round .game .team-stanford {
border-color: #8C1515;
}
.round .game .team-syracuse {
border-color: #FF5113;
}
.round .game .team-western-mich {
border-color: #62390E;
}
.round .game .team-ohio-st {
border-color: #AD0F20;
}
.round .game .team-dayton {
border-color: #C40023;
}
var Tournament = function() {
var defaults = {
width: 3,
color: '#BBB',
radius: 15,
regionSelector: '.region'
};
return {
init: function(options) {
this.options = $.extend(defaults, options);
this.canvas = document.getElementById('canvas');
this.ctx = this.canvas.getContext('2d');
this.bindEvents();
this.render();
},
bindEvents: function() {
$(window).on('resize', this.render.bind(this));
$('.team').on({
mouseenter: function(e) {
$('.team-' + $(this).attr('data-team'))
.addClass('team-hover')
.css('background', $(this).css('border-left-color'));
},
mouseleave: function(e) {
$('.team-' + $(this).attr('data-team'))
.removeClass('team-hover')
.css('background', '');
}
});
},
render: function() {
var w = $(document).width();
var h = $(document).height();
this.canvas.width = w * 2; // retina
this.canvas.height = h * 2; // retina
this.canvas.style.width = w + 'px';
this.canvas.style.height = h + 'px';
this.ctx.clearRect( 0, 0, this.canvas.width, this.canvas.height );
this.ctx.scale(2, 2); // retina
var that = this;
$(this.options.regionSelector).each(function() {
var rightAlign = $(this).hasClass('region-right');
$(this).find('.round').each(function(round) {
var $nextGames = $(this).next('.round').find('.game');
if (!$nextGames.length) return;
$(this).find('.game').each(function(i) {
var $winner = $(this).find('.team-winner'),
$nextGame = $nextGames.eq( Math.floor(i/2) ),
color = $winner.length ? $winner.css('border-left-color') : that.options.color,
width = $winner.length ? that.options.width : 0.5,
calcFn = rightAlign ? that.calcLeft : that.calcRight,
start = calcFn( $winner.length ? $winner : $(this) );
if (round == 0) {
// s-curve
var endNode = $nextGame;
if ($winner.length) {
endNode = $nextGame.find('.team-' + $winner.attr('data-team'));
}
calcFn = rightAlign ? that.calcRight : that.calcLeft;
var end = calcFn(endNode);
var radiusAdjust = Math.min(that.options.radius, Math.abs(start.y - end.y)/2);
that.drawSCurve(start, end, color, width, that.options.radius, radiusAdjust);
} else {
// single curve for collapsed columns
var end = that.calcCenter($nextGame);
that.drawCurve(start, end, 'horizontal', color, width, that.options.radius);
}
}); // /game
}); // /round
}); // /region
},
// Calculate center points
// +-----+
// | x
// +-----+
calcRight: function ($object) {
return {
x: $object.offset().left + $object.outerWidth(),
y: $object.offset().top + $object.outerHeight() / 2
};
},
// +-----+
// x |
// +-----+
calcLeft: function ($object) {
return {
x: $object.offset().left,
y: $object.offset().top + $object.outerHeight() / 2
};
},
// +-----+
// | x |
// +-----+
calcCenter: function ($object) {
return {
x: $object.offset().left + $object.outerWidth() / 2,
y: $object.offset().top + $object.outerHeight() / 2
};
},
drawLine: function (start, end) {
this.ctx.moveTo( start.x, start.y );
this.ctx.lineTo( end.x, end.y );
},
// one curve
drawCurve: function (start, end, orientation, color, width, radius, radius2) {
if (!radius2) radius2 = radius;
this.ctx.beginPath();
if (orientation == 'horizontal') {
var anchor = { x: end.x, y: start.y };
} else {
var anchor = { x: start.x, y: end.y };
}
// calculate the point a certain distance along the line
var m1 = this.lineDistanceFromEnd(start, anchor, radius);
var m2 = this.lineDistanceFromEnd(end, anchor, radius2);
this.drawLine(start, m1);
this.ctx.bezierCurveTo(m1.x, m1.y, anchor.x, anchor.y, m2.x, m2.y);
this.drawLine(m2, end);
this.ctx.strokeStyle = color;
this.ctx.lineWidth = width;
this.ctx.lineCap = 'square';
this.ctx.stroke();
this.ctx.closePath();
},
// two curves
drawSCurve: function (start, end, color, width, radius, radius2) {
var midpoint = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };
if (!radius2) radius2 = radius;
this.drawCurve(start, midpoint, 'horizontal', color, width, radius, radius2);
this.drawCurve(midpoint, end, 'vertical', color, width, radius2, radius);
},
lineDistanceFromEnd: function (start, end, d) {
var x = end.x, y = end.y;
if (end.x - start.x < 0) x += d; // left
if (end.x - start.x > 0) x -= d; // right
if (end.y - start.y < 0) y += d; // up
if (end.y - start.y > 0) y -= d; // down
return { x: x, y: y };
}
};
};
var tournament = new Tournament();
tournament.init();