Skip to content
Snippets Groups Projects
index.html 11.7 KiB
Newer Older
Andreas Horn's avatar
Andreas Horn committed
<!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;
        }

Andreas Horn's avatar
Andreas Horn committed
        #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;;
Andreas Horn's avatar
Andreas Horn committed
        }

        #ruler {
            background-color: lightgreen;
            width: 600px;
            height: 150px;
            position: absolute;
            z-index: 20;
            touch-action: none;
        }

#helper, #helper1, #helper2, #helper3 {
    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;
}

Andreas Horn's avatar
Andreas Horn committed
    </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" src="js/PointerManager.js"></script>
    <script type="application/javascript" src="js/Ruler.js"></script>
Andreas Horn's avatar
Andreas Horn committed

    <script type="application/javascript">
        let redrawAllTimeout;
Andreas Horn's avatar
Andreas Horn committed
        function init() {
            Board.init("board");
            Ruler.init(document.getElementById("ruler"));
Andreas Horn's avatar
Andreas Horn committed

            setColor("#000000");
            setLineWidth("8");

Andreas Horn's avatar
Andreas Horn committed
            // Attach event listener
            var pointerDown = function (e) {
                //console.log("down");
                clearTimeout(redrawAllTimeout);
Andreas Horn's avatar
Andreas Horn committed
                if (e.pointerType == "pen") {
                    // Initialise pointer
Andreas Horn's avatar
Andreas Horn committed
                    pointer.set(Board.getPointerPos(e));

                    // start new stroke
                    Board.strokeData.push([]);
                    collectStrokeInfo(e, pointer);
                } else if (e.pointerType == "touch") {
                    // Initialise pointer
                    var pointer = new Pointer(e);
                    pointer.set(Board.getPointerPos(e));
Andreas Horn's avatar
Andreas Horn committed
                }
            }

            var pointerMove = function (e) {
                //console.log("move");
Andreas Horn's avatar
Andreas Horn committed

                    collectStrokeInfo(e, pointer);
Andreas Horn's avatar
Andreas Horn committed
                }
            }
            var pointerCancel = function (e) {
                //console.log("cancel");
                if (pointer.pointerType == "pen") {
                    drawCurve(Board.ctx, Board.strokeData[Board.strokeData.length - 1]);

                    clearTimeout(redrawAllTimeout);
                    redrawAllTimeout = setTimeout(redrawAll, 1000);
                }
Andreas Horn's avatar
Andreas Horn committed
                Pointer.destruct(e.pointerId);


                PointerManager.unregisterPointer(e);
Andreas Horn's avatar
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's avatar
Andreas Horn committed

            setInterval(_ => {
                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's avatar
Andreas Horn committed

        function redrawAll() {
            Board.ctx.clearRect(0, 0, Board.canvas.width, Board.canvas.height);
            for (var stroke of Board.strokeData) {
                drawCurve(Board.ctx, stroke);
            }
        }
Andreas Horn's avatar
Andreas Horn committed

        // Draw method
        function collectStrokeInfo(e, pointerObj) {
            if (pointerObj) {
                pointerObj.set(Board.getPointerPos(e));
                if (pointerObj.pos0.x < 0) {
                    pointerObj.pos0.x = pointerObj.pos1.x - 1;
                    pointerObj.pos0.y = pointerObj.pos1.y - 1;
Andreas Horn's avatar
Andreas Horn committed
                }
                // save stroke info
                Board.strokeData[Board.strokeData.length - 1].push({ 
                    x: pointerObj.pos1.x, 
                    y: pointerObj.pos1.y, 
                    width: getLineWidth(e), 
                    pen: JSON.parse(JSON.stringify(Pen)) 
                });
                console.log(Board.strokeData[Board.strokeData.length - 1]);
                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);
            ctx.stroke();
        }
        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
            }
            // ok, lets start..
            // 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;
                    //store points in array
                    res.push({ 
                        x: x, 
                        y: y, 
                        width: lerp(_pts[i].width, _pts[i + 1].width, t / numOfSegments),
                        pen: pts[i].pen}
                    );
        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();
        function getLineWidth(e) {
            return e.pressure * Pen.lineWidth;
Andreas Horn's avatar
Andreas Horn committed
        }

        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");
        }
Andreas Horn's avatar
Andreas Horn committed
    </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>

    <div id="helper"></div>
    <div id="helper1"></div>
    <div id="helper2"></div>
    <div id="helper3"></div>
Andreas Horn's avatar
Andreas Horn committed
</body>

</html>