Skip to content

Instantly share code, notes, and snippets.

@robhimslf
Last active September 8, 2018 02:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robhimslf/73709b7bf6494049c59f04e1f2a97c2b to your computer and use it in GitHub Desktop.
Save robhimslf/73709b7bf6494049c59f04e1f2a97c2b to your computer and use it in GitHub Desktop.
A quick-and-dirty example of taking the various components of a clan's banner, and compositing them using HTML5 canvas. This assumes you already have a clan's banner data, such as with the example below. Please note that Bungie's manifest files AREN'T static, which means the hashes you used to fetch image paths and color definitions this week...…
<!doctype html>
<html>
<head></head>
<body>
<canvas id="banner" width="496" height="1034"></canvas>
<script>
/**
* Note: you'll need to host the banner-stand and banner-overlay images yourself in
* certain cases. You can find them here:
*
* Banner Stand: https://www.bungie.net/img/bannercreator/FlagStand00.png
* Banner Overlay: https://www.bungie.net/img/bannercreator/flag_overlay.png
*/
let canvasWidth = 496,
canvasHeight = 1034,
imageWidth = 402,
imageHeight = 594,
offset = 47,
banner = {
images: {
stand: '/img_banner-stand.png',
overlay: '/img_banner-overlay.png',
decalFgImg: 'https://www.bungie.net/common/destiny2_content/icons/cb_decal_3a8cc352aa5cf3db65a6f49cb7587ef0.png',
decalBgImg: 'https://www.bungie.net/common/destiny2_content/icons/cb_decal_1a9b6f77aba7d1080eca53f7bf0c8632.png',
gonfalonImg: 'https://www.bungie.net/common/destiny2_content/icons/cb_gonfalon_f6fada502c393950662df87b131bbb6f.png',
detailImg: 'https://www.bungie.net/common/destiny2_content/icons/cb_gdetail_70eee553054563de23a74b8c707d5cbc.png'
},
colors: {
decalFgClr: 'rgba(255,70,70,1)',
decalBgClr: 'rgba(17,17,18,1)',
gonfalonClr: 'rgba(17,17,18,1)',
detailClr: 'rgba(240,240,240,1)'
}
};
const bannerImagePromises = Object.keys( banner.images )
.map( bannerImageKey => new Promise( resolve => {
let bannerImage = new Image();
bannerImage.onload = () => { resolve({ key: bannerImageKey, img: bannerImage })};
bannerImage.src = banner.images[ bannerImageKey ];
bannerImage.setAttribute( 'crossOrigin', 'anonymous' );
bannerImage.crossOrigin = 'anonymous';
}));
Promise.all( bannerImagePromises ).then( resolvedImages => {
let images = {};
resolvedImages.forEach( sourceImage => { images[ sourceImage.key ] = sourceImage.img });
// Places an image into a given canvas with a fill of the provided color.
const getImage = ( c, ctx, img, imgElem, imgColor ) => {
let naturalWidth,
naturalHeight;
try {
if ( imgElem && imgElem !== null ) {
ctx.drawImage( imgElem, c.width / 2 - imgElem.naturalWidth / 2,
offset, imgElem.naturalWidth, imgElem.naturalHeight );
ctx.globalCompositeOperation = 'source-in';
}
if ( img && img !== null ) {
naturalWidth = img.naturalWidth;
naturalHeight = img.naturalHeight;
ctx.drawImage( img, c.width / 2 - naturalWidth / 2,
offset, naturalWidth, naturalHeight );
}
if ( imgColor && imgColor !== null ) {
ctx.globalCompositeOperation = 'source-in';
ctx.fillStyle = imgColor;
ctx.fillRect( 0, 0, c.width, c.height );
}
} catch ( err ) {
console.warn( err );
}
return c;
};
let canvas = document.getElementById( 'banner' ),
canvasContext = canvas.getContext( '2d' ),
decalForeground = canvas.cloneNode( true ),
decalForegroundContext = decalForeground.getContext( '2d' ),
decalBackground = canvas.cloneNode( true ),
decalBackgroundContext = decalBackground.getContext( '2d' ),
detail = canvas.cloneNode( true ),
detailContext = detail.getContext( '2d' ),
background = canvas.cloneNode( true ),
backgroundContext = background.getContext( '2d' ),
combined = canvas.cloneNode( true ),
combinedContext = combined.getContext( '2d' ),
masked = canvas.cloneNode( true ),
maskedContext = masked.getContext( '2d' );
canvasContext.globalCompositeOperation =
combinedContext.globalCompositeOperation =
maskedContext.globalCompositeOperation = 'source-over';
canvasContext.clearRect( 0, 0, canvas.width, canvas.height );
combinedContext.clearRect( 0, 0, combined.width, combined.height );
maskedContext.clearRect( 0, 0, masked.width, masked.height );
try {
combinedContext.drawImage( getImage( background, backgroundContext, images.gonfalonImg, null, banner.colors.gonfalonClr ), 0, 0, canvasWidth, canvasHeight );
combinedContext.drawImage( getImage( detail, detailContext, images.detailImg, null, banner.colors.detailClr ), 0, 0, canvasWidth, canvasHeight );
combinedContext.drawImage( getImage( decalBackground, decalBackgroundContext, images.decalBgImg, null, banner.colors.decalBgClr ), 0, 0, canvasWidth, canvasHeight );
combinedContext.drawImage( getImage( decalForeground, decalForegroundContext, images.decalFgImg, null, banner.colors.decalFgClr ), 0, 0, canvasWidth, canvasHeight );
combinedContext.drawImage( images.overlay, 0, 0, canvasWidth, canvasHeight );
maskedContext.drawImage( images.gonfalonImg, ( canvasWidth / 2 ) - ( imageWidth / 2 ), offset, imageWidth, imageHeight );
maskedContext.globalCompositeOperation = 'source-in';
maskedContext.drawImage( combined, 0, 0, canvasWidth, canvasHeight );
canvasContext.drawImage( masked, 0, 0, canvasWidth, canvasHeight );
canvasContext.drawImage( images.stand, ( canvasWidth / 2 ) - ( imageWidth / 2 ) - 10, 6, canvasWidth * 0.85, canvasHeight * 0.85 );
} catch ( err ) {
console.warn( err );
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment