[adapters] 增加小游戏适配部分源码

This commit is contained in:
SmallMain
2024-10-16 17:12:08 +08:00
parent 887d4a96c9
commit 07bf3b7a96
345 changed files with 38447 additions and 0 deletions

View File

@@ -0,0 +1,205 @@
import HTMLAudioElement from './HTMLAudioElement'
const HAVE_NOTHING = 0
const HAVE_METADATA = 1
const HAVE_CURRENT_DATA = 2
const HAVE_FUTURE_DATA = 3
const HAVE_ENOUGH_DATA = 4
let SN_SEED = 1
const _innerAudioContextMap = {}
export default class Audio extends HTMLAudioElement {
constructor(url) {
super()
this._$sn = SN_SEED++;
this.HAVE_NOTHING = HAVE_NOTHING
this.HAVE_METADATA = HAVE_METADATA
this.HAVE_CURRENT_DATA = HAVE_CURRENT_DATA
this.HAVE_FUTURE_DATA = HAVE_FUTURE_DATA
this.HAVE_ENOUGH_DATA = HAVE_ENOUGH_DATA
this.readyState = HAVE_NOTHING
const innerAudioContext = tt.createInnerAudioContext()
_innerAudioContextMap[this._$sn] = innerAudioContext
this._canplayEvents = [
'load',
'loadend',
'canplay',
'canplaythrough',
'loadedmetadata'
]
innerAudioContext.onCanplay(() => {
this._loaded = true
this.readyState = this.HAVE_CURRENT_DATA
this._canplayEvents.forEach((type) => {
this.dispatchEvent({ type: type })
})
})
innerAudioContext.onPlay(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
this.dispatchEvent({ type: 'play' })
})
innerAudioContext.onPause(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
this.dispatchEvent({ type: 'pause' })
})
innerAudioContext.onEnded(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
if (_innerAudioContextMap[this._$sn].loop === false) {
this.dispatchEvent({ type: 'ended' })
}
this.readyState = HAVE_ENOUGH_DATA
})
innerAudioContext.onError(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
this.dispatchEvent({ type: 'error' })
})
if (url) {
this.src = url
} else {
this._src = ''
}
this._loop = innerAudioContext.loop
this._autoplay = innerAudioContext.autoplay
this._paused = innerAudioContext.paused
this._volume = innerAudioContext.volume
this._muted = false;
}
addEventListener(type, listener, options = {}) {
super.addEventListener(type, listener, options)
type = String(type).toLowerCase()
if (this._loaded && this._canplayEvents.indexOf(type) !== -1) {
this.dispatchEvent({ type: type })
}
}
load() {
// console.warn('HTMLAudioElement.load() is not implemented.')
// weixin doesn't need call load() manually
}
play() {
_innerAudioContextMap[this._$sn].play()
}
resume() {
_innerAudioContextMap[this._$sn].resume()
}
pause() {
_innerAudioContextMap[this._$sn].pause()
}
stop() {
_innerAudioContextMap[this._$sn].stop()
}
destroy() {
_innerAudioContextMap[this._$sn].destroy()
}
canPlayType(mediaType = '') {
if (typeof mediaType !== 'string') {
return ''
}
if (mediaType.indexOf('audio/mpeg') > -1 || mediaType.indexOf('audio/mp4')) {
return 'probably'
}
return ''
}
get currentTime() {
return _innerAudioContextMap[this._$sn].currentTime
}
set currentTime(value) {
_innerAudioContextMap[this._$sn].seek(value)
}
get duration () {
return _innerAudioContextMap[this._$sn].duration
}
get src() {
return this._src
}
set src(value) {
this._src = value
this._loaded = false
this.readyState = this.HAVE_NOTHING
const innerAudioContext = _innerAudioContextMap[this._$sn]
innerAudioContext.src = value
}
get loop() {
return this._loop
}
set loop(value) {
this._loop = value
_innerAudioContextMap[this._$sn].loop = value
}
get autoplay() {
return this.autoplay
}
set autoplay(value) {
this._autoplay = value
_innerAudioContextMap[this._$sn].autoplay = value
}
get paused() {
return this._paused;
}
get volume() {
return this._volume;
}
set volume(value) {
this._volume = value;
if (!this._muted) {
_innerAudioContextMap[this._$sn].volume = value;
}
}
get muted() {
return this._muted;
}
set muted(value) {
this._muted = value;
if (value) {
_innerAudioContextMap[this._$sn].volume = 0;
} else {
_innerAudioContextMap[this._$sn].volume = this._volume;
}
}
cloneNode() {
const newAudio = new Audio()
newAudio.loop = this.loop
newAudio.autoplay = this.autoplay
newAudio.src = this.src
return newAudio
}
}

View File

@@ -0,0 +1,64 @@
// import HTMLCanvasElement from './HTMLCanvasElement'
import { innerWidth, innerHeight } from './WindowProperties'
let hasModifiedCanvasPrototype = false
let hasInit2DContextConstructor = false
let hasInitWebGLContextConstructor = false
export default function Canvas() {
const canvas = tt.createCanvas()
canvas.type = 'canvas'
// canvas.__proto__.__proto__.__proto__ = new HTMLCanvasElement()
const _getContext = canvas.getContext
canvas.getBoundingClientRect = () => {
const ret = {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight
}
return ret
}
canvas.style = {
top: '0px',
left: '0px',
width: innerWidth + 'px',
height: innerHeight + 'px',
}
canvas.addEventListener = function (type, listener, options = {}) {
// console.log('canvas.addEventListener', type);
document.addEventListener(type, listener, options);
}
canvas.removeEventListener = function (type, listener) {
// console.log('canvas.removeEventListener', type);
document.removeEventListener(type, listener);
}
canvas.dispatchEvent = function (event = {}) {
console.log('canvas.dispatchEvent' , event.type, event);
// nothing to do
}
Object.defineProperty(canvas, 'clientWidth', {
enumerable: true,
get: function get() {
return innerWidth
}
})
Object.defineProperty(canvas, 'clientHeight', {
enumerable: true,
get: function get() {
return innerHeight
}
})
return canvas
}

View File

@@ -0,0 +1,10 @@
import Node from './Node'
export default class Element extends Node {
className = ''
children = []
constructor() {
super()
}
}

View File

@@ -0,0 +1,14 @@
import {noop} from './util'
export default class Event {
cancelBubble = false
cancelable = false
target = null
timestampe = Date.now()
preventDefault = noop
stopPropagation = noop
constructor(type) {
this.type = type
}
}

View File

@@ -0,0 +1,5 @@
export default class MouseEvent {
constructor() {
}
}

View File

@@ -0,0 +1,32 @@
import { noop } from '../util/index.js'
export default class TouchEvent {
touches = []
targetTouches = []
changedTouches = []
preventDefault = noop
stopPropagation = noop
constructor(type) {
this.type = type
this.target = window.canvas
this.currentTarget = window.canvas
}
}
function touchEventHandlerFactory(type) {
return (event) => {
const touchEvent = new TouchEvent(type)
touchEvent.touches = event.touches
touchEvent.targetTouches = Array.prototype.slice.call(event.touches)
touchEvent.changedTouches = event.changedTouches
touchEvent.timeStamp = event.timeStamp
document.dispatchEvent(touchEvent)
}
}
tt.onTouchStart(touchEventHandlerFactory('touchstart'))
tt.onTouchMove(touchEventHandlerFactory('touchmove'))
tt.onTouchEnd(touchEventHandlerFactory('touchend'))
tt.onTouchCancel(touchEventHandlerFactory('touchcancel'))

View File

@@ -0,0 +1,2 @@
export TouchEvent from './TouchEvent'
export MouseEvent from './MouseEvent'

View File

@@ -0,0 +1,57 @@
const _events = new WeakMap()
export default class EventTarget {
constructor() {
_events.set(this, {})
}
addEventListener(type, listener, options = {}) {
let events = _events.get(this)
if (!events) {
events = {}
_events.set(this, events)
}
if (!events[type]) {
events[type] = []
}
events[type].push(listener)
if (options.capture) {
// console.warn('EventTarget.addEventListener: options.capture is not implemented.')
}
if (options.once) {
// console.warn('EventTarget.addEventListener: options.once is not implemented.')
}
if (options.passive) {
// console.warn('EventTarget.addEventListener: options.passive is not implemented.')
}
}
removeEventListener(type, listener) {
const events = _events.get(this)
if (events) {
const listeners = events[type]
if (listeners && listeners.length > 0) {
for (let i = listeners.length; i--; i > 0) {
if (listeners[i] === listener) {
listeners.splice(i, 1)
break
}
}
}
}
}
dispatchEvent(event = {}) {
const listeners = _events.get(this)[event.type]
if (listeners) {
for (let i = 0; i < listeners.length; i++) {
listeners[i](event)
}
}
}
}

View File

@@ -0,0 +1,7 @@
/*
* TODO 使用 tt.readFile 来封装 FileReader
*/
export default class FileReader {
construct() {
}
}

View File

@@ -0,0 +1,7 @@
import HTMLMediaElement from './HTMLMediaElement'
export default class HTMLAudioElement extends HTMLMediaElement {
constructor() {
super('audio')
}
}

View File

@@ -0,0 +1,20 @@
// import HTMLElement from './HTMLElement';
// export default class HTMLCanvasElement extends HTMLElement
// {
// constructor(){
// super('canvas')
// }
// };
import Canvas from './Canvas'
import HTMLElement from './HTMLElement'
GameGlobal.screencanvas = GameGlobal.screencanvas || new Canvas();
const canvas = GameGlobal.screencanvas;
const canvasConstructor = canvas.constructor;
// canvasConstructor.__proto__.__proto__ = new HTMLElement();
export default canvasConstructor;

View File

@@ -0,0 +1,54 @@
import Element from './Element'
import { noop } from './util/index.js'
import { innerWidth, innerHeight } from './WindowProperties'
export default class HTMLElement extends Element {
className = ''
childern = []
style = {
width: `${innerWidth}px`,
height: `${innerHeight}px`
}
insertBefore = noop
innerHTML = ''
constructor(tagName = '') {
super()
this.tagName = tagName.toUpperCase()
}
setAttribute(name, value) {
this[name] = value
}
getAttribute(name) {
return this[name]
}
get clientWidth() {
const ret = parseInt(this.style.fontSize, 10) * this.innerHTML.length
return Number.isNaN(ret) ? 0 : ret
}
get clientHeight() {
const ret = parseInt(this.style.fontSize, 10)
return Number.isNaN(ret) ? 0 : ret
}
getBoundingClientRect() {
return {
top: 0,
left: 0,
width: innerWidth,
height: innerHeight
}
}
focus() {
}
}

View File

@@ -0,0 +1,16 @@
// import HTMLElement from './HTMLElement';
// export default class HTMLImageElement extends HTMLElement
// {
// constructor(){
// super('img')
// }
// };
import HTMLElement from './HTMLElement'
const imageConstructor = tt.createImage().constructor;
// imageConstructor.__proto__.__proto__ = new HTMLElement();
export default imageConstructor;

View File

@@ -0,0 +1,25 @@
import HTMLElement from './HTMLElement'
export default class HTMLMediaElement extends HTMLElement {
constructor(type) {
super(type)
}
addTextTrack() {
}
captureStream() {
}
fastSeek() {
}
load() {
}
pause() {
}
play() {
}
}

View File

@@ -0,0 +1,8 @@
import HTMLMediaElement from './HTMLMediaElement';
export default class HTMLVideoElement extends HTMLMediaElement
{
constructor(){
super('video')
}
};

View File

@@ -0,0 +1,9 @@
import HTMLImageElement from './HTMLImageElement'
export default function() {
const image = tt.createImage();
// image.__proto__.__proto__.__proto__ = new HTMLImageElement();
return image;
};

View File

@@ -0,0 +1,5 @@
export default class ImageBitmap {
constructor() {
// TODO
}
}

View File

@@ -0,0 +1,34 @@
import EventTarget from './EventTarget.js'
export default class Node extends EventTarget {
constructor() {
super()
}
childNodes = []
appendChild(node) {
this.childNodes.push(node)
// if (node instanceof Node) {
// this.childNodes.push(node)
// } else {
// throw new TypeError('Failed to executed \'appendChild\' on \'Node\': parameter 1 is not of type \'Node\'.')
// }
}
cloneNode() {
const copyNode = Object.create(this)
Object.assign(copyNode, this)
return copyNode
}
removeChild(node) {
const index = this.childNodes.findIndex((child) => child === node)
if (index > -1) {
return this.childNodes.splice(index, 1)
}
return null
}
}

View File

@@ -0,0 +1,5 @@
export default class WebGLRenderingContext {
constructor() {
// TODO
}
}

View File

@@ -0,0 +1,87 @@
const _socketTask = new WeakMap()
export default class WebSocket {
static CONNECTING = 0 // The connection is not yet open.
static OPEN = 1 // The connection is open and ready to communicate.
static CLOSING = 2 // The connection is in the process of closing.
static CLOSED = 3 // The connection is closed or couldn't be opened.
binaryType = '' // TODO 更新 binaryType
bufferedAmount = 0 // TODO 更新 bufferedAmount
extensions = ''
onclose = null
onerror = null
onmessage = null
onopen = null
protocol = '' // TODO 小程序内目前获取不到,实际上需要根据服务器选择的 sub-protocol 返回
readyState = 3
constructor(url, protocols = []) {
if (typeof url !== 'string' || !(/(^ws:\/\/)|(^wss:\/\/)/).test(url)) {
throw new TypeError(`Failed to construct 'WebSocket': The URL '${url}' is invalid`)
}
this.url = url
this.readyState = WebSocket.CONNECTING
const socketTask = tt.connectSocket({
url,
protocols: Array.isArray(protocols) ? protocols : [protocols],
tcpNoDelay: true
})
_socketTask.set(this, socketTask)
socketTask.onClose((res) => {
this.readyState = WebSocket.CLOSED
if (typeof this.onclose === 'function') {
this.onclose(res)
}
})
socketTask.onMessage((res) => {
if (typeof this.onmessage === 'function') {
this.onmessage(res)
}
})
socketTask.onOpen(() => {
this.readyState = WebSocket.OPEN
if (typeof this.onopen === 'function') {
this.onopen()
}
})
socketTask.onError((res) => {
if (typeof this.onerror === 'function') {
this.onerror(new Error(res.errMsg))
}
})
return this
}
close(code, reason) {
this.readyState = WebSocket.CLOSING
const socketTask = _socketTask.get(this)
socketTask.close({
code,
reason
})
}
send(data) {
if (typeof data !== 'string' && !(data instanceof ArrayBuffer) && !ArrayBuffer.isView(data)) {
throw new TypeError(`Failed to send message: The data ${data} is invalid`)
}
const socketTask = _socketTask.get(this)
socketTask.send({
data
})
}
}

View File

@@ -0,0 +1,21 @@
const { screenWidth, screenHeight, devicePixelRatio } = tt.getSystemInfoSync()
export const innerWidth = screenWidth
export const innerHeight = screenHeight
export { devicePixelRatio }
export const screen = {
width: screenWidth,
height: screenHeight,
availWidth: innerWidth,
availHeight: innerHeight,
availLeft: 0,
availTop: 0,
}
export const performance = {
now: Date.now
};
export const ontouchstart = null;
export const ontouchmove = null;
export const ontouchend = null;

View File

@@ -0,0 +1,5 @@
export default function(file) {
const worker = tt.createWorker(file);
return worker;
};

View File

@@ -0,0 +1,170 @@
import EventTarget from './EventTarget.js'
const _url = new WeakMap()
const _method = new WeakMap()
const _requestHeader = new WeakMap()
const _responseHeader = new WeakMap()
const _requestTask = new WeakMap()
function _triggerEvent(type, ...args) {
if (typeof this[`on${type}`] === 'function') {
this[`on${type}`].apply(this, args)
}
}
function _changeReadyState(readyState) {
this.readyState = readyState
_triggerEvent.call(this, 'readystatechange')
}
export default class XMLHttpRequest extends EventTarget {
// TODO 没法模拟 HEADERS_RECEIVED 和 LOADING 两个状态
static UNSEND = 0
static OPENED = 1
static HEADERS_RECEIVED = 2
static LOADING = 3
static DONE = 4
/*
* TODO 这一批事件应该是在 XMLHttpRequestEventTarget.prototype 上面的
*/
onabort = null
onerror = null
onload = null
onloadstart = null
onprogress = null
ontimeout = null
onloadend = null
onreadystatechange = null
readyState = 0
response = null
responseText = null
responseType = ''
responseXML = null
status = 0
statusText = ''
upload = {}
withCredentials = false
constructor() {
super();
_requestHeader.set(this, {
'content-type': 'application/x-www-form-urlencoded'
})
_responseHeader.set(this, {})
}
abort() {
const myRequestTask = _requestTask.get(this)
if (myRequestTask) {
myRequestTask.abort()
}
}
getAllResponseHeaders() {
const responseHeader = _responseHeader.get(this)
return Object.keys(responseHeader).map((header) => {
return `${header}: ${responseHeader[header]}`
}).join('\n')
}
getResponseHeader(header) {
return _responseHeader.get(this)[header]
}
open(method, url/* async, user, password 这几个参数在小程序内不支持*/) {
_method.set(this, method)
_url.set(this, url)
_changeReadyState.call(this, XMLHttpRequest.OPENED)
}
overrideMimeType() {
}
send(data = '') {
if (this.readyState !== XMLHttpRequest.OPENED) {
throw new Error("Failed to execute 'send' on 'XMLHttpRequest': The object's state must be OPENED.")
} else {
let myRequestTask = tt.request({
data,
url: _url.get(this),
method: _method.get(this),
header: _requestHeader.get(this),
dataType: 'other',
responseType: this.responseType === 'arraybuffer' ? 'arraybuffer' : 'text',
success: ({ data, statusCode, header }) => {
this.status = statusCode
_responseHeader.set(this, header)
_triggerEvent.call(this, 'loadstart')
_changeReadyState.call(this, XMLHttpRequest.HEADERS_RECEIVED)
_changeReadyState.call(this, XMLHttpRequest.LOADING)
switch (this.responseType) {
case 'json':
this.responseText = data;
try {
this.response = JSON.parse(data);
}
catch (e) {
this.response = null;
}
break;
case '':
case 'text':
this.responseText = this.response = data;
break;
case 'arraybuffer':
this.response = data;
this.responseText = '';
var bytes = new Uint8Array(data);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
this.responseText += String.fromCharCode(bytes[i]);
}
break;
default:
this.response = null;
}
_changeReadyState.call(this, XMLHttpRequest.DONE)
_triggerEvent.call(this, 'load')
_triggerEvent.call(this, 'loadend')
},
fail: ({ errMsg }) => {
// TODO 规范错误
if (errMsg.indexOf('abort') !== -1) {
_triggerEvent.call(this, 'abort')
} else if (errMsg.indexOf('timeout') !== -1) {
_triggerEvent.call(this, 'timeout')
} else {
_triggerEvent.call(this, 'error', errMsg)
}
_triggerEvent.call(this, 'loadend')
}
})
_requestTask.set(this, myRequestTask);
}
}
setRequestHeader(header, value) {
const myHeader = _requestHeader.get(this)
myHeader[header] = value
_requestHeader.set(this, myHeader)
}
addEventListener(type, listener) {
if (typeof listener === 'function') {
let _this = this
let event = { target: _this }
this['on' + type] = function (event) {
listener.call(_this, event)
}
}
}
}

View File

@@ -0,0 +1,127 @@
import * as window from './window'
import HTMLElement from './HTMLElement'
import HTMLVideoElement from './HTMLVideoElement'
import Image from './Image'
import Audio from './Audio'
import Canvas from './Canvas'
import './EventIniter/index.js'
const events = {}
const document = {
readyState: 'complete',
visibilityState: 'visible',
documentElement: window,
hidden: false,
style: {},
location: window.location,
ontouchstart: null,
ontouchmove: null,
ontouchend: null,
head: new HTMLElement('head'),
body: new HTMLElement('body'),
createElement(tagName) {
if (tagName === 'canvas') {
return new Canvas()
} else if (tagName === 'audio') {
return new Audio()
} else if (tagName === 'img') {
return new Image()
}else if (tagName === 'video') {
return new HTMLVideoElement()
}
return new HTMLElement(tagName)
},
createElementNS(nameSpace, tagName) {
return this.createElement(tagName);
},
getElementById(id) {
if (id === window.canvas.id) {
return window.canvas
}
return null
},
getElementsByTagName(tagName) {
if (tagName === 'head') {
return [document.head]
} else if (tagName === 'body') {
return [document.body]
} else if (tagName === 'canvas') {
return [window.canvas]
}
return []
},
getElementsByName(tagName) {
if (tagName === 'head') {
return [document.head]
} else if (tagName === 'body') {
return [document.body]
} else if (tagName === 'canvas') {
return [window.canvas]
}
return []
},
querySelector(query) {
if (query === 'head') {
return document.head
} else if (query === 'body') {
return document.body
} else if (query === 'canvas') {
return window.canvas
} else if (query === `#${window.canvas.id}`) {
return window.canvas
}
return null
},
querySelectorAll(query) {
if (query === 'head') {
return [document.head]
} else if (query === 'body') {
return [document.body]
} else if (query === 'canvas') {
return [window.canvas]
}
return []
},
addEventListener(type, listener) {
if (!events[type]) {
events[type] = []
}
events[type].push(listener)
},
removeEventListener(type, listener) {
const listeners = events[type]
if (listeners && listeners.length > 0) {
for (let i = listeners.length; i--; i > 0) {
if (listeners[i] === listener) {
listeners.splice(i, 1)
break
}
}
}
},
dispatchEvent(event) {
const listeners = events[event.type]
if (listeners) {
for (let i = 0; i < listeners.length; i++) {
listeners[i](event)
}
}
}
}
export default document

View File

@@ -0,0 +1,55 @@
import * as _window from './window'
import document from './document'
import HTMLElement from './HTMLElement'
const global = GameGlobal
function inject () {
_window.document = document;
_window.addEventListener = (type, listener) => {
_window.document.addEventListener(type, listener)
}
_window.removeEventListener = (type, listener) => {
_window.document.removeEventListener(type, listener)
}
_window.dispatchEvent = _window.document.dispatchEvent;
const { platform } = tt.getSystemInfoSync()
// 开发者工具无法重定义 window
if (typeof __devtoolssubcontext === 'undefined' && platform === 'devtools') {
for (const key in _window) {
const descriptor = Object.getOwnPropertyDescriptor(global, key)
if (!descriptor || descriptor.configurable === true) {
Object.defineProperty(window, key, {
value: _window[key]
})
}
}
for (const key in _window.document) {
const descriptor = Object.getOwnPropertyDescriptor(global.document, key)
if (!descriptor || descriptor.configurable === true) {
Object.defineProperty(global.document, key, {
value: _window.document[key]
})
}
}
window.parent = window
} else {
for (const key in _window) {
global[key] = _window[key]
}
global.window = _window
window = global
window.top = window.parent = window
}
}
if (!GameGlobal.__isAdapterInjected) {
GameGlobal.__isAdapterInjected = true
inject()
}

View File

@@ -0,0 +1,31 @@
const localStorage = {
get length() {
const { keys } = tt.getStorageInfoSync()
return keys.length
},
key(n) {
const { keys } = tt.getStorageInfoSync()
return keys[n]
},
getItem(key) {
return tt.getStorageSync(key)
},
setItem(key, value) {
return tt.setStorageSync(key, value)
},
removeItem(key) {
tt.removeStorageSync(key)
},
clear() {
tt.clearStorageSync()
}
}
export default localStorage

View File

@@ -0,0 +1,7 @@
const location = {
href: 'game.js',
reload() {
}
}
export default location

View File

@@ -0,0 +1,38 @@
import { noop } from './util/index.js'
// TODO 需要 tt.getSystemInfo 获取更详细信息
const systemInfo = tt.getSystemInfoSync()
console.log(systemInfo)
const system = systemInfo.system;
const platform = systemInfo.platform;
const language = systemInfo.language;
const version = systemInfo.version;
const android = system.toLowerCase().indexOf('android') !== -1;
const uaDesc = android ? `Android; CPU ${system}` : `iPhone; CPU iPhone OS ${system} like Mac OS X`;
const ua = `Mozilla/5.0 (${uaDesc}) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E8301 MicroMessenger/${version} MiniGame NetType/WIFI Language/${language}`;
const navigator = {
platform,
language: language,
appVersion: `5.0 (${uaDesc}) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1`,
userAgent: ua,
onLine: true, // TODO 用 tt.getNetworkStateChange 和 tt.onNetworkStateChange 来返回真实的状态
// TODO 用 tt.getLocation 来封装 geolocation
geolocation: {
getCurrentPosition: noop,
watchPosition: noop,
clearWatch: noop
}
}
if (tt.onNetworkStatusChange) {
tt.onNetworkStatusChange(function(event){
navigator.onLine = event.isConnected;
});
}
export default navigator

View File

@@ -0,0 +1 @@
export function noop() {}

View File

@@ -0,0 +1,41 @@
import Canvas from './Canvas'
export navigator from './navigator'
export XMLHttpRequest from './XMLHttpRequest'
export WebSocket from './WebSocket'
export Image from './Image'
export ImageBitmap from './ImageBitmap'
export Audio from './Audio'
export FileReader from './FileReader'
export HTMLElement from './HTMLElement'
export HTMLImageElement from './HTMLImageElement'
export HTMLCanvasElement from './HTMLCanvasElement'
export HTMLMediaElement from './HTMLMediaElement'
export HTMLAudioElement from './HTMLAudioElement'
export HTMLVideoElement from './HTMLVideoElement'
export WebGLRenderingContext from './WebGLRenderingContext'
export { TouchEvent, MouseEvent, DeviceMotionEvent } from './EventIniter/index.js'
export localStorage from './localStorage'
export location from './location'
export * from './WindowProperties'
// 暴露全局的 canvas
GameGlobal.screencanvas = GameGlobal.screencanvas || new Canvas()
const canvas = GameGlobal.screencanvas;
const {
setTimeout,
setInterval,
clearTimeout,
clearInterval,
requestAnimationFrame,
cancelAnimationFrame,
} = GameGlobal;
export { canvas }
export { setTimeout }
export { setInterval }
export { clearTimeout }
export { clearInterval }
export { requestAnimationFrame }
export { cancelAnimationFrame }