Newer
Older
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
#board {
touch-action: none;
border: 0.5px solid rgba(0, 0, 0, 0.1);
box-shadow: 1px 1px 1px 0.5px rgba(0, 0, 0, 0.1);
cursor: crosshair;
}
</style>
<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/Pointer.js"></script>
<script type="application/javascript">
function init() {
Board.init("board");
Pen.init(Board.ctx);
// Attach event listener
var pointerDown = function (e) {
//console.log("down");
clearTimeout(redrawAllTimeout);

Andreas Horn
committed
var pointer = new Pointer(e);

Andreas Horn
committed
// start new stroke
Board.strokeData.push([]);
collectStrokeInfo(e, pointer, Pen);
} else if (e.pointerType == "touch") {
// Initialise pointer
var pointer = new Pointer(e);
pointer.set(Board.getPointerPos(e));
var pointerMove = function (e) {
//console.log("move");

Andreas Horn
committed
var pointer = Pointer.get(e.pointerId);

Andreas Horn
committed
if (pointer.pointerType == "pen") {
var pointerCancel = function (e) {
//console.log("cancel");

Andreas Horn
committed
var pointer = Pointer.get(e.pointerId);

Andreas Horn
committed
if (pointer.pointerType == "pen") {
drawCurve(Board.ctx, Board.strokeData[Board.strokeData.length - 1]);
clearTimeout(redrawAllTimeout);
redrawAllTimeout = setTimeout(redrawAll, 1000);
}

Andreas Horn
committed
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);
for (var stroke of Board.strokeData) {
drawCurve(Board.ctx, stroke);
}
}
// Draw method
function collectStrokeInfo(e, pointerObj, Pen) {
if (pointerObj) {
pointerObj.set(Board.getPointerPos(e));
Pen.setPen(Board.ctx, e);
if (pointerObj.pos0.x < 0) {
pointerObj.pos0.x = pointerObj.pos1.x - 1;
pointerObj.pos0.y = pointerObj.pos1.y - 1;

Andreas Horn
committed
Board.strokeData[Board.strokeData.length - 1].push({ x: pointerObj.pos1.x, y: pointerObj.pos1.y, width: getLineWidth(e) });
pointerObj.pos0.x = pointerObj.pos1.x;
pointerObj.pos0.y = pointerObj.pos1.y;
}
}
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);
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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;

Andreas Horn
committed
res.push({ x: x, y: y, width: lerp(_pts[i].width, _pts[i + 1].width, t / numOfSegments)});

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.moveTo(pts[i].x, pts[i].y);
ctx.lineTo(pts[i + 1].x, pts[i + 1].y);
ctx.stroke();
function getLineWidth(e) {
switch (e.pointerType) {
case 'touch':
if (e.width < 10 && e.height < 10) {
return (e.width + e.height) * 2 + 10;
} else {
return (e.width + e.height - 40) / 2;
}
case 'pen':
return e.pressure * 8;
default:
return (e.pressure) ? e.pressure * 8 : 4;
}
}
</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>

Andreas Horn
committed
<button style="z-index: 9001; position:absolute; top:20px; left: 20px; width: 80px; height: 80px;" onclick="Board.save();">save</button>
<button style="z-index: 9001; position:absolute; top:20px; left: 120px; width: 80px; height: 80px;" onclick="Board.clear();">clear</button>