UNPKG

scrawl-canvas

Version:

Responsive, interactive and more accessible HTML5 canvas elements. Scrawl-canvas is a JavaScript library designed to make using the HTML5 canvas element easier, and more fun

253 lines (178 loc) 6.46 kB
// # Pattern factory // Scrawl-canvas Pattern objects implement the Canvas API's [createPattern](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createPattern) method. The resulting [CanvasPattern](https://developer.mozilla.org/en-US/docs/Web/API/CanvasPattern) object can be used by any Scrawl-canvas entity as its `fillStyle` or `strokeStyle`. // #### Imports import { constructors, entity } from '../core/library.js'; import { doCreate, pushUnique, isa_obj, Ωempty } from '../helper/utilities.js'; import { gettableVideoAssetAtributes, settableVideoAssetAtributes } from '../asset-management/video-asset.js'; import { gettableImageAssetAtributes, settableImageAssetAtributes } from '../asset-management/image-asset.js'; import baseMix from '../mixin/base.js'; import patternMix from '../mixin/pattern.js'; import assetConsumerMix from '../mixin/asset-consumer.js'; // Shared constants import { $IMAGE, $VIDEO, _isArray, _keys, _values, NAME, STYLES, UNDEF } from '../helper/shared-vars.js'; // Local constants const T_PATTERN = 'Pattern'; // #### Pattern constructor const Pattern = function (items = Ωempty) { this.makeName(items.name); this.register(); this.set(this.defs); this.source = null; this.sourceNaturalWidth = 0; this.sourceNaturalHeight = 0; this.sourceLoaded = false; this.dirtyImage = true; this.dirtyCopyStart = true; this.dirtyCopyDimensions = true; this.dirtyImageSubscribers = true; this.patternMatrix = null; this.set(items); return this; }; // #### Pattern prototype const P = Pattern.prototype = doCreate(); P.type = T_PATTERN; P.lib = STYLES; P.isArtefact = false; P.isAsset = false; // #### Mixins baseMix(P); patternMix(P); assetConsumerMix(P); // #### Pattern attributes // No additional attributes required beyond those supplied by the mixins // #### Packet management P.packetObjects = pushUnique(P.packetObjects, ['asset']); P.finalizePacketOut = function (copy, items) { if (_isArray(items.patternMatrix)) copy.patternMatrix = items.patternMatrix; else { const m = this.patternMatrix; if (m) copy.patternMatrix = [m.a, m.b, m.c, m.d, m.e, m.f]; } return copy; }; // #### Clone management // No additional clone functionality required // #### Kill management P.kill = function () { const { name, asset, removeAssetOnKill } = this; let state, defs, fill, stroke; if (isa_obj(asset)) asset.unsubscribe(this); // Remove style from all entity state objects _values(entity).forEach(ent => { state = ent.state; defs = state.defs; if (state) { fill = state.fillStyle; stroke = state.strokeStyle; if (isa_obj(fill) && fill.name === name) state.fillStyle = defs.fillStyle; if (isa_obj(stroke) && stroke.name === name) state.strokeStyle = defs.strokeStyle; } }); // Cascade kill invocation to the asset object, if required if (removeAssetOnKill) { if (removeAssetOnKill.substring) asset.kill(true); else asset.kill(); } // Remove style from the Scrawl-canvas library this.deregister(); return this; }; // #### Get, Set, deltaSet // Pattern `get` and `set` (but not `deltaSet`) functions need to take into account their current source, whose attributes can be retrieved/amended directly on the Picture object // `get` P.get = function (item) { const source = this.source; if ((item.indexOf($VIDEO) === 0 || item.indexOf($IMAGE) === 0) && source) { if (gettableVideoAssetAtributes.includes(item)) return source[item.substring(6)]; else if (gettableImageAssetAtributes.includes(item)) return source[item.substring(6)]; } else { const getter = this.getters[item]; if (getter) return getter.call(this); else { const def = this.defs[item]; if (typeof def !== UNDEF) { const val = this[item]; return (typeof val !== UNDEF) ? val : def; } return undefined; } } }; // `set` P.set = function (items = Ωempty) { const keys = _keys(items), keysLen = keys.length; if (keysLen) { const setters = this.setters, source = this.source, defs = this.defs; let fn, i, key, value; for (i = 0; i < keysLen; i++) { key = keys[i]; value = items[key]; if ((key.indexOf($VIDEO) === 0 || key.indexOf($IMAGE) === 0) && source) { if (settableVideoAssetAtributes.includes(key)) source[key.substring(6)] = value else if (settableImageAssetAtributes.includes(key)) source[key.substring(6)] = value } else if (key && key !== NAME && value != null) { fn = setters[key]; if (fn) fn.call(this, value); else if (typeof defs[key] !== UNDEF) this[key] = value; } } this.dirtyFilterIdentifier = true; } return this; }; // #### Prototype functions // `getData` function called by Cell objects when calculating required updates to its CanvasRenderingContext2D engine, specifically for an entity's __fillStyle__, __strokeStyle__ and __shadowColor__ attributes. // + This is the point when we clean Scrawl-canvas assets which have told their subscribers that asset data/attributes have updated P.getData = function (entity, cell) { if (this.dirtyAsset) this.cleanAsset(); this.asset.checkSource(this.sourceNaturalWidth, this.sourceNaturalHeight); return this.buildStyle(cell); }; // #### Factory // ``` // scrawl.importDomImage('.mypatterns'); // // scrawl.makePattern({ // // name: 'brick-pattern', // asset: 'brick', // // }).clone({ // // name: 'marble-pattern', // imageSource: 'img/marble.png', // }); // // scrawl.makeBlock({ // // name: 'marble-block', // // width: '40%', // height: '40%', // // startX: '25%', // startY: '25%', // // handleX: 'center', // handleY: 'center', // // lineWidth: 20, // lineJoin: 'round', // // method: 'fillThenDraw', // // fillStyle: 'marble-pattern', // strokeStyle: 'brick-pattern', // }); // ``` export const makePattern = function (items) { if (!items) return false; return new Pattern(items); }; constructors.Pattern = Pattern;