preface

I have been developing H5 mini-games with Egret for some time, and I have also developed several H5 mini-games. This article mainly sorts out some experience in the development process and a development scaffolding that I gradually encapsulated.

Scaffold source code address

Github.com/CB-ysx/egre…

Create a project

Create a project directly with EgretLauncher

Platform. The ts file

LoadingUI. Ts file

Delete the whole file, and we’ll create another one later.

The Main ts file

Create a project directory

Start by creating some folders in the project SRC directory like this:

Outside SRC ┣━ Base ┃ OUTSIDE - where there are some base classes ┣━ Common ┃ outside - where there are some public classes ┣━ Component outside - where there are some components ┣━data outside - where there is data individualism - NET outside - WHERE THERE is API, network requirement - Page outside - where there is page outside - where there is page outside - where there is service lurking ┣━Main. Ts ---- entry file L ━Platform. Ts ---- some variables, etcCopy the code

The project is roughly divided into the above structure, and then some implementation of base classes, public classes, and so on is written

The base class for creating the view, baseView.ts

In the base directory to create BaseView ts file, BaseView class inherits from the egret, DisplayObjectContainer, containers can be as a view, mainly implemented some view (pop-up, page, etc.) often need to use the method.

class BaseView extends egret.DisplayObjectContainer {


    /** * load box (can be used before a network request, or before some asynchronous time-consuming operation) */
    private _loading: egret.DisplayObjectContainer
    private _loadingIcon: egret.Bitmap

    /** * Records whether the loading box is displayed */
    private isLoading: boolean = false


    /**
     * 加载动画
     */
    private loadingTween: egret.Tween

    public constructor() {
        super(a)this.addEventListener(egret.Event.ADDED_TO_STAGE, this.onAddToStage, this)}/** * is added to the stage */
    public onAddToStage(event: egret.Event) {
    }

    /** * View destruction */
    public onDestory() {
    }


    /**
     * 消息提示
     */
    protected toast(msg: string, duration = 2000, cb? :Function) {
	// How to encapsulate the Toast class
        Toast.instance.show(msg, duration, cb)
    }

    /** * displays the loading box */
    protected showLoading() {
        if (this.isLoading) {
            return
        }
        this.isLoading = true
        if (!this._loading) {
            this._loading = new egret.DisplayObjectContainer()
            let bg = new egret.Shape()
            bg.graphics.beginFill(0x000000.1)
            bg.graphics.drawRect(0.0.this.width, this.height)
            bg.graphics.endFill()
            bg.alpha = 0.3
            bg.touchEnabled = true
            this._loading.addChild(bg)
        }
        if (!this._loadingIcon) {
            this._loadingIcon = ResUtil.createBitmap('loading_icon') // This is a self-encapsulated method to get a bitmap, as will be written later
            this._loadingIcon.width = 50
            this._loadingIcon.height = 50
            this._loadingIcon.anchorOffsetX = this._loadingIcon.width / 2
            this._loadingIcon.anchorOffsetY = this._loadingIcon.height / 2
            this._loadingIcon.x = this.width / 2
            this._loadingIcon.y = this.height / 2
            this._loading.addChild(this._loadingIcon)
        }
        egret.MainContext.instance.stage.addChild(this._loading)
        this.loadingTween = egret.Tween
                                .get(this._loadingIcon, {loop: true})
                                .to({
                                    rotation: 360
                                }, 500, egret.Ease.sineIn)
    }

    /** * close the loading box */
    protected closeLoading() {
        if (!this.isLoading) {
            return
        }
        if (this.loadingTween) {
            this.loadingTween.setPaused(true)}this.loadingTween = null
        egret.MainContext.instance.stage.removeChild(this._loading)
        this.isLoading = false}}Copy the code

Create the page base class basescene.ts

The BaseScene class inherits from BaseView. Games are usually classified by scenes, such as the start scene, the game scene, the end scene, etc. This is considered as a page, so create a scene base class, set some default values, such as the full screen height, of course, Maybe some scenes don’t need full screen, you can inherit BaseScene and override the width and height, or use BaseView.

Class BaseScene extends BaseView {/** ** private _constructor: string = "public constructor(); String = ") {super() this._pagename = pageName // Set the width of the scene to the width of the stage this.width = gameutil.getStageWidth () this.height = Gameutil.getstageheight () // Prevent page clickthrough this.touchEnabled = true} public get pageName() {return this._pagename} /** * As for the scene animation, */ public afterEnterAnimation() {// console.log(this._pagename, 'afterEnterAnimation')} public afterLeaveAnimation() {// console.log(this._pagename, 'afterLeaveAnimation') } }Copy the code

Our scene base class is as simple as that, and we can add more generic scene methods to it in the future. Because it inherits from BaseView, scenarios can also use the public methods encapsulated in BaseView directly, for example, using the this.toast() method to pop up a prompt.

Toast.ts

This is a prompt component that creates a toasts. ts file in the Component directory and uses singleton mode to manage prompts uniformly.

class ToastMsg { // Message format
    public msg: string
    public duration: number = 2000
    public cb: Function

    public constructor(msg, duration, cb) {
        this.msg = msg
        this.duration = duration
        this.cb = cb
    }
}

/** * This is modeled after Toast * in Android to implement a pop-up message prompt and automatically close */
class Toast {

    /** * Message list */
    private _msgList: Array<ToastMsg> = new Array<ToastMsg>()

    private _toasting: boolean = false

    private _stage: egret.Stage / / stage

    private _toastContainer: egret.DisplayObjectContainer
    private _shapeBg: egret.Bitmap
    
    public static toast: Toast

    public constructor() {
        // Toast the stage, make sure the message is displayed on the top
        this._stage = egret.MainContext.instance.stage
        this._toastContainer = new egret.DisplayObjectContainer()
    }

    public static get instance() {
        if (!this.toast) {
            this.toast = new Toast()
        }
        return this.toast
    }

    public show(msg: string, duration = 2000, cb? :Function) {
        // Save the message to the list
        this._msgList.push(new ToastMsg(msg, duration, cb))

        // If the message is currently displayed, do not use the display method, polling will be called inside
        if (!this._toasting) {
            this._toasting = true
            this._show()
        }
    }

    private _show() {
	// If the list length is greater than 0, there are more messages in the queue
        if(this._msgList.length > 0) {
	    // Get the first message
            let msg = this._msgList.shift()

            this._toastContainer.alpha = 0

            // Create text
            let textField: egret.TextField = new egret.TextField()
            textField.text = msg.msg
            textField.textAlign = 'center'
            textField.textColor = 0xffffff
            textField.size = 24

            // The width of the edge between the text and the black background
            let space = 40

            // If the text width is larger than 0.8 times the screen width, set it.
            if (textField.textWidth >= GameUtil.getStageWidth() * 0.8) {
                textField.width = GameUtil.getStageWidth() * 0.8 - space
            }
            // Set the width and height of the container containing the text and the anchor point, the anchor point of the text
            this._toastContainer.width = textField.width + space
            this._toastContainer.height = textField.height + space

            this._toastContainer.anchorOffsetX = this._toastContainer.width / 2
            this._toastContainer.anchorOffsetY = this._toastContainer.height

            textField.anchorOffsetX = textField.width / 2
            textField.anchorOffsetY = textField.height / 2

            textField.x = this._toastContainer.width / 2
            textField.y = this._toastContainer.height / 2

            // The container position is horizontally centered at the bottom
            this._toastContainer.x = GameUtil.getStageWidth() / 2
            this._toastContainer.y = GameUtil.getStageHeight() * 0.8

            let shapeBg = new egret.Shape()
            shapeBg.graphics.beginFill(0x000000.1)
            shapeBg.graphics.drawRoundRect(0.0.this._toastContainer.width, this._toastContainer.height, 20.20)
            shapeBg.graphics.endFill()

            this._toastContainer.addChild(shapeBg)
            this._toastContainer.addChild(textField)

            this._stage.addChild(this._toastContainer)
            
            // Pop and disappear animations
            egret.Tween
                .get(this._toastContainer)
                .to({
                    alpha: 1
                }, 200, egret.Ease.sineIn)
                .wait(msg.duration) // Wait XXX milliseconds before disappearing
                .to({
                    alpha: 0
                }, 200, egret.Ease.sineIn)
                .wait(100)
                .call((a)= > {
                    this._toastContainer.removeChild(shapeBg)
                    this._toastContainer.removeChild(textField)
                    this._stage.removeChild(this._toastContainer)
                    msg.cb && msg.cb()
                    this._show() // Continue to display the next message
                }, this)}else {
            this._toasting = false}}}Copy the code

GameUtil.ts

Next create a utility class for getting the stage width and create Gameutil.ts in Common

/** * tool class */
/** * tool class */
class GameUtil {

    public static isIos: boolean = /iphone|ipad|ipod/.test(navigator.userAgent.toLowerCase())

    public static getTopStage(): egret.Stage {
        return egret.MainContext.instance.stage
    }
    /**
     * 获取舞台高度
     */
    public static getStageHeight(): number {
        return egret.MainContext.instance.stage.stageHeight
    }

    /* * Get the stage width */
    public static getStageWidth(): number {
        return egret.MainContext.instance.stage.stageWidth
    }

    // The container is clickable
    public static tap(bitmap: egret.DisplayObject, callback, thisObject) {
        bitmap.touchEnabled = true
        bitmap.addEventListener(egret.TouchEvent.TOUCH_TAP, callback, thisObject)
    }

    // Create rounded rectangle
    public static drawRoundRect(shape: egret.Shape, color: number, width: number, height: number, round: number, rArr: Array<number>) {
        shape.graphics.clear()
        shape.graphics.beginFill(color, 1)
        shape.graphics.drawRoundRect(0.0, width, height, round, round)
        shape.graphics.endFill()
        let roundArr: Array<number> = [0.1.2.3].filter(item= > rArr.indexOf(item) === - 1)
        let rectData: Array<Array<number>> = [
            [0.0],
            [1.0],
            [0.1],
            [1.1]]for (let i = 0; i < roundArr.length; ++i) {let x = (width - round) * rectData[roundArr[i]][0]
            let y = (height - round) * rectData[roundArr[i]][1]
            shape.graphics.beginFill(color, 1)
            shape.graphics.drawRect(x, y, round, round)
            shape.graphics.endFill()
        }
    }
}
Copy the code

ResUtil.ts

Resource management, for getting images, etc., to create resutil.ts in common

class ResUtil {
    private bitmapList: {[index: string]: egret.Texture} = {}
    private movieClipList: {[index: string]: egret.MovieClip} = {}

    private static resUtil: ResUtil

    public static get instance() {
        if (!this.resUtil) {
            this.resUtil = new ResUtil()
        }
        return this.resUtil
    }

    /**
     * 加载网络图片
     * item: {
     *  url: 'xxxx' // 图片地址
     *  xxxx // 自己附带其他参数
     * }
     */
    public static loadImageByUrl(item, callback) {
        try {
            RES.getResByUrl(item.url, (event) = > {
                callback && callback({
                    status: 1,
                    event: event,
                    item: item
                })
            }, this, RES.ResourceItem.TYPE_IMAGE)
        } catch (e) {
            console.error(e)
            callback && callback({
                status: 0}}})/** * suffix * @param suffix */
    public static createBitmap(name: string, suffix: string = 'png'): egret.Bitmap {
        const key = `${name}_${suffix}`
        let result = new egret.Bitmap()
        if (!this.instance.bitmapList[key]) {
            this.instance.bitmapList[key] = RES.getRes(key)
        }
        result.texture = this.instance.bitmapList[key]
        return result
    }

    /** * suffix * @param suffix */
    public static createMovieClip(name: string): egret.MovieClip {
        let result = new egret.MovieClip()
        if (!this.instance.movieClipList[name]) {
            const data_stay: any = RES.getRes(name + '_json')
            const texture_stay: egret.Texture = RES.getRes(name + '_png')
            const mcFactory_stay: egret.MovieClipDataFactory = new egret.MovieClipDataFactory(data_stay, texture_stay)
            this.instance.movieClipList[name] = new egret.MovieClip(mcFactory_stay.generateMovieClipData(name))
        }
        result.movieClipData = this.instance.movieClipList[name].movieClipData
        return result
    }

    /** * get the maximum width and height of the GIF * @param name * @param suffix */
    public static getMoviceClipWH(name: string): {w: number, h: number} {
        let mw = 0
        let mh = 0
        const movie = ResUtil.createMovieClip(name)
        const tData = movie.movieClipData.textureData
        for (let key in tData) {
            mw = Math.max(tData[key].w, mw)
            mh = Math.max(tData[key].h, mh)
        }
        return {
            w: mw,
            h: mh
        }
    }
}
Copy the code

routing

With the above base classes, we also need to do a page routing jump control. Here, we will copy vue-router to create a simple router, directly in the SRC directory router.ts file.

class Router {

	private static router: Router

    	private _stage: egret.DisplayObjectContainer // Scene container

	/** * Current routing information */
	private _route: {
		name: string,
		meta: any,
		params: any,
		scene: BaseScene
	} = {name: ' ', meta: null, scene: null, params: null}

	private _params: {
		meta: any,
		params: any,
	} = {meta: null, params: null}

	/** * Route mapping table */
	private _routeMap: SelfMap<{
		className: any,
		name: string,
		meta: any, params? :any} > =new SelfMap<{
		className: any,
		name: string,
		meta: any, params? :any} > ()/** * the page leaves the animation */
	private _leaveAnimation: SelfAnimation

	/** * the page goes to animation */
	private _enterAnimation: SelfAnimation

	/ * * * * /
	private _beforeEach: Function = (to, from, next) = > {next()}

	private constructor() {}public static get instance() {
        	if (!this.router) {
            		this.router = new Router()
        	}
        	return this.router
    	}

	public static get params() {
		return this.instance._params.params
	}

	public static get meta() {
		return this.instance._params.meta
	}

	/** * initialize, set the container to place the page */
	public static init(main: egret.DisplayObjectContainer) {
        	if (this.instance._stage) {
            		console.log('Already created scene container')
            		return
        	}
        	if(! main) {console.log('Main scene cannot be empty')
            		return
        	}
		// Create a new container and place it on main. This will ensure that the toast is always on all pages
		this.instance._stage = new egret.DisplayObjectContainer()
        	main.addChild(this.instance._stage)
		return this
	}

	/** * Registration page */
	public static register(key: string, className: any, meta: any = null) {
		this.instance._routeMap.put(key, {className: className, name: key, meta: meta})
		return this
	}

	/** * What to do before jumping routes */
	public static beforeEach(beforeEach: Function) {
		this.instance._beforeEach = beforeEach
		return this
	}

	/** * set the page to leave animation */
	public staticsetLeaveAnimation(animation? : SelfAnimation) {this.instance._leaveAnimation = animation
		return this
	}

	/** * Set the page to animation */
	public staticsetEnterAnimation(animation? : SelfAnimation) {this.instance._enterAnimation = animation
		return this
	}

	/** * exit the page, execute animation */ if there is animation
	private leavePage(page: BaseScene, callback: Function, leaveAnimation: SelfAnimation) {
		if(! page) { callback && callback()return
		}
		let animation = leaveAnimation || this._leaveAnimation

		page.onDestory()
		if (animation) {
			animation.execute(page, (a)= > {
				callback && callback()
			})
		} else {
			callback && callback()
		}
	}

	/** * load the page, execute animation */ if there is animation
	private enterPage(page: BaseScene, callback: Function, enterAnimation: SelfAnimation) {
		let animation = enterAnimation || this._enterAnimation
		if (animation) {
			animation.beforeAnim(page)
		}
		this._stage.addChild(page)
		if (animation) {
			animation.execute(page, (a)= > {
				callback && callback()
			})
		} else {
			callback && callback()
		}
	}

	private _currentLeavePage: BaseScene = null

	public static to(config: {
		name: string, params? :any, leaveAnimation? : SelfAnimation, enterAnimation? : SelfAnimation }) {let page = this.instance._routeMap.get(config.name)
		if (page == undefined) {
			console.error(`scene ${config.name} is not register`)
			return
		}
		let currentRoute = this.instance._route
		this.instance._beforeEach(page, currentRoute, (to: string = config.name) = > {
			if(to ! = config.name) { config.name = to page =this.instance._routeMap.get(config.name)
				if (page == undefined) {
					console.error(`scene ${config.name} is not register`)
					return}}let currentLeavePage = this.instance._currentLeavePage
			// If the current leaving page is the same as the leaving page, do not execute
			if (currentLeavePage && currentRoute.scene && currentLeavePage.pageName === currentRoute.scene.pageName) {
				return
			}
			// If you want to enter the same page as the current page, do not execute
			if (config.name === currentRoute.name) {
				return
			}
			this.instance._currentLeavePage = currentRoute.scene
			currentLeavePage = currentRoute.scene
			this.instance.leavePage(currentRoute.scene, (a)= > {
				currentLeavePage && currentLeavePage.afterLeaveAnimation()
				let newPage = new page.className(config.name)
				this.instance._params.meta = page.meta
				this.instance._params.params = config.params
				this.instance.enterPage(newPage, (a)= > {
					currentLeavePage && this.instance._stage.removeChild(currentLeavePage)
					currentLeavePage = this.instance._currentLeavePage = null
					currentRoute.name = config.name
					currentRoute.meta = page.meta
					currentRoute.scene = newPage
					currentRoute.params = config.params
					newPage.afterEnterAnimation()
				}, config.enterAnimation)
			}, config.leaveAnimation)
		})
	}
}
Copy the code

SelfMap and SelfAnimation are self-implemented simple tool classes, which will not be introduced here and can be viewed in the source code.

Method of use

Register the route in the runGame method of the entry file main. ts

.private async runGame() {
        Router.init(this)
            .setLeaveAnimation(new SelfAnimation().add({alpha: 0}, 300))// Set the page to leave the animation
            .setEnterAnimation(new Animation().init({alpha: 0}).add({alpha: 1}, 300)) // Set the page to animation
            .register('loading', LoadingScene) // Register the load page
            .register('game', GameScene) // Register the game scene route
            .to({ // Jump to the load page page
                name: 'loading'})... }...// Jump pages in other places can be used directly
// Router.to({name: xxxx})
Copy the code

Loading page

It is used to display the progress of loading resources and perform some initialization operations

class LoadingScene extends BaseScene {

    private progressBar: egret.Shape
    private progressText: egret.TextField
    private barWidth: number = 0
    private barHeight: number = 0
    private startTime: number = 0

    private preload: number = 2 // Preload Number of resource groups
    private main: number = 100 - this.preload

    public async onAddToStage() {
        this.drawBg();
        this.initProgress();

        try {
            this.startTime = new Date().getTime()
            await RES.loadConfig(`resource/default.res.json? v=${Math.random()}`."resource/")
            await RES.loadGroup("preload".0, {
                onProgress: (current: number, total: number) = > {
                    let progress = current / total * this.preload
                    let width = this.barWidth * progress / 100
                    this.drawProgress(width, Math.floor(progress))
                }
            })
            // Since the logo is an image, it can only be drawn after the resource is loaded back
            this.drawLogo();
            this.load()
        } catch(e) {
            console.error(e)
        }
    }

    private async load() {
        await RES.loadGroup("main".0, {
            onProgress: (current: number, total: number) = > {
                let progress = current / total * this.main + this.preload
                let width = this.barWidth * progress / 100
                this.drawProgress(width, Math.floor(progress))
            }
        })
    }

    private initProgress() {
        this.barWidth = this.width * 0.6
        this.barHeight = 20
        this.progressBar = new egret.Shape()
        this.progressBar.width = this.barWidth
        this.progressBar.height = this.barHeight
        this.progressBar.x = (this.width - this.barWidth) / 2
        this.progressBar.y = (this.height - this.barHeight) / 2
        this.addChild(this.progressBar)

        this.progressText = new egret.TextField()
        this.progressText.textColor = 0x005660
        this.progressText.size = 28
        this.progressText.strokeColor = 0xFFFFFF
        this.progressText.stroke = 3
        this.progressText.width = this.width
        this.progressText.textAlign = 'center'
        this.progressText.text = '0%'
        this.progressText.y = this.progressBar.y - this.progressText.height - 20
        this.addChild(this.progressText)
    }

    private drawProgress(width, progress) {
        this.progressBar.graphics.clear()
        this.progressBar.graphics.beginFill(0xFFFFFF.1)
        this.progressBar.graphics.drawRoundRect(0.0, width, this.barHeight, this.barHeight, this.barHeight)
        this.progressBar.graphics.endFill()
        this.progressText.text = `${progress}% `
        if (progress == 100) {
            let diff = new Date().getTime() - this.startTime
            diff = diff < 1000 ? (1000 - diff) : 300
            egret.setTimeout((a)= > {
                // Jump to the game page after loading
                Router.to({name: 'game'})},this, diff)
        }
    }

    private drawBg() {
        let bg: egret.Shape = new egret.Shape();
        bg.graphics.beginFill(0x56A1D2);
        bg.graphics.drawRect(0.0.this.width, this.height);
        bg.graphics.endFill();
        this.addChild(bg);
    }

    private drawLogo() {
        let logo: egret.Bitmap = ResUtil.createBitmap('egret_icon');
        logo.x = (this.width - logo.width) / 2
        this.addChild(logo); }}Copy the code

The game page

class GameScene extends BaseScene {

    public async onAddToStage() {
        let bg: egret.Bitmap = ResUtil.createBitmap('bg'.'jpg');
        bg.width = this.width;
        bg.height = this.height;
        this.addChild(bg);

        let btn: egret.Shape = new egret.Shape();
        GameUtil.drawRoundRect(btn, 0xFFFFFF.300.100.10[0.1.2.3]);
        btn.x = (this.width - btn.width) / 2;
        btn.y = (this.height - btn.height) / 2;
        this.addChild(btn);

        let btnText: egret.TextField = new egret.TextField();
        btnText.text = 'click';
        btnText.textColor = 0x000000;
        btnText.x = (this.width - btnText.width) / 2;
        btnText.y = (this.height - btnText.height) / 2;
        this.addChild(btnText);

        GameUtil.tap(btn, (a)= > {
            this.toast('Click the button');
        }, this)}}Copy the code

Encapsulate the network request facility

Egret.HttpRequest is used here, or you can import frameworks like Axios yourself.

Http.ts

Create HTTP. ts in the NET directory, wrap egret.HttpRequest, support Promise

class Http {

    private baseUrl: string = ' '

    public static http: Http

    public static get instance() {
        if (!this.http) {
            this.http = new Http()
        }
        return this.http
    }

    private constructor() {}private request({method = egret.HttpMethod.GET, url, params = {}, headers = {}}): Promise<any> {
        if(! (/http(|s):\/\//.test(url))) {
            url = this.baseUrl + url
        }
        let _params: any = ' '
        let _headers = {}
        _headers['Content-Type'] = 'application/x-www-form-urlencoded'
        // If incoming, overwrite
        for (let key in headers) {
            _headers[key] = headers[key]
        }
        if (_headers['Content-Type'= = ='application/json') {
            _params = JSON.stringify(params)
            _params = _params.replace(/\+/g."%2B").replace(/\&/g."% 26")
            // console.log(_params)
        } else {
            for (let key in params) {
                _params += `${key}=${('' + params[key]).replace(/\&/g, "%26")}& `
            }
            _params = _params.replace(/\+/g."%2B")
            // console.log(_params)
            if (_params.length > 0) {
                _params = _params.substring(0, _params.length - 1)}if (method === egret.HttpMethod.GET) {
                url += `?${_params}`}}return new Promise((resolve, reject) = > {
            let request = new egret.HttpRequest()
            request.responseType = egret.HttpResponseType.TEXT
            request.open(url, method)
            for (let key in _headers) {
                request.setRequestHeader(key, _headers[key])
            }
            if (method === egret.HttpMethod.GET) {
                request.send()
            } else {
                request.send(_params)
            }
            request.addEventListener(egret.Event.COMPLETE, (event) = > {
                dealResult(event)
            }, this)
            request.addEventListener(egret.IOErrorEvent.IO_ERROR, (event) = > {
                dealResult(event, false)},this)
            function dealResult(event, success = true) {
                let response
                try {
                    response = JSON.parse(request.response)
                } catch(e) {
                    response = request.response
                }
                if (success) {
                    resolve(response)
                } else {
                    reject(response)
                }
            }
        })
    }

    public setBaseUrl(baseUrl: string) {
        this.baseUrl = baseUrl
    }

    public get(url: string, params = {}, headers = {}): Promise<any> {
        return this.request({
            url: url,
            params: params,
            headers: headers
        })
    }

    public post(url: string, params = {}, headers = {}): Promise<any> {
        return this.request({
            method: egret.HttpMethod.POST,
            url: url,
            params: params,
            headers: headers
        })
    }
}
Copy the code

Api.ts

Also in the net directory to create api. ts file, used to manage their OWN Api interface

class Api {

    private static api: Api

    private constructor() {
        let baseUrl: string = 'http://xxx.com'
        if (DEBUG) {
            baseUrl = 'http://localhost:3000'
        }
        Http.instance.setBaseUrl(baseUrl)
    }

    public static get instance() {
        if (!this.api) {
            this.api = new Api()
        }
        return this.api
    }

    /** ** Unified processing result */
    private predeal(http: Promise<any>) :Promise<any> {
        return http.then(response= > {
                    if (response.code == '0') {
                        // This structure is determined according to its own data
                        return Promise.resolve(response.payload.data || response.payload || response)
                    } else {
                        return Promise.reject(response)
                    }
                })
                .catch(response= > {
                    return Promise.reject(response.msg || 'Request error')})}/** * get */
    private get(url: string, params = {}, headers = {}): Promise<any> {
        return this.predeal(Http.instance.get(url, params, headers))
    }

    /** * post */
    private post(url: string, params = {}, headers = {}): Promise<any> {
        return this.predeal(Http.instance.post(url, params, headers))
    }

    /** * get example */
    public getInfo(): Promise<any> {
        return this.get('/info')}/** * Post example */
    public postInfo(params): Promise<any> {
        return this.post('/info', params)
    }
}
Copy the code

Audio management

Now H5 is basically inseparable from BGM and other music playback, therefore, scaffolding also encapsulated an audio management class, easy to use, this is written relatively rough, still need to optimize. The specific code can be viewed in the project.

Localstorage storage

CacheStorge encapsulates the CacheStorge class to manage localstorage data. You can directly store data such as acquired objects. For details, see the common/ cacheStorge. ts file

Wechat JSSDK related

The introduction of JSSDK

To import the external JS library, you need to handle it in a special way, you can refer to the project, add the WXJSSDK in the lib directory, and configure the path in modules in egretproperties.json

{
  "engineVersion": "5.2.17"."compilerVersion": "5.2.17"."template": {},
  "target": {
    "current": "web"
  },
  "modules": [{"name": "egret"
    },
    {
      "name": "game"
    },
    {
      "name": "tween"
    },
    {
      "name": "assetsmanager"
    },
    {
      "name": "promise"
    },
		{
			"name": "wxjssdk"."path": "./libs/wxjssdk"}}]Copy the code

Encapsulate your own WXSDK

GetWxSignPackage encapsulates the acquisition of signature information, has shared the interface, first call getWxSignPackage to obtain signature configuration, data processing in this method needs to be processed according to its own interface data.

class WxSdk {

    private signPackage: BodyConfig = new BodyConfig()

    public static wxsdk: WxSdk

    public static get instance() {
        if (!this.wxsdk) {
            this.wxsdk = new WxSdk()
        }
        return this.wxsdk
    }

    private constructor() {}/** * Configure wechat signature */
    public configWx(): Promise<any> {
        // There is a pit. NonceStr is capitalized when using //api.24haowan.com. With platforms: noncESTr. Remember to switch when switching
        if (!this.signPackage.appId || !this.signPackage.timestamp ||
            !this.signPackage.nonceStr || !this.signPackage.signature) {
            return Promise.reject('Wechat signature parameter error')}this.signPackage.debug = false
        this.signPackage.jsApiList = [
                // All apis to be called are added to this list
                'onMenuShareTimeline'.'onMenuShareAppMessage'.'hideMenuItems'.// Record related
                'startRecord'.'stopRecord'.'onVoiceRecordEnd'.'playVoice'.'onVoicePlayEnd'.'pauseVoice'.'stopVoice'.'uploadVoice'.'downloadVoice',]/* wechat interface */
        wx.config(this.signPackage)
        wx.error(err= > {
            console.error('error: configWx of wxsdk.ts', err)
        })
        return Promise.resolve('wechat signature configuration completed')}/** * get wechat signature information */
    public getWxSignPackage(): Promise<any> {
        let params = {
            url: encodeURIComponent(window.location.href.split(The '#') [0])}// Get wechat signature
        return Http.instance.get('xxxxx', params)
                .then(response= > {
                    let data = response.payload
                    if(! data.appId) {return Promise.reject(response.msg || response)
                    }
                    this.signPackage.appId = data.appId
                    this.signPackage.timestamp = data.timestamp
                    this.signPackage.nonceStr = data.nonceStr
                    this.signPackage.signature = data.signature
                    return Promise.resolve(this.configWx())
                })
                .catch(error= > {
                    console.error('error: getWxSignPackage of wxsdk.js'.JSON.stringify(error))
                    return Promise.reject(error)
                })
    }

    public share(shareInfo: ShareBody) {
        if(! shareInfo.title) { shareInfo.title ='Share title'
        }
        if(! shareInfo.link) { shareInfo.link =window.location.href.split(The '#') [0]}if(! shareInfo.desc) { shareInfo.desc ='Share description'
        }
        wx.ready((a)= > {
            console.log('wx ready'. shareInfo) wx.onMenuShareAppMessage(shareInfo) wx.onMenuShareTimeline(shareInfo) wx.onMenuShareQQ(shareInfo) wx.onMenuShareWeibo(shareInfo) }) } }Copy the code

The end of the

At this point, a simple framework is basically complete. It may not be perfect, but it satisfies some simple needs, and I have developed three or four games with this structure that have gone live.

Finally, attach github address: github.com/CB-ysx/egre…

Original link: codebear.cn/article?id=…