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">
let canvasStore;
let redrawAllTimeout;
function init() {
Board.init("board");
Pen.init(Board.ctx);
// Attach event listener
var pointerDown = function (e) {
//console.log("down");
clearTimeout(redrawAllTimeout);
if (e.pointerType == "pen") {
// Initialise pointer
var pointer = new Pointer(e.pointerId);
pointer.set(Board.getPointerPos(e));
// Get function type
Pen.setFuncType(e);
if (Pen.funcType === Pen.funcTypes.menu) Board.clearMemory();
else collectStrokeInfo(e, pointer, Pen);
var pointerMove = function (e) {
//console.log("move");
if (Pen.funcType && (Pen.funcType.indexOf(Pen.funcTypes.draw) !== -1)) {
var pointer = Pointer.get(e.pointerId);
var pointerCancel = function (e) {
//console.log("cancel");
drawCurve(Board.ctx, strokeData[strokeData.length - 1]);
clearTimeout(redrawAllTimeout);
redrawAllTimeout = setTimeout(redrawAll, 1000);
Board.dom.addEventListener('pointerdown', pointerDown);
Board.dom.addEventListener('pointermove', pointerMove);
Board.dom.addEventListener('pointerup', pointerCancel);
Board.dom.addEventListener('pointerleave', pointerCancel);
setInterval(_ => {
if (strokeData[strokeData.length - 1] && strokeData[strokeData.length - 1].length) {
drawCurve(Board.ctx, strokeData[strokeData.length - 1])
}
}, 30);
}
function redrawAll() {
Board.ctx.clearRect(0, 0, Board.dom.width, Board.dom.height);
for (var stroke of 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;
// save stroke info
strokeData[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);
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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>
<button style="z-index: 9001; position:absolute; top:100px; left: 100px; width: 200px;"
onclick="Board.storeMemory();">save</button>
<button style="z-index: 9001; position:absolute; top:100px; left: 320px; width: 200px;"
onclick="Board.clearMemory();">clear</button>