var crdEdt = window.crdEdt || {}; /** * * @class * @namespace crdEdt * @static */ crdEdt.entities = new function() { this.boundingBox = function(pos, dimensions) { this.x = pos ? pos.x : 0; this.y = pos ? pos.y : 0; this.width = dimensions ? dimensions.width : 1; this.height = dimensions ? dimensions.height : 1; } }; /** * * @class * @namespace crdEdt * @static */ crdEdt.settings = new function() { this.anchorPos = { x: 60, y: 40 }; this.cursor = { fillColor: 'rgba(220, 216, 73, 0.35)', // 'rgba(245, 127, 18, 0.3)', strokeWidth: 1, strokeColor: '#AAB444', // '#F57F12', radius: 9 }; this.fretBoard = { numFrets: 5, stringNames: ['G', 'C', 'E', 'A'], //fillColor: '#E3DEB8', strokeWidth: 4, strokeColor: '#8F8569', //stringWidth: 2, //fretLines: 'black', //lineWidth: 2, /** * Row Height -- vertical height between frets (pixels) * @property settings.fretBox.fretSpace * @type int */ fretSpace: 35, /** * String Spacing -- horizontal distance between strings (pixels) * @property settings.fretBox.stringSpace * @type int */ stringSpace: 30 }; this.dot = { fillColor: '#F68014', radius: 11, strokeWidth: 2, strokeColor: '#D56333' }; this.fretLabel = { fontFamily: 'Calibri', fontSize: 28, // Pixels color: '#6A6A63', lightColor: '#EAEAE8' //D6D6D6' //A4A4A3' }; this.stringLabel = { fontFamily: 'Calibri', fontSize: 34, // Pixels color: '#DCD849' // #AAB444'// }; this.target = { fillColor: { r: 159, g: 207, b: 101 } }; /** * Dimensions of a single target * @return {JSON} {width: ?, height: ? } */ this.targetDimensions = function() { return { height: crdEdt.settings.fretBoard.fretSpace, width: crdEdt.settings.fretBoard.stringSpace }; }; /** * Top left-hand corner where Targets begin positioning * @return {JSON} {x: ?, y: ? } */ this.targetAnchorPos = function() { var dimensions = this.targetDimensions(); return { x: crdEdt.settings.anchorPos.x - 0.5 * dimensions.width, y: crdEdt.settings.anchorPos.y - dimensions.height - 0.2 * crdEdt.settings.fretBoard.strokeWidth }; }; }; /** * * @class * @namespace crdEdt * @static */ crdEdt.tracking = new function() { var targetBox = null; var getTarget = function() { if (targetBox) { return targetBox; } var dimensions = crdEdt.settings.targetDimensions(); dimensions.width = dimensions.width * crdEdt.settings.fretBoard.stringNames.length; dimensions.height = dimensions.height * (crdEdt.settings.fretBoard.numFrets + 1); targetBox = new crdEdt.entities.boundingBox(crdEdt.settings.targetAnchorPos(), dimensions); return targetBox; }; var collision = function(object1, object2) { return (object1.x < object2.x + object2.width) && (object1.x + object1.width > object2.x) && (object1.y < object2.y + object2.height) && (object1.y + object1.height > object2.y); }; this.check = function(pos) { var cursorBox = new crdEdt.entities.boundingBox(pos); var box = getTarget(); if (!collision(cursorBox, box)) { return null; } var dimensions = crdEdt.settings.targetDimensions(); var x = Math.floor((pos.x - box.x) / dimensions.width); var y = Math.floor((pos.y - box.y) / dimensions.height); return { x: x, y: y }; }; }; /** * * @class fretDots * @namespace crdEdt * @static */ crdEdt.fretDots = new function() { var _dots = []; this.getDots = function() { return _dots.slice(); }; this.toggle = function(dot) { if (dot.y == 0) { clearColumn(dot.x); return; } var index = find(dot); if (index < 0) { // console.log('add') _dots.push(dot); } else { // console.log('remove') _dots.splice(index, 1); } //console.log(_dots); }; var find = function(dot) { for (var i = _dots.length - 1; i >= 0; i--) { if (_dots[i].x == dot.x && _dots[i].y == dot.y) { return i; } }; return -1; }; var clearColumn = function(x) { //console.log('clearColumn ' + x); for (var i = _dots.length - 1; i >= 0; i--) { //console.log(_dots[i].x); if (_dots[i].x == x) { //console.log(_dots[i]); _dots.splice(i, 1); } }; }; var getPosition = function(dot) { return { x: crdEdt.settings.anchorPos.x + 0.47 * crdEdt.settings.fretBoard.strokeWidth + dot.x * crdEdt.settings.fretBoard.stringSpace, y: crdEdt.settings.anchorPos.y + 0.47 * crdEdt.settings.fretBoard.strokeWidth + (dot.y - 0.5) * crdEdt.settings.fretBoard.fretSpace }; }; var drawDot = function(context, pos) { var _s = crdEdt.settings.dot; context.beginPath(); context.arc(pos.x, pos.y, _s.radius, 0, 2 * Math.PI, false); context.fillStyle = _s.fillColor; context.fill(); context.lineWidth = _s.strokeWidth; context.strokeStyle = _s.strokeColor; context.stroke(); }; this.draw = function(context) { for (var i = _dots.length - 1; i >= 0; i--) { var pos = getPosition(_dots[i]); drawDot(context, pos); }; } }; /** * * @class * @namespace crdEdt * @static */ crdEdt.renderTools = new function() { var context = null, canvas = null; this.init = function(ctx, ele) { context = ctx; canvas = ele; }; var erase = function() { context.clearRect(0, 0, canvas.width, canvas.height); }; var addLabel = function(text, color, pos) { context.font = crdEdt.settings.fretLabel.fontSize +'px ' + crdEdt.settings.fretLabel.fontFamily; context.textAlign = 'right'; context.fillStyle = color; context.fillText(text, pos.x, pos.y); }; var addLabels = function(startingFret) { var pos = { x: crdEdt.settings.anchorPos.x - 0.3 * crdEdt.settings.fretLabel.fontSize, y: crdEdt.settings.anchorPos.y + crdEdt.settings.fretLabel.fontSize }; var color = startingFret > 1 ? crdEdt.settings.fretLabel.color : crdEdt.settings.fretLabel.lightColor; for (var i = 0; i < crdEdt.settings.fretBoard.numFrets; i++) { addLabel(startingFret + i, color, pos); pos.y += crdEdt.settings.fretBoard.fretSpace; color = crdEdt.settings.fretLabel.lightColor; }; }; var addStringName = function(text, pos){ context.font = crdEdt.settings.stringLabel.fontSize +'px ' + crdEdt.settings.stringLabel.fontFamily; context.textAlign = 'center'; context.fillStyle = crdEdt.settings.stringLabel.color; context.fillText(text, pos.x, pos.y); }; var addStringNames = function(){ var _f = crdEdt.settings.fretBoard; var pos = { x: crdEdt.settings.anchorPos.x + 0.5 * _f.strokeWidth, y: crdEdt.settings.anchorPos.y - 0.25 * crdEdt.settings.fretLabel.fontSize }; // stringNames for (var i = 0; i < _f.stringNames.length; i++) { addStringName(_f.stringNames[i], pos); pos.x += _f.stringSpace; } }; var drawFretboard = function() { // shorthand handles: var pos = crdEdt.settings.anchorPos; var ctx = context; var fretBox = crdEdt.settings.fretBoard; // width offset, a "subpixel" adjustment var offset = fretBox.strokeWidth / 2; // locals var stringHeight = fretBox.numFrets * fretBox.fretSpace; var fretWidth = (fretBox.stringNames.length - 1) * fretBox.stringSpace; // build shape ctx.beginPath(); // add "C" & "E" strings for (var i = 1; i < (fretBox.stringNames.length - 1); i++) { var x = pos.x + i * fretBox.stringSpace + offset; ctx.moveTo(x, pos.y + offset); ctx.lineTo(x, pos.y + stringHeight + offset); } // add frets for (var i = 1; i < fretBox.numFrets; i++) { var y = pos.y + i * fretBox.fretSpace + offset; ctx.moveTo(pos.x + offset, y); ctx.lineTo(pos.x + fretWidth + offset, y); } // ctx.rect(pos.x + offset, pos.y + offset, fretWidth, stringHeight); // stroke shape ctx.strokeStyle = fretBox.strokeColor; ctx.lineWidth = fretBox.strokeWidth; ctx.stroke(); ctx.closePath(); }; var drawCursor = function(pos) { var _s = crdEdt.settings.cursor; context.beginPath(); // context.rect(pos.x - _s.cursor.radius, pos.y - _s.cursor.radius, 2 * _s.cursor.radius, 2 * _s.cursor.radius); context.arc(pos.x, pos.y, _s.radius, 0, 2 * Math.PI, false); context.fillStyle = _s.fillColor; context.fill(); context.lineWidth = _s.strokeWidth; context.strokeStyle = _s.strokeColor; context.stroke(); }; this.draw = function(pos, startingFret) { erase(); // crdEdt.debugTargets.drawTargets(context); addLabels(startingFret); addStringNames(); drawFretboard(); crdEdt.fretDots.draw(context); drawCursor(pos); }; }; /** * * @class * @namespace crdEdt * @static */ crdEdt.export = new function() { var organizedDots = function() { // initialize empty string array var strings = []; for (var stringNumber = 1; stringNumber <= crdEdt.settings.fretBoard.stringNames.length; stringNumber++) { strings.push({ string: stringNumber, frets: [] }); }; // add dots var dots = crdEdt.fretDots.getDots(); for (var stringNumber = strings.length - 1; stringNumber >= 0; stringNumber--) { for (var i = dots.length - 1; i >= 0; i--) { if (strings[stringNumber].string == dots[i].x + 1) { strings[stringNumber].frets.push(dots[i].y); } }; }; return strings; }; var getMinMax = function(ary) { var max = 0; var min = 900; for (var i = ary.length - 1; i >= 0; i--) { if (ary[i] > max) { max = ary[i]; } if (ary[i] < min) { min = ary[i]; } } return { max: max, min: (min < 900) ? min : max }; }; var fretNumber = function(fret) { return fret > 0 ? fretOffset + fret : 0; }; var fretOffset = null; this.show = function(chordName, startingFret) { fretOffset = startingFret - 1; var dots = organizedDots(); var s = ''; var addIns = ''; for (var i = 0; i < dots.length; i++) { var minMax = getMinMax(dots[i].frets); s += fretNumber(minMax.max) + ' '; if (minMax.max != minMax.min) { addIns += ' add: string ' + dots[i].string + ' fret ' + fretNumber(minMax.min) + ' finger ' + '1'; } }; var name = (chordName && chordName.length > 0) ? chordName : 'CHORDNAME'; document.getElementById('myOutput').innerHTML = '{define: ' + name + ' frets ' + s + addIns + '}'; }; } /** * Doing * http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ * @class canvasEditor * @namespace crdEdt * @static */ crdEdt.canvasEditor = new function() { //console.log('canvasEditor instantiated'); var canvas = null, context = null var startingFret = 1; var currentName = ''; var init = function() { var startingFret = document.getElementById('startingFret'); addStartingFretOptions(startingFret); startingFret.addEventListener('change', onFretChange, false); document.getElementById('chordName').addEventListener('keyup', onNameChange, false); canvas = document.getElementById('myCanvas'); context = canvas.getContext('2d'); canvas.addEventListener('mousemove', onMouseMove, false); canvas.addEventListener('click', onMouseClick, false); crdEdt.renderTools.init(context, canvas); redraw(); }; var addStartingFretOptions = function(ele) { var s = ''; for (var i = 1; i < 17; i++) { s += '<option value="' + i + '">' + i + '</option>'; }; ele.innerHTML = s; }; var onNameChange = function(evt) { currentName = this.value; exportDefinition(); }; var onFretChange = function(evt) { startingFret = parseInt(this.value); exportDefinition(); redraw(); }; var onMouseMove = function(evt) { redraw(getPosition(canvas, evt)); }; var onMouseClick = function(evt) { var pos = getPosition(canvas, evt); var dot = crdEdt.tracking.check(pos); if (dot) { // console.log('In bounds! x: ' + dot.x + ', y: ' + dot.y); crdEdt.fretDots.toggle(dot); redraw(pos); exportDefinition(); } }; var getPosition = function(canvas, evt) { var rect = canvas.getBoundingClientRect(); return { x: evt.clientX - rect.left, y: evt.clientY - rect.top }; }; var redraw = function(pos) { pos = pos || { x: 0, y: 0 }; crdEdt.renderTools.draw(pos, startingFret); }; var exportDefinition = function() { crdEdt.export.show(currentName, startingFret); }; this.run = function() { }; init(); } crdEdt.canvasEditor.run();
<h2>Chord Definition Editor</h2> <p><label for="chordName">Chord Name: <input class="chordName" type="text" id="chordName" value="CHORDNAME" /></label></p> <p><label for="startingFret">Starting fret: <select id="startingFret"></select></label></p> <canvas id="myCanvas" width="400" height="250"></canvas> <p>Your ChordPro <code>define</code> tag:</p> <pre id="myOutput"></pre>
input[type=text]{ border: 1px solid #D7D7D7; border-radius: 5px 5px 5px 5px; display: block; font-size: 30pt; text-align: center; width:400px; } canvas{ border: solid 1px #f2f2f2; } pre { font-size:12pt; white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word; }