Newer
Older
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
* {
touch-action: none;
}
body {
background-color: red;
}
button {
background-color: #D8D8D8;
border: 1px solid #999999;
border-radius: 5px;
}
button.selected {
box-shadow: 0 0 2px 2px #0597f8;
}
#board {
touch-action: none;
box-shadow: 1px 1px 1px 0.5px rgba(0, 0, 0, 0.1);
cursor: crosshair;
position: absolute;
left: 0px;
top: 0px;;
#ruler {
background-color: lightgreen;
width: 600px;
height: 150px;
position: absolute;
z-index: 20;
touch-action: none;
}
#ruler #rulerTopLeft {
position: relative;
top: 0px;
left: 0px;
width: 0px;
height: 0px;
background-color: blue;
}
#helper, #helper1, #helper2, #helper3, #helper4 {
background-color: fuchsia;
width: 10px;
height: 10px;
position: absolute;
z-index: 21;
touch-action: none;
left: 200px;
top: 200px;
}
#helper1, #helper2 {
width: 150px;
height: 150px;
border-radius: 75px;
opacity: 0.8;
}
#helper1 {
background-color: yellow;
}
#helper2{
background-color: orange;
}
#helper3{
background-color: lime;
border-radius: 5px;
}
#helper4{
background-color: red;
border-radius: 5px;
}

Andreas Horn
committed
<script type="application/javascript" src="js/Vector.js"></script>
<script type="application/javascript" src="js/main.js"></script>
<script type="application/javascript" src="js/Board.js"></script>
<script type="application/javascript" src="js/Pen.js"></script>
<script type="application/javascript" src="js/PointerManager.js"></script>
<script type="application/javascript" src="js/Ruler.js"></script>
Ruler.init(document.getElementById("ruler"));
setColor("#000000");
setLineWidth("8");
var pointerDown = function (e) {
//console.log("down");
clearTimeout(redrawAllTimeout);
PointerManager.registerPointer(e);
if (e.pointerType == "pen") {

Andreas Horn
committed
Board.strokeData.push([]);
// check if we must follow the ruler
collectStrokeInfo(e);

Andreas Horn
committed
} else if (e.pointerType == "touch") {
var pointerMove = function (e) {
collectStrokeInfo(e);
var pointerCancel = function (e) {
//console.log("cancel");

Andreas Horn
committed
drawCurve(Board.ctx, Board.strokeData[Board.strokeData.length - 1]);
clearTimeout(redrawAllTimeout);
redrawAllTimeout = setTimeout(redrawAll, 1000);
}
Board.canvas.addEventListener("pointerdown", pointerDown);
Board.canvas.addEventListener("pointermove", pointerMove);
Board.canvas.addEventListener("pointerup", pointerCancel);
Board.canvas.addEventListener("pointerleave", pointerCancel);

Andreas Horn
committed
// set drawing call

Andreas Horn
committed
if (Board.strokeData[Board.strokeData.length - 1] && Board.strokeData[Board.strokeData.length - 1].length) {
drawCurve(Board.ctx, Board.strokeData[Board.strokeData.length - 1])

Andreas Horn
committed
Board.ctx.clearRect(0, 0, Board.canvas.width, Board.canvas.height);
Board.ctx.fillStyle = Board.bgColor;
Board.ctx.fillRect(0, 0, Board.canvas.width, Board.canvas.height);

Andreas Horn
committed
for (var stroke of Board.strokeData) {
drawCurve(Board.ctx, stroke);
}
}
function collectStrokeInfo(e) {
if (PointerManager.isRegistered(e.pointerId)) {
var pos = PointerManager.getPointerPos(e);
if (Ruler.inRulerSnappingArea(pos)) {
console.log("SNAP to ruler");
let pointOnRulerPos = Ruler.getNearestPointOnRuler(pos);
pointOnRulerPos.x *= Board.resolution;
pointOnRulerPos.y *= Board.resolution;
pos = pointOnRulerPos;
} else {
pos = Board.getPointerPos(e);
Board.strokeData[Board.strokeData.length - 1].push({
x: pos.x,
y: pos.y,
width: getLineWidth(e),
pen: JSON.parse(JSON.stringify(Pen))
});
console.log(Board.strokeData[Board.strokeData.length - 1]);
function drawCurve(ctx, ptsa, tension, isClosed, numOfSegments, showPoints) {
ctx.beginPath();
drawLines(ctx, getCurvePoints(ptsa, tension, isClosed, numOfSegments));
if (showPoints) {
ctx.beginPath();
for (var i = 0; i < ptsa.length - 1; i += 2)
ctx.rect(ptsa[i] - 2, ptsa[i + 1] - 2, 4, 4);
function getCurvePoints(pts, tension, isClosed, numOfSegments) {
// use input value if provided, or use a default value
tension = (typeof tension != "undefined") ? tension : 0.5;
isClosed = isClosed ? isClosed : false;
numOfSegments = numOfSegments ? numOfSegments : 16;
var _pts = [], res = [], // clone array
x, y, // our x,y coords
t1x, t2x, t1y, t2y, // tension vectors
c1, c2, c3, c4, // cardinal points
st, t, i; // steps based on num. of segments
// clone array so we don"t change the original
_pts = pts.slice(0);
// The algorithm require a previous and next point to the actual point array.
// Check if we will draw closed or open curve.
// If closed, copy end points to beginning and first points to end
// If open, duplicate first points to befinning, end points to end
if (isClosed) {
_pts.unshift(pts[pts.length - 1]);
_pts.unshift(pts[pts.length - 1]);
_pts.push(pts[0]);
}
else {
_pts.unshift(pts[0]); //copy 1. point and insert at beginning
_pts.push(pts[pts.length - 1]); //copy last point and append
}
// 1. loop goes through point array
// 2. loop goes through each segment between the 2 pts + 1e point before and after
for (var i = 1; i < (_pts.length - 2); i++) {
for (var t = 0; t <= numOfSegments; t++) {
// calc tension vectors
t1x = (_pts[i + 1].x - _pts[i - 1].x) * tension;
t2x = (_pts[i + 2].x - _pts[i].x) * tension;
t1y = (_pts[i + 1].y - _pts[i - 1].y) * tension;
t2y = (_pts[i + 2].y - _pts[i].y) * tension;
// calc step
st = t / numOfSegments;
// calc cardinals
c1 = 2 * Math.pow(st, 3) - 3 * Math.pow(st, 2) + 1;
c2 = -(2 * Math.pow(st, 3)) + 3 * Math.pow(st, 2);
c3 = Math.pow(st, 3) - 2 * Math.pow(st, 2) + st;
c4 = Math.pow(st, 3) - Math.pow(st, 2);
// calc x and y cords with common control vectors
x = c1 * _pts[i].x + c2 * _pts[i + 1].x + c3 * t1x + c4 * t2x;
y = c1 * _pts[i].y + c2 * _pts[i + 1].y + c3 * t1y + c4 * t2y;
res.push({
x: x,
y: y,
width: lerp(_pts[i].width, _pts[i + 1].width, t / numOfSegments),
pen: pts[i].pen}
);

Andreas Horn
committed
function lerp (start, end, pos){
return (1 - pos) * start + pos * end;
}
function drawLines(ctx, pts) {
for (i = 0; i < pts.length - 2; i++) {
ctx.beginPath();
ctx.lineWidth = pts[i].width;
ctx.strokeStyle = pts[i].pen.colors.fg;
ctx.moveTo(pts[i].x, pts[i].y);
ctx.lineTo(pts[i + 1].x, pts[i + 1].y);
ctx.stroke();
return e.pressure * Pen.lineWidth;
function setColor(btn) {
Pen.colors.fg = (btn.dataset) ? btn.dataset.color : btn;
document.querySelectorAll("button[data-color]").forEach(b => b.classList.remove("selected"));
document.querySelector("button[data-color='" + Pen.colors.fg + "']").classList.add("selected");
}
function setLineWidth(btn) {
Pen.lineWidth = (btn.dataset) ? btn.dataset.lineWidth : btn;
document.querySelectorAll("button[data-line-width]").forEach(b => b.classList.remove("selected"));
document.querySelector("button[data-line-width='" + Pen.lineWidth + "']").classList.add("selected");
}
</script>
</head>
<body onload="init();">
<canvas touch-action="none" id="board" width="640" height="640" style="width:320px; height:320px;">
Opps, you cannot play draw N guess with this browser!
</canvas>
<button style="position:absolute; top:20px; left: 20px; width: 80px; height: 80px;" onclick="Board.save();">save</button>
<button style="position:absolute; top:20px; left: 120px; width: 80px; height: 80px;" onclick="Board.clear();">clear</button>
<button data-color="#000000" style="position:absolute; top:20px; left: 220px; width: 35px; height: 35px; background-color: black; color: white" onclick="setColor(this);"> </button>
<button data-color="#FF0000" style="position:absolute; top:20px; left: 265px; width: 35px; height: 35px; background-color: red;" onclick="setColor(this);"> </button>
<button data-color="#4169E1" style="position:absolute; top:65px; left: 220px; width: 35px; height: 35px; background-color: royalblue;" onclick="setColor(this);"> </button>
<button data-color="#32CD32" style="position:absolute; top:65px; left: 265px; width: 35px; height: 35px; background-color: limegreen;" onclick="setColor(this);"> </button>
<button data-line-width="4" style="position:absolute; top:20px; left: 320px; width: 35px; height: 35px;" onclick="setLineWidth(this);">4</button>
<button data-line-width="8" style="position:absolute; top:20px; left: 365px; width: 35px; height: 35px;" onclick="setLineWidth(this);">8</button>
<button data-line-width="16" style="position:absolute; top:65px; left: 320px; width: 35px; height: 35px;" onclick="setLineWidth(this);">16</button>
<button data-line-width="24" style="position:absolute; top:65px; left: 365px; width: 35px; height: 35px;" onclick="setLineWidth(this);">24</button>
<div id="ruler"><div id="rulerTopLeft">top left</div><div id="rulerValues"></div></div>
<div id="helper"></div>
<div id="helper1"></div>
<div id="helper2"></div>
<div id="helper3"></div>