modules_pattern_pattern.js
import Position from "@pencil.js/position";
import Rectangle from "@pencil.js/rectangle";
import Image from "@pencil.js/image";
import OffScreenCanvas from "@pencil.js/offscreen-canvas";
import NetworkEvent from "@pencil.js/network-event";
/**
* @module Pattern
*/
const sourceKey = Symbol("_source");
/**
* Background pattern filing
* @class
*/
export default class Pattern {
/**
* Pattern constructor
* @param {HTMLImageElement|Image|HTMLCanvasElement|OffScreenCanvas} source - Source of the pattern
* @param {PatternOptions} options - Some options
*/
constructor (source, options) {
this.options = {
...Pattern.defaultOptions,
...options,
};
this.source = source;
}
/**
* @return {HTMLImageElement}
*/
get source () {
const source = this[sourceKey];
if (source instanceof OffScreenCanvas) {
if (!source.isLooped) {
source.render();
}
return source.ctx.canvas;
}
return source;
}
/**
* @param {HTMLImageElement|Image} newSource - New source for the pattern
*/
set source (newSource) {
if (newSource instanceof window.HTMLImageElement ||
newSource instanceof window.HTMLCanvasElement ||
newSource instanceof OffScreenCanvas) {
this[sourceKey] = newSource;
}
else if (newSource instanceof Image) {
if (newSource.isLoaded) {
this[sourceKey] = newSource.file;
}
else {
newSource.on(NetworkEvent.events.ready, () => {
this[sourceKey] = newSource.file;
});
}
}
else if (newSource instanceof Pattern) {
this[sourceKey] = newSource.source;
}
}
/**
* @return {Number}
*/
get width () {
return this[sourceKey].width;
}
/**
* @return {Number}
*/
get height () {
return this[sourceKey].height;
}
/**
* Return the pattern object
* @param {CanvasRenderingContext2D} ctx - Drawing context
* @return {CanvasPattern}
*/
toString (ctx) {
const { repeat, scale } = this.options;
const pattern = ctx.createPattern(this.source, repeat);
const matrix = new window.DOMMatrix();
if (typeof scale === "number") {
matrix.a = scale;
matrix.d = scale;
}
else {
const s = Position.from(scale);
matrix.a = s.x;
matrix.d = s.y;
}
const origin = Rectangle.prototype.getOrigin.call(this);
matrix.e = origin.x * matrix.a;
matrix.f = origin.y * matrix.d;
pattern.setTransform(matrix);
return pattern;
}
// TODO: add toJSON and static from function
/**
* @typedef {Object} PatternOptions
* @prop {String} [repeat=Pattern.repetition.repeat] - Repetition rule
* @prop {PositionDefinition} [origin=new Position()] - Relative offset
* @prop {Number|PositionDefinition} [scale=1] - Scaling ratio or a pair of value for horizontal and vertical scaling
*/
/**
* @return {PatternOptions}
*/
static get defaultOptions () {
return {
repeat: this.repetition.repeat,
origin: new Position(),
scale: 1,
};
}
/**
* @typedef {Object} PatternRepetitions
* @prop {String} repeat -
* @prop {String} x -
* @prop {String} y -
* @prop {String} none -
*/
/**
* @return {PatternRepetitions}
*/
static get repetition () {
return {
repeat: "repeat",
x: "repeat-x",
y: "repeat-y",
none: "no-repeat",
};
}
}