import { isa_fn, isa_number, mergeOver, pushUnique, λnull, Ωempty } from '../helper/utilities.js';This mixin defines additional attributes and functions for Stack and Canvas artefacts, in particular adding hooks for functions that will be automatically invoked when the artefact’s dimensions update.
import { isa_fn, isa_number, mergeOver, pushUnique, λnull, Ωempty } from '../helper/utilities.js';Shared constants
import { _entries, RECTANGLE, ZERO_STR } from '../helper/shared-vars.js';Local constants
const BANNER = 'banner',
LANDSCAPE = 'landscape',
LARGER = 'larger',
LARGEST = 'largest',
PORTRAIT = 'portrait',
REGULAR = 'regular',
SKYSCRAPER = 'skyscraper',
SMALLER = 'smaller',
SMALLEST = 'smallest';export default function (P = Ωempty) { const defaultAttributes = {Scrawl-canvas recognises five shapes, separated by four breakpoints:
bannerlandscaperectangleportraitskyscraperThe values assigned to the breakpoints are Float numbers for the displayed Canvas element’s width/height ratio - the value 3 represents the case where the width value is three times more than the height value, while 0.35 represents a width (roughly) 3 times less than the height.
We can set a Canvas artefact’s breakpoints in one go using the dedicated setDisplayShapeBreakpoints() function, as below. Alternatively we can use the regular set() function, supplying the attributes breakToBanner, breakToLandscape, breakToPortrait and breakToSkyscraper as required. The values given here are the default values for Canvas artefacts.
breakToBanner: 3,
breakToLandscape: 1.5,
breakToPortrait: 0.65,
breakToSkyscraper: 0.35,
actionBannerShape: null,
actionLandscapeShape: null,
actionRectangleShape: null,
actionPortraitShape: null,
actionSkyscraperShape: null,Scrawl-canvas also recognises five areas, again separated by four breakpoints:
smallestsmallerregularlargerlargestThe values assigned to the breakpoints are Float numbers for the displayed Canvas element’s pixel area (width * height).
baseMatchesCanvasDimensions flag has been set to true; in these situations we can use the area breakpoints to adjust entity dimensions and scales, and text font sizes, to better match the changed environment.fit attribute - suffers from image degredation when the canvas and its base cell’s dimensions are excessively different. breakToSmallest: 20000,
breakToSmaller: 80000,
breakToLarger: 180000,
breakToLargest: 320000,
actionSmallestArea: null,
actionSmallerArea: null,
actionRegularArea: null,
actionLargerArea: null,
actionLargestArea: null,
};
P.defs = mergeOver(P.defs, defaultAttributes); P.packetFunctions = pushUnique(P.packetFunctions, ['actionBannerShape', 'actionLandscapeShape', 'actionRectangleShape', 'actionPortraitShape', 'actionSkyscraperShape', 'actionSmallestArea', 'actionSmallerArea', 'actionRegularArea', 'actionLargerArea', 'actionLargestArea']); const G = P.getters,
S = P.setters;Get displayShape - returns the current display shape for the Canvas or Stack artefact. Returns a string whose value can be one of banner, landscape, rectangle, portrait, or skyscraper.
G.displayShape = function () {
return this.currentDisplayShape;
};Get displayShapeBreakpoints - returns an object with the current Number values for each of the breakpoint attributes.
G.displayShapeBreakpoints = function () {
return {
breakToBanner: this.breakToBanner,
breakToLandscape: this.breakToLandscape,
breakToPortrait: this.breakToPortrait,
breakToSkyscraper: this.breakToSkyscraper,
breakToSmallest: this.breakToSmallest,
breakToSmaller: this.breakToSmaller,
breakToLarger: this.breakToLarger,
breakToLargest: this.breakToLargest,
};
};Set displayShapeBreakpoints - breakpoints can be set individually, or alternatively they can be supplied in an object keyed to this attribute
S.displayShapeBreakpoints = function (items = Ωempty) {
for (const [key, val] of _entries(items)) {
if (isa_number(val)) {
switch (key) {
case 'breakToBanner' :
this.breakToBanner = val;
break;
case 'breakToLandscape' :
this.breakToLandscape = val;
break;
case 'breakToPortrait' :
this.breakToPortrait = val;
break;
case 'breakToSkyscraper' :
this.breakToSkyscraper = val;
break;
case 'breakToSmallest' :
this.breakToSmallest = val;
break;
case 'breakToSmaller' :
this.breakToSmaller = val;
break;
case 'breakToLarger' :
this.breakToLarger = val;
break;
case 'breakToLargest' :
this.breakToLargest = val;
break;
}
}
}
this.dirtyDisplayShape = true;
this.dirtyDisplayArea = true;
};setDisplayShapeBreakpoints - an alternative mechanism to set breakpoints beyond the normal set function
P.setDisplayShapeBreakpoints = S.displayShapeBreakpoints;Set breakToBanner - the breakpoint between landscape and banner display shapes; value will generally be a number greater than 1
S.breakToBanner = function (item) {
if (isa_number(item)) this.breakToBanner = item;
this.dirtyDisplayShape = true;
};Set breakToLandscape - the breakpoint between landscape and rectangle display shapes; value will generally be a number greater than 1
S.breakToLandscape = function (item) {
if (isa_number(item)) this.breakToLandscape = item;
this.dirtyDisplayShape = true;
};Set breakToPortrait - the breakpoint between portrait and rectangle display shapes; value will generally be a number greater than 0 and less than 1
S.breakToPortrait = function (item) {
if (isa_number(item)) this.breakToPortrait = item;
this.dirtyDisplayShape = true;
};Set breakToSkyscraper - the breakpoint between portrait and skyscraper display shapes; value will generally be a number greater than 0 and less than 1
S.breakToSkyscraper = function (item) {
if (isa_number(item)) this.breakToSkyscraper = item;
this.dirtyDisplayShape = true;
};Set breakToSmallest - the breakpoint between smaller and smallest display shapes
S.breakToSmallest = function (item) {
if (isa_number(item)) this.breakToSmallest = item;
this.dirtyDisplayArea = true;
};Set breakToSmaller - the breakpoint between regular and smaller display shapes
S.breakToSmaller = function (item) {
if (isa_number(item)) this.breakToSmaller = item;
this.dirtyDisplayArea = true;
};Set breakToLarger - the breakpoint between regular and larger display shapes
S.breakToLarger = function (item) {
if (isa_number(item)) this.breakToLarger = item;
this.dirtyDisplayArea = true;
};Set breakToLargest - the breakpoint between larger and largest display shapes
S.breakToLargest = function (item) {
if (isa_number(item)) this.breakToLargest = item;
this.dirtyDisplayArea = true;
};Each display shape has an associated hook function (by default a function that does nothing) which Scrawl-canvas will run each time it detects that the Canvas display shape has changed to that shape. We can replace these null-functions with our own; this allows us to configure the scene/animation to accommodate different display shapes, thus making the code reusable in a range of different web page environments.
We can set/update these functions at any time using the normal set() function. We can also set/update the functions using dedicated setAction???Shape() functions:
Set actionBannerShape - must be a Function
S.actionBannerShape = function (item) {
if (isa_fn(item)) this.actionBannerShape = item;
this.dirtyDisplayShape = true;
};setActionBannerShape - an alternative mechanism to set the actionBannerShape function, beyond the normal set functionality
P.setActionBannerShape = S.actionBannerShape;Set actionLandscapeShape - must be a Function
S.actionLandscapeShape = function (item) {
if (isa_fn(item)) this.actionLandscapeShape = item;
this.dirtyDisplayShape = true;
};setActionLandscapeShape - an alternative mechanism to set the actionLandscapeShape function, beyond the normal set functionality
P.setActionLandscapeShape = S.actionLandscapeShape;Set actionRectangleShape - must be a Function
S.actionRectangleShape = function (item) {
if (isa_fn(item)) this.actionRectangleShape = item;
this.dirtyDisplayShape = true;
};setActionRectangleShape - an alternative mechanism to set the actionRectangleShape function, beyond the normal set functionality
P.setActionRectangleShape = S.actionRectangleShape;Set actionPortraitShape - must be a Function
S.actionPortraitShape = function (item) {
if (isa_fn(item)) this.actionPortraitShape = item;
this.dirtyDisplayShape = true;
};setActionPortraitShape - an alternative mechanism to set the actionPortraitShape function, beyond the normal set functionality
P.setActionPortraitShape = S.actionPortraitShape;Set actionSkyscraperShape - must be a Function
S.actionSkyscraperShape = function (item) {
if (isa_fn(item)) this.actionSkyscraperShape = item;
this.dirtyDisplayShape = true;
};setActionSkyscraperShape - an alternative mechanism to set the actionSkyscraperShape function, beyond the normal set functionality
P.setActionSkyscraperShape = S.actionSkyscraperShape;Set actionSmallestArea - must be a Function
S.actionSmallestArea = function (item) {
if (isa_fn(item)) this.actionSmallestArea = item;
this.dirtyDisplayArea = true;
};setActionSmallestArea - an alternative mechanism to set the actionSmallestArea function, beyond the normal set functionality
P.setActionSmallestArea = S.actionSmallestArea;Set actionSmallerArea - must be a Function
S.actionSmallerArea = function (item) {
if (isa_fn(item)) this.actionSmallerArea = item;
this.dirtyDisplayArea = true;
};setActionSmallerArea - an alternative mechanism to set the actionSmallerArea function, beyond the normal set functionality
P.setActionSmallerArea = S.actionSmallerArea;Set actionRegularArea - must be a Function
S.actionRegularArea = function (item) {
if (isa_fn(item)) this.actionRegularArea = item;
this.dirtyDisplayArea = true;
};setActionRegularArea - an alternative mechanism to set the actionRegularArea function, beyond the normal set functionality
P.setActionRegularArea = S.actionRegularArea;Set actionLargerArea - must be a Function
S.actionLargerArea = function (item) {
if (isa_fn(item)) this.actionLargerArea = item;
this.dirtyDisplayArea = true;
};setActionLargerArea - an alternative mechanism to set the actionLargerArea function, beyond the normal set functionality
P.setActionLargerArea = S.actionLargerArea;Set actionLargestArea - must be a Function
S.actionLargestArea = function (item) {
if (isa_fn(item)) this.actionLargestArea = item;
this.dirtyDisplayArea = true;
};setActionLargestArea - an alternative mechanism to set the actionLargestArea function, beyond the normal set functionality
P.setActionLargestArea = S.actionLargestArea;initializeDisplayShapeActions - internal function; called by the Canvas and Stack artefact constructors
P.initializeDisplayShapeActions = function () {
this.actionBannerShape = λnull;
this.actionLandscapeShape = λnull;
this.actionRectangleShape = λnull;
this.actionPortraitShape = λnull;
this.actionSkyscraperShape = λnull;
this.currentDisplayShape = ZERO_STR;
this.dirtyDisplayShape = true;
this.actionSmallestArea = λnull;
this.actionSmallerArea = λnull;
this.actionRegularArea = λnull;
this.actionLargerArea = λnull;
this.actionLargestArea = λnull;
this.currentDisplayArea = ZERO_STR;
this.dirtyDisplayArea = true;
};cleanDisplayShape - internal function; replaces the function defined in the dom.js mixin, invoked when required as part of the DOM artefact prestamp functionality
P.cleanDisplayShape = function () {
this.dirtyDisplayShape = false;
const [width, height] = this.currentDimensions;
if (width > 0 && height > 0) {
const ratio = width / height,
current = this.currentDisplayShape,
banner = this.breakToBanner,
landscape = this.breakToLandscape,
portrait = this.breakToPortrait,
skyscraper = this.breakToSkyscraper;
if (ratio > banner) {
if (current !== BANNER) {
this.currentDisplayShape = BANNER;
this.actionBannerShape();
return true;
}
return false;
}
else if (ratio > landscape) {
if (current !== LANDSCAPE) {
this.currentDisplayShape = LANDSCAPE;
this.actionLandscapeShape();
return true;
}
return false;
}
else if (ratio < skyscraper) {
if (current !== SKYSCRAPER) {
this.currentDisplayShape = SKYSCRAPER;
this.actionSkyscraperShape();
return true;
}
return false;
}
else if (ratio < portrait) {
if (current !== PORTRAIT) {
this.currentDisplayShape = PORTRAIT;
this.actionPortraitShape();
return true;
}
return false;
}
else {
if (current !== RECTANGLE) {
this.currentDisplayShape = RECTANGLE;
this.actionRectangleShape();
return true;
}
return false;
}
}
else {
this.dirtyDisplayShape = true;
return false;
}
};cleanDisplayArea - internal function; replaces the function defined in the dom.js mixin, invoked when required as part of the DOM artefact prestamp functionality
cleanDisplayArea fires before cleanDisplayShape! P.cleanDisplayArea = function () {
this.dirtyDisplayArea = false;
const [width, height] = this.currentDimensions;
if (width > 0 && height > 0) {
const area = width * height,
current = this.currentDisplayArea,
largest = this.breakToLargest,
larger = this.breakToLarger,
smaller = this.breakToSmaller,
smallest = this.breakToSmallest;
if (area > largest) {
if (current !== LARGEST) {
this.currentDisplayArea = LARGEST;
this.actionLargestArea();
return true;
}
return false;
}
else if (area > larger) {
if (current !== LARGER) {
this.currentDisplayArea = LARGER;
this.actionLargerArea();
return true;
}
return false;
}
else if (area < smallest) {
if (current !== SMALLEST) {
this.currentDisplayArea = SMALLEST;
this.actionSmallestArea();
return true;
}
return false;
}
else if (area < smaller) {
if (current !== SMALLER) {
this.currentDisplayArea = SMALLER;
this.actionSmallerArea();
return true;
}
return false;
}
else {
if (current !== REGULAR) {
this.currentDisplayArea = REGULAR;
this.actionRegularArea();
return true;
}
return false;
}
}
else {
this.dirtyDisplayArea = true;
return false;
}
};updateDisplayShape - use this function to force the Canvas or Stack artefact to re-evaluate its current display shape, and invoke the action hook function associated with that shape.
P.updateDisplayShape = function () {
this.currentDisplayShape = ZERO_STR;
this.dirtyDisplayShape = true;
};updateDisplayArea - use this function to force the Canvas or Stack artefact to re-evaluate its current display area, and invoke the action hook function associated with that area.
P.updateDisplayArea = function () {
this.currentDisplayArea = ZERO_STR;
this.dirtyDisplayArea = true;
};updateDisplay - perform update for both display shape and area.
P.updateDisplay = function () {
this.updateDisplayShape();
this.updateDisplayArea();
};
}