• Jump To … +
    ./source/asset-management/image-asset.js ./source/asset-management/noise-asset.js ./source/asset-management/raw-asset.js ./source/asset-management/reaction-diffusion-asset.js ./source/asset-management/sprite-asset.js ./source/asset-management/video-asset.js ./source/core/animation-loop.js ./source/core/display-cycle.js ./source/core/document.js ./source/core/events.js ./source/core/init.js ./source/core/library.js ./source/core/snippets.js ./source/core/user-interaction.js ./source/factory/action.js ./source/factory/anchor.js ./source/factory/animation.js ./source/factory/bezier.js ./source/factory/block.js ./source/factory/button.js ./source/factory/canvas.js ./source/factory/cell.js ./source/factory/cog.js ./source/factory/color.js ./source/factory/conic-gradient.js ./source/factory/crescent.js ./source/factory/element.js ./source/factory/emitter.js ./source/factory/enhanced-label.js ./source/factory/filter.js ./source/factory/gradient.js ./source/factory/grid.js ./source/factory/group.js ./source/factory/label.js ./source/factory/line-spiral.js ./source/factory/line.js ./source/factory/loom.js ./source/factory/mesh.js ./source/factory/net.js ./source/factory/oval.js ./source/factory/particle-force.js ./source/factory/particle-spring.js ./source/factory/particle-world.js ./source/factory/particle.js ./source/factory/pattern.js ./source/factory/picture.js ./source/factory/polygon.js ./source/factory/polyline.js ./source/factory/quadratic.js ./source/factory/radial-gradient.js ./source/factory/rectangle.js ./source/factory/render-animation.js ./source/factory/shape.js ./source/factory/spiral.js ./source/factory/stack.js ./source/factory/star.js ./source/factory/tetragon.js ./source/factory/ticker.js ./source/factory/tracer.js ./source/factory/tween.js ./source/factory/unstacked-element.js ./source/factory/wheel.js ./source/helper/array-pool.js ./source/helper/color-engine.js ./source/helper/document-root-elements.js ./source/helper/filter-engine-bluenoise-data.js ./source/helper/filter-engine.js ./source/helper/random-seed.js ./source/helper/shape-path-calculation.js ./source/helper/shared-vars.js ./source/helper/system-flags.js ./source/helper/utilities.js ./source/helper/workstore.js ./source/mixin/anchor.js ./source/mixin/asset-advanced-functionality.js ./source/mixin/asset-consumer.js ./source/mixin/asset.js ./source/mixin/base.js ./source/mixin/button.js ./source/mixin/cascade.js ./source/mixin/cell-key-functions.js ./source/mixin/delta.js ./source/mixin/display-shape.js ./source/mixin/dom.js ./source/mixin/entity.js ./source/mixin/filter.js ./source/mixin/hidden-dom-elements.js ./source/mixin/mimic.js ./source/mixin/path.js ./source/mixin/pattern.js ./source/mixin/pivot.js ./source/mixin/position.js ./source/mixin/shape-basic.js ./source/mixin/shape-curve.js ./source/mixin/styles.js ./source/mixin/text.js ./source/mixin/tween.js ./source/scrawl.js ./source/untracked-factory/cell-fragment.js ./source/untracked-factory/coordinate.js ./source/untracked-factory/drag-zone.js ./source/untracked-factory/keyboard-zone.js ./source/untracked-factory/observe-update.js ./source/untracked-factory/palette.js ./source/untracked-factory/particle-history.js ./source/untracked-factory/quaternion.js ./source/untracked-factory/state.js ./source/untracked-factory/text-style.js ./source/untracked-factory/vector.js
  • §

    Observe and update

    Okay, so I got very bored of writing boilerplate to react to various form elements user interactions across the demos. So I wrote some functions to setup (and take down) batches of DOM event listeners to make my life easier. These are:

    • scrawl.addNativeListener()
    • scrawl.removeNativeListener()

    Then there was the use case for reacting to various mouse (and touch) events, so I bundled all those up into a set of complementary functions:

    • scrawl.addListener()
    • scrawl.removeListener()

    Even so, there was still a lot of boilerplate code to write, in particular to listening for user interaction with form elements (which can be anywhere on the web page). So I further factorised that code into an observeAndUpdate function which uses the listener functions internally.

    I have no idea how useful the observeAndUpdate function will be to anyone else. All I know is that it works for me and my demos, and that makes me happy.

    Note: observeAndUpdate() returns a function that will (in theory) remove all the bundled event listeners from their DOM elements when it is invoked. Not yet tested.

  • §

    Exported function (to modules and the scrawl object). Capture changes in a form and apply them to a target Scrawl-canvas artefact, asset, style, group, etc object

    observeAndUpdate - example

    scrawl.observeAndUpdate({
    
        // The input event which will trigger the O&U
        event: ['input', 'change'],
    
        // CSS selector used to gather the DOM elements this O&U will be attached to
        origin: '.controlItem',
    
        // The SC artefact, or Group object, to be updated by user interaction changes
        target: piccy,
    
        // Performs an `addNativeListener` when true; `addListener` when false
        useNativeListener: true,
    
        // Invokes the event listener's `preventDefault` function
        preventDefault: true,
    
        // The SC artefact attributes to be updated by various DOM inputs. See below
        updates: {
    
            copy_start_xPercent: ['copyStartX', '%'],
            copy_start_xAbsolute: ['copyStartX', 'round'],
    
            copy_start_yPercent: ['copyStartY', '%'],
            copy_start_yAbsolute: ['copyStartY', 'round'],
    
            copy_dims_widthPercent: ['copyWidth', '%'],
            copy_dims_widthAbsolute: ['copyWidth', 'round'],
    
            copy_dims_heightPercent: ['copyHeight', '%'],
            copy_dims_heightAbsolute: ['copyHeight', 'round'],
        },
    
        // A callback function to be performed after any attributes updates
        callback: () => myLoom.update(),
    
        // Similarly, we can invoke a function to run before performing the updates
        setup: () => myLoom.update(),
    });
    
  • §

    Imports

    import * as library from "../core/library.js";
    
    import { isa_fn, xt, xta, λnull, Ωempty } from "../helper/utilities.js";
    
    import { addListener, addNativeListener, removeListener, removeNativeListener } from "../core/events.js";
  • §

    Shared constants

    import { _ceil, _floor, _round, T_GROUP, TRUE } from '../helper/shared-vars.js';
  • §

    Local constants

    const FALSE = 'false';
  • §

    observeAndUpdate - exported function

    export const observeAndUpdate = function (items = Ωempty) {
    
        if (!xta(items.event, items.origin, items.updates)) return false;
    
        const target = (items.target.substring && items.targetLibrarySection) ?
            library[items.targetLibrarySection][items.target] :
            items.target;
    
        if (!target) return false;
    
        const event = items.event,
            origin = items.origin;
    
        const listener = (items.useNativeListener) ? addNativeListener : addListener,
            killListener = (items.useNativeListener) ? removeNativeListener : removeListener;
    
        let stop = λnull;
    
        if (items.preventDefault) {
    
            stop = (e) => {
    
                e.preventDefault();
                e.returnValue = false;
            };
        }
    
        const setup = (isa_fn(items.setup)) ? items.setup : λnull;
    
        const callback = (isa_fn(items.callback)) ? items.callback : λnull;
    
        const func = function (e) {
    
            stop(e);
    
            const id = (e && e.target) ? e.target.id : false;
    
            if (id) {
    
                const updates = items.updates,
                    actionArray = updates[id];
    
                if (actionArray) {
    
                    setup();
    
                    const actionAttribute = actionArray[0],
                        action = actionArray[1],
                        targetVal = e.target.value;
    
                    let actionFlag = true,
                        val;
    
                    switch (action) {
  • §

    Supplied value converted to float Number

                        case 'float' :
                            val = parseFloat(targetVal);
                            break;
  • §

    Supplied value converted to integer Number

                        case 'int' :
                            val = parseInt(targetVal, 10);
                            break;
  • §

    Supplied value rounded up or down to nearest integer Number

                        case 'round' :
                            val = _round(targetVal);
                            break;
  • §

    Supplied value rounded down to nearest integer Number

                        case 'roundDown' :
                            val = _floor(targetVal);
                            break;
  • §

    Supplied value rounded up to nearest integer Number

                        case 'roundUp' :
                            val = _ceil(targetVal);
                            break;
  • §

    Supplied value not modified in any way

                        case 'raw' :
                            val = targetVal;
                            break;
  • §

    Supplied value not modified in any way

                        case 'parse' :
                            val = (targetVal.substring) ? JSON.parse(targetVal) : targetVal;
                            break;
  • §

    Supplied value converted to String

                        case 'string' :
                            val = `${targetVal}`;
                            break;
  • §

    Supplied value converted to Boolean

                        case 'boolean' :
                            if (xt(targetVal)) {
    
                                if (targetVal.substring) {
    
                                    if (TRUE === targetVal.toLowerCase()) val = true;
                                    else if (FALSE === targetVal.toLowerCase()) val = false;
                                    else val = (parseFloat(targetVal)) ? true : false;
                                }
                                else val = (targetVal) ? true : false;
                            }
                            else val = false;
                            break;
  • §

    Supplied value converted to float number, then action value (eg: ‘%’) added to result to output a String

                        default :
                            if (action.substring) val = `${parseFloat(targetVal)}${action}`;
                            else actionFlag = false;
                    }
  • §

    Update - we can apply updates to a Group of artefacts, or to a single artefact

                    if (actionFlag) {
    
                        if (target.type === T_GROUP) {
    
                            target.setArtefacts({
                                [actionAttribute]: val
                            });
                        }
                        else {
    
                            target.set({
                                [actionAttribute]: val
                            });
                        }
  • §

    Invoke any supplied callback function

                        callback(e);
                    }
                }
            }
        };
    
        const kill = function () {
    
            killListener(event, func, origin);
        };
    
        listener(event, func, origin);
    
        return kill;
    };
    
    export const makeUpdater = (items = Ωempty) => observeAndUpdate(items);
  • §

    initializeDomInputs - exported helper function

    • Argument is an Array of arrays, where each element Array holds the following data:
    • [kind: String, selector: String, value: String | number], where
    • kind defines the kind of element to be processed - input, select, button, element
    • selector - for input, select and button elements, that element’s unique id value; for anything else, a valid CSS query selector string
    • value - the initial value to set the input to - for select this will be a number
    • Returns an object of elements keyed to the selector string
    export const initializeDomInputs = (items) => {
    
        const results = {};
    
        items.forEach(item => {
    
            const [kind, selector, value] = item;
    
            switch (kind) {
    
                case 'input' : {
    
                    if (value.substring) {
    
                        /** @type {HTMLInputElement} */
                        const S = document.querySelector(`#${selector}`);
    
                        if (S) {
    
                            S.value = value;
                            results[selector] = S;
                        }
                        else results[selector] = {};
                    }
                    else results[selector] = {};
                    break;
                }
    
                case 'select' : {
    
                    if (value.toFixed) {
    
                        /** @type {HTMLSelectElement} */
                        const S = document.querySelector(`#${selector}`);
    
                        if (S) {
    
                            S.options.selectedIndex = value;
                            results[selector] = S;
                        }
                        else results[selector] = {};
                    }
                    else results[selector] = {};
                    break;
                }
    
                case 'button' : {
    
                    if (value.substring) {
    
                        /** @type {HTMLButtonElement} */
                        const S = document.querySelector(`#${selector}`);
    
                        if (S) {
    
                            S.textContent = value;
                            results[selector] = S;
                        }
                        else results[selector] = {};
                    }
                    else results[selector] = {};
                    break;
                }
    
                case 'element' : {
    
                    /** @type {HTMLElement} */
                    const S = document.querySelector(`${selector}`);
    
                    if (S) results[selector] = S;
                    else results[selector] = {};
                    break;
                }
    
                default : {
    
                    /** @type {HTMLElement} */
                    const S = document.querySelector(`#${selector}`);
    
                    if (S) results[selector] = S;
                    else results[selector] = {};
                }
            }
        });
    
        return results;
    };