drawing shapes on a canvas when the user clicks
This commit is contained in:
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
*~
|
||||||
|
*.swp
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
node_modules/*
|
||||||
|
hexmine
|
||||||
|
|
||||||
17
package.json
Normal file
17
package.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "tessellate",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"devDependencies": {
|
||||||
|
"webpack": "~1.12.13",
|
||||||
|
"babel-loader": "~6.2.2",
|
||||||
|
"babel-core": "~6.5.2",
|
||||||
|
"babel-preset-es2015": "~6.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
278
public/css/main.css
Normal file
278
public/css/main.css
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
/*! HTML5 Boilerplate v5.2.0 | MIT License | https://html5boilerplate.com/ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* What follows is the result of much research on cross-browser styling.
|
||||||
|
* Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,
|
||||||
|
* Kroc Camen, and the H5BP dev community and team.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
Base styles: opinionated defaults
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
html {
|
||||||
|
color: #222;
|
||||||
|
font-size: 1em;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove text-shadow in selection highlight:
|
||||||
|
* https://twitter.com/miketaylr/status/12228805301
|
||||||
|
*
|
||||||
|
* These selection rule sets have to be separate.
|
||||||
|
* Customize the background color to match your design.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-moz-selection {
|
||||||
|
background: #b3d4fc;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: #b3d4fc;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A better looking default horizontal rule
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
display: block;
|
||||||
|
height: 1px;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
margin: 1em 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove the gap between audio, canvas, iframes,
|
||||||
|
* images, videos and the bottom of their containers:
|
||||||
|
* https://github.com/h5bp/html5-boilerplate/issues/440
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio,
|
||||||
|
canvas,
|
||||||
|
iframe,
|
||||||
|
img,
|
||||||
|
svg,
|
||||||
|
video {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove default fieldset styles.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allow only vertical resizing of textareas.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
Browser Upgrade Prompt
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.browserupgrade {
|
||||||
|
margin: 0.2em 0;
|
||||||
|
background: #ccc;
|
||||||
|
color: #000;
|
||||||
|
padding: 0.2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
Author's custom styles
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
Helper classes
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hide visually and from screen readers:
|
||||||
|
*/
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hide only visually, but have it available for screen readers:
|
||||||
|
* http://snook.ca/archives/html_and_css/hiding-content-for-accessibility
|
||||||
|
*/
|
||||||
|
|
||||||
|
.visuallyhidden {
|
||||||
|
border: 0;
|
||||||
|
clip: rect(0 0 0 0);
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extends the .visuallyhidden class to allow the element
|
||||||
|
* to be focusable when navigated to via the keyboard:
|
||||||
|
* https://www.drupal.org/node/897638
|
||||||
|
*/
|
||||||
|
|
||||||
|
.visuallyhidden.focusable:active,
|
||||||
|
.visuallyhidden.focusable:focus {
|
||||||
|
clip: auto;
|
||||||
|
height: auto;
|
||||||
|
margin: 0;
|
||||||
|
overflow: visible;
|
||||||
|
position: static;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hide visually and from screen readers, but maintain layout
|
||||||
|
*/
|
||||||
|
|
||||||
|
.invisible {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clearfix: contain floats
|
||||||
|
*
|
||||||
|
* For modern browsers
|
||||||
|
* 1. The space content is one way to avoid an Opera bug when the
|
||||||
|
* `contenteditable` attribute is included anywhere else in the document.
|
||||||
|
* Otherwise it causes space to appear at the top and bottom of elements
|
||||||
|
* that receive the `clearfix` class.
|
||||||
|
* 2. The use of `table` rather than `block` is only necessary if using
|
||||||
|
* `:before` to contain the top-margins of child elements.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.clearfix:before,
|
||||||
|
.clearfix:after {
|
||||||
|
content: " "; /* 1 */
|
||||||
|
display: table; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix:after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
EXAMPLE Media Queries for Responsive Design.
|
||||||
|
These examples override the primary ('mobile first') styles.
|
||||||
|
Modify as content requires.
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
@media only screen and (min-width: 35em) {
|
||||||
|
/* Style adjustments for viewports that meet the condition */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print,
|
||||||
|
(-webkit-min-device-pixel-ratio: 1.25),
|
||||||
|
(min-resolution: 1.25dppx),
|
||||||
|
(min-resolution: 120dpi) {
|
||||||
|
/* Style adjustments for high resolution devices */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
Print styles.
|
||||||
|
Inlined to avoid the additional HTTP request:
|
||||||
|
http://www.phpied.com/delay-loading-your-print-css/
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
background: transparent !important;
|
||||||
|
color: #000 !important; /* Black prints faster:
|
||||||
|
http://www.sanbeiji.com/archives/953 */
|
||||||
|
box-shadow: none !important;
|
||||||
|
text-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
a:visited {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
a[href]:after {
|
||||||
|
content: " (" attr(href) ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title]:after {
|
||||||
|
content: " (" attr(title) ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't show links that are fragment identifiers,
|
||||||
|
* or use the `javascript:` pseudo protocol
|
||||||
|
*/
|
||||||
|
|
||||||
|
a[href^="#"]:after,
|
||||||
|
a[href^="javascript:"]:after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
blockquote {
|
||||||
|
border: 1px solid #999;
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Printing Tables:
|
||||||
|
* http://css-discuss.incutio.com/wiki/Printing_Tables
|
||||||
|
*/
|
||||||
|
|
||||||
|
thead {
|
||||||
|
display: table-header-group;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr,
|
||||||
|
img {
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
orphans: 3;
|
||||||
|
widows: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
page-break-after: avoid;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
public/index.html
Normal file
14
public/index.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
|
|
||||||
|
<title>Tessellate</title>
|
||||||
|
<link rel="stylesheet" href="css/main.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container" style="width: 100vw; height: 100vh;"></div>
|
||||||
|
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
788
public/js/main.js
Normal file
788
public/js/main.js
Normal file
File diff suppressed because one or more lines are too long
123
src/hex.js
Normal file
123
src/hex.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
|
||||||
|
import Point from './point.js';
|
||||||
|
|
||||||
|
function computeY(x, z) {
|
||||||
|
return -x - z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert real numbers to integers:
|
||||||
|
// round off coords
|
||||||
|
// throw out whichever one changed the most
|
||||||
|
// re-establish "x + y + z = 0"
|
||||||
|
function roundOff(x, y, z) {
|
||||||
|
let rX = Math.round(x);
|
||||||
|
let rY = Math.round(y);
|
||||||
|
let rZ = Math.round(z);
|
||||||
|
|
||||||
|
let xDiff = Math.abs(rX - x);
|
||||||
|
let yDiff = Math.abs(rY - y);
|
||||||
|
let zDiff = Math.abs(rZ - z);
|
||||||
|
|
||||||
|
if ((xDiff > yDiff) && (xDiff > zDiff)) {
|
||||||
|
rX = -rY-rZ;
|
||||||
|
}
|
||||||
|
else if (yDiff > zDiff) {
|
||||||
|
rY = -rX-rZ;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rZ = -rX-rY;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = rX === -0 ? 0 : rX;
|
||||||
|
y = rY === -0 ? 0 : rY;
|
||||||
|
z = rZ === -0 ? 0 : rZ;
|
||||||
|
|
||||||
|
return {x, y, z};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Hex extends Point {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
if (arguments.length === 2) { // hex = Hex(q, r);
|
||||||
|
this.x = arguments[0];
|
||||||
|
this.z = arguments[1];
|
||||||
|
this.y = computeY(this.x, this.z);
|
||||||
|
}
|
||||||
|
else if (arguments.length === 3) { // hex = Hex(x, y, z);
|
||||||
|
this.x = arguments[0];
|
||||||
|
this.y = arguments[1];
|
||||||
|
this.z = arguments[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
roundOff(this.x, this.y, this.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getX: function() {return this.x;},
|
||||||
|
getY: function() {return this.y;},
|
||||||
|
getZ: function() {return this.z;},
|
||||||
|
|
||||||
|
setX: function(newX) {this.x = newX; return this;},
|
||||||
|
setY: function(newY) {this.y = newY; return this;},
|
||||||
|
setZ: function(newZ) {this.z = newZ; return this;},
|
||||||
|
|
||||||
|
moveX: function(byX) {this.x += byX; return this;},
|
||||||
|
moveY: function(byY) {this.y += byY; return this;},
|
||||||
|
moveZ: function(byZ) {this.z += byZ; return this;},
|
||||||
|
|
||||||
|
getQ: function() {return this.x;},
|
||||||
|
getR: function() {return this.z;},
|
||||||
|
|
||||||
|
setQ: function(newQ) {
|
||||||
|
this.x = newQ;
|
||||||
|
this.y = computeY(this.x, this.z);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
setR: function(newR) {
|
||||||
|
this.z = newR;
|
||||||
|
this.y = computeY(this.x, this.z);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
moveQ: function(byQ) {
|
||||||
|
this.x += byQ;
|
||||||
|
this.y = computeY(this.x, this.z);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
moveR: function(byR) {
|
||||||
|
this.z += byR;
|
||||||
|
this.y = computeY(this.x, this.z);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
getHex: function() { return {x: this.x, y: this.y, z: this.z}; },
|
||||||
|
setHex: function(newHex) {
|
||||||
|
this.x = newHex.x;
|
||||||
|
this.y = newHex.y;
|
||||||
|
this.z = newHex.z;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
moveHex: function(byHex) {
|
||||||
|
this.x += byHex.x;
|
||||||
|
this.y += byHex.y;
|
||||||
|
this.z += byHex.z;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
getAxial: function() {return {x: this.x, z: this.z};},
|
||||||
|
setAxial: function(newAxial) {
|
||||||
|
this.x = newAxial.q;
|
||||||
|
this.z = newAxial.r;
|
||||||
|
this.y = computeY(this.x, this.y);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
moveAxial: function(byAxial) {
|
||||||
|
this.x += byAxial.q;
|
||||||
|
this.z += byAxial.r;
|
||||||
|
this.y = computeY(this.x, this.z);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
7
src/main.js
Normal file
7
src/main.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
import Tessellate from './tessellate.js';
|
||||||
|
|
||||||
|
var tessellate = new Tessellate({
|
||||||
|
element: '#container'
|
||||||
|
});
|
||||||
|
|
||||||
24
src/onTap.js
Normal file
24
src/onTap.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
export default class OnTap {
|
||||||
|
constructor(settings) {
|
||||||
|
this.element = settings.element || document.body;
|
||||||
|
|
||||||
|
this.callbacks = {};
|
||||||
|
|
||||||
|
this.events = ['click'];
|
||||||
|
|
||||||
|
this.events.map(method => {
|
||||||
|
this[method] = this[method].bind(this);
|
||||||
|
|
||||||
|
if (typeof settings[method] === 'function') {
|
||||||
|
this.element.addEventListener(method, this[method]);
|
||||||
|
this.callbacks[method] = settings[method];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
click(event) {
|
||||||
|
this.callbacks.click(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
15
src/point.js
Normal file
15
src/point.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
export default class Point {
|
||||||
|
constructor(newX, newY) {
|
||||||
|
this.x = newX;
|
||||||
|
this.y = newY;
|
||||||
|
}
|
||||||
|
|
||||||
|
getX() { return this.x; }
|
||||||
|
getY() { return this.y; }
|
||||||
|
|
||||||
|
setX(newX) { this.x = newX; }
|
||||||
|
setY(newY) { this.y = newY; }
|
||||||
|
|
||||||
|
getPoint() { return {x: this.x, y: this.y}; }
|
||||||
|
};
|
||||||
37
src/sketch.js
Normal file
37
src/sketch.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
export default class Sketch {
|
||||||
|
constructor(settings) {
|
||||||
|
this.lastTime = null;
|
||||||
|
|
||||||
|
['getContext', 'onResize', 'render'].map(method => this[method] = this[method].bind(this));
|
||||||
|
|
||||||
|
this.draw = settings.draw || function() {}; // () => {};
|
||||||
|
this.container = settings.element || document.body;
|
||||||
|
|
||||||
|
window.addEventListener('optimizedResize', this.onResize);
|
||||||
|
|
||||||
|
this.canvas = document.createElement('canvas');
|
||||||
|
this.canvas.width = this.container.offsetWidth;
|
||||||
|
this.canvas.height = this.container.offsetHeight;
|
||||||
|
|
||||||
|
this.context = this.canvas.getContext('2d');
|
||||||
|
|
||||||
|
this.container.appendChild(this.canvas);
|
||||||
|
|
||||||
|
requestAnimationFrame(this.render);
|
||||||
|
}
|
||||||
|
|
||||||
|
getContext() { return this.context; }
|
||||||
|
|
||||||
|
onResize(event) {
|
||||||
|
console.log('sketch - onResize', arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(now) {
|
||||||
|
this.draw(this.context, now, this.lastTime);
|
||||||
|
|
||||||
|
this.lastTime = now;
|
||||||
|
requestAnimationFrame(this.render);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
62
src/tessellate.js
Normal file
62
src/tessellate.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
import {random} from './utils.js';
|
||||||
|
|
||||||
|
import OnTap from './onTap.js';
|
||||||
|
import Point from './point.js';
|
||||||
|
import Sketch from './sketch.js';
|
||||||
|
|
||||||
|
import TileCircle from './tileCircle.js';
|
||||||
|
import TileDiamond from './tileDiamond.js';
|
||||||
|
import TileHex from './tileHex.js';
|
||||||
|
import TileSquare from './tileSquare.js';
|
||||||
|
|
||||||
|
export default class Tessellate {
|
||||||
|
constructor(settings) {
|
||||||
|
['draw'].map(method => {this[method] = this[method].bind(this)});
|
||||||
|
|
||||||
|
this.element = document.querySelector(settings.element);
|
||||||
|
|
||||||
|
this.sketch = new Sketch({
|
||||||
|
element: this.element,
|
||||||
|
draw: this.draw
|
||||||
|
});
|
||||||
|
|
||||||
|
this.map = [];
|
||||||
|
|
||||||
|
this.onTap = new OnTap({
|
||||||
|
element: this.element,
|
||||||
|
click: (event) => {
|
||||||
|
let x = event.pageX;
|
||||||
|
let y = event.pageY;
|
||||||
|
let tiles = ['circle', 'diamond', 'hex', 'square'];
|
||||||
|
let styles = ['filled', 'outline'];
|
||||||
|
let scale = random(10, 50);
|
||||||
|
|
||||||
|
this.map.push({
|
||||||
|
tile: tiles[random(tiles.length-1)],
|
||||||
|
style: styles[random(styles.length-1)],
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
scale,
|
||||||
|
pointy: random(1) ? true : false,
|
||||||
|
color: `rgba(${ random(255) }, ${ random(255) }, ${ random(255) }, 0.5)`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.circle = new TileCircle({context: this.sketch.getContext()});
|
||||||
|
this.diamond = new TileDiamond({context: this.sketch.getContext()});
|
||||||
|
this.hex = new TileHex({context: this.sketch.getContext()});
|
||||||
|
this.square = new TileSquare({context: this.sketch.getContext()});
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(context, now, lastTime) {
|
||||||
|
let canvas = context.canvas;
|
||||||
|
let width = canvas.width;
|
||||||
|
let height = canvas.height;
|
||||||
|
|
||||||
|
context.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
this.map.forEach(cell => this[cell.tile][cell.style](cell));
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/tile.js
Normal file
11
src/tile.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
export default class Tile {
|
||||||
|
constructor(settings) {
|
||||||
|
this.context = settings.context || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setContext(newContext) {
|
||||||
|
this.context = newContext || this.context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
26
src/tileCircle.js
Normal file
26
src/tileCircle.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
import Tile from './tile.js';
|
||||||
|
|
||||||
|
let RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)';
|
||||||
|
|
||||||
|
export default class TileCircle extends Tile {
|
||||||
|
constructor(settings) {
|
||||||
|
super(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
filled(settings) {
|
||||||
|
this.context.beginPath();
|
||||||
|
this.context.arc(settings.x, settings.y, settings.scale, 0, 2*Math.PI, false);
|
||||||
|
this.context.fillStyle = settings.color || RGBA_DEFAULT;
|
||||||
|
this.context.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
outline(settings) {
|
||||||
|
this.context.beginPath();
|
||||||
|
this.context.arc(settings.x, settings.y, settings.scale, 0, 2*Math.PI, false);
|
||||||
|
this.context.lineWidth = settings.width ? settings.width : 1;
|
||||||
|
this.context.strokeStyle = settings.color || RGBA_DEFAULT;
|
||||||
|
this.context.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
45
src/tileDiamond.js
Normal file
45
src/tileDiamond.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
import Tile from './tile.js';
|
||||||
|
|
||||||
|
import {hypotenuse} from './utils.js';
|
||||||
|
|
||||||
|
let RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)';
|
||||||
|
|
||||||
|
export default class TileDiamond extends Tile {
|
||||||
|
constructor(settings) {
|
||||||
|
super(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
filled(settings) {
|
||||||
|
let x = settings.x;
|
||||||
|
let y = settings.y;
|
||||||
|
let scale = settings.scale;
|
||||||
|
|
||||||
|
this.context.beginPath();
|
||||||
|
this.context.moveTo(x+hypotenuse(scale), y);
|
||||||
|
this.context.lineTo(x, y-hypotenuse(scale));
|
||||||
|
this.context.lineTo(x-hypotenuse(scale), y);
|
||||||
|
this.context.lineTo(x, y+hypotenuse(scale));
|
||||||
|
|
||||||
|
this.context.fillStyle = settings.color || RGBA_DEFAULT;
|
||||||
|
this.context.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
outline(settings) {
|
||||||
|
let x = settings.x;
|
||||||
|
let y = settings.y;
|
||||||
|
let scale = settings.scale;
|
||||||
|
|
||||||
|
this.context.beginPath();
|
||||||
|
this.context.moveTo(x+hypotenuse(scale), y);
|
||||||
|
this.context.lineTo(x, y-hypotenuse(scale));
|
||||||
|
this.context.lineTo(x-hypotenuse(scale), y);
|
||||||
|
this.context.lineTo(x, y+hypotenuse(scale));
|
||||||
|
this.context.closePath();
|
||||||
|
|
||||||
|
this.context.lineWidth = settings.width ? settings.width : 1;
|
||||||
|
this.context.strokeStyle = settings.color || RGBA_DEFAULT;
|
||||||
|
this.context.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
97
src/tileHex.js
Normal file
97
src/tileHex.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
|
||||||
|
import Tile from './tile.js';
|
||||||
|
|
||||||
|
let RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)';
|
||||||
|
|
||||||
|
export default class TileHex extends Tile {
|
||||||
|
constructor(settings) {
|
||||||
|
super(settings);
|
||||||
|
|
||||||
|
this.sqrt3 = Math.sqrt(3);
|
||||||
|
|
||||||
|
this.hexSides = 6;
|
||||||
|
this.pointyTopCornerX = [];
|
||||||
|
this.pointyTopCornerY = [];
|
||||||
|
this.flatTopCornerX = [];
|
||||||
|
this.flatTopCornerY = [];
|
||||||
|
|
||||||
|
// dice dots, 0,0 is center
|
||||||
|
// TODO: if pointy: [0].concat(flatTop), if flat: [0].concat(pointy)
|
||||||
|
// this.hexDotX = [0];
|
||||||
|
// this.hexDotY = [0];
|
||||||
|
|
||||||
|
this.hexSlices = 24;
|
||||||
|
this.hexSliceX = [0];
|
||||||
|
this.hexSliceY = [0];
|
||||||
|
|
||||||
|
for (let hexSlice = 0; hexSlice < this.hexSlices; hexSlice++) {
|
||||||
|
this.hexSliceX[hexSlice] = parseInt(Math.cos(((hexSlice/this.hexSlices)*this.hexSides) * (2 * Math.PI) / this.hexSides) * 1000) / 1000;
|
||||||
|
this.hexSliceY[hexSlice] = parseInt(Math.sin(((hexSlice/this.hexSlices)*this.hexSides) * (2 * Math.PI) / this.hexSides) * 1000) / 1000;
|
||||||
|
|
||||||
|
if (((hexSlice-2) % 4) === 0) {
|
||||||
|
let cur = (hexSlice-2) / 4;
|
||||||
|
this.pointyTopCornerX[cur] = this.hexSliceX[hexSlice];
|
||||||
|
this.pointyTopCornerY[cur] = this.hexSliceY[hexSlice];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((hexSlice % 4) === 0) {
|
||||||
|
let cur = hexSlice / 4;
|
||||||
|
this.flatTopCornerX[cur] = this.hexSliceX[hexSlice];
|
||||||
|
this.flatTopCornerY[cur] = this.hexSliceY[hexSlice];
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (((hexSlice-2) % 4) === 0) {
|
||||||
|
// let cur = (hexSlice-2) / 4;
|
||||||
|
// this.hexCornerX[cur] = this.hexSliceX[hexSlice];
|
||||||
|
// this.hexCornerY[cur] = this.hexSliceY[hexSlice];
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if ((hexSlice % 4) === 0) {
|
||||||
|
// let cur = hexSlice / 4;
|
||||||
|
// this.hexDotX[cur+1] = this.hexSliceX[hexSlice];
|
||||||
|
// this.hexDotY[cur+1] = this.hexSliceY[hexSlice];
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outline(settings) {
|
||||||
|
let x = settings.x;
|
||||||
|
let y = settings.y;
|
||||||
|
let scale = settings.scale;
|
||||||
|
let hexCornerX = settings.pointy ? this.pointyTopCornerX : this.flatTopCornerX;
|
||||||
|
let hexCornerY = settings.pointy ? this.pointyTopCornerY : this.flatTopCornerY;
|
||||||
|
|
||||||
|
this.context.beginPath();
|
||||||
|
this.context.moveTo(x + scale * hexCornerX[0], y + scale * hexCornerY[0]);
|
||||||
|
this.context.lineTo(x + scale * hexCornerX[1], y + scale * hexCornerY[1]);
|
||||||
|
this.context.lineTo(x + scale * hexCornerX[2], y + scale * hexCornerY[2]);
|
||||||
|
this.context.lineTo(x + scale * hexCornerX[3], y + scale * hexCornerY[3]);
|
||||||
|
this.context.lineTo(x + scale * hexCornerX[4], y + scale * hexCornerY[4]);
|
||||||
|
this.context.lineTo(x + scale * hexCornerX[5], y + scale * hexCornerY[5]);
|
||||||
|
this.context.closePath();
|
||||||
|
|
||||||
|
this.context.lineWidth = settings.width ? settings.width : 1;
|
||||||
|
this.context.strokeStyle = settings.color || RGBA_DEFAULT;
|
||||||
|
this.context.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
filled(settings) {
|
||||||
|
let x = settings.x;
|
||||||
|
let y = settings.y;
|
||||||
|
let scale = settings.scale;
|
||||||
|
let hexCornerX = settings.pointy ? this.pointyTopCornerX : this.flatTopCornerX;
|
||||||
|
let hexCornerY = settings.pointy ? this.pointyTopCornerY : this.flatTopCornerY;
|
||||||
|
|
||||||
|
this.context.beginPath();
|
||||||
|
this.context.moveTo(x + scale * hexCornerX[0], y + scale * hexCornerY[0]);
|
||||||
|
this.context.lineTo(x + scale * hexCornerX[1], y + scale * hexCornerY[1]);
|
||||||
|
this.context.lineTo(x + scale * hexCornerX[2], y + scale * hexCornerY[2]);
|
||||||
|
this.context.lineTo(x + scale * hexCornerX[3], y + scale * hexCornerY[3]);
|
||||||
|
this.context.lineTo(x + scale * hexCornerX[4], y + scale * hexCornerY[4]);
|
||||||
|
this.context.lineTo(x + scale * hexCornerX[5], y + scale * hexCornerY[5]);
|
||||||
|
|
||||||
|
this.context.fillStyle = settings.color || RGBA_DEFAULT;
|
||||||
|
this.context.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
43
src/tileSquare.js
Normal file
43
src/tileSquare.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
import Tile from './tile.js';
|
||||||
|
|
||||||
|
let RGBA_DEFAULT = 'rgba(0, 0, 0, 0.5)';
|
||||||
|
|
||||||
|
export default class TileSquare extends Tile {
|
||||||
|
constructor(settings) {
|
||||||
|
super(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
filled(settings) {
|
||||||
|
let x = settings.x;
|
||||||
|
let y = settings.y;
|
||||||
|
let scale = settings.scale;
|
||||||
|
|
||||||
|
this.context.beginPath();
|
||||||
|
this.context.moveTo(x+scale, y+scale);
|
||||||
|
this.context.lineTo(x+scale, y-scale);
|
||||||
|
this.context.lineTo(x-scale, y-scale);
|
||||||
|
this.context.lineTo(x-scale, y+scale);
|
||||||
|
|
||||||
|
this.context.fillStyle = settings.color || RGBA_DEFAULT;
|
||||||
|
this.context.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
outline(settings) {
|
||||||
|
let x = settings.x;
|
||||||
|
let y = settings.y;
|
||||||
|
let scale = settings.scale;
|
||||||
|
|
||||||
|
this.context.beginPath();
|
||||||
|
this.context.moveTo(x+scale, y+scale);
|
||||||
|
this.context.lineTo(x+scale, y-scale);
|
||||||
|
this.context.lineTo(x-scale, y-scale);
|
||||||
|
this.context.lineTo(x-scale, y+scale);
|
||||||
|
this.context.closePath();
|
||||||
|
|
||||||
|
this.context.lineWidth = settings.width ? settings.width : 1;
|
||||||
|
this.context.strokeStyle = settings.color || RGBA_DEFAULT;
|
||||||
|
this.context.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
48
src/utils.js
Normal file
48
src/utils.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
export function throttleEvent(type, name, obj) {
|
||||||
|
obj = obj || window;
|
||||||
|
let running = false;
|
||||||
|
|
||||||
|
let throttle = () => {
|
||||||
|
if (!running) {
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
obj.dispatchEvent(new CustomEvent(name));
|
||||||
|
running = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.addEventListener(type, throttle);
|
||||||
|
}
|
||||||
|
|
||||||
|
throttleEvent('resize', 'optimizedResize');
|
||||||
|
|
||||||
|
export function clone(obj) {
|
||||||
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extend(obj, src) {
|
||||||
|
for (let key in src) {
|
||||||
|
if (src.hasOwnProperty(key)) obj[key] = src[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hypotenuse(a, b) {
|
||||||
|
if (b == null) b = a;
|
||||||
|
|
||||||
|
return Math.sqrt(a*a + b*b);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function random(min, max) {
|
||||||
|
if (max == null) {
|
||||||
|
max = min;
|
||||||
|
min = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return min + Math.floor(Math.random() * (max - min + 1));
|
||||||
|
}
|
||||||
|
|
||||||
21
webpack.config.js
Normal file
21
webpack.config.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: path.join(__dirname, 'src', 'main.js'),
|
||||||
|
output: {
|
||||||
|
libraryTarget: 'this',
|
||||||
|
path: path.join(__dirname, 'public', 'js'),
|
||||||
|
filename: 'main.js'
|
||||||
|
},
|
||||||
|
devtool: 'inline-source-map',
|
||||||
|
module: {
|
||||||
|
loaders: [{
|
||||||
|
test: path.join(__dirname, 'src'),
|
||||||
|
loader: 'babel-loader',
|
||||||
|
query: {
|
||||||
|
presets: ['es2015']
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user