• 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 031

    Cell generation and processing order - kaleidoscope clock

  • ¶

    Run code

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

    This code can be factored away into its own module

    • Because: clocks are useful; most of this code can be thought of as clock boilerplate
    const buildClockface = function (canvas, namespace) {
    
        let entity = scrawl.library.entity;
  • ¶

    The clock face will go into its own Cell

        let myFace = canvas.buildCell({
    
            name: `${namespace}-face`,
    
            width: '100%',
            height: '100%',
        });
  • ¶

    The clock frame is a wheel, as is the center pin

        scrawl.makeWheel({
    
            name: `${namespace}-clock-frame`,
            group: `${namespace}-face`,
    
            radius: '40%',
    
            start: ['center', 'center'],
            handle: ['center', 'center'],
    
            lineWidth: 6,
            strokeStyle: 'black',
            method: 'draw',
    
        }).clone({
    
            name: `${namespace}-center-pin`,
    
            radius: '2%',
    
            lineWidth: 2,
            fillStyle: 'darkred',
            strokeStyle: 'gold',
            method: 'fillThenDraw',
    
            order: 1,
    
            shadowOffsetX: 1,
            shadowOffsetY: 1,
            shadowColor: 'black',
            shadowBlur: 3,
        });
  • ¶

    The hour, minute and second hands are all Line shapes

        scrawl.makeLine({
    
            name: `${namespace}-hour-hand`,
            group: `${namespace}-face`,
    
            startX: 'center',
            startY: 'center',
            endX: 'center',
            endY: '23%',
    
            handleY: '-15%',
    
            lineWidth: 10,
            lineCap: 'round',
            strokeStyle: 'darkblue',
            method: 'draw',
    
            shadowOffsetX: 3,
            shadowOffsetY: 3,
            shadowColor: 'black',
            shadowBlur: 3,
    
        }).clone({
    
            name: `${namespace}-minute-hand`,
    
            endY: '15%',
            strokeStyle: 'blue',
            lineWidth: 6,
    
        }).clone({
    
            name: `${namespace}-second-hand`,
    
            endY: '12%',
            lineWidth: 4,
            strokeStyle: 'red',
        });
  • ¶

    Function to make the clock tick

        const updateClockHands = function () {
    
            const hourHand = entity[`${namespace}-hour-hand`],
                minuteHand = entity[`${namespace}-minute-hand`],
                secondHand = entity[`${namespace}-second-hand`];
    
            const secondsSinceMidnight = () => {
    
                let now = new Date(),
                    then = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);
    
                return (now.getTime() - then.getTime()) / 1000;
            };
    
            return function () {
    
                let time = secondsSinceMidnight(),
                    hour = ((time % 43200) / 12) * (360 / 3600),
                    minute = ((time % 3600)) * (360 / 3600),
                    second = ((time % 60)) * (360 / 60);
    
                hourHand.set({ roll: hour });
                minuteHand.set({ roll: minute });
                secondHand.set({ roll: second });
            }
    
        }();
  • ¶

    Once modularized, we can export a function to run the above code

    • When the function is invoked it builds the clock face Cell and entitys
    • It returns the cell, and the the clock tick function
        return {
    
            cell: myFace,
            update: updateClockHands
        }
    };
  • ¶

    Scene setup

    let canvas = scrawl.library.canvas.mycanvas,
        base = canvas.base,
        namespace = 'kaliedoscope-clock';
  • ¶

    Prepare the canvas

    canvas.set({
    
        backgroundColor: 'honeydew',
        checkForResize: true,
        fit: 'cover',
    });
  • ¶

    Building the background - this goes in a separate Cell

    let myBackground = canvas.buildCell({
    
        name: `${namespace}-background`,
    
        width: '100%',
        height: '100%',
    });
  • ¶

    We use a wheel segment as a stencil

    scrawl.makeWheel({
    
        name: `${namespace}-clock-stencil`,
        group: `${namespace}-background`,
    
        order: 0,
    
        radius: '40%',
        startAngle: -30,
        endAngle: 30,
        includeCenter: true,
    
        start: ['center', 'center'],
        handle: ['center', 'center'],
    
        fillStyle: 'white',
        method: 'fill',
    });
  • ¶

    Use a color factory object to generate random colors within a restricted palette

    let myColorFactory = scrawl.makeColor({
    
        name: `${namespace}-color-factory`,
    
        rMax: 160,
        gMax: 160,
        bMax: 160,
    });
  • ¶

    Add some blocks to create the animated background

    for (let i = 0; i < 50; i++) {
    
        scrawl.makeBlock({
    
            name: `${namespace}-decoration-block-${i}`,
            group: `${namespace}-background`,
    
            globalCompositeOperation: 'source-atop',
            globalAlpha: 0.3,
    
            order: 1,
    
            startX: `${10 + (Math.random() * 80)}%`,
            startY: `${10 + (Math.random() * 80)}%`,
    
            handleX: `${(Math.random() * 400) - 200}%`,
            handleY: `${(Math.random() * 400) - 200}%`,
    
            width: `${10 + (Math.random() * 15)}%`,
            height: `${2 + (Math.random() * 10)}%`,
            roll: Math.floor(`${Math.random() * 360}`),
    
            fillStyle: myColorFactory.get('random'),
    
            delta: {
                roll: ((Math.random() * 2) - 1) / 4,
            },
        });
    }
  • ¶

    We don’t display the background Cell. Instead we use it as the source for a set of Picture entitys

    scrawl.makePicture({
    
        name: `${namespace}-segment-0`,
        group: canvas.base.name,
    
        width: '100%',
        height: '100%',
    
        copyWidth: '100%',
        copyHeight: '100%',
    
        start: ['center', 'center'],
        handle: ['center', 'center'],
    
        asset: `${namespace}-background`,
    
    }).clone({
    
        name: `${namespace}-segment-1`,
        roll: 120,
    
    }).clone({
    
        name: `${namespace}-segment-2`,
        roll: 240,
    
    }).clone({
    
        name: `${namespace}-segment-3`,
        flipReverse: true,
    
    }).clone({
    
        name: `${namespace}-segment-4`,
        roll: 120,
    
    }).clone({
    
        name: `${namespace}-segment-5`,
        roll: 0,
    });
  • ¶

    Build the clock face

    let clock = buildClockface(canvas, namespace);
  • ¶

    Cell display and compile ordering

    myBackground.set({
        compileOrder: 0,
        shown: false,
    });
    
    canvas.base.set({
        compileOrder: 1,
    });
  • ¶

    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)}`;
        };
    }();
  • ¶

    Create the Display cycle animation

    const myAnimation = scrawl.makeRender({
    
        name: `${namespace}-animation`,
        target: canvas,
    
        commence: clock.update,
        afterShow: report,
    });