• Jump To … +
    ./demo/canvas-001.js ./demo/canvas-002.js ./demo/canvas-003.js ./demo/canvas-004.js ./demo/canvas-005.js ./demo/canvas-006.js ./demo/canvas-007.js ./demo/canvas-008.js ./demo/canvas-009.js ./demo/canvas-010.js ./demo/canvas-011.js ./demo/canvas-012.js ./demo/canvas-013.js ./demo/canvas-014.js ./demo/canvas-015.js ./demo/canvas-016.js ./demo/canvas-017.js ./demo/canvas-018.js ./demo/canvas-019.js ./demo/canvas-020.js ./demo/canvas-021.js ./demo/canvas-022.js ./demo/canvas-023.js ./demo/canvas-024.js ./demo/canvas-025.js ./demo/canvas-026.js ./demo/canvas-027.js ./demo/canvas-028.js ./demo/canvas-029.js ./demo/canvas-030.js ./demo/canvas-031.js ./demo/canvas-032.js ./demo/canvas-033.js ./demo/canvas-034.js ./demo/canvas-035.js ./demo/canvas-036.js ./demo/canvas-037.js ./demo/canvas-038.js ./demo/canvas-039.js ./demo/canvas-040.js ./demo/canvas-041.js ./demo/canvas-042.js ./demo/canvas-043.js ./demo/canvas-044.js ./demo/canvas-045.js ./demo/canvas-046.js ./demo/canvas-047.js ./demo/component-001.js ./demo/component-002.js ./demo/component-003.js ./demo/component-004.js ./demo/component-005.js ./demo/component-006.js ./demo/component-007.js ./demo/core-001.js ./demo/dom-001.js ./demo/dom-002.js ./demo/dom-003.js ./demo/dom-004.js ./demo/dom-005.js ./demo/dom-006.js ./demo/dom-007.js ./demo/dom-008.js ./demo/dom-009.js ./demo/dom-010.js ./demo/dom-011.js ./demo/dom-012.js ./demo/dom-013.js ./demo/dom-014a.js ./demo/dom-014b.js ./demo/dom-014c.js ./demo/dom-015.js ./demo/dom-016.js ./demo/filters-001.js ./demo/filters-002.js ./demo/filters-003.js ./demo/filters-004.js ./demo/filters-005.js ./demo/filters-006.js ./demo/filters-007.js ./demo/filters-008.js ./demo/filters-009.js ./demo/filters-010.js ./demo/filters-011.js ./demo/filters-012.js ./demo/filters-013.js ./demo/filters-014.js ./demo/filters-015.js ./demo/filters-016.js ./demo/filters-017.js ./demo/filters-018.js ./demo/filters-019.js ./demo/filters-020.js ./demo/filters-501.js ./demo/filters-502.js ./demo/filters-503.js ./demo/filters-504.js ./demo/filters-505.js ./demo/particles-001.js ./demo/particles-002.js ./demo/particles-003.js ./demo/particles-004.js ./demo/particles-005.js ./demo/particles-006.js ./demo/particles-007.js ./demo/particles-008.js ./demo/particles-009.js ./demo/particles-010.js ./demo/particles-011.js ./demo/particles-012.js ./demo/particles-013.js ./demo/particles-014.js ./demo/particles-015.js ./demo/particles-016.js ./demo/temp-001.js ./demo/temp-inkscapeSvgFilters.js
  • ¶

    Demo Canvas 009

    Pattern styles; Entity web link anchors; Dynamic accessibility

  • ¶

    Run code

    import scrawl from '../source/scrawl.js'
  • ¶

    Google Analytics

    We load GA code in the normal way through markup in the dom-006.html file (lines 11-21), and get a handle on the ga object here

    let ga = window[window['GoogleAnalyticsObject'] || 'ga'],
        myTracker;
  • ¶

    Create a new tracker to handle tween and ticker action/progress, and set some attributes on it.

    ga('create', 'UA-000000-0', 'auto', 'demoCanvasTracker');
  • ¶

    We can then incorporate the tracker’s functionality in our various hook functions defined further down in this script

    ga(function() {
    
        let ga = window[window['GoogleAnalyticsObject'] || 'ga'];
    
        myTracker = ga.getByName('demoCanvasTracker');
        myTracker.set('transport', 'beacon');
        myTracker.set('campaignKeyword', 'Scrawl-canvas demo');
  • ¶

    Comment out the next line to send tracker packets (so they show up in the console)

        myTracker.set('sendHitTask', null);
    });
  • ¶

    Scene setup

    let canvas = scrawl.library.artefact.mycanvas;
  • ¶

    Get images from DOM

    scrawl.importDomImage('.mypatterns');
  • ¶

    Create Pattern styles using imported images

    scrawl.makePattern({
    
        name: 'brick-pattern',
        asset: 'brick',
    
    }).clone({
    
        name: 'leaves-pattern',
        asset: 'leaves',
    
    });
  • ¶

    Create Pattern styles dynamically

    scrawl.makePattern({
    
        name: 'water-pattern',
        imageSource: 'img/water.png',
    
    }).clone({
    
        name: 'marble-pattern',
        imageSource: 'img/marble.png',
    
    });
  • ¶

    Create a canvas-based Cell pattern

    canvas.buildCell({
    
        name: 'cell-pattern',
    
        width: 50,
        height: 50,
    
        backgroundColor: 'lightblue',
    
        shown: false,
    });
    
    canvas.base.set({
    
        compileOrder: 1,
    });
  • ¶

    Create a Block entity to display in the new Cell pattern

    scrawl.makeBlock({
    
        name: 'cell-pattern-block',
        group: 'cell-pattern',
    
        width: 40,
        height: 40,
    
        startX: 'center',
        startY: 'center',
    
        handleX: 'center',
        handleY: 'center',
    
        method: 'fill',
    
        fillStyle: 'water-pattern',
    
        delta: {
            roll: -0.3
        },
    });
  • ¶

    Main canvas display - create Block entitys which will use the patterns defined above

    scrawl.makeBlock({
    
        name: 'water-in-leaves',
        group: canvas.base.name,
    
        width: '40%',
        height: '40%',
    
        startX: '25%',
        startY: '25%',
    
        handleX: 'center',
        handleY: 'center',
    
        lineWidth: 20,
        lineJoin: 'round',
    
        method: 'fillThenDraw',
    
        fillStyle: 'cell-pattern',
        strokeStyle: 'leaves-pattern',
    
        shadowOffsetX: 5,
        shadowOffsetY: 5,
        shadowBlur: 3,
        shadowColor: 'black',
  • ¶

    Include an anchor (href) link which can be tied to user interaction (in this case, mouse clicks on the canvas element) through events defined further down in this script.

  • ¶

    ALWAYS include a description! We will be using the anchor’s description string later as part of our asistive technology solution for this demo.

        anchor: {
            name: 'wikipedia-water-link',
            href: 'https://en.wikipedia.org/wiki/Water',
            description: 'Link to the Wikipedia article on water (opens in new tab)',
  • ¶

    The clickAction attribute captures both Scrawl-canvas trigger clicks and also non-mouse ‘clicks’ on the anchor element hidden at the top of the web page, for example using the keyboard (tab, return) or other assistive technology.

  • ¶

    The function returns a string which Scrawl-canvas will add to the anchor’s ‘onclick’ attribute when it creates the anchor element dynamically and adds it to the DOM.

            clickAction: function () {
    
                return `ga('demoCanvasTracker.send', 'event', 'Outbound link', 'click', '${this.href}')`;
            },
  • ¶

    Include focus and blur actions, which will trigger the onEnter and onLeave functions (below) for visitors using non-mouse/touch navigation (for example: keyboard tabbing)

            focusAction: true,
            blurAction: true,
        },
  • ¶

    Accessibility functionality to be used by event functions defined below in response to user activity - this time moving the mouse cursor across the <canvas> element. Note that ‘this’ refers to the entity object, meaning the functions can be safely cloned into other entitys.

        onEnter: function () {
  • ¶

    Update the block entity’s visual display

            this.set({
                lineWidth: 30,
            });
  • ¶

    This is where we update the accessibility information tied to the canvas element. We’re using the anchor attribute object’s description value to supply details of what actions will happen when the user clicks on the canvas while the mouse is over the block entity.

            canvas.set({
                title: `${this.name} tile`,
                label: this.get('anchorDescription'),
            });
  • ¶

    Track the action in Google Analytics

            myTracker.send('event', 'Canvas Entity', 'hover start', `${this.name} ${this.type}`);
        },
    
        onLeave: function () {
  • ¶

    Reset the block entity’s visual display

            this.set({
                lineWidth: 20,
            });
  • ¶

    Reset the accessibility information tied to the canvas element.

            canvas.set({
                title: '',
                label: `${canvas.name} canvas element`,
            });
  • ¶

    Track the action in Google Analytics

            myTracker.send('event', 'Canvas Entity', 'hover end', `${this.name} ${this.type}`);
        },
  • ¶

    Used by the Scrawl-canvas click event, below. This hit report will only be generated from user interaction on the canvas element, thus will supply different numbers to the anchor’s clickAction function above - a useful way to help calculate the volume of users bypassing the canvas and opening the Wikipedia page using the keyboard or assistive technology

        onUp: function () {
  • ¶

    Track the action in Google Analytics

            myTracker.send('event', 'Canvas Entity Link', 'click', `${this.name} ${this.type} ${this.anchor.href}`);
  • ¶

    Trigger the click event on the anchor element we added to the DOM

            this.clickAnchor();
        },
    
    }).clone({
    
        name: 'leaves-in-brick',
    
        startX: '75%',
    
        fillStyle: 'leaves-pattern',
        strokeStyle: 'brick-pattern',
    
        anchor: {
            name: 'wikipedia-leaf-link',
            href: 'https://en.wikipedia.org/wiki/Leaf',
            description: 'Link to the Wikipedia article on leaves (opens in new tab)',
        },
    
    }).clone({
        
        name: 'brick-in-marble',
    
        startY: '75%',
    
        fillStyle: 'brick-pattern',
        strokeStyle: 'marble-pattern',
    
        anchor: {
            name: 'wikipedia-brick-link',
            href: 'https://en.wikipedia.org/wiki/Brick',
            description: 'Link to the Wikipedia article on bricks (opens in new tab)',
        },
    
    }).clone({
        
        name: 'marble-in-water',
    
        startX: '25%',
    
        fillStyle: 'marble-pattern',
        strokeStyle: 'water-pattern',
    
        anchor: {
            name: 'wikipedia-marble-link',
            href: 'https://en.wikipedia.org/wiki/Marble',
            description: 'Link to the Wikipedia article on marble (opens in new tab)',
        },
    });
  • ¶

    Demonstrate zoned actions on a canvas element as a result of user interaction

    • Available cascadeEventAction arguments are: ‘enter’, ‘leave’, ‘down’, or ‘up’
    • Also, the ‘move’ argument will trigger enter and leave actions on the entitys, as appropriate to each

    In this case, moving the mouse cursor over a block entity will increase its line width, as specified in the onEnter and onLeave functions in the block factories above.

    Additionally, it will update the <canvas> element’s title attribute (for tool tips) and its ARIA label value (for accessibility)

    The cascadeEventAction function returns an Array of name Strings for the entitys at the current mouse cursor coordinates

    let interactionResults = '';
    let interactions = function () {
    
        if (canvas.here.active) interactionResults = canvas.cascadeEventAction('move');
        else interactionResults = '';
    };
    scrawl.addListener('move', interactions, canvas.domElement);
  • ¶

    To capture other user interaction with the <a> DOM elements which, while being visually hidden, are still accessible - for instance when a user keyboard-tabs through the web page

    In this case, clicking on one of the tiles will open a related Wikipedia page in a new browser tab - as defined in the onUp function in the block factories above

    let mylinks = function () {
    
        if (canvas.here.active) canvas.cascadeEventAction('up');
    };
    scrawl.addListener('up', mylinks, canvas.domElement);
  • ¶

    Scene animation

    Function to display frames-per-second data, and other information relevant to the demo

    let report = function () {
    
        let testTicker = Date.now(),
            testTime, testNow,
            testMessage = document.querySelector('#reportmessage');
    
        return function () {
    
            testNow = Date.now();
            testTime = testNow - testTicker;
            testTicker = testNow;
    
            testMessage.textContent = `Screen refresh: ${Math.ceil(testTime)}ms; fps: ${Math.floor(1000 / testTime)}
    Hits: ${interactionResults}`;
        };
    }();
  • ¶

    Create the Display cycle animation

    scrawl.makeRender({
    
        name: 'demo-animation',
        target: canvas,
        afterShow: report,
    });
  • ¶

    Development and testing

    console.log(scrawl.library);
  • ¶

    To test artefact kill functionality

    let killArtefact = (name, anchorname, time, finishResurrection) => {
    
        let groupname = 'mycanvas_base',
            packet;
    
        let checkGroupBucket = (name, groupname) => {
    
            let res = scrawl.library.group[groupname].artefactBuckets.filter(e => e.name === name );
            return (res.length) ? 'no' : 'yes';
        };
    
        setTimeout(() => {
    
            console.log(`${name} alive
        removed from artefact: ${(scrawl.library.artefact[name]) ? 'no' : 'yes'}
        removed from artefactnames: ${(scrawl.library.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from entity: ${(scrawl.library.entity[name]) ? 'no' : 'yes'}
        removed from entitynames: ${(scrawl.library.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefacts: ${(scrawl.library.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefactBuckets: ${checkGroupBucket(name, groupname)}
        anchor removed: ${(scrawl.library.anchor[anchorname]) ? 'no' : 'yes'}`);
    
            packet = scrawl.library.artefact[name].saveAsPacket();
    
            scrawl.library.artefact[name].kill();
    
            setTimeout(() => {
    
                console.log(`${name} killed
        removed from artefact: ${(scrawl.library.artefact[name]) ? 'no' : 'yes'}
        removed from artefactnames: ${(scrawl.library.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from entity: ${(scrawl.library.entity[name]) ? 'no' : 'yes'}
        removed from entitynames: ${(scrawl.library.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefacts: ${(scrawl.library.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefactBuckets: ${checkGroupBucket(name, groupname)}
        anchor removed: ${(scrawl.library.anchor[anchorname]) ? 'no' : 'yes'}`);
    
                canvas.actionPacket(packet);
    
                setTimeout(() => {
    
                    console.log(`${name} resurrected
        removed from artefact: ${(scrawl.library.artefact[name]) ? 'no' : 'yes'}
        removed from artefactnames: ${(scrawl.library.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from entity: ${(scrawl.library.entity[name]) ? 'no' : 'yes'}
        removed from entitynames: ${(scrawl.library.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefacts: ${(scrawl.library.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefactBuckets: ${checkGroupBucket(name, groupname)}
        anchor removed: ${(scrawl.library.anchor[anchorname]) ? 'no' : 'yes'}`);
    
                    finishResurrection();
    
                }, 100);
            }, 100);
        }, time);
    };
  • ¶

    We use the canvas and myTracker variables in our blocks’ onEnter, onLeave and onUp functions. While this works fine for the blocks created in the scope of this module file’s code, it will fail when we kill and resurrect a block - in the resurrected block the canvas and myTracker variables will be ‘undefined’. So we need to reset the block’s ‘on…’ functions (in this module file’s code) after the block has resurrected

    killArtefact('brick-in-marble', 'wikipedia-brick-link', 2000, () => {
    
        scrawl.library.artefact['brick-in-marble'].set({
    
            onEnter: function () {
                this.set({ lineWidth: 30 });
                canvas.set({
                    title: `${this.name} tile`,
                    label: this.get('anchorDescription'),
                });
                myTracker.send('event', 'Canvas Entity', 'hover start', `${this.name} ${this.type}`);
            },
            onLeave: function () {
                this.set({ lineWidth: 20 });
                canvas.set({
                    title: '',
                    label: `${canvas.name} canvas element`,
                });
                myTracker.send('event', 'Canvas Entity', 'hover end', `${this.name} ${this.type}`);
            },
            onUp: function () {
                myTracker.send('event', 'Canvas Entity Link', 'click', `${this.name} ${this.type} ${this.anchor.href}`);
                this.clickAnchor();
            },
        });
    });
  • ¶

    To test styles (Pattern) kill functionality

    let killStyle = (name, time, finishResurrection) => {
    
        let packet;
    
        setTimeout(() => {
    
            console.log(`${name} alive
        removed from styles: ${(scrawl.library.styles[name]) ? 'no' : 'yes'}
        removed from stylesnames: ${(scrawl.library.stylesnames.indexOf(name) >= 0) ? 'no' : 'yes'}`);
    
            packet = scrawl.library.styles[name].saveAsPacket();
    
            console.log(packet);
    
            scrawl.library.styles[name].kill();
    
            setTimeout(() => {
    
                console.log(`${name} killed
        removed from styles: ${(scrawl.library.styles[name]) ? 'no' : 'yes'}
        removed from stylesnames: ${(scrawl.library.stylesnames.indexOf(name) >= 0) ? 'no' : 'yes'}`);
    
                canvas.actionPacket(packet);
    
                setTimeout(() => {
    
                    console.log(`${name} resurrected
        removed from styles: ${(scrawl.library.styles[name]) ? 'no' : 'yes'}
        removed from stylesnames: ${(scrawl.library.stylesnames.indexOf(name) >= 0) ? 'no' : 'yes'}`);
    
                    finishResurrection();
    
                }, 100);
            }, 100);
        }, time);
    };
    
    killStyle('marble-pattern', 3000, () => {
  • ¶

    Reset entitys, whose fill/strokeStyles will have been set to default values when the Pattern died

        scrawl.library.entity['brick-in-marble'].set({
            strokeStyle: 'marble-pattern',
        });
    
        scrawl.library.entity['marble-in-water'].set({
            fillStyle: 'marble-pattern',
        });
    });