I have a transparent PNG sprite sheet which I load into an tag and then when 'onload' fires I save the element into a context object (ImageLoader.js) so I can later use it with drawImage on a canvas (Sprite.js) to extract various sprites and overlay them to produce a composite tile for a game.
Home.js simply chooses to display a dummy holding message but redraws as soon as 'onload' fires which then renders a component
I have tried to use react-native-canvas and various other approaches but I'm not sure if what I am trying to do is possible.
The various code snippets are below as maybe easier to show than try to describe...
The code works fine in browser but this is a React Native app using the Expo framework and fails due to my tag in the ImageLoader.js component. Ideally I would use a React Native Image but this is not an option for the Canvas drawImage method which requires a DOM style Image object.
I am open to other ways of achieving this but effectively I need to be able to create a custom React Native Image component where the base image is generated in the app by using a local transparent PNG sprite sheet and overlaying various sections of it into a canvas so that I create a composite image...
App.js
import { Text, View } from 'react-native';
import Home from './components/Home';
import ConfigContextProvider from './contexts/ConfigContext';
export default class App extends Component {
render() {
return (
<ConfigContextProvider>
<Home />
</ConfigContextProvider>
);
}
}
Home.js
import { Text, View, Button } from 'react-native';
import { ConfigContext } from '../contexts/ConfigContext';
import Sprite from './Sprite';
import ImageLoader from './ImageLoader';
class Home extends Component {
render() {
return (
<ConfigContext.Consumer>{(configContext) => {
if(configContext.spriteMapIsLoaded) {
return (
<View>
<Text>It's Alive!</Text>
<Text>Screen Width: {configContext.screenWidth}</Text>
<Text>Screen Height: {configContext.screenHeight}</Text>
<Sprite />
</View>
);
}
else {
return (
<View>
<Text>Image is not loaded!</Text>
<ImageLoader />
</View>
);
}
}}</ConfigContext.Consumer>
);
}
}
export default Home;
ImageLoader.js
import { ConfigContext } from '../contexts/ConfigContext';
class ImageLoader extends Component {
static contextType = ConfigContext;
componentDidMount() {
const spriteMapImage = this.refs.spritemap;
spriteMapImage.onload = () => {
console.log("Image has loaded");
this.context.setSpriteMapImage(this.refs.spritemap);
}
}
render() {
return (
<img ref="spritemap" src={require('../assets/SpriteMap.png')} style={{width:0, height:0}} />
)
}
}
export default ImageLoader;
ConfigContext.js
import { Dimensions } from 'react-native';
export const ConfigContext = createContext();
class ConfigContextProvider extends Component {
state = {
screenWidth: Dimensions.get('window').width,
screenHeight: Dimensions.get('window').height,
spriteSize: 50,
spriteMapIsLoaded: false,
spriteMapImage: null
}
setSpriteMapImage = (spriteMapImage) => {
this.setState({
spriteMapIsLoaded: true,
spriteMapImage: spriteMapImage});
}
render() {
return (
<ConfigContext.Provider value={{...this.state, setSpriteMapImage: this.setSpriteMapImage}}>
{this.props.children}
</ConfigContext.Provider>
);
}
}
export default ConfigContextProvider;
Sprite.js
import { ConfigContext } from '../contexts/ConfigContext';
class Sprite extends Component {
static contextType = ConfigContext;
componentDidMount() {
console.log(this.context);
const canvas = this.refs.canvas;
const ctx = canvas.getContext('2d');
for(let x = 0; x <= 360; x += 120) {
let y = Math.floor(Math.random() * 8) * 120;
console.log(x, y);
ctx.drawImage(this.context.spriteMapImage, x, y, 120, 120, 0, 0, this.context.spriteSize, this.context.spriteSize);
}
ctx.drawImage(this.context.spriteMapImage, 960, 120, 120, 120, 0, 0, this.context.spriteSize, this.context.spriteSize);
const cardColour = Math.floor(Math.random() * 2);
const cardSuit = Math.floor(Math.random() * 2);
const cardSymbol = Math.floor(Math.random() * 4);
ctx.drawImage(this.context.spriteMapImage, 480 + cardSymbol * 120, cardColour * 120, 120, 120, 0, 0, this.context.spriteSize, this.context.spriteSize);
ctx.drawImage(this.context.spriteMapImage, 480 + cardColour * 240 + cardSuit * 120, 240, 120, 120, 0, 0, this.context.spriteSize, this.context.spriteSize);
}
render() {
return (
<div>
<canvas ref="canvas" width={this.context.spriteSize} height={this.context.spriteSize} />
</div>
);
}
}
export default Sprite;