Circles


An HTML / JavaScript exploration of the canvas element and touch and mouse event listeners. Touch or click and drag in frame below!


Source:
<script id="myScript">
    thisScript = document.getElementById("myScript");

    class CanvasClass {
        onscreenCanvas; // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas
        offscreenCanvas; // https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas/OffscreenCanvas

        mouseDownHere = false; // indicates whether mouseDown has been started within canvas

        constructor(width = 400, height = 300) {
            this.onscreenCanvas = document.createElement("canvas");
            Object.assign(this.onscreenCanvas, { width: width, height: height, style: "display: block; border: solid 1px black; width: 100%; aspect-ratio:" + width + "/" + height });

            this.offscreenCanvas = new OffscreenCanvas(width, height);

            if (this.onscreenCanvas.getContext("2d") && this.offscreenCanvas.getContext("2d")) {
                thisScript.parentNode.appendChild(this.onscreenCanvas);

                let that = this,
                    canvas = this.onscreenCanvas;

                canvas.addEventListener("mousedown", function (e) {
                    that.mousedown(e);
                });
                window.addEventListener("mouseup", function (e) {
                    that.mouseup(e);
                });
                window.addEventListener("mousemove", function (e) {
                    that.mousemove(e);
                });
                canvas.addEventListener("touchstart", function (e) {
                    that.touchstart(e);
                });
                canvas.addEventListener("touchmove", function (e) {
                    that.touchmove(e);
                });
            } else {
                alert("This post requires HTML canvas element support to view");
                // canvas-unsupported code here
                return;
            }
        }

        resize() {
            this.fitToContainer();
        }
        clientToCanvasCoords(p) {
            let canvas = this.onscreenCanvas;
            let rect = canvas.getBoundingClientRect();
            p.x = (canvas.width * (p.x - rect.left)) / canvas.offsetWidth;
            p.y = (canvas.height * (p.y - rect.top)) / canvas.offsetHeight;
        }

        touchstart(touchEvent) {
            touchmove(touchEvent);
            touchEvent.preventDefault();
        }

        touchmove(touchEvent) {
            var i, p, touches;
            touches = touchEvent.touches;
            for (i = 0; i < touches.length; ++i) {
                p = { x: touches.item(i).clientX, y: touches.item(i).clientY };
                this.clientToCanvasCoords(p);
                this.paintRandomCircle(p.x, p.y);
            }
            touchEvent.preventDefault();
        }

        mousemove(mouseEvent) {
            if (this.mouseDownHere && mouseEvent.buttons & 1) {
                let p = { x: mouseEvent.clientX, y: mouseEvent.clientY };
                this.clientToCanvasCoords(p);
                this.paintRandomCircle(p.x, p.y);
                mouseEvent.preventDefault();
            } else {
                this.mouseDownHere = false;
            }
        }
        mouseup(mouseEvent) {
            if (this.mouseDownHere) {
                // did we drag something out of the canvas?
                this.mouseDownHere = false;
            }
        }
        mousedown(mouseEvent) {
            if (mouseEvent.buttons & 1) {
                this.mouseDownHere = true;
                this.mousemove(mouseEvent);
                mouseEvent.preventDefault();
            } else if (mouseEvent.buttons & 2) {
                // ?
            }
        }

        fitToContainer() {
            let c = this.onscreenCanvas;
            // https://stackoverflow.com/questions/10214873/make-canvas-as-wide-and-as-high-as-parent
            // Make it visually fill the positioned parent
            // you can use calc here, e.g. 'calc(100% - 10px)' -- must use spacing in calculation!
            c.style.width = "100%";
            c.style.height = "100%";

            // optionally set the internal size to match
            // canvas.width = canvas.offsetWidth;
            // canvas.height = canvas.offsetHeight;
        }

        fade() {
            // https://stackoverflow.com/questions/41483806/painting-in-canvas-which-fades-with-time-strange-alpha-layering-behaviour
            // https:stackoverflow.com/questions/4405336/how-to-copy-contents-of-one-canvas-to-another-canvas-locally
        }

        paintRandomCircle(x, y) {
            // Draw the ellipse
            let ctx = this.onscreenCanvas.getContext("2d");
            ctx.fillStyle = "#" + (((1 << 24) * Math.random()) | 0).toString(16);
            ctx.strokeStyle = ctx.fillStyle;
            ctx.beginPath();
            let r = 75 * Math.random() + 25;
            ctx.ellipse(x, y, r, r, 0, 0, 2 * Math.PI);
            ctx.stroke();
            ctx.fill();
            this.fade();
        }
    }

    new CanvasClass();
</script>
<noscript></noscript>

Comments