React Native (Expo) app - Draw from local image into canvas using drawImage() method

Viewed 1578

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;
0 Answers
Related