Canvas JavaScript – und der Entwickler sprach: „Es werde Licht“

Es ist schön, wenn einem mal ein Licht auf geht. Ein schöner Moment.

Hier mal ein Canvas-Beispiel, wie man – auch wenn es dunkel ist – seine Webseite noch sehen kann. Mit Hilfe einer (JavaScript/Canvas) – Taschenlampe.

Wo liegen die Probleme, für eine „Taschenlampe“?

Überlegungen zur optische Realisierung

Wir brauchen einen „hellen“ Kreis mit einem weichen Rand.  Aber was wir vorher noch brauchen, ist „die Nacht“ – also es muss dunkel werden.

Das erreicht man am einfachsten, in dem man eine Schwarze Fläche über die ganze Webseite legt. Aber diese Fläche braucht „ein Loch“ mit weichem Rand.

Man könnte ja eine PNG-Grafik nehmen, welches riesig groß ist und in der Mitte ein „Loch“ hat – in Form einer Transparenz. Da es aber mit dem Mauszeiger mitgehen soll, müsste diese Grafik sehr sehr groß sein – irgendwie suboptimal.

Da bietet sich ein Canvas an. Dabei müssen die Breite und Höhe auf 100% gesetzt werden, und die Position auf fixed. Bei einem Canvas kann man einen transparenten Kreis definieren, der nach außen hin schwarz wird.

Stolpersteine

Ein großes Problem ergibt sich aus dem überlagertem Element, welches alle Clicks von der Maus abfängt. Somit sind Links auf der Webseite zunächst nicht anklickbar.

Man könnte jetzt versuchen mit JavaScript die Mausposition zu lokalisieren und  diese irgendwie an die unterliegenden Elemente weiter zu leiten.  Das ist bestimmt eine nette Übung.

Viel einfacher geht es mit der nicht so bekannten css – Eigenschaft:

pointer-events: none;

Man muss aber zugeben, dass es nicht komplett funktioniert. Z.B. werden einige Hover-Effekte und rechte MaustastenEvents nicht richtig ausgewertet.
Aber die Links funktionieren.

Den interessanten Teil (das JavaScript, welches das Canvas steuert) setze ich hier mal als Beispiel rein. Das hier benutze JavaScript hat dazu kleine Abweichungen (weil z.B. der Button und ein TimeOut die Steuerung übernehmen).

Bei mir ist jQuery auf der Webseite vorhanden – somit nutze ich auch die Funktionen davon:


$(function () {
    function MouseCanvas() {
        'use strict';
        var cuLayer                 = document.getElementById("cuFlashlight");
        cuLayer.style.pointerEvents = "none";
        this.cuLayer                = cuLayer;
        this.radius                 = 300;
        this.$body                  = $("body");
        this.relativeCanvasPosition = {left: 0, top: 0};
    }

    MouseCanvas.prototype.moveCanvas   = function () {
        'use strict';
        var ctx = this.cuLayer.getContext("2d");
        if (ctx) {
            this.ctx            = ctx;
            var widthLayer      = document.documentElement.clientWidth;
            var heightLayer     = document.documentElement.clientHeight;
            var radius          = this.radius;
            var radiusInner     = radius / 3;
            this.cuLayer.width  = widthLayer;
            this.cuLayer.height = heightLayer;
            this.ctx.rect(0, 0, this.cuLayer.width, this.cuLayer.height);
            var grd = this.ctx.createRadialGradient(this.relativeCanvasPosition.left,
                                                    this.relativeCanvasPosition.top,
                                                    radiusInner,
                                                    this.relativeCanvasPosition.left,
                                                    this.relativeCanvasPosition.top,
                                                    radius);
            this.ctx.clearRect(0, 0, this.cuLayer.width, this.cuLayer.height);
            grd.addColorStop(0, "rgba(55,55,0,0)");
            grd.addColorStop(1, "rgba(0,0,0,0.96)");
            this.ctx.fillStyle = grd;
            this.ctx.fill();
        }
        else {
            console.log("Sorry, no canvas-context");
        }
    };
    MouseCanvas.prototype.startFollow  = function () {
        var mouseCanvas = this;
        this.$body.on("mousemove", function (mouse) {
            var scrollLeft                          = $(document).scrollLeft();
            var scrollTop                           = $(document).scrollTop();
            scrollLeft                              = scrollLeft === undefined ? 0 : scrollLeft;
            scrollTop                               = scrollTop === undefined ? 0 : scrollTop;
            mouseCanvas.relativeCanvasPosition.left = mouse.pageX - scrollLeft;
            mouseCanvas.relativeCanvasPosition.top  = mouse.pageY - scrollTop;
            mouseCanvas.moveCanvas();
        });
        this.changeRadius();
    };
    MouseCanvas.prototype.changeRadius = function () {
        var mouseCanvas = this;
        this.$body[0].addEventListener("keyup", function (event) {
            var keyCode = event.keyCode;
            var isAlt   = event.altKey;
            if (isAlt) {
                mouseCanvas.radius = 109 === keyCode ? mouseCanvas.radius - 50 : mouseCanvas.radius;
                mouseCanvas.radius = 107 === keyCode ? mouseCanvas.radius + 50 : mouseCanvas.radius;
                mouseCanvas.radius = 0 >= mouseCanvas.radius ? mouseCanvas.radius = 50 : mouseCanvas.radius;
                mouseCanvas.moveCanvas();
            }
        });
    };
    $("body")
        .append(
            '<canvas id="cuFlashlight" style="border: 1px solid grey; position: fixed;top: 0; left: 0;z-index: 1000;pointer-events : none;display: none;"></canvas>');
    var mouseCanvas = new MouseCanvas();
    var mouseX      = 100;
    var mouseY      = 100;
    mouseCanvas.moveCanvas(mouseX, mouseY);
    mouseCanvas.startFollow();
});

Viel Spass damit!