// really dummy implementation...
function AssetsLoader() {}
AssetsLoader.prototype = Object.create({
addImage: function(objs, cb) {
if (!objs || typeof objs !== 'object') return;
if (!Array.isArray(objs)) objs = [];
var toLoad = objs.length,
imgs = objs.map(loadImage, this);
function loadImage(obj) {
if (!obj.src) return;
this.toLoad++;
var img = new Image();
img.src = obj.src;
img.onload = onimgload;
img.onerror = onimgerror;
return obj.img = img;
}
function onimgload(evt) {
if (--toLoad <= 0 && typeof cb === 'function') cb(imgs);
}
function onimgerror(evt) {
console.warn('failed to load image at ', evt.target.src);
}
}
});
// Some info about our assets, there are an infinity of ways to deal with it,
// You would have to determine yourself what's best for your own case
var dests = {
sofa: {
name: 'sofa',
src: 'https://i.stack.imgur.com/ryO42.png'
},
shirt: {
name: 'shirt',
src: 'https://i.stack.imgur.com/cPNbe.png'
}
};
var patterns = {
name: 'patterns',
src: 'https://i.stack.imgur.com/TdIAJ.png',
positions: {
orange: {
x: 0,
y: 0,
width: 173,
height: 173,
out_width: 75,
out_height: 75
},
violet: {
x: 173,
y: 0,
width: 173,
height: 173,
out_width: 35,
out_height: 35
},
psyche: {
x: 0,
y: 173,
width: 173,
height: 173,
out_width: 25,
out_height: 25
},
pink: {
x: 173,
y: 173,
width: 173,
height: 173,
out_width: 125,
out_height: 125
}
}
}
var assets = new AssetsLoader();
// first load all our images, and only then, start the whole thing
assets.addImage([dests.shirt, dests.sofa, patterns], init);
function init() {
// populate our selects
for (var key in dests) {
dest_select.appendChild(new Option(key, key));
}
for (var key in patterns.positions) {
pat_select.appendChild(new Option(key, key));
}
dest_select.onchange = pat_select.onchange = draw;
var ctx = canvas.getContext('2d');
var offscreenCtx = document.createElement('canvas').getContext('2d');
draw();
function draw() {
var dest = dests[dest_select.value].img;
ctx.canvas.width = dest.width;
ctx.canvas.height = dest.height;
ctx.globalCompositeOperation = 'source-over';
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(dest, 0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'source-in';
// here we generate the CanvasPattern
ctx.fillStyle = getPattern(pat_select.value);
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'luminosity';
ctx.drawImage(dest, 0, 0, canvas.width, canvas.height);
}
// now that we have our patterns loaded in a single sprite-sheet,
// we can generate the CanvasPatterns synchronously
function getPattern(key) {
var pos = patterns.positions[key]; // get our pattern's position in our dictionary
// resize the offscreen canvas so it matches our pattern
offscreenCtx.canvas.width = pos.out_width;
offscreenCtx.canvas.height = pos.out_height;
offscreenCtx.drawImage(patterns.img, pos.x, pos.y, pos.width, pos.height, 0, 0, pos.out_width, pos.out_height);
return offscreenCtx.createPattern(offscreenCtx.canvas, 'repeat');
}
}
<select id="dest_select"></select>
<select id="pat_select"></select>
<br><br><br>
<canvas id="canvas"></canvas>