Instantly share code, notes, and snippets.
Last active
September 8, 2018 02:48
-
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...…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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