Created
October 10, 2017 01:36
-
-
Save ebhoren/9c5901d3b246f2d029d7fec5cb279ab2 to your computer and use it in GitHub Desktop.
aframe-spritesheet component
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
/** | |
* A-Frame Spritesheet Component for A-Frame. | |
* Enables dynamic control of animation spritesheets | |
*/ | |
AFRAME.registerComponent('sprite-sheet', { | |
schema: { | |
progress: { type: 'number', default: 0 }, | |
frameName: { type: 'string', default: null }, | |
firstFrame: { type: 'number', default: 0 }, | |
lastFrame: { type: 'number', default: null }, | |
dataJSON: { type: 'string', default: null }, | |
debug: { default: false }, | |
}, | |
/** | |
* Called once when component is attached. | |
*/ | |
init: function() { | |
this.mapCanvas = document.createElement( 'canvas' ); | |
this.textureCtx = this.mapCanvas.getContext( '2d' ); | |
this.texture = new THREE.Texture(this.mapCanvas); | |
this.parseJSONData(this.data.dataJSON); | |
// callback for when the image has loaded | |
this.el.addEventListener('materialtextureloaded', () => { | |
this.imageLoaded = true; | |
// save reference to the original image | |
this.spriteSheetImage = this.el.object3D.children[0].material.map.image; | |
this.el.object3D.children[0].material.map = this.texture; | |
this.setTextureRepeat(); | |
this.adjustTexture(this.currentFrame); | |
}); | |
this.currentFrame = 0; | |
}, | |
/** | |
* Called when component is attached and when component data changes. | |
*/ | |
update: function() { | |
// no actual animation | |
if (!this.framesData) return; | |
// if no last frame is specified use the number of available frames | |
let lastFrame = this.numFrames - 1; | |
if (this.data.frameName && this.frameNameToIndex) { | |
let frameIndex = this.frameNameToIndex[this.data.frameName]; | |
if (frameIndex != null){ | |
this.currentFrame = frameIndex; | |
this.resizeTextureForFrame(frameIndex); | |
this.setTextureRepeat(); | |
} | |
else | |
console.warn(`Spritesheet error - No such frame with name ${this.data.frameName}`); | |
} else { | |
this.currentFrame = Math.round(this.data.progress * (lastFrame - this.data.firstFrame)) + this.data.firstFrame; | |
} | |
this.adjustTexture(this.currentFrame); | |
}, | |
/** | |
* Called when a component is removed (e.g., via removeAttribute). | |
*/ | |
remove: function() { | |
if( this.texture ) this.texture.dispose(); | |
// Cleanup | |
this.mapCanvas = null; | |
this.textureCtx = null; | |
this.texture = null; | |
this.spriteSheetImage = null; | |
this.spriteSheetData = null; | |
this.framesData = null; | |
this.imageLoaded = null; | |
this.currentFrame = null; | |
this.frameNameToIndex = null; | |
this.numFrames = null; | |
this.frameWidth = null; | |
this.frameHeight = null; | |
this.lastDrawnFrame = null; | |
delete this.mapCanvas; | |
delete this.textureCtx; | |
delete this.texture; | |
delete this.spriteSheetImage; | |
delete this.spriteSheetData; | |
delete this.framesData; | |
delete this.imageLoaded; | |
delete this.currentFrame; | |
delete this.frameNameToIndex; | |
delete this.numFrames; | |
delete this.frameWidth; | |
delete this.frameHeight; | |
delete this.lastDrawnFrame; | |
}, | |
parseJSONData: function(data) { | |
this.spriteSheetData = JSON.parse(data); | |
this.framesData = Object.keys(this.spriteSheetData.frames).map(key => { | |
return this.spriteSheetData.frames[key]; | |
}); | |
// create a dictionary to map from keyframe names to frame number | |
this.frameNameToIndex = {}; | |
Object.keys(this.spriteSheetData.frames).map((key, index) => { | |
this.frameNameToIndex[key] = index; | |
return null; | |
}); | |
this.numFrames = this.framesData.length; | |
this.resizeTextureForFrame(0); | |
}, | |
resizeTextureForFrame: function(frame) { | |
this.frameWidth = this.framesData[frame].sourceSize.w; | |
this.frameHeight = this.framesData[frame].sourceSize.h; | |
this.mapCanvas.width = pow2ceil(this.frameWidth); | |
this.mapCanvas.height = pow2ceil(this.frameHeight); | |
}, | |
setTextureRepeat: function() { | |
this.texture.repeat.set( | |
this.frameWidth / this.mapCanvas.width, | |
this.frameHeight / this.mapCanvas.height | |
); | |
this.texture.offset.x = 0; | |
this.texture.offset.y = 1 - this.frameHeight / this.mapCanvas.height; | |
}, | |
/** | |
* Adjust the texture to a specific frame index | |
* @param {number} frameNum | |
*/ | |
adjustTexture: function(frameNum) { | |
// image hasn't loaded, can't draw anything | |
if (!this.imageLoaded) return; | |
// no need to draw the same frame twice | |
if (this.lastDrawnFrame === frameNum) return; | |
this.adjustFrameBySpriteSheet(frameNum); | |
this.lastDrawnFrame = frameNum; | |
}, | |
/** | |
* Adjust the spritesheet texture to a certain frame on a TexturePacker JSON spritesheet | |
* @param {number} frameNum | |
*/ | |
adjustFrameBySpriteSheet: function(frameNum) { | |
let frameData = this.framesData[frameNum]; | |
this.textureCtx.clearRect(0, 0, this.mapCanvas.width, this.mapCanvas.height); | |
if( this.data.debug === true ) { | |
this.textureCtx.fillStyle = 'green'; | |
this.textureCtx.fillRect(0, 0, this.mapCanvas.width, this.mapCanvas.height); | |
} | |
this.textureCtx.save(); | |
if (frameData.rotated) { | |
// drawing is rotated, so axes are rotated | |
let drawData = { | |
dX: this.frameHeight - (frameData.frame.h + frameData.spriteSourceSize.y), | |
dY: frameData.spriteSourceSize.x, | |
width: frameData.frame.h, | |
height: frameData.frame.w | |
}; | |
this.textureCtx.rotate(-90 * Math.PI / 180); | |
this.textureCtx.translate(-this.frameHeight, 0); | |
this.textureCtx.drawImage(this.spriteSheetImage, | |
frameData.frame.x, frameData.frame.y, frameData.frame.h, frameData.frame.w, | |
drawData.dX, | |
drawData.dY, | |
drawData.width, | |
drawData.height | |
); | |
} else { | |
let drawData = { | |
dX: this.frameWidth/2 + (frameData.spriteSourceSize.x - frameData.sourceSize.w / 2), | |
dY: this.frameHeight/2 + (frameData.spriteSourceSize.y - frameData.sourceSize.h / 2), | |
width: frameData.frame.w, | |
height: frameData.frame.h | |
}; | |
this.textureCtx.drawImage(this.spriteSheetImage, | |
frameData.frame.x, frameData.frame.y, frameData.frame.w, frameData.frame.h, | |
drawData.dX, | |
drawData.dY, | |
drawData.width, | |
drawData.height | |
); | |
} | |
this.textureCtx.restore(); | |
this.texture.needsUpdate = true; | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment