From 03abe28d8c97bdc646d04cb2d022529ea8b9899d Mon Sep 17 00:00:00 2001 From: zhangyan Date: Thu, 19 Mar 2026 15:58:48 +0800 Subject: [PATCH] feat: initialize mini program map engine --- .gitignore | 5 + miniprogram/app.json | 15 + miniprogram/app.ts | 18 + miniprogram/app.wxss | 10 + miniprogram/engine/camera/camera.ts | 98 + miniprogram/engine/layer/gpsLayer.ts | 51 + miniprogram/engine/layer/mapLayer.ts | 15 + miniprogram/engine/layer/tileLayer.ts | 124 + miniprogram/engine/layer/trackLayer.ts | 61 + miniprogram/engine/map/mapEngine.ts | 1429 ++ .../engine/renderer/canvasMapRenderer.ts | 247 + .../engine/renderer/canvasOverlayRenderer.ts | 67 + miniprogram/engine/renderer/mapRenderer.ts | 44 + .../engine/renderer/tilePersistentCache.ts | 213 + .../engine/renderer/webglMapRenderer.ts | 161 + .../engine/renderer/webglTileRenderer.ts | 329 + .../engine/renderer/webglVectorRenderer.ts | 234 + .../engine/sensor/compassHeadingController.ts | 206 + miniprogram/engine/tile/tileStore.ts | 567 + miniprogram/pages/index/index.json | 4 + miniprogram/pages/index/index.ts | 54 + miniprogram/pages/index/index.wxml | 27 + miniprogram/pages/index/index.wxss | 62 + miniprogram/pages/logs/logs.json | 4 + miniprogram/pages/logs/logs.ts | 21 + miniprogram/pages/logs/logs.wxml | 6 + miniprogram/pages/logs/logs.wxss | 16 + miniprogram/pages/map/map.json | 4 + miniprogram/pages/map/map.ts | 173 + miniprogram/pages/map/map.wxml | 152 + miniprogram/pages/map/map.wxss | 337 + miniprogram/utils/projection.ts | 61 + miniprogram/utils/tile.ts | 61 + miniprogram/utils/util.ts | 19 + package-lock.json | 37 + package.json | 16 + project.config.json | 50 + readme.md | 207 + tsconfig.json | 33 + typings/index.d.ts | 8 + typings/types/index.d.ts | 1 + typings/types/wx/index.d.ts | 74 + typings/types/wx/lib.wx.api.d.ts | 19671 ++++++++++++++++ typings/types/wx/lib.wx.app.d.ts | 270 + typings/types/wx/lib.wx.behavior.d.ts | 68 + typings/types/wx/lib.wx.cloud.d.ts | 924 + typings/types/wx/lib.wx.component.d.ts | 636 + typings/types/wx/lib.wx.event.d.ts | 1435 ++ typings/types/wx/lib.wx.page.d.ts | 259 + 49 files changed, 28584 insertions(+) create mode 100644 .gitignore create mode 100644 miniprogram/app.json create mode 100644 miniprogram/app.ts create mode 100644 miniprogram/app.wxss create mode 100644 miniprogram/engine/camera/camera.ts create mode 100644 miniprogram/engine/layer/gpsLayer.ts create mode 100644 miniprogram/engine/layer/mapLayer.ts create mode 100644 miniprogram/engine/layer/tileLayer.ts create mode 100644 miniprogram/engine/layer/trackLayer.ts create mode 100644 miniprogram/engine/map/mapEngine.ts create mode 100644 miniprogram/engine/renderer/canvasMapRenderer.ts create mode 100644 miniprogram/engine/renderer/canvasOverlayRenderer.ts create mode 100644 miniprogram/engine/renderer/mapRenderer.ts create mode 100644 miniprogram/engine/renderer/tilePersistentCache.ts create mode 100644 miniprogram/engine/renderer/webglMapRenderer.ts create mode 100644 miniprogram/engine/renderer/webglTileRenderer.ts create mode 100644 miniprogram/engine/renderer/webglVectorRenderer.ts create mode 100644 miniprogram/engine/sensor/compassHeadingController.ts create mode 100644 miniprogram/engine/tile/tileStore.ts create mode 100644 miniprogram/pages/index/index.json create mode 100644 miniprogram/pages/index/index.ts create mode 100644 miniprogram/pages/index/index.wxml create mode 100644 miniprogram/pages/index/index.wxss create mode 100644 miniprogram/pages/logs/logs.json create mode 100644 miniprogram/pages/logs/logs.ts create mode 100644 miniprogram/pages/logs/logs.wxml create mode 100644 miniprogram/pages/logs/logs.wxss create mode 100644 miniprogram/pages/map/map.json create mode 100644 miniprogram/pages/map/map.ts create mode 100644 miniprogram/pages/map/map.wxml create mode 100644 miniprogram/pages/map/map.wxss create mode 100644 miniprogram/utils/projection.ts create mode 100644 miniprogram/utils/tile.ts create mode 100644 miniprogram/utils/util.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 project.config.json create mode 100644 readme.md create mode 100644 tsconfig.json create mode 100644 typings/index.d.ts create mode 100644 typings/types/index.d.ts create mode 100644 typings/types/wx/index.d.ts create mode 100644 typings/types/wx/lib.wx.api.d.ts create mode 100644 typings/types/wx/lib.wx.app.d.ts create mode 100644 typings/types/wx/lib.wx.behavior.d.ts create mode 100644 typings/types/wx/lib.wx.cloud.d.ts create mode 100644 typings/types/wx/lib.wx.component.d.ts create mode 100644 typings/types/wx/lib.wx.event.d.ts create mode 100644 typings/types/wx/lib.wx.page.d.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35de4ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +.tmp-ts/ +project.private.config.json +.DS_Store +Thumbs.db diff --git a/miniprogram/app.json b/miniprogram/app.json new file mode 100644 index 0000000..8b2af6d --- /dev/null +++ b/miniprogram/app.json @@ -0,0 +1,15 @@ +{ + "pages": [ + "pages/map/map", + "pages/index/index", + "pages/logs/logs" + ], + "window": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "CMR Mini", + "navigationBarBackgroundColor": "#ffffff" + }, + "style": "v2", + "componentFramework": "glass-easel", + "lazyCodeLoading": "requiredComponents" +} diff --git a/miniprogram/app.ts b/miniprogram/app.ts new file mode 100644 index 0000000..1af73a8 --- /dev/null +++ b/miniprogram/app.ts @@ -0,0 +1,18 @@ +// app.ts +App({ + globalData: {}, + onLaunch() { + // 展示本地存储能力 + const logs = wx.getStorageSync('logs') || [] + logs.unshift(Date.now()) + wx.setStorageSync('logs', logs) + + // 登录 + wx.login({ + success: res => { + console.log(res.code) + // 发送 res.code 到后台换取 openId, sessionKey, unionId + }, + }) + }, +}) \ No newline at end of file diff --git a/miniprogram/app.wxss b/miniprogram/app.wxss new file mode 100644 index 0000000..06c6fc9 --- /dev/null +++ b/miniprogram/app.wxss @@ -0,0 +1,10 @@ +/**app.wxss**/ +.container { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + padding: 200rpx 0; + box-sizing: border-box; +} diff --git a/miniprogram/engine/camera/camera.ts b/miniprogram/engine/camera/camera.ts new file mode 100644 index 0000000..4baf3fd --- /dev/null +++ b/miniprogram/engine/camera/camera.ts @@ -0,0 +1,98 @@ +export interface CameraState { + centerWorldX: number + centerWorldY: number + viewportWidth: number + viewportHeight: number + visibleColumns: number + translateX?: number + translateY?: number + rotationRad?: number +} + +export interface ScreenPoint { + x: number + y: number +} + +export interface WorldPoint { + x: number + y: number +} + +export function getTileSizePx(camera: CameraState): number { + if (!camera.viewportWidth || !camera.visibleColumns) { + return 0 + } + + return camera.viewportWidth / camera.visibleColumns +} + +export function rotateScreenPoint(point: ScreenPoint, centerX: number, centerY: number, rotationRad: number): ScreenPoint { + if (!rotationRad) { + return point + } + + const deltaX = point.x - centerX + const deltaY = point.y - centerY + const cos = Math.cos(rotationRad) + const sin = Math.sin(rotationRad) + + return { + x: centerX + deltaX * cos - deltaY * sin, + y: centerY + deltaX * sin + deltaY * cos, + } +} + +export function worldToScreen( + camera: CameraState, + world: WorldPoint, + includeTranslate = false, +): ScreenPoint { + const tileSize = getTileSizePx(camera) + const translateX = includeTranslate ? (camera.translateX || 0) : 0 + const translateY = includeTranslate ? (camera.translateY || 0) : 0 + const centerX = camera.viewportWidth / 2 + const centerY = camera.viewportHeight / 2 + + const rotated = rotateScreenPoint( + { + x: centerX + (world.x - camera.centerWorldX) * tileSize, + y: centerY + (world.y - camera.centerWorldY) * tileSize, + }, + centerX, + centerY, + camera.rotationRad || 0, + ) + + return { + x: rotated.x + translateX, + y: rotated.y + translateY, + } +} + +export function screenToWorld( + camera: CameraState, + screen: ScreenPoint, + includeTranslate = true, +): WorldPoint { + const tileSize = getTileSizePx(camera) + const translateX = includeTranslate ? (camera.translateX || 0) : 0 + const translateY = includeTranslate ? (camera.translateY || 0) : 0 + const centerX = camera.viewportWidth / 2 + const centerY = camera.viewportHeight / 2 + + const unrotated = rotateScreenPoint( + { + x: screen.x - translateX, + y: screen.y - translateY, + }, + centerX, + centerY, + -(camera.rotationRad || 0), + ) + + return { + x: camera.centerWorldX + (unrotated.x - centerX) / tileSize, + y: camera.centerWorldY + (unrotated.y - centerY) / tileSize, + } +} diff --git a/miniprogram/engine/layer/gpsLayer.ts b/miniprogram/engine/layer/gpsLayer.ts new file mode 100644 index 0000000..5c78d71 --- /dev/null +++ b/miniprogram/engine/layer/gpsLayer.ts @@ -0,0 +1,51 @@ +import { type CameraState } from '../camera/camera' +import { lonLatToWorldTile } from '../../utils/projection' +import { worldToScreen } from '../camera/camera' +import { type MapLayer, type LayerRenderContext } from './mapLayer' +import { type MapScene } from '../renderer/mapRenderer' +import { type ScreenPoint } from './trackLayer' + +export class GpsLayer implements MapLayer { + projectPoint(scene: MapScene, camera: CameraState): ScreenPoint { + const worldPoint = lonLatToWorldTile(scene.gpsPoint, scene.zoom) + const screenPoint = worldToScreen(camera, worldPoint, false) + return { + x: screenPoint.x + scene.translateX, + y: screenPoint.y + scene.translateY, + } + } + + getPulseRadius(pulseFrame: number): number { + return 18 + 6 * (0.5 + 0.5 * Math.sin(pulseFrame / 6)) + } + + draw(context: LayerRenderContext): void { + const { ctx, camera, scene, pulseFrame } = context + const gpsScreenPoint = this.projectPoint(scene, camera) + const pulse = this.getPulseRadius(pulseFrame) + + ctx.save() + ctx.beginPath() + ctx.fillStyle = 'rgba(33, 158, 188, 0.22)' + ctx.arc(gpsScreenPoint.x, gpsScreenPoint.y, pulse, 0, Math.PI * 2) + ctx.fill() + + ctx.beginPath() + ctx.fillStyle = '#21a1bc' + ctx.arc(gpsScreenPoint.x, gpsScreenPoint.y, 9, 0, Math.PI * 2) + ctx.fill() + + ctx.beginPath() + ctx.strokeStyle = '#ffffff' + ctx.lineWidth = 3 + ctx.arc(gpsScreenPoint.x, gpsScreenPoint.y, 13, 0, Math.PI * 2) + ctx.stroke() + + ctx.fillStyle = '#0b3d4a' + ctx.font = 'bold 16px sans-serif' + ctx.textAlign = 'left' + ctx.textBaseline = 'bottom' + ctx.fillText('GPS', gpsScreenPoint.x + 14, gpsScreenPoint.y - 12) + ctx.restore() + } +} diff --git a/miniprogram/engine/layer/mapLayer.ts b/miniprogram/engine/layer/mapLayer.ts new file mode 100644 index 0000000..4db5d54 --- /dev/null +++ b/miniprogram/engine/layer/mapLayer.ts @@ -0,0 +1,15 @@ +import { type CameraState } from '../camera/camera' +import { type MapScene } from '../renderer/mapRenderer' +import { type TileStore } from '../tile/tileStore' + +export interface LayerRenderContext { + ctx: any + camera: CameraState + scene: MapScene + pulseFrame: number + tileStore: TileStore +} + +export interface MapLayer { + draw(context: LayerRenderContext): void +} diff --git a/miniprogram/engine/layer/tileLayer.ts b/miniprogram/engine/layer/tileLayer.ts new file mode 100644 index 0000000..54dacbe --- /dev/null +++ b/miniprogram/engine/layer/tileLayer.ts @@ -0,0 +1,124 @@ +import { createTileGrid, type TileItem } from '../../utils/tile' +import { getTileSizePx, type CameraState } from '../camera/camera' +import { type MapScene } from '../renderer/mapRenderer' +import { type TileStore } from '../tile/tileStore' +import { type MapLayer, type LayerRenderContext } from './mapLayer' + +function buildGridKey(scene: MapScene, tileSize: number): string { + return [ + scene.tileSource, + scene.zoom, + scene.centerTileX, + scene.centerTileY, + scene.viewportWidth, + scene.viewportHeight, + tileSize, + scene.overdraw, + ].join('|') +} + +export class TileLayer implements MapLayer { + lastVisibleTileCount: number + lastReadyTileCount: number + cachedGridKey: string + cachedTiles: TileItem[] + + constructor() { + this.lastVisibleTileCount = 0 + this.lastReadyTileCount = 0 + this.cachedGridKey = '' + this.cachedTiles = [] + } + + prepareTiles(scene: MapScene, camera: CameraState, tileStore: TileStore): TileItem[] { + const tileSize = getTileSizePx(camera) + if (!tileSize) { + this.lastVisibleTileCount = 0 + this.lastReadyTileCount = 0 + this.cachedGridKey = '' + this.cachedTiles = [] + return [] + } + + const gridKey = buildGridKey(scene, tileSize) + if (gridKey !== this.cachedGridKey) { + this.cachedGridKey = gridKey + this.cachedTiles = createTileGrid({ + urlTemplate: scene.tileSource, + zoom: scene.zoom, + centerTileX: scene.centerTileX, + centerTileY: scene.centerTileY, + viewportWidth: scene.viewportWidth, + viewportHeight: scene.viewportHeight, + tileSize, + overdraw: scene.overdraw, + }) + } + + tileStore.queueVisibleTiles(this.cachedTiles, scene, gridKey) + this.lastVisibleTileCount = this.cachedTiles.length + this.lastReadyTileCount = 0 + return this.cachedTiles + } + + draw(context: LayerRenderContext): void { + const { ctx, scene, camera, tileStore } = context + const tiles = this.prepareTiles(scene, camera, tileStore) + + for (const tile of tiles) { + const entry = tileStore.getEntry(tile.url) + const drawLeft = tile.leftPx + scene.translateX + const drawTop = tile.topPx + scene.translateY + + if (entry && entry.status === 'ready' && entry.image) { + ctx.drawImage(entry.image, drawLeft, drawTop, tile.sizePx, tile.sizePx) + this.lastReadyTileCount += 1 + continue + } + + const parentFallback = tileStore.getParentFallbackSlice(tile, scene) + let drewFallback = false + if (parentFallback) { + ctx.drawImage( + parentFallback.entry.image, + parentFallback.sourceX, + parentFallback.sourceY, + parentFallback.sourceWidth, + parentFallback.sourceHeight, + drawLeft, + drawTop, + tile.sizePx, + tile.sizePx, + ) + drewFallback = true + } + + const childFallback = tileStore.getChildFallback(tile, scene) + if (childFallback) { + const cellWidth = tile.sizePx / childFallback.division + const cellHeight = tile.sizePx / childFallback.division + for (const child of childFallback.children) { + const childImageWidth = child.entry.image.width || 256 + const childImageHeight = child.entry.image.height || 256 + ctx.drawImage( + child.entry.image, + 0, + 0, + childImageWidth, + childImageHeight, + drawLeft + child.offsetX * cellWidth, + drawTop + child.offsetY * cellHeight, + cellWidth, + cellHeight, + ) + } + drewFallback = true + } + + if (!drewFallback) { + ctx.fillStyle = entry && entry.status === 'error' ? '#d9b2b2' : '#dbeed4' + ctx.fillRect(drawLeft, drawTop, tile.sizePx, tile.sizePx) + } + } + } +} diff --git a/miniprogram/engine/layer/trackLayer.ts b/miniprogram/engine/layer/trackLayer.ts new file mode 100644 index 0000000..fcb58ca --- /dev/null +++ b/miniprogram/engine/layer/trackLayer.ts @@ -0,0 +1,61 @@ +import { type CameraState } from '../camera/camera' +import { lonLatToWorldTile } from '../../utils/projection' +import { worldToScreen } from '../camera/camera' +import { type MapLayer, type LayerRenderContext } from './mapLayer' +import { type MapScene } from '../renderer/mapRenderer' + +export interface ScreenPoint { + x: number + y: number +} + +export class TrackLayer implements MapLayer { + projectPoints(scene: MapScene, camera: CameraState): ScreenPoint[] { + return scene.track.map((point) => { + const worldPoint = lonLatToWorldTile(point, scene.zoom) + const screenPoint = worldToScreen(camera, worldPoint, false) + return { + x: screenPoint.x + scene.translateX, + y: screenPoint.y + scene.translateY, + } + }) + } + + draw(context: LayerRenderContext): void { + const { ctx, camera, scene } = context + const points = this.projectPoints(scene, camera) + + ctx.save() + ctx.lineCap = 'round' + ctx.lineJoin = 'round' + ctx.strokeStyle = 'rgba(23, 109, 93, 0.96)' + ctx.lineWidth = 6 + ctx.beginPath() + + points.forEach((screenPoint, index) => { + if (index === 0) { + ctx.moveTo(screenPoint.x, screenPoint.y) + return + } + ctx.lineTo(screenPoint.x, screenPoint.y) + }) + ctx.stroke() + + ctx.fillStyle = '#f7fbf2' + ctx.strokeStyle = '#176d5d' + ctx.lineWidth = 4 + points.forEach((screenPoint, index) => { + ctx.beginPath() + ctx.arc(screenPoint.x, screenPoint.y, 10, 0, Math.PI * 2) + ctx.fill() + ctx.stroke() + ctx.fillStyle = '#176d5d' + ctx.font = 'bold 14px sans-serif' + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + ctx.fillText(String(index + 1), screenPoint.x, screenPoint.y) + ctx.fillStyle = '#f7fbf2' + }) + ctx.restore() + } +} diff --git a/miniprogram/engine/map/mapEngine.ts b/miniprogram/engine/map/mapEngine.ts new file mode 100644 index 0000000..11cf138 --- /dev/null +++ b/miniprogram/engine/map/mapEngine.ts @@ -0,0 +1,1429 @@ +import { getTileSizePx, screenToWorld, worldToScreen, type CameraState } from '../camera/camera' +import { CompassHeadingController } from '../sensor/compassHeadingController' +import { WebGLMapRenderer } from '../renderer/webglMapRenderer' +import { type MapRendererStats } from '../renderer/mapRenderer' +import { worldTileToLonLat, type LonLatPoint } from '../../utils/projection' + +const RENDER_MODE = 'Single WebGL Pipeline' +const PROJECTION_MODE = 'WGS84 -> WorldTile -> Camera -> Screen' +const MAP_NORTH_OFFSET_DEG = 0 +const MIN_ZOOM = 15 +const MAX_ZOOM = 20 +const DEFAULT_ZOOM = 17 +const DESIRED_VISIBLE_COLUMNS = 3 +const OVERDRAW = 1 +const DEFAULT_TOP_LEFT_TILE_X = 108132 +const DEFAULT_TOP_LEFT_TILE_Y = 51199 +const DEFAULT_CENTER_TILE_X = DEFAULT_TOP_LEFT_TILE_X + 1 +const DEFAULT_CENTER_TILE_Y = DEFAULT_TOP_LEFT_TILE_Y + 1 +const TILE_SOURCE = 'https://oss-mbh5.colormaprun.com/wxMap/lcx/{z}/{x}/{y}.png' +const MIN_PREVIEW_SCALE = 0.55 +const MAX_PREVIEW_SCALE = 1.85 +const INERTIA_FRAME_MS = 16 +const INERTIA_DECAY = 0.92 +const INERTIA_MIN_SPEED = 0.02 +const PREVIEW_RESET_DURATION_MS = 140 +const UI_SYNC_INTERVAL_MS = 80 +const ROTATE_STEP_DEG = 15 +const AUTO_ROTATE_FRAME_MS = 12 +const AUTO_ROTATE_EASE = 0.2 +const AUTO_ROTATE_SNAP_DEG = 0.1 +const AUTO_ROTATE_DEADZONE_DEG = 0.35 +const AUTO_ROTATE_MAX_STEP_DEG = 1.35 +const AUTO_ROTATE_HEADING_SMOOTHING = 0.32 + +const SAMPLE_TRACK_WGS84: LonLatPoint[] = [ + worldTileToLonLat({ x: DEFAULT_CENTER_TILE_X - 0.72, y: DEFAULT_CENTER_TILE_Y + 0.44 }, DEFAULT_ZOOM), + worldTileToLonLat({ x: DEFAULT_CENTER_TILE_X - 0.18, y: DEFAULT_CENTER_TILE_Y + 0.08 }, DEFAULT_ZOOM), + worldTileToLonLat({ x: DEFAULT_CENTER_TILE_X + 0.22, y: DEFAULT_CENTER_TILE_Y - 0.16 }, DEFAULT_ZOOM), + worldTileToLonLat({ x: DEFAULT_CENTER_TILE_X + 0.64, y: DEFAULT_CENTER_TILE_Y - 0.52 }, DEFAULT_ZOOM), +] +const SAMPLE_GPS_WGS84: LonLatPoint = worldTileToLonLat( + { x: DEFAULT_CENTER_TILE_X + 0.12, y: DEFAULT_CENTER_TILE_Y - 0.06 }, + DEFAULT_ZOOM, +) + +type TouchPoint = WechatMiniprogram.TouchDetail + +type GestureMode = 'idle' | 'pan' | 'pinch' +type RotationMode = 'manual' | 'auto' +type OrientationMode = 'manual' | 'north-up' | 'heading-up' +type AutoRotateSourceMode = 'sensor' | 'course' | 'fusion' + +export interface MapEngineStageRect { + width: number + height: number + left: number + top: number +} + +export interface MapEngineViewState { + buildVersion: string + renderMode: string + projectionMode: string + mapReady: boolean + mapReadyText: string + mapName: string + zoom: number + rotationDeg: number + rotationText: string + rotationMode: RotationMode + rotationModeText: string + rotationToggleText: string + orientationMode: OrientationMode + orientationModeText: string + sensorHeadingText: string + autoRotateSourceText: string + autoRotateCalibrationText: string + northReferenceText: string + compassNeedleDeg: number + centerTileX: number + centerTileY: number + centerText: string + tileSource: string + visibleColumnCount: number + visibleTileCount: number + readyTileCount: number + memoryTileCount: number + diskTileCount: number + memoryHitCount: number + diskHitCount: number + networkFetchCount: number + cacheHitRateText: string + tileTranslateX: number + tileTranslateY: number + tileSizePx: number + stageWidth: number + stageHeight: number + stageLeft: number + stageTop: number + statusText: string +} + +export interface MapEngineCallbacks { + onData: (patch: Partial) => void +} + +const VIEW_SYNC_KEYS: Array = [ + 'buildVersion', + 'renderMode', + 'projectionMode', + 'mapReady', + 'mapReadyText', + 'mapName', + 'zoom', + 'rotationDeg', + 'rotationText', + 'rotationMode', + 'rotationModeText', + 'rotationToggleText', + 'orientationMode', + 'orientationModeText', + 'sensorHeadingText', + 'autoRotateSourceText', + 'autoRotateCalibrationText', + 'northReferenceText', + 'compassNeedleDeg', + 'centerText', + 'tileSource', + 'visibleTileCount', + 'readyTileCount', + 'memoryTileCount', + 'diskTileCount', + 'memoryHitCount', + 'diskHitCount', + 'networkFetchCount', + 'cacheHitRateText', + 'tileSizePx', + 'statusText', +] + +function buildCenterText(zoom: number, x: number, y: number): string { + return `z${zoom} / x${x} / y${y}` +} + +function clamp(value: number, min: number, max: number): number { + return Math.max(min, Math.min(max, value)) +} + +function normalizeRotationDeg(rotationDeg: number): number { + const normalized = rotationDeg % 360 + return normalized < 0 ? normalized + 360 : normalized +} + +function normalizeAngleDeltaRad(angleDeltaRad: number): number { + let normalized = angleDeltaRad + + while (normalized > Math.PI) { + normalized -= Math.PI * 2 + } + + while (normalized < -Math.PI) { + normalized += Math.PI * 2 + } + + return normalized +} + +function normalizeAngleDeltaDeg(angleDeltaDeg: number): number { + let normalized = angleDeltaDeg + + while (normalized > 180) { + normalized -= 360 + } + + while (normalized < -180) { + normalized += 360 + } + + return normalized +} + +function interpolateAngleDeg(currentDeg: number, targetDeg: number, factor: number): number { + return normalizeRotationDeg(currentDeg + normalizeAngleDeltaDeg(targetDeg - currentDeg) * factor) +} + +function formatRotationText(rotationDeg: number): string { + return `${Math.round(normalizeRotationDeg(rotationDeg))}deg` +} + +function formatHeadingText(headingDeg: number | null): string { + if (headingDeg === null) { + return '--' + } + + return `${Math.round(normalizeRotationDeg(headingDeg))}deg` +} + +function formatOrientationModeText(mode: OrientationMode): string { + if (mode === 'north-up') { + return 'North Up' + } + + if (mode === 'heading-up') { + return 'Heading Up' + } + + return 'Manual Gesture' +} + +function formatRotationModeText(mode: OrientationMode): string { + return formatOrientationModeText(mode) +} + +function formatRotationToggleText(mode: OrientationMode): string { + if (mode === 'manual') { + return '切到北朝上' + } + + if (mode === 'north-up') { + return '切到朝向朝上' + } + + return '切到手动旋转' +} + +function formatAutoRotateSourceText(mode: AutoRotateSourceMode, hasCourseHeading: boolean): string { + if (mode === 'sensor') { + return 'Sensor Only' + } + + if (mode === 'course') { + return hasCourseHeading ? 'Course Only' : 'Course Pending' + } + + return hasCourseHeading ? 'Sensor + Course' : 'Sensor Only' +} + +function formatAutoRotateCalibrationText(pending: boolean, offsetDeg: number | null): string { + if (pending) { + return 'Pending' + } + + if (offsetDeg === null) { + return '--' + } + + return `Offset ${Math.round(normalizeRotationDeg(offsetDeg))}deg` +} + +function formatNorthReferenceText(): string { + return 'Map North = 0deg (TFW aligned)' +} + +function formatCompassNeedleDeg(headingDeg: number | null): number { + if (headingDeg === null) { + return 0 + } + + return normalizeRotationDeg(360 - headingDeg) +} + +function formatCacheHitRate(memoryHitCount: number, diskHitCount: number, networkFetchCount: number): string { + const total = memoryHitCount + diskHitCount + networkFetchCount + if (!total) { + return '--' + } + + const hitRate = ((memoryHitCount + diskHitCount) / total) * 100 + return `${Math.round(hitRate)}%` +} +export class MapEngine { + buildVersion: string + renderer: WebGLMapRenderer + compassController: CompassHeadingController + onData: (patch: Partial) => void + state: MapEngineViewState + previewScale: number + previewOriginX: number + previewOriginY: number + panLastX: number + panLastY: number + panLastTimestamp: number + panVelocityX: number + panVelocityY: number + pinchStartDistance: number + pinchStartScale: number + pinchStartAngle: number + pinchStartRotationDeg: number + pinchAnchorWorldX: number + pinchAnchorWorldY: number + gestureMode: GestureMode + inertiaTimer: number + previewResetTimer: number + viewSyncTimer: number + autoRotateTimer: number + pendingViewPatch: Partial + mounted: boolean + sensorHeadingDeg: number | null + smoothedSensorHeadingDeg: number | null + autoRotateHeadingDeg: number | null + courseHeadingDeg: number | null + targetAutoRotationDeg: number | null + autoRotateSourceMode: AutoRotateSourceMode + autoRotateCalibrationOffsetDeg: number | null + autoRotateCalibrationPending: boolean + + constructor(buildVersion: string, callbacks: MapEngineCallbacks) { + this.buildVersion = buildVersion + this.onData = callbacks.onData + this.renderer = new WebGLMapRenderer( + (stats) => { + this.applyStats(stats) + }, + (message) => { + this.setState({ + statusText: `${message} (${this.buildVersion})`, + }) + }, + ) + this.compassController = new CompassHeadingController({ + onHeading: (headingDeg) => { + this.handleCompassHeading(headingDeg) + }, + onError: (message) => { + this.handleCompassError(message) + }, + }) + this.state = { + buildVersion: this.buildVersion, + renderMode: RENDER_MODE, + projectionMode: PROJECTION_MODE, + mapReady: false, + mapReadyText: 'BOOTING', + mapName: 'LCX 测试地图', + zoom: DEFAULT_ZOOM, + rotationDeg: 0, + rotationText: formatRotationText(0), + rotationMode: 'manual', + rotationModeText: formatRotationModeText('manual'), + rotationToggleText: formatRotationToggleText('manual'), + orientationMode: 'manual', + orientationModeText: formatOrientationModeText('manual'), + sensorHeadingText: '--', + autoRotateSourceText: formatAutoRotateSourceText('fusion', false), + autoRotateCalibrationText: formatAutoRotateCalibrationText(false, MAP_NORTH_OFFSET_DEG), + northReferenceText: formatNorthReferenceText(), + compassNeedleDeg: 0, + centerTileX: DEFAULT_CENTER_TILE_X, + centerTileY: DEFAULT_CENTER_TILE_Y, + centerText: buildCenterText(DEFAULT_ZOOM, DEFAULT_CENTER_TILE_X, DEFAULT_CENTER_TILE_Y), + tileSource: TILE_SOURCE, + visibleColumnCount: DESIRED_VISIBLE_COLUMNS, + visibleTileCount: 0, + readyTileCount: 0, + memoryTileCount: 0, + diskTileCount: 0, + memoryHitCount: 0, + diskHitCount: 0, + networkFetchCount: 0, + cacheHitRateText: '--', + tileTranslateX: 0, + tileTranslateY: 0, + tileSizePx: 0, + stageWidth: 0, + stageHeight: 0, + stageLeft: 0, + stageTop: 0, + statusText: `单 WebGL 管线已准备接入方向传感器 (${this.buildVersion})`, + } + this.previewScale = 1 + this.previewOriginX = 0 + this.previewOriginY = 0 + this.panLastX = 0 + this.panLastY = 0 + this.panLastTimestamp = 0 + this.panVelocityX = 0 + this.panVelocityY = 0 + this.pinchStartDistance = 0 + this.pinchStartScale = 1 + this.pinchStartAngle = 0 + this.pinchStartRotationDeg = 0 + this.pinchAnchorWorldX = 0 + this.pinchAnchorWorldY = 0 + this.gestureMode = 'idle' + this.inertiaTimer = 0 + this.previewResetTimer = 0 + this.viewSyncTimer = 0 + this.autoRotateTimer = 0 + this.pendingViewPatch = {} + this.mounted = false + this.sensorHeadingDeg = null + this.smoothedSensorHeadingDeg = null + this.autoRotateHeadingDeg = null + this.courseHeadingDeg = null + this.targetAutoRotationDeg = null + this.autoRotateSourceMode = 'fusion' + this.autoRotateCalibrationOffsetDeg = MAP_NORTH_OFFSET_DEG + this.autoRotateCalibrationPending = false + } + + getInitialData(): MapEngineViewState { + return { ...this.state } + } + + destroy(): void { + this.clearInertiaTimer() + this.clearPreviewResetTimer() + this.clearViewSyncTimer() + this.clearAutoRotateTimer() + this.compassController.destroy() + this.renderer.destroy() + this.mounted = false + } + + setStage(rect: MapEngineStageRect): void { + this.previewScale = 1 + this.previewOriginX = rect.width / 2 + this.previewOriginY = rect.height / 2 + this.commitViewport( + { + stageWidth: rect.width, + stageHeight: rect.height, + stageLeft: rect.left, + stageTop: rect.top, + }, + `地图视口与 WebGL 引擎已对齐 (${this.buildVersion})`, + true, + ) + } + + attachCanvas(canvasNode: any, width: number, height: number, dpr: number): void { + this.renderer.attachCanvas(canvasNode, width, height, dpr) + this.mounted = true + this.state.mapReady = true + this.state.mapReadyText = 'READY' + this.onData({ + mapReady: true, + mapReadyText: 'READY', + statusText: `单 WebGL 管线已就绪,可切换手动或自动朝向 (${this.buildVersion})`, + }) + this.syncRenderer() + this.compassController.start() + } + + handleTouchStart(event: WechatMiniprogram.TouchEvent): void { + this.clearInertiaTimer() + this.clearPreviewResetTimer() + this.renderer.setAnimationPaused(true) + this.panVelocityX = 0 + this.panVelocityY = 0 + + if (event.touches.length >= 2) { + const origin = this.getStagePoint(event.touches) + this.gestureMode = 'pinch' + this.pinchStartDistance = this.getTouchDistance(event.touches) + this.pinchStartScale = this.previewScale || 1 + this.pinchStartAngle = this.getTouchAngle(event.touches) + this.pinchStartRotationDeg = this.state.rotationDeg + const anchorWorld = screenToWorld(this.getCameraState(), origin, true) + this.pinchAnchorWorldX = anchorWorld.x + this.pinchAnchorWorldY = anchorWorld.y + this.setPreviewState(this.pinchStartScale, origin.x, origin.y) + this.syncRenderer() + this.compassController.start() + return + } + + if (event.touches.length === 1) { + this.gestureMode = 'pan' + this.panLastX = event.touches[0].pageX + this.panLastY = event.touches[0].pageY + this.panLastTimestamp = event.timeStamp || Date.now() + } + } + + handleTouchMove(event: WechatMiniprogram.TouchEvent): void { + if (event.touches.length >= 2) { + const distance = this.getTouchDistance(event.touches) + const angle = this.getTouchAngle(event.touches) + const origin = this.getStagePoint(event.touches) + if (!this.pinchStartDistance) { + this.pinchStartDistance = distance + this.pinchStartScale = this.previewScale || 1 + this.pinchStartAngle = angle + this.pinchStartRotationDeg = this.state.rotationDeg + const anchorWorld = screenToWorld(this.getCameraState(), origin, true) + this.pinchAnchorWorldX = anchorWorld.x + this.pinchAnchorWorldY = anchorWorld.y + } + + this.gestureMode = 'pinch' + const nextRotationDeg = this.state.orientationMode === 'heading-up' + ? this.state.rotationDeg + : normalizeRotationDeg(this.pinchStartRotationDeg + normalizeAngleDeltaRad(angle - this.pinchStartAngle) * 180 / Math.PI) + const anchorOffset = this.getWorldOffsetFromScreen(origin.x, origin.y, nextRotationDeg) + const resolvedViewport = this.resolveViewportForExactCenter( + this.pinchAnchorWorldX - anchorOffset.x, + this.pinchAnchorWorldY - anchorOffset.y, + nextRotationDeg, + ) + this.setPreviewState( + clamp(this.pinchStartScale * (distance / this.pinchStartDistance), MIN_PREVIEW_SCALE, MAX_PREVIEW_SCALE), + origin.x, + origin.y, + ) + this.commitViewport( + { + ...resolvedViewport, + rotationDeg: nextRotationDeg, + rotationText: formatRotationText(nextRotationDeg), + }, + this.state.orientationMode === 'heading-up' + ? `双指缩放中,自动朝向保持开启 (${this.buildVersion})` + : `双指缩放与旋转中 (${this.buildVersion})`, + ) + return + } + + if (this.gestureMode !== 'pan' || event.touches.length !== 1) { + return + } + const touch = event.touches[0] + const deltaX = touch.pageX - this.panLastX + const deltaY = touch.pageY - this.panLastY + const nextTimestamp = event.timeStamp || Date.now() + const elapsed = Math.max(nextTimestamp - this.panLastTimestamp, 16) + const instantVelocityX = deltaX / elapsed + const instantVelocityY = deltaY / elapsed + + this.panVelocityX = this.panVelocityX * 0.72 + instantVelocityX * 0.28 + this.panVelocityY = this.panVelocityY * 0.72 + instantVelocityY * 0.28 + this.panLastX = touch.pageX + this.panLastY = touch.pageY + this.panLastTimestamp = nextTimestamp + + this.normalizeTranslate( + this.state.tileTranslateX + deltaX, + this.state.tileTranslateY + deltaY, + `已拖拽单 WebGL 地图引擎 (${this.buildVersion})`, + ) + } + + handleTouchEnd(event: WechatMiniprogram.TouchEvent): void { + if (this.gestureMode === 'pinch' && event.touches.length < 2) { + const gestureScale = this.previewScale || 1 + const zoomDelta = Math.round(Math.log2(gestureScale)) + const originX = this.previewOriginX || this.state.stageWidth / 2 + const originY = this.previewOriginY || this.state.stageHeight / 2 + + if (zoomDelta) { + const residualScale = gestureScale / Math.pow(2, zoomDelta) + this.zoomAroundPoint(zoomDelta, originX, originY, residualScale) + } else { + this.animatePreviewToRest() + } + + this.resetPinchState() + this.panVelocityX = 0 + this.panVelocityY = 0 + + if (event.touches.length === 1) { + this.gestureMode = 'pan' + this.panLastX = event.touches[0].pageX + this.panLastY = event.touches[0].pageY + this.panLastTimestamp = event.timeStamp || Date.now() + return + } + + this.gestureMode = 'idle' + this.renderer.setAnimationPaused(false) + this.scheduleAutoRotate() + return + } + + if (event.touches.length === 1) { + this.gestureMode = 'pan' + this.panLastX = event.touches[0].pageX + this.panLastY = event.touches[0].pageY + this.panLastTimestamp = event.timeStamp || Date.now() + return + } + + if (this.gestureMode === 'pan' && (Math.abs(this.panVelocityX) >= INERTIA_MIN_SPEED || Math.abs(this.panVelocityY) >= INERTIA_MIN_SPEED)) { + this.startInertia() + this.gestureMode = 'idle' + this.resetPinchState() + return + } + + this.gestureMode = 'idle' + this.resetPinchState() + this.renderer.setAnimationPaused(false) + this.scheduleAutoRotate() + } + + handleTouchCancel(): void { + this.gestureMode = 'idle' + this.resetPinchState() + this.panVelocityX = 0 + this.panVelocityY = 0 + this.clearInertiaTimer() + this.animatePreviewToRest() + this.renderer.setAnimationPaused(false) + this.scheduleAutoRotate() + } + + handleRecenter(): void { + this.clearInertiaTimer() + this.clearPreviewResetTimer() + this.panVelocityX = 0 + this.panVelocityY = 0 + this.renderer.setAnimationPaused(false) + this.commitViewport( + { + zoom: DEFAULT_ZOOM, + centerTileX: DEFAULT_CENTER_TILE_X, + centerTileY: DEFAULT_CENTER_TILE_Y, + tileTranslateX: 0, + tileTranslateY: 0, + }, + `已回到单 WebGL 引擎默认首屏 (${this.buildVersion})`, + true, + () => { + this.resetPreviewState() + this.syncRenderer() + this.compassController.start() + this.scheduleAutoRotate() + }, + ) + } + + handleRotateStep(stepDeg = ROTATE_STEP_DEG): void { + if (this.state.rotationMode === 'auto') { + this.setState({ + statusText: `当前不是手动旋转模式,请先切回手动 (${this.buildVersion})`, + }, true) + return + } + + const exactCenter = this.getExactCenterFromTranslate(this.state.tileTranslateX, this.state.tileTranslateY) + const nextRotationDeg = normalizeRotationDeg(this.state.rotationDeg + stepDeg) + const resolvedViewport = this.resolveViewportForExactCenter(exactCenter.x, exactCenter.y, nextRotationDeg) + + this.clearInertiaTimer() + this.clearPreviewResetTimer() + this.panVelocityX = 0 + this.panVelocityY = 0 + this.renderer.setAnimationPaused(false) + this.commitViewport( + { + ...resolvedViewport, + rotationDeg: nextRotationDeg, + rotationText: formatRotationText(nextRotationDeg), + }, + `旋转角度调整到 ${formatRotationText(nextRotationDeg)} (${this.buildVersion})`, + true, + () => { + this.resetPreviewState() + this.syncRenderer() + this.compassController.start() + }, + ) + } + + handleRotationReset(): void { + if (this.state.rotationMode === 'auto') { + this.setState({ + statusText: `当前不是手动旋转模式,请先切回手动 (${this.buildVersion})`, + }, true) + return + } + + if (!this.state.rotationDeg) { + return + } + + const exactCenter = this.getExactCenterFromTranslate(this.state.tileTranslateX, this.state.tileTranslateY) + const resolvedViewport = this.resolveViewportForExactCenter(exactCenter.x, exactCenter.y, 0) + + this.clearInertiaTimer() + this.clearPreviewResetTimer() + this.panVelocityX = 0 + this.panVelocityY = 0 + this.renderer.setAnimationPaused(false) + this.commitViewport( + { + ...resolvedViewport, + rotationDeg: 0, + rotationText: formatRotationText(0), + }, + `旋转角度已归零 (${this.buildVersion})`, + true, + () => { + this.resetPreviewState() + this.syncRenderer() + this.compassController.start() + }, + ) + } + + handleToggleRotationMode(): void { + if (this.state.orientationMode === 'manual') { + this.setNorthUpMode() + return + } + + if (this.state.orientationMode === 'north-up') { + this.setHeadingUpMode() + return + } + + this.setManualMode() + } + + handleSetManualMode(): void { + this.setManualMode() + } + + handleSetNorthUpMode(): void { + this.setNorthUpMode() + } + + handleSetHeadingUpMode(): void { + this.setHeadingUpMode() + } + + handleAutoRotateCalibrate(): void { + if (this.state.orientationMode !== 'heading-up') { + this.setState({ + statusText: `请先切到朝向朝上模式再校准 (${this.buildVersion})`, + }, true) + return + } + + if (!this.calibrateAutoRotateToCurrentOrientation()) { + this.setState({ + statusText: `当前还没有传感器方向数据,暂时无法校准 (${this.buildVersion})`, + }, true) + return + } + + this.setState({ + statusText: `已按当前持机方向完成朝向校准 (${this.buildVersion})`, + }, true) + this.scheduleAutoRotate() + } + + setManualMode(): void { + this.clearAutoRotateTimer() + this.targetAutoRotationDeg = null + this.autoRotateCalibrationPending = false + this.setState({ + rotationMode: 'manual', + rotationModeText: formatRotationModeText('manual'), + rotationToggleText: formatRotationToggleText('manual'), + orientationMode: 'manual', + orientationModeText: formatOrientationModeText('manual'), + autoRotateCalibrationText: formatAutoRotateCalibrationText(false, this.autoRotateCalibrationOffsetDeg), + statusText: `已切回手动地图旋转 (${this.buildVersion})`, + }, true) + } + + setNorthUpMode(): void { + this.clearAutoRotateTimer() + this.targetAutoRotationDeg = null + this.autoRotateCalibrationPending = false + + const exactCenter = this.getExactCenterFromTranslate(this.state.tileTranslateX, this.state.tileTranslateY) + const resolvedViewport = this.resolveViewportForExactCenter(exactCenter.x, exactCenter.y, MAP_NORTH_OFFSET_DEG) + + this.commitViewport( + { + ...resolvedViewport, + rotationDeg: MAP_NORTH_OFFSET_DEG, + rotationText: formatRotationText(MAP_NORTH_OFFSET_DEG), + rotationMode: 'manual', + rotationModeText: formatRotationModeText('north-up'), + rotationToggleText: formatRotationToggleText('north-up'), + orientationMode: 'north-up', + orientationModeText: formatOrientationModeText('north-up'), + autoRotateCalibrationText: formatAutoRotateCalibrationText(false, this.autoRotateCalibrationOffsetDeg), + }, + `地图已固定为北朝上 (${this.buildVersion})`, + true, + () => { + this.resetPreviewState() + this.syncRenderer() + }, + ) + } + + setHeadingUpMode(): void { + this.autoRotateCalibrationPending = false + this.autoRotateCalibrationOffsetDeg = MAP_NORTH_OFFSET_DEG + this.targetAutoRotationDeg = null + this.setState({ + rotationMode: 'auto', + rotationModeText: formatRotationModeText('heading-up'), + rotationToggleText: formatRotationToggleText('heading-up'), + orientationMode: 'heading-up', + orientationModeText: formatOrientationModeText('heading-up'), + autoRotateCalibrationText: formatAutoRotateCalibrationText(false, this.autoRotateCalibrationOffsetDeg), + statusText: `正在启用朝向朝上模式 (${this.buildVersion})`, + }, true) + if (this.refreshAutoRotateTarget()) { + this.scheduleAutoRotate() + } + } + + handleCompassHeading(headingDeg: number): void { + this.sensorHeadingDeg = normalizeRotationDeg(headingDeg) + this.smoothedSensorHeadingDeg = this.smoothedSensorHeadingDeg === null + ? this.sensorHeadingDeg + : interpolateAngleDeg(this.smoothedSensorHeadingDeg, this.sensorHeadingDeg, AUTO_ROTATE_HEADING_SMOOTHING) + + this.autoRotateHeadingDeg = this.smoothedSensorHeadingDeg + + this.setState({ + sensorHeadingText: formatHeadingText(this.smoothedSensorHeadingDeg), + autoRotateSourceText: formatAutoRotateSourceText(this.autoRotateSourceMode, this.courseHeadingDeg !== null), + compassNeedleDeg: formatCompassNeedleDeg(this.smoothedSensorHeadingDeg), + }) + + if (!this.refreshAutoRotateTarget()) { + return + } + + if (this.state.orientationMode === 'heading-up') { + this.scheduleAutoRotate() + } + } + + handleCompassError(message: string): void { + this.clearAutoRotateTimer() + this.targetAutoRotationDeg = null + this.autoRotateCalibrationPending = false + this.setState({ + autoRotateCalibrationText: formatAutoRotateCalibrationText(false, this.autoRotateCalibrationOffsetDeg), + statusText: `${message} (${this.buildVersion})`, + }, true) + } + setCourseHeading(headingDeg: number | null): void { + this.courseHeadingDeg = headingDeg === null ? null : normalizeRotationDeg(headingDeg) + this.setState({ + autoRotateSourceText: formatAutoRotateSourceText(this.autoRotateSourceMode, this.courseHeadingDeg !== null), + }) + + if (this.refreshAutoRotateTarget()) { + this.scheduleAutoRotate() + } + } + + resolveAutoRotateInputHeadingDeg(): number | null { + const sensorHeadingDeg = this.smoothedSensorHeadingDeg + const courseHeadingDeg = this.courseHeadingDeg + + if (this.autoRotateSourceMode === 'sensor') { + return sensorHeadingDeg + } + + if (this.autoRotateSourceMode === 'course') { + return courseHeadingDeg === null ? sensorHeadingDeg : courseHeadingDeg + } + + if (sensorHeadingDeg !== null && courseHeadingDeg !== null) { + return interpolateAngleDeg(sensorHeadingDeg, courseHeadingDeg, 0.35) + } + + return sensorHeadingDeg === null ? courseHeadingDeg : sensorHeadingDeg + } + + calibrateAutoRotateToCurrentOrientation(): boolean { + const inputHeadingDeg = this.resolveAutoRotateInputHeadingDeg() + if (inputHeadingDeg === null) { + return false + } + + this.autoRotateCalibrationOffsetDeg = normalizeRotationDeg(this.state.rotationDeg + inputHeadingDeg) + this.autoRotateCalibrationPending = false + this.targetAutoRotationDeg = normalizeRotationDeg(this.autoRotateCalibrationOffsetDeg - inputHeadingDeg) + this.setState({ + autoRotateCalibrationText: formatAutoRotateCalibrationText(false, this.autoRotateCalibrationOffsetDeg), + }) + return true + } + + refreshAutoRotateTarget(): boolean { + const inputHeadingDeg = this.resolveAutoRotateInputHeadingDeg() + if (inputHeadingDeg === null) { + return false + } + + if (this.autoRotateCalibrationPending || this.autoRotateCalibrationOffsetDeg === null) { + if (!this.calibrateAutoRotateToCurrentOrientation()) { + return false + } + return true + } + + this.targetAutoRotationDeg = normalizeRotationDeg(this.autoRotateCalibrationOffsetDeg - inputHeadingDeg) + this.setState({ + autoRotateCalibrationText: formatAutoRotateCalibrationText(false, this.autoRotateCalibrationOffsetDeg), + }) + return true + } + + scheduleAutoRotate(): void { + if (this.autoRotateTimer || this.state.rotationMode !== 'auto' || this.targetAutoRotationDeg === null) { + return + } + + const step = () => { + this.autoRotateTimer = 0 + + if (this.state.rotationMode !== 'auto' || this.targetAutoRotationDeg === null) { + return + } + + if (this.gestureMode !== 'idle' || this.inertiaTimer || this.previewResetTimer) { + this.scheduleAutoRotate() + return + } + + const currentRotationDeg = this.state.rotationDeg + const deltaDeg = normalizeAngleDeltaDeg(this.targetAutoRotationDeg - currentRotationDeg) + if (Math.abs(deltaDeg) <= AUTO_ROTATE_SNAP_DEG) { + if (Math.abs(deltaDeg) > 0.01) { + this.applyAutoRotation(this.targetAutoRotationDeg) + } + this.scheduleAutoRotate() + return + } + + if (Math.abs(deltaDeg) <= AUTO_ROTATE_DEADZONE_DEG) { + this.scheduleAutoRotate() + return + } + + const easedStepDeg = clamp(deltaDeg * AUTO_ROTATE_EASE, -AUTO_ROTATE_MAX_STEP_DEG, AUTO_ROTATE_MAX_STEP_DEG) + this.applyAutoRotation(normalizeRotationDeg(currentRotationDeg + easedStepDeg)) + this.scheduleAutoRotate() + } + + this.autoRotateTimer = setTimeout(step, AUTO_ROTATE_FRAME_MS) as unknown as number + } + + applyAutoRotation(nextRotationDeg: number): void { + const exactCenter = this.getExactCenterFromTranslate(this.state.tileTranslateX, this.state.tileTranslateY) + const resolvedViewport = this.resolveViewportForExactCenter(exactCenter.x, exactCenter.y, nextRotationDeg) + + this.state = { + ...this.state, + ...resolvedViewport, + rotationDeg: nextRotationDeg, + rotationText: formatRotationText(nextRotationDeg), + centerText: buildCenterText(this.state.zoom, resolvedViewport.centerTileX, resolvedViewport.centerTileY), + } + this.syncRenderer() + } + + applyStats(stats: MapRendererStats): void { + this.setState({ + visibleTileCount: stats.visibleTileCount, + readyTileCount: stats.readyTileCount, + memoryTileCount: stats.memoryTileCount, + diskTileCount: stats.diskTileCount, + memoryHitCount: stats.memoryHitCount, + diskHitCount: stats.diskHitCount, + networkFetchCount: stats.networkFetchCount, + cacheHitRateText: formatCacheHitRate(stats.memoryHitCount, stats.diskHitCount, stats.networkFetchCount), + }) + } + + setState(patch: Partial, immediateUi = false): void { + this.state = { + ...this.state, + ...patch, + } + + const viewPatch = this.pickViewPatch(patch) + if (!Object.keys(viewPatch).length) { + return + } + + this.pendingViewPatch = { + ...this.pendingViewPatch, + ...viewPatch, + } + + if (immediateUi) { + this.flushViewPatch() + return + } + + if (this.viewSyncTimer) { + return + } + + this.viewSyncTimer = setTimeout(() => { + this.viewSyncTimer = 0 + this.flushViewPatch() + }, UI_SYNC_INTERVAL_MS) as unknown as number + } + commitViewport( + patch: Partial, + statusText: string, + immediateUi = false, + afterUpdate?: () => void, + ): void { + const nextZoom = typeof patch.zoom === 'number' ? patch.zoom : this.state.zoom + const nextCenterTileX = typeof patch.centerTileX === 'number' ? patch.centerTileX : this.state.centerTileX + const nextCenterTileY = typeof patch.centerTileY === 'number' ? patch.centerTileY : this.state.centerTileY + const nextStageWidth = typeof patch.stageWidth === 'number' ? patch.stageWidth : this.state.stageWidth + const nextStageHeight = typeof patch.stageHeight === 'number' ? patch.stageHeight : this.state.stageHeight + const tileSizePx = getTileSizePx({ + centerWorldX: nextCenterTileX, + centerWorldY: nextCenterTileY, + viewportWidth: nextStageWidth, + viewportHeight: nextStageHeight, + visibleColumns: DESIRED_VISIBLE_COLUMNS, + }) + + this.setState({ + ...patch, + tileSizePx, + centerText: buildCenterText(nextZoom, nextCenterTileX, nextCenterTileY), + statusText, + }, immediateUi) + this.syncRenderer() + this.compassController.start() + if (afterUpdate) { + afterUpdate() + } + } + + buildScene() { + return { + tileSource: this.state.tileSource, + zoom: this.state.zoom, + centerTileX: this.state.centerTileX, + centerTileY: this.state.centerTileY, + viewportWidth: this.state.stageWidth, + viewportHeight: this.state.stageHeight, + visibleColumns: DESIRED_VISIBLE_COLUMNS, + overdraw: OVERDRAW, + translateX: this.state.tileTranslateX, + translateY: this.state.tileTranslateY, + rotationRad: this.getRotationRad(this.state.rotationDeg), + previewScale: this.previewScale || 1, + previewOriginX: this.previewOriginX || this.state.stageWidth / 2, + previewOriginY: this.previewOriginY || this.state.stageHeight / 2, + track: SAMPLE_TRACK_WGS84, + gpsPoint: SAMPLE_GPS_WGS84, + } + } + + syncRenderer(): void { + if (!this.mounted || !this.state.stageWidth || !this.state.stageHeight) { + return + } + + this.renderer.updateScene(this.buildScene()) + } + + getCameraState(rotationDeg = this.state.rotationDeg): CameraState { + return { + centerWorldX: this.state.centerTileX, + centerWorldY: this.state.centerTileY, + viewportWidth: this.state.stageWidth, + viewportHeight: this.state.stageHeight, + visibleColumns: DESIRED_VISIBLE_COLUMNS, + translateX: this.state.tileTranslateX, + translateY: this.state.tileTranslateY, + rotationRad: this.getRotationRad(rotationDeg), + } + } + + getRotationRad(rotationDeg = this.state.rotationDeg): number { + return normalizeRotationDeg(rotationDeg) * Math.PI / 180 + } + + getBaseCamera(centerWorldX = this.state.centerTileX, centerWorldY = this.state.centerTileY, rotationDeg = this.state.rotationDeg): CameraState { + return { + centerWorldX, + centerWorldY, + viewportWidth: this.state.stageWidth, + viewportHeight: this.state.stageHeight, + visibleColumns: DESIRED_VISIBLE_COLUMNS, + rotationRad: this.getRotationRad(rotationDeg), + } + } + + getWorldOffsetFromScreen(stageX: number, stageY: number, rotationDeg = this.state.rotationDeg): { x: number; y: number } { + const baseCamera = { + centerWorldX: 0, + centerWorldY: 0, + viewportWidth: this.state.stageWidth, + viewportHeight: this.state.stageHeight, + visibleColumns: DESIRED_VISIBLE_COLUMNS, + rotationRad: this.getRotationRad(rotationDeg), + } + + return screenToWorld(baseCamera, { x: stageX, y: stageY }, false) + } + + getExactCenterFromTranslate(translateX: number, translateY: number): { x: number; y: number } { + if (!this.state.stageWidth || !this.state.stageHeight) { + return { + x: this.state.centerTileX, + y: this.state.centerTileY, + } + } + + const screenCenterX = this.state.stageWidth / 2 + const screenCenterY = this.state.stageHeight / 2 + return screenToWorld(this.getBaseCamera(), { + x: screenCenterX - translateX, + y: screenCenterY - translateY, + }, false) + } + + resolveViewportForExactCenter(centerWorldX: number, centerWorldY: number, rotationDeg = this.state.rotationDeg): { + centerTileX: number + centerTileY: number + tileTranslateX: number + tileTranslateY: number + } { + const nextCenterTileX = Math.round(centerWorldX) + const nextCenterTileY = Math.round(centerWorldY) + + if (!this.state.stageWidth || !this.state.stageHeight) { + return { + centerTileX: nextCenterTileX, + centerTileY: nextCenterTileY, + tileTranslateX: 0, + tileTranslateY: 0, + } + } + + const roundedCamera = this.getBaseCamera(nextCenterTileX, nextCenterTileY, rotationDeg) + const projectedCenter = worldToScreen(roundedCamera, { x: centerWorldX, y: centerWorldY }, false) + + return { + centerTileX: nextCenterTileX, + centerTileY: nextCenterTileY, + tileTranslateX: this.state.stageWidth / 2 - projectedCenter.x, + tileTranslateY: this.state.stageHeight / 2 - projectedCenter.y, + } + } + + setPreviewState(scale: number, originX: number, originY: number): void { + this.previewScale = scale + this.previewOriginX = originX + this.previewOriginY = originY + } + + resetPreviewState(): void { + this.setPreviewState(1, this.state.stageWidth / 2, this.state.stageHeight / 2) + } + + resetPinchState(): void { + this.pinchStartDistance = 0 + this.pinchStartScale = 1 + this.pinchStartAngle = 0 + this.pinchStartRotationDeg = this.state.rotationDeg + this.pinchAnchorWorldX = 0 + this.pinchAnchorWorldY = 0 + } + + clearPreviewResetTimer(): void { + if (this.previewResetTimer) { + clearTimeout(this.previewResetTimer) + this.previewResetTimer = 0 + } + } + + clearInertiaTimer(): void { + if (this.inertiaTimer) { + clearTimeout(this.inertiaTimer) + this.inertiaTimer = 0 + } + } + + clearViewSyncTimer(): void { + if (this.viewSyncTimer) { + clearTimeout(this.viewSyncTimer) + this.viewSyncTimer = 0 + } + } + + clearAutoRotateTimer(): void { + if (this.autoRotateTimer) { + clearTimeout(this.autoRotateTimer) + this.autoRotateTimer = 0 + } + } + pickViewPatch(patch: Partial): Partial { + const viewPatch = {} as Partial + for (const key of VIEW_SYNC_KEYS) { + if (Object.prototype.hasOwnProperty.call(patch, key)) { + ;(viewPatch as any)[key] = patch[key] + } + } + return viewPatch + } + + flushViewPatch(): void { + if (!Object.keys(this.pendingViewPatch).length) { + return + } + + const patch = this.pendingViewPatch + this.pendingViewPatch = {} + this.onData(patch) + } + + getTouchDistance(touches: TouchPoint[]): number { + if (touches.length < 2) { + return 0 + } + + const first = touches[0] + const second = touches[1] + const deltaX = first.pageX - second.pageX + const deltaY = first.pageY - second.pageY + return Math.sqrt(deltaX * deltaX + deltaY * deltaY) + } + + getTouchAngle(touches: TouchPoint[]): number { + if (touches.length < 2) { + return 0 + } + + const first = touches[0] + const second = touches[1] + return Math.atan2(second.pageY - first.pageY, second.pageX - first.pageX) + } + + getStagePoint(touches: TouchPoint[]): { x: number; y: number } { + if (!touches.length) { + return { + x: this.state.stageWidth / 2, + y: this.state.stageHeight / 2, + } + } + + let pageX = 0 + let pageY = 0 + for (const touch of touches) { + pageX += touch.pageX + pageY += touch.pageY + } + + return { + x: pageX / touches.length - this.state.stageLeft, + y: pageY / touches.length - this.state.stageTop, + } + } + + animatePreviewToRest(): void { + this.clearPreviewResetTimer() + + const startScale = this.previewScale || 1 + const originX = this.previewOriginX || this.state.stageWidth / 2 + const originY = this.previewOriginY || this.state.stageHeight / 2 + + if (Math.abs(startScale - 1) < 0.01) { + this.resetPreviewState() + this.syncRenderer() + this.compassController.start() + this.scheduleAutoRotate() + return + } + + const startAt = Date.now() + const step = () => { + const progress = Math.min(1, (Date.now() - startAt) / PREVIEW_RESET_DURATION_MS) + const eased = 1 - Math.pow(1 - progress, 3) + const nextScale = startScale + (1 - startScale) * eased + this.setPreviewState(nextScale, originX, originY) + this.syncRenderer() + this.compassController.start() + + if (progress >= 1) { + this.resetPreviewState() + this.syncRenderer() + this.compassController.start() + this.previewResetTimer = 0 + this.scheduleAutoRotate() + return + } + + this.previewResetTimer = setTimeout(step, INERTIA_FRAME_MS) as unknown as number + } + + step() + } + + normalizeTranslate(translateX: number, translateY: number, statusText: string): void { + if (!this.state.stageWidth) { + this.setState({ + tileTranslateX: translateX, + tileTranslateY: translateY, + }) + this.syncRenderer() + this.compassController.start() + return + } + + const exactCenter = this.getExactCenterFromTranslate(translateX, translateY) + const resolvedViewport = this.resolveViewportForExactCenter(exactCenter.x, exactCenter.y) + const centerChanged = resolvedViewport.centerTileX !== this.state.centerTileX || resolvedViewport.centerTileY !== this.state.centerTileY + + if (centerChanged) { + this.commitViewport(resolvedViewport, statusText) + return + } + + this.setState({ + tileTranslateX: resolvedViewport.tileTranslateX, + tileTranslateY: resolvedViewport.tileTranslateY, + }) + this.syncRenderer() + this.compassController.start() + } + + zoomAroundPoint(zoomDelta: number, stageX: number, stageY: number, residualScale: number): void { + const nextZoom = clamp(this.state.zoom + zoomDelta, MIN_ZOOM, MAX_ZOOM) + const appliedDelta = nextZoom - this.state.zoom + if (!appliedDelta) { + this.animatePreviewToRest() + return + } + + if (!this.state.stageWidth || !this.state.stageHeight) { + this.commitViewport( + { + zoom: nextZoom, + centerTileX: appliedDelta > 0 ? this.state.centerTileX * 2 : Math.floor(this.state.centerTileX / 2), + centerTileY: appliedDelta > 0 ? this.state.centerTileY * 2 : Math.floor(this.state.centerTileY / 2), + tileTranslateX: 0, + tileTranslateY: 0, + }, + `缩放级别调整到 ${nextZoom}`, + true, + () => { + this.setPreviewState(residualScale, stageX, stageY) + this.syncRenderer() + this.compassController.start() + this.animatePreviewToRest() + }, + ) + return + } + + const camera = this.getCameraState() + const world = screenToWorld(camera, { x: stageX, y: stageY }, true) + const zoomFactor = Math.pow(2, appliedDelta) + const nextWorldX = world.x * zoomFactor + const nextWorldY = world.y * zoomFactor + const anchorOffset = this.getWorldOffsetFromScreen(stageX, stageY) + const exactCenterX = nextWorldX - anchorOffset.x + const exactCenterY = nextWorldY - anchorOffset.y + const resolvedViewport = this.resolveViewportForExactCenter(exactCenterX, exactCenterY) + + this.commitViewport( + { + zoom: nextZoom, + ...resolvedViewport, + }, + `缩放级别调整到 ${nextZoom}`, + true, + () => { + this.setPreviewState(residualScale, stageX, stageY) + this.syncRenderer() + this.compassController.start() + this.animatePreviewToRest() + }, + ) + } + + startInertia(): void { + this.clearInertiaTimer() + + const step = () => { + this.panVelocityX *= INERTIA_DECAY + this.panVelocityY *= INERTIA_DECAY + + if (Math.abs(this.panVelocityX) < INERTIA_MIN_SPEED && Math.abs(this.panVelocityY) < INERTIA_MIN_SPEED) { + this.setState({ + statusText: `惯性滑动结束 (${this.buildVersion})`, + }) + this.renderer.setAnimationPaused(false) + this.inertiaTimer = 0 + this.scheduleAutoRotate() + return + } + + this.normalizeTranslate( + this.state.tileTranslateX + this.panVelocityX * INERTIA_FRAME_MS, + this.state.tileTranslateY + this.panVelocityY * INERTIA_FRAME_MS, + `惯性滑动中 (${this.buildVersion})`, + ) + + this.inertiaTimer = setTimeout(step, INERTIA_FRAME_MS) as unknown as number + } + + this.inertiaTimer = setTimeout(step, INERTIA_FRAME_MS) as unknown as number + } +} + + + + + + + + + + + + + + + + + + + diff --git a/miniprogram/engine/renderer/canvasMapRenderer.ts b/miniprogram/engine/renderer/canvasMapRenderer.ts new file mode 100644 index 0000000..c7650f4 --- /dev/null +++ b/miniprogram/engine/renderer/canvasMapRenderer.ts @@ -0,0 +1,247 @@ +import { getTileSizePx, type CameraState } from '../camera/camera' +import { + TileStore, + type TileStoreCallbacks, + type TileStoreStats, +} from '../tile/tileStore' +import { type LonLatPoint } from '../../utils/projection' +import { type MapLayer } from '../layer/mapLayer' +import { TileLayer } from '../layer/tileLayer' +import { TrackLayer } from '../layer/trackLayer' +import { GpsLayer } from '../layer/gpsLayer' + +const RENDER_FRAME_MS = 16 + +export interface CanvasMapScene { + tileSource: string + zoom: number + centerTileX: number + centerTileY: number + viewportWidth: number + viewportHeight: number + visibleColumns: number + overdraw: number + translateX: number + translateY: number + rotationRad: number + previewScale: number + previewOriginX: number + previewOriginY: number + track: LonLatPoint[] + gpsPoint: LonLatPoint +} + +export type CanvasMapRendererStats = TileStoreStats + +function buildCamera(scene: CanvasMapScene): CameraState { + return { + centerWorldX: scene.centerTileX, + centerWorldY: scene.centerTileY, + viewportWidth: scene.viewportWidth, + viewportHeight: scene.viewportHeight, + visibleColumns: scene.visibleColumns, + rotationRad: scene.rotationRad, + } +} + +export class CanvasMapRenderer { + canvas: any + ctx: any + dpr: number + scene: CanvasMapScene | null + tileStore: TileStore + tileLayer: TileLayer + layers: MapLayer[] + renderTimer: number + animationTimer: number + destroyed: boolean + animationPaused: boolean + pulseFrame: number + lastStats: CanvasMapRendererStats + onStats?: (stats: CanvasMapRendererStats) => void + onTileError?: (message: string) => void + + constructor( + onStats?: (stats: CanvasMapRendererStats) => void, + onTileError?: (message: string) => void, + ) { + this.onStats = onStats + this.onTileError = onTileError + this.canvas = null + this.ctx = null + this.dpr = 1 + this.scene = null + this.tileStore = new TileStore({ + onTileReady: () => { + this.scheduleRender() + }, + onTileError: (message) => { + if (this.onTileError) { + this.onTileError(message) + } + this.scheduleRender() + }, + } satisfies TileStoreCallbacks) + this.tileLayer = new TileLayer() + this.layers = [ + this.tileLayer, + new TrackLayer(), + new GpsLayer(), + ] + this.renderTimer = 0 + this.animationTimer = 0 + this.destroyed = false + this.animationPaused = false + this.pulseFrame = 0 + this.lastStats = { + visibleTileCount: 0, + readyTileCount: 0, + memoryTileCount: 0, + diskTileCount: 0, + memoryHitCount: 0, + diskHitCount: 0, + networkFetchCount: 0, + } + } + + attachCanvas(canvasNode: any, width: number, height: number, dpr: number): void { + this.canvas = canvasNode + this.ctx = canvasNode.getContext('2d') + this.dpr = dpr || 1 + + canvasNode.width = Math.max(1, Math.floor(width * this.dpr)) + canvasNode.height = Math.max(1, Math.floor(height * this.dpr)) + + if (typeof this.ctx.setTransform === 'function') { + this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0) + } else { + this.ctx.scale(this.dpr, this.dpr) + } + + this.tileStore.attachCanvas(canvasNode) + this.startAnimation() + this.scheduleRender() + } + + updateScene(scene: CanvasMapScene): void { + this.scene = scene + this.scheduleRender() + } + + setAnimationPaused(paused: boolean): void { + this.animationPaused = paused + if (!paused) { + this.scheduleRender() + } + } + + destroy(): void { + this.destroyed = true + if (this.renderTimer) { + clearTimeout(this.renderTimer) + this.renderTimer = 0 + } + if (this.animationTimer) { + clearTimeout(this.animationTimer) + this.animationTimer = 0 + } + this.tileStore.destroy() + this.canvas = null + this.ctx = null + this.scene = null + } + + startAnimation(): void { + if (this.animationTimer) { + return + } + + const tick = () => { + if (this.destroyed) { + this.animationTimer = 0 + return + } + + if (!this.animationPaused) { + this.pulseFrame = (this.pulseFrame + 1) % 360 + this.scheduleRender() + } + + this.animationTimer = setTimeout(tick, 33) as unknown as number + } + + tick() + } + + scheduleRender(): void { + if (this.renderTimer || !this.ctx || !this.scene || this.destroyed) { + return + } + + this.renderTimer = setTimeout(() => { + this.renderTimer = 0 + this.renderFrame() + }, RENDER_FRAME_MS) as unknown as number + } + + emitStats(stats: CanvasMapRendererStats): void { + if ( + stats.visibleTileCount === this.lastStats.visibleTileCount + && stats.readyTileCount === this.lastStats.readyTileCount + && stats.memoryTileCount === this.lastStats.memoryTileCount + && stats.diskTileCount === this.lastStats.diskTileCount + && stats.memoryHitCount === this.lastStats.memoryHitCount + && stats.diskHitCount === this.lastStats.diskHitCount + && stats.networkFetchCount === this.lastStats.networkFetchCount + ) { + return + } + + this.lastStats = stats + if (this.onStats) { + this.onStats(stats) + } + } + + renderFrame(): void { + if (!this.ctx || !this.scene) { + return + } + + const scene = this.scene + const ctx = this.ctx + const camera = buildCamera(scene) + const tileSize = getTileSizePx(camera) + + ctx.clearRect(0, 0, scene.viewportWidth, scene.viewportHeight) + ctx.fillStyle = '#dbeed4' + ctx.fillRect(0, 0, scene.viewportWidth, scene.viewportHeight) + + if (!tileSize) { + this.emitStats(this.tileStore.getStats(0, 0)) + return + } + + const previewScale = scene.previewScale || 1 + const previewOriginX = scene.previewOriginX || scene.viewportWidth / 2 + const previewOriginY = scene.previewOriginY || scene.viewportHeight / 2 + + ctx.save() + ctx.translate(previewOriginX, previewOriginY) + ctx.scale(previewScale, previewScale) + ctx.translate(-previewOriginX, -previewOriginY) + + for (const layer of this.layers) { + layer.draw({ + ctx, + camera, + scene, + pulseFrame: this.pulseFrame, + tileStore: this.tileStore, + }) + } + + ctx.restore() + this.emitStats(this.tileStore.getStats(this.tileLayer.lastVisibleTileCount, this.tileLayer.lastReadyTileCount)) + } +} diff --git a/miniprogram/engine/renderer/canvasOverlayRenderer.ts b/miniprogram/engine/renderer/canvasOverlayRenderer.ts new file mode 100644 index 0000000..51f66c7 --- /dev/null +++ b/miniprogram/engine/renderer/canvasOverlayRenderer.ts @@ -0,0 +1,67 @@ +import { type MapLayer } from '../layer/mapLayer' +import { buildCamera, type MapScene } from './mapRenderer' +import { type TileStore } from '../tile/tileStore' + +export class CanvasOverlayRenderer { + canvas: any + ctx: any + dpr: number + layers: MapLayer[] + + constructor(layers: MapLayer[]) { + this.canvas = null + this.ctx = null + this.dpr = 1 + this.layers = layers + } + + attachCanvas(canvasNode: any, width: number, height: number, dpr: number): void { + this.canvas = canvasNode + this.ctx = canvasNode.getContext('2d') + this.dpr = dpr || 1 + + canvasNode.width = Math.max(1, Math.floor(width * this.dpr)) + canvasNode.height = Math.max(1, Math.floor(height * this.dpr)) + + if (typeof this.ctx.setTransform === 'function') { + this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0) + } else { + this.ctx.scale(this.dpr, this.dpr) + } + } + + clear(): void { + this.canvas = null + this.ctx = null + } + + render(scene: MapScene, tileStore: TileStore, pulseFrame: number): void { + if (!this.ctx) { + return + } + + const camera = buildCamera(scene) + const ctx = this.ctx + const previewScale = scene.previewScale || 1 + const previewOriginX = scene.previewOriginX || scene.viewportWidth / 2 + const previewOriginY = scene.previewOriginY || scene.viewportHeight / 2 + + ctx.clearRect(0, 0, scene.viewportWidth, scene.viewportHeight) + ctx.save() + ctx.translate(previewOriginX, previewOriginY) + ctx.scale(previewScale, previewScale) + ctx.translate(-previewOriginX, -previewOriginY) + + for (const layer of this.layers) { + layer.draw({ + ctx, + camera, + scene, + pulseFrame, + tileStore, + }) + } + + ctx.restore() + } +} diff --git a/miniprogram/engine/renderer/mapRenderer.ts b/miniprogram/engine/renderer/mapRenderer.ts new file mode 100644 index 0000000..8679794 --- /dev/null +++ b/miniprogram/engine/renderer/mapRenderer.ts @@ -0,0 +1,44 @@ +import { type CameraState } from '../camera/camera' +import { type TileStoreStats } from '../tile/tileStore' +import { type LonLatPoint } from '../../utils/projection' + +export interface MapScene { + tileSource: string + zoom: number + centerTileX: number + centerTileY: number + viewportWidth: number + viewportHeight: number + visibleColumns: number + overdraw: number + translateX: number + translateY: number + rotationRad: number + previewScale: number + previewOriginX: number + previewOriginY: number + track: LonLatPoint[] + gpsPoint: LonLatPoint +} + +export type MapRendererStats = TileStoreStats + +export interface MapRenderer { + attachCanvas(canvasNode: any, width: number, height: number, dpr: number): void + updateScene(scene: MapScene): void + setAnimationPaused(paused: boolean): void + destroy(): void +} + +export function buildCamera(scene: MapScene): CameraState { + return { + centerWorldX: scene.centerTileX, + centerWorldY: scene.centerTileY, + viewportWidth: scene.viewportWidth, + viewportHeight: scene.viewportHeight, + visibleColumns: scene.visibleColumns, + translateX: scene.translateX, + translateY: scene.translateY, + rotationRad: scene.rotationRad, + } +} diff --git a/miniprogram/engine/renderer/tilePersistentCache.ts b/miniprogram/engine/renderer/tilePersistentCache.ts new file mode 100644 index 0000000..8bbe161 --- /dev/null +++ b/miniprogram/engine/renderer/tilePersistentCache.ts @@ -0,0 +1,213 @@ +const STORAGE_KEY = 'cmr-tile-disk-cache-index-v1' +const ROOT_DIR = `${wx.env.USER_DATA_PATH}/cmr-tile-cache` +const MAX_PERSISTED_TILES = 600 +const PERSIST_DELAY_MS = 300 + +export interface TilePersistentCacheRecord { + filePath: string + lastAccessedAt: number +} + +type TilePersistentCacheIndex = Record + +let sharedTilePersistentCache: TilePersistentCache | null = null + +function getFileExtension(url: string): string { + const matched = url.match(/\.(png|jpg|jpeg|webp)(?:$|\?)/i) + if (!matched) { + return '.tile' + } + + return `.${matched[1].toLowerCase()}` +} + +function hashUrl(url: string): string { + let hash = 2166136261 + for (let index = 0; index < url.length; index += 1) { + hash ^= url.charCodeAt(index) + hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24) + } + + return (hash >>> 0).toString(36) +} + +function cloneIndex(rawIndex: any): TilePersistentCacheIndex { + const nextIndex: TilePersistentCacheIndex = {} + if (!rawIndex || typeof rawIndex !== 'object') { + return nextIndex + } + + Object.keys(rawIndex).forEach((url) => { + const record = rawIndex[url] + if (!record || typeof record.filePath !== 'string') { + return + } + + nextIndex[url] = { + filePath: record.filePath, + lastAccessedAt: typeof record.lastAccessedAt === 'number' ? record.lastAccessedAt : 0, + } + }) + + return nextIndex +} + +export class TilePersistentCache { + fs: WechatMiniprogram.FileSystemManager + rootDir: string + index: TilePersistentCacheIndex + persistTimer: number + + constructor() { + this.fs = wx.getFileSystemManager() + this.rootDir = ROOT_DIR + this.index = {} + this.persistTimer = 0 + + this.ensureRootDir() + this.loadIndex() + this.pruneMissingFiles() + this.pruneIfNeeded() + } + + ensureRootDir(): void { + try { + this.fs.accessSync(this.rootDir) + } catch { + this.fs.mkdirSync(this.rootDir, true) + } + } + + loadIndex(): void { + try { + this.index = cloneIndex(wx.getStorageSync(STORAGE_KEY)) + } catch { + this.index = {} + } + } + + getCount(): number { + return Object.keys(this.index).length + } + + schedulePersist(): void { + if (this.persistTimer) { + return + } + + this.persistTimer = setTimeout(() => { + this.persistTimer = 0 + wx.setStorageSync(STORAGE_KEY, this.index) + }, PERSIST_DELAY_MS) as unknown as number + } + + pruneMissingFiles(): void { + let changed = false + + Object.keys(this.index).forEach((url) => { + const record = this.index[url] + try { + this.fs.accessSync(record.filePath) + } catch { + delete this.index[url] + changed = true + } + }) + + if (changed) { + this.schedulePersist() + } + } + + getCachedPath(url: string): string | null { + const record = this.index[url] + if (!record) { + return null + } + + try { + this.fs.accessSync(record.filePath) + record.lastAccessedAt = Date.now() + this.schedulePersist() + return record.filePath + } catch { + delete this.index[url] + this.schedulePersist() + return null + } + } + + getTargetPath(url: string): string { + const existingRecord = this.index[url] + if (existingRecord) { + existingRecord.lastAccessedAt = Date.now() + this.schedulePersist() + return existingRecord.filePath + } + + const filePath = `${this.rootDir}/${hashUrl(url)}${getFileExtension(url)}` + this.index[url] = { + filePath, + lastAccessedAt: Date.now(), + } + this.schedulePersist() + return filePath + } + + markReady(url: string, filePath: string): void { + this.index[url] = { + filePath, + lastAccessedAt: Date.now(), + } + this.pruneIfNeeded() + this.schedulePersist() + } + + remove(url: string): void { + const record = this.index[url] + if (!record) { + return + } + + try { + this.fs.unlinkSync(record.filePath) + } catch { + // Ignore unlink errors for already-missing files. + } + + delete this.index[url] + this.schedulePersist() + } + + pruneIfNeeded(): void { + const urls = Object.keys(this.index) + if (urls.length <= MAX_PERSISTED_TILES) { + return + } + + const removableUrls = urls.sort((leftUrl, rightUrl) => { + return this.index[leftUrl].lastAccessedAt - this.index[rightUrl].lastAccessedAt + }) + + while (removableUrls.length > MAX_PERSISTED_TILES) { + const nextUrl = removableUrls.shift() as string + const record = this.index[nextUrl] + if (record) { + try { + this.fs.unlinkSync(record.filePath) + } catch { + // Ignore unlink errors for already-missing files. + } + } + delete this.index[nextUrl] + } + } +} + +export function getTilePersistentCache(): TilePersistentCache { + if (!sharedTilePersistentCache) { + sharedTilePersistentCache = new TilePersistentCache() + } + + return sharedTilePersistentCache +} diff --git a/miniprogram/engine/renderer/webglMapRenderer.ts b/miniprogram/engine/renderer/webglMapRenderer.ts new file mode 100644 index 0000000..c21898a --- /dev/null +++ b/miniprogram/engine/renderer/webglMapRenderer.ts @@ -0,0 +1,161 @@ +import { TrackLayer } from '../layer/trackLayer' +import { GpsLayer } from '../layer/gpsLayer' +import { TileLayer } from '../layer/tileLayer' +import { TileStore, type TileStoreCallbacks } from '../tile/tileStore' +import { type MapRenderer, type MapRendererStats, type MapScene } from './mapRenderer' +import { WebGLTileRenderer } from './webglTileRenderer' +import { WebGLVectorRenderer } from './webglVectorRenderer' + +const RENDER_FRAME_MS = 16 +const ANIMATION_FRAME_MS = 33 + +export class WebGLMapRenderer implements MapRenderer { + tileStore: TileStore + tileLayer: TileLayer + trackLayer: TrackLayer + gpsLayer: GpsLayer + tileRenderer: WebGLTileRenderer + vectorRenderer: WebGLVectorRenderer + scene: MapScene | null + renderTimer: number + animationTimer: number + destroyed: boolean + animationPaused: boolean + pulseFrame: number + lastStats: MapRendererStats + onStats?: (stats: MapRendererStats) => void + onTileError?: (message: string) => void + + constructor(onStats?: (stats: MapRendererStats) => void, onTileError?: (message: string) => void) { + this.onStats = onStats + this.onTileError = onTileError + this.tileStore = new TileStore({ + onTileReady: () => { + this.scheduleRender() + }, + onTileError: (message) => { + if (this.onTileError) { + this.onTileError(message) + } + this.scheduleRender() + }, + } satisfies TileStoreCallbacks) + this.tileLayer = new TileLayer() + this.trackLayer = new TrackLayer() + this.gpsLayer = new GpsLayer() + this.tileRenderer = new WebGLTileRenderer(this.tileLayer, this.tileStore) + this.vectorRenderer = new WebGLVectorRenderer(this.trackLayer, this.gpsLayer) + this.scene = null + this.renderTimer = 0 + this.animationTimer = 0 + this.destroyed = false + this.animationPaused = false + this.pulseFrame = 0 + this.lastStats = { + visibleTileCount: 0, + readyTileCount: 0, + memoryTileCount: 0, + diskTileCount: 0, + memoryHitCount: 0, + diskHitCount: 0, + networkFetchCount: 0, + } + } + + attachCanvas(canvasNode: any, width: number, height: number, dpr: number): void { + this.tileRenderer.attachCanvas(canvasNode, width, height, dpr) + this.vectorRenderer.attachContext(this.tileRenderer.gl, canvasNode) + this.startAnimation() + this.scheduleRender() + } + + updateScene(scene: MapScene): void { + this.scene = scene + this.scheduleRender() + } + + setAnimationPaused(paused: boolean): void { + this.animationPaused = paused + if (!paused) { + this.scheduleRender() + } + } + + destroy(): void { + this.destroyed = true + if (this.renderTimer) { + clearTimeout(this.renderTimer) + this.renderTimer = 0 + } + if (this.animationTimer) { + clearTimeout(this.animationTimer) + this.animationTimer = 0 + } + this.vectorRenderer.destroy() + this.tileRenderer.destroy() + this.tileStore.destroy() + this.scene = null + } + + startAnimation(): void { + if (this.animationTimer) { + return + } + + const tick = () => { + if (this.destroyed) { + this.animationTimer = 0 + return + } + + if (!this.animationPaused) { + this.pulseFrame = (this.pulseFrame + 1) % 360 + this.scheduleRender() + } + + this.animationTimer = setTimeout(tick, ANIMATION_FRAME_MS) as unknown as number + } + + tick() + } + + scheduleRender(): void { + if (this.renderTimer || !this.scene || this.destroyed) { + return + } + + this.renderTimer = setTimeout(() => { + this.renderTimer = 0 + this.renderFrame() + }, RENDER_FRAME_MS) as unknown as number + } + + renderFrame(): void { + if (!this.scene) { + return + } + + this.tileRenderer.render(this.scene) + this.vectorRenderer.render(this.scene, this.pulseFrame) + this.emitStats(this.tileStore.getStats(this.tileLayer.lastVisibleTileCount, this.tileLayer.lastReadyTileCount)) + } + + emitStats(stats: MapRendererStats): void { + if ( + stats.visibleTileCount === this.lastStats.visibleTileCount + && stats.readyTileCount === this.lastStats.readyTileCount + && stats.memoryTileCount === this.lastStats.memoryTileCount + && stats.diskTileCount === this.lastStats.diskTileCount + && stats.memoryHitCount === this.lastStats.memoryHitCount + && stats.diskHitCount === this.lastStats.diskHitCount + && stats.networkFetchCount === this.lastStats.networkFetchCount + ) { + return + } + + this.lastStats = stats + if (this.onStats) { + this.onStats(stats) + } + } +} diff --git a/miniprogram/engine/renderer/webglTileRenderer.ts b/miniprogram/engine/renderer/webglTileRenderer.ts new file mode 100644 index 0000000..b40875c --- /dev/null +++ b/miniprogram/engine/renderer/webglTileRenderer.ts @@ -0,0 +1,329 @@ +import { rotateScreenPoint, type ScreenPoint } from '../camera/camera' +import { type TileStore, type TileStoreEntry } from '../tile/tileStore' +import { TileLayer } from '../layer/tileLayer' +import { buildCamera, type MapScene } from './mapRenderer' + +interface TextureRecord { + key: string + texture: any +} + +function createShader(gl: any, type: number, source: string): any { + const shader = gl.createShader(type) + if (!shader) { + throw new Error('WebGL shader 创建失败') + } + + gl.shaderSource(shader, source) + gl.compileShader(shader) + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + const message = gl.getShaderInfoLog(shader) || 'unknown shader error' + gl.deleteShader(shader) + throw new Error(message) + } + + return shader +} + +function createProgram(gl: any, vertexSource: string, fragmentSource: string): any { + const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource) + const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource) + const program = gl.createProgram() + if (!program) { + throw new Error('WebGL program 创建失败') + } + + gl.attachShader(program, vertexShader) + gl.attachShader(program, fragmentShader) + gl.linkProgram(program) + gl.deleteShader(vertexShader) + gl.deleteShader(fragmentShader) + + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + const message = gl.getProgramInfoLog(program) || 'unknown program error' + gl.deleteProgram(program) + throw new Error(message) + } + + return program +} + +export class WebGLTileRenderer { + canvas: any + gl: any + tileLayer: TileLayer + tileStore: TileStore + dpr: number + program: any + positionBuffer: any + texCoordBuffer: any + positionLocation: number + texCoordLocation: number + textureCache: Map + + constructor(tileLayer: TileLayer, tileStore: TileStore) { + this.canvas = null + this.gl = null + this.tileLayer = tileLayer + this.tileStore = tileStore + this.dpr = 1 + this.program = null + this.positionBuffer = null + this.texCoordBuffer = null + this.positionLocation = -1 + this.texCoordLocation = -1 + this.textureCache = new Map() + } + + attachCanvas(canvasNode: any, width: number, height: number, dpr: number): void { + this.canvas = canvasNode + this.dpr = dpr || 1 + canvasNode.width = Math.max(1, Math.floor(width * this.dpr)) + canvasNode.height = Math.max(1, Math.floor(height * this.dpr)) + + const gl = canvasNode.getContext('webgl') || canvasNode.getContext('experimental-webgl') + if (!gl) { + throw new Error('当前环境不支持 WebGL') + } + + this.gl = gl + this.program = createProgram( + gl, + 'attribute vec2 a_position; attribute vec2 a_texCoord; varying vec2 v_texCoord; void main() { gl_Position = vec4(a_position, 0.0, 1.0); v_texCoord = a_texCoord; }', + 'precision mediump float; varying vec2 v_texCoord; uniform sampler2D u_texture; void main() { gl_FragColor = texture2D(u_texture, v_texCoord); }', + ) + this.positionBuffer = gl.createBuffer() + this.texCoordBuffer = gl.createBuffer() + this.positionLocation = gl.getAttribLocation(this.program, 'a_position') + this.texCoordLocation = gl.getAttribLocation(this.program, 'a_texCoord') + + gl.viewport(0, 0, canvasNode.width, canvasNode.height) + gl.disable(gl.DEPTH_TEST) + gl.enable(gl.BLEND) + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) + + this.tileStore.attachCanvas(canvasNode) + } + + destroy(): void { + if (this.gl) { + this.textureCache.forEach((record) => { + this.gl && this.gl.deleteTexture(record.texture) + }) + if (this.program) { + this.gl.deleteProgram(this.program) + } + if (this.positionBuffer) { + this.gl.deleteBuffer(this.positionBuffer) + } + if (this.texCoordBuffer) { + this.gl.deleteBuffer(this.texCoordBuffer) + } + } + + this.textureCache.clear() + this.program = null + this.positionBuffer = null + this.texCoordBuffer = null + this.gl = null + this.canvas = null + } + + render(scene: MapScene): void { + if (!this.gl || !this.program || !this.positionBuffer || !this.texCoordBuffer) { + return + } + + const gl = this.gl + const camera = buildCamera(scene) + const tiles = this.tileLayer.prepareTiles(scene, camera, this.tileStore) + + gl.viewport(0, 0, this.canvas.width, this.canvas.height) + gl.clearColor(0.8588, 0.9333, 0.8314, 1) + gl.clear(gl.COLOR_BUFFER_BIT) + gl.useProgram(this.program) + + for (const tile of tiles) { + const readyEntry = this.tileStore.getEntry(tile.url) + + if (readyEntry && readyEntry.status === 'ready' && readyEntry.image) { + this.drawEntry(readyEntry, tile.url, 0, 0, readyEntry.image.width || 256, readyEntry.image.height || 256, tile.leftPx, tile.topPx, tile.sizePx, tile.sizePx, scene) + this.tileLayer.lastReadyTileCount += 1 + continue + } + + const parentFallback = this.tileStore.getParentFallbackSlice(tile, scene) + if (parentFallback) { + this.drawEntry( + parentFallback.entry, + tile.url + '|parent', + parentFallback.sourceX, + parentFallback.sourceY, + parentFallback.sourceWidth, + parentFallback.sourceHeight, + tile.leftPx, + tile.topPx, + tile.sizePx, + tile.sizePx, + scene, + ) + } + + const childFallback = this.tileStore.getChildFallback(tile, scene) + if (!childFallback) { + continue + } + + const cellWidth = tile.sizePx / childFallback.division + const cellHeight = tile.sizePx / childFallback.division + for (const child of childFallback.children) { + this.drawEntry( + child.entry, + tile.url + '|child|' + child.offsetX + '|' + child.offsetY, + 0, + 0, + child.entry.image.width || 256, + child.entry.image.height || 256, + tile.leftPx + child.offsetX * cellWidth, + tile.topPx + child.offsetY * cellHeight, + cellWidth, + cellHeight, + scene, + ) + } + } + } + + drawEntry( + entry: TileStoreEntry, + cacheKey: string, + sourceX: number, + sourceY: number, + sourceWidth: number, + sourceHeight: number, + drawLeft: number, + drawTop: number, + drawWidth: number, + drawHeight: number, + scene: MapScene, + ): void { + if (!this.gl || !entry.image) { + return + } + + const texture = this.getTexture(cacheKey, entry) + if (!texture) { + return + } + + const gl = this.gl + const imageWidth = entry.image.width || 256 + const imageHeight = entry.image.height || 256 + const texLeft = sourceX / imageWidth + const texTop = sourceY / imageHeight + const texRight = (sourceX + sourceWidth) / imageWidth + const texBottom = (sourceY + sourceHeight) / imageHeight + + const topLeft = this.transformToClip(drawLeft, drawTop, scene) + const topRight = this.transformToClip(drawLeft + drawWidth, drawTop, scene) + const bottomLeft = this.transformToClip(drawLeft, drawTop + drawHeight, scene) + const bottomRight = this.transformToClip(drawLeft + drawWidth, drawTop + drawHeight, scene) + + const positions = new Float32Array([ + topLeft.x, topLeft.y, + topRight.x, topRight.y, + bottomLeft.x, bottomLeft.y, + bottomLeft.x, bottomLeft.y, + topRight.x, topRight.y, + bottomRight.x, bottomRight.y, + ]) + const texCoords = new Float32Array([ + texLeft, texTop, + texRight, texTop, + texLeft, texBottom, + texLeft, texBottom, + texRight, texTop, + texRight, texBottom, + ]) + + gl.bindTexture(gl.TEXTURE_2D, texture.texture) + gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer) + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STREAM_DRAW) + gl.enableVertexAttribArray(this.positionLocation) + gl.vertexAttribPointer(this.positionLocation, 2, gl.FLOAT, false, 0, 0) + + gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer) + gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STREAM_DRAW) + gl.enableVertexAttribArray(this.texCoordLocation) + gl.vertexAttribPointer(this.texCoordLocation, 2, gl.FLOAT, false, 0, 0) + + gl.drawArrays(gl.TRIANGLES, 0, 6) + } + + getTexture(cacheKey: string, entry: TileStoreEntry): TextureRecord | null { + if (!this.gl || !entry.image) { + return null + } + + const key = cacheKey + '|' + entry.sourcePath + const existing = this.textureCache.get(key) + if (existing) { + return existing + } + + const texture = this.gl.createTexture() + if (!texture) { + return null + } + + this.gl.bindTexture(this.gl.TEXTURE_2D, texture) + this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE) + this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE) + this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR) + this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR) + this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1) + this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, entry.image) + + const record = { key, texture } + this.textureCache.set(key, record) + return record + } + + transformToClip(x: number, y: number, scene: MapScene): ScreenPoint { + const rotated = rotateScreenPoint( + { x, y }, + scene.viewportWidth / 2, + scene.viewportHeight / 2, + scene.rotationRad || 0, + ) + const translated = { + x: rotated.x + scene.translateX, + y: rotated.y + scene.translateY, + } + const previewed = this.applyPreview(translated.x, translated.y, scene) + + return { + x: this.toClipX(previewed.x, scene.viewportWidth), + y: this.toClipY(previewed.y, scene.viewportHeight), + } + } + + applyPreview(x: number, y: number, scene: MapScene): { x: number; y: number } { + const scale = scene.previewScale || 1 + const originX = scene.previewOriginX || scene.viewportWidth / 2 + const originY = scene.previewOriginY || scene.viewportHeight / 2 + + return { + x: originX + (x - originX) * scale, + y: originY + (y - originY) * scale, + } + } + + toClipX(x: number, width: number): number { + return x / width * 2 - 1 + } + + toClipY(y: number, height: number): number { + return 1 - y / height * 2 + } +} diff --git a/miniprogram/engine/renderer/webglVectorRenderer.ts b/miniprogram/engine/renderer/webglVectorRenderer.ts new file mode 100644 index 0000000..7e7f50e --- /dev/null +++ b/miniprogram/engine/renderer/webglVectorRenderer.ts @@ -0,0 +1,234 @@ +import { buildCamera, type MapScene } from './mapRenderer' +import { TrackLayer } from '../layer/trackLayer' +import { GpsLayer } from '../layer/gpsLayer' + +function createShader(gl: any, type: number, source: string): any { + const shader = gl.createShader(type) + if (!shader) { + throw new Error('WebGL shader 创建失败') + } + + gl.shaderSource(shader, source) + gl.compileShader(shader) + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + const message = gl.getShaderInfoLog(shader) || 'unknown shader error' + gl.deleteShader(shader) + throw new Error(message) + } + + return shader +} + +function createProgram(gl: any, vertexSource: string, fragmentSource: string): any { + const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource) + const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource) + const program = gl.createProgram() + if (!program) { + throw new Error('WebGL program 创建失败') + } + + gl.attachShader(program, vertexShader) + gl.attachShader(program, fragmentShader) + gl.linkProgram(program) + gl.deleteShader(vertexShader) + gl.deleteShader(fragmentShader) + + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + const message = gl.getProgramInfoLog(program) || 'unknown program error' + gl.deleteProgram(program) + throw new Error(message) + } + + return program +} + +export class WebGLVectorRenderer { + canvas: any + gl: any + dpr: number + trackLayer: TrackLayer + gpsLayer: GpsLayer + program: any + positionBuffer: any + colorBuffer: any + positionLocation: number + colorLocation: number + + constructor(trackLayer: TrackLayer, gpsLayer: GpsLayer) { + this.canvas = null + this.gl = null + this.dpr = 1 + this.trackLayer = trackLayer + this.gpsLayer = gpsLayer + this.program = null + this.positionBuffer = null + this.colorBuffer = null + this.positionLocation = -1 + this.colorLocation = -1 + } + + attachCanvas(canvasNode: any, width: number, height: number, dpr: number): void { + this.canvas = canvasNode + this.dpr = dpr || 1 + canvasNode.width = Math.max(1, Math.floor(width * this.dpr)) + canvasNode.height = Math.max(1, Math.floor(height * this.dpr)) + this.attachContext(canvasNode.getContext('webgl') || canvasNode.getContext('experimental-webgl'), canvasNode) + } + + attachContext(gl: any, canvasNode: any): void { + if (!gl) { + throw new Error('当前环境不支持 WebGL Vector Layer') + } + + this.canvas = canvasNode + this.gl = gl + this.program = createProgram( + gl, + 'attribute vec2 a_position; attribute vec4 a_color; varying vec4 v_color; void main() { gl_Position = vec4(a_position, 0.0, 1.0); v_color = a_color; }', + 'precision mediump float; varying vec4 v_color; void main() { gl_FragColor = v_color; }', + ) + this.positionBuffer = gl.createBuffer() + this.colorBuffer = gl.createBuffer() + this.positionLocation = gl.getAttribLocation(this.program, 'a_position') + this.colorLocation = gl.getAttribLocation(this.program, 'a_color') + + gl.viewport(0, 0, canvasNode.width, canvasNode.height) + gl.enable(gl.BLEND) + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) + } + + destroy(): void { + if (this.gl) { + if (this.program) { + this.gl.deleteProgram(this.program) + } + if (this.positionBuffer) { + this.gl.deleteBuffer(this.positionBuffer) + } + if (this.colorBuffer) { + this.gl.deleteBuffer(this.colorBuffer) + } + } + + this.program = null + this.positionBuffer = null + this.colorBuffer = null + this.gl = null + this.canvas = null + } + + render(scene: MapScene, pulseFrame: number): void { + if (!this.gl || !this.program || !this.positionBuffer || !this.colorBuffer || !this.canvas) { + return + } + + const gl = this.gl + const camera = buildCamera(scene) + const trackPoints = this.trackLayer.projectPoints(scene, camera) + const gpsPoint = this.gpsLayer.projectPoint(scene, camera) + const positions: number[] = [] + const colors: number[] = [] + + for (let index = 1; index < trackPoints.length; index += 1) { + this.pushSegment(positions, colors, trackPoints[index - 1], trackPoints[index], 6, [0.09, 0.43, 0.36, 0.96], scene) + } + + for (const point of trackPoints) { + this.pushCircle(positions, colors, point.x, point.y, 10, [0.09, 0.43, 0.36, 1], scene) + this.pushCircle(positions, colors, point.x, point.y, 6.5, [0.97, 0.98, 0.95, 1], scene) + } + + this.pushCircle(positions, colors, gpsPoint.x, gpsPoint.y, this.gpsLayer.getPulseRadius(pulseFrame), [0.13, 0.62, 0.74, 0.22], scene) + this.pushCircle(positions, colors, gpsPoint.x, gpsPoint.y, 13, [1, 1, 1, 0.95], scene) + this.pushCircle(positions, colors, gpsPoint.x, gpsPoint.y, 9, [0.13, 0.63, 0.74, 1], scene) + + gl.viewport(0, 0, this.canvas.width, this.canvas.height) + gl.useProgram(this.program) + + gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer) + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STREAM_DRAW) + gl.enableVertexAttribArray(this.positionLocation) + gl.vertexAttribPointer(this.positionLocation, 2, gl.FLOAT, false, 0, 0) + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer) + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STREAM_DRAW) + gl.enableVertexAttribArray(this.colorLocation) + gl.vertexAttribPointer(this.colorLocation, 4, gl.FLOAT, false, 0, 0) + + gl.drawArrays(gl.TRIANGLES, 0, positions.length / 2) + } + + pushSegment( + positions: number[], + colors: number[], + start: { x: number; y: number }, + end: { x: number; y: number }, + width: number, + color: [number, number, number, number], + scene: MapScene, + ): void { + const deltaX = end.x - start.x + const deltaY = end.y - start.y + const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY) + if (!length) { + return + } + + const normalX = -deltaY / length * (width / 2) + const normalY = deltaX / length * (width / 2) + const topLeft = this.toClip(start.x + normalX, start.y + normalY, scene) + const topRight = this.toClip(end.x + normalX, end.y + normalY, scene) + const bottomLeft = this.toClip(start.x - normalX, start.y - normalY, scene) + const bottomRight = this.toClip(end.x - normalX, end.y - normalY, scene) + + this.pushTriangle(positions, colors, topLeft, topRight, bottomLeft, color) + this.pushTriangle(positions, colors, bottomLeft, topRight, bottomRight, color) + } + + pushCircle( + positions: number[], + colors: number[], + centerX: number, + centerY: number, + radius: number, + color: [number, number, number, number], + scene: MapScene, + ): void { + const segments = 20 + const center = this.toClip(centerX, centerY, scene) + for (let index = 0; index < segments; index += 1) { + const startAngle = index / segments * Math.PI * 2 + const endAngle = (index + 1) / segments * Math.PI * 2 + const start = this.toClip(centerX + Math.cos(startAngle) * radius, centerY + Math.sin(startAngle) * radius, scene) + const end = this.toClip(centerX + Math.cos(endAngle) * radius, centerY + Math.sin(endAngle) * radius, scene) + this.pushTriangle(positions, colors, center, start, end, color) + } + } + + pushTriangle( + positions: number[], + colors: number[], + first: { x: number; y: number }, + second: { x: number; y: number }, + third: { x: number; y: number }, + color: [number, number, number, number], + ): void { + positions.push(first.x, first.y, second.x, second.y, third.x, third.y) + for (let index = 0; index < 3; index += 1) { + colors.push(color[0], color[1], color[2], color[3]) + } + } + + toClip(x: number, y: number, scene: MapScene): { x: number; y: number } { + const previewScale = scene.previewScale || 1 + const originX = scene.previewOriginX || scene.viewportWidth / 2 + const originY = scene.previewOriginY || scene.viewportHeight / 2 + const scaledX = originX + (x - originX) * previewScale + const scaledY = originY + (y - originY) * previewScale + + return { + x: scaledX / scene.viewportWidth * 2 - 1, + y: 1 - scaledY / scene.viewportHeight * 2, + } + } +} diff --git a/miniprogram/engine/sensor/compassHeadingController.ts b/miniprogram/engine/sensor/compassHeadingController.ts new file mode 100644 index 0000000..87dcd57 --- /dev/null +++ b/miniprogram/engine/sensor/compassHeadingController.ts @@ -0,0 +1,206 @@ +export interface CompassHeadingControllerCallbacks { + onHeading: (headingDeg: number) => void + onError: (message: string) => void +} + +type SensorSource = 'compass' | 'motion' | null + +const ABSOLUTE_HEADING_CORRECTION = 0.24 + +function normalizeHeadingDeg(headingDeg: number): number { + const normalized = headingDeg % 360 + return normalized < 0 ? normalized + 360 : normalized +} + +function normalizeHeadingDeltaDeg(deltaDeg: number): number { + let normalized = deltaDeg + + while (normalized > 180) { + normalized -= 360 + } + + while (normalized < -180) { + normalized += 360 + } + + return normalized +} + +function interpolateHeadingDeg(currentDeg: number, targetDeg: number, factor: number): number { + return normalizeHeadingDeg(currentDeg + normalizeHeadingDeltaDeg(targetDeg - currentDeg) * factor) +} + +export class CompassHeadingController { + callbacks: CompassHeadingControllerCallbacks + listening: boolean + source: SensorSource + compassCallback: ((result: WechatMiniprogram.OnCompassChangeCallbackResult) => void) | null + motionCallback: ((result: WechatMiniprogram.OnDeviceMotionChangeCallbackResult) => void) | null + absoluteHeadingDeg: number | null + pitchDeg: number | null + rollDeg: number | null + motionReady: boolean + compassReady: boolean + + constructor(callbacks: CompassHeadingControllerCallbacks) { + this.callbacks = callbacks + this.listening = false + this.source = null + this.compassCallback = null + this.motionCallback = null + this.absoluteHeadingDeg = null + this.pitchDeg = null + this.rollDeg = null + this.motionReady = false + this.compassReady = false + } + + start(): void { + if (this.listening) { + return + } + + this.absoluteHeadingDeg = null + this.pitchDeg = null + this.rollDeg = null + this.motionReady = false + this.compassReady = false + this.source = null + + if (typeof wx.startCompass === 'function' && typeof wx.onCompassChange === 'function') { + this.startCompassSource() + return + } + + this.callbacks.onError('当前环境不支持罗盘方向监听') + } + + stop(): void { + this.detachCallbacks() + + if (this.motionReady) { + wx.stopDeviceMotionListening({ complete: () => {} }) + } + + if (this.compassReady) { + wx.stopCompass({ complete: () => {} }) + } + + this.listening = false + this.source = null + this.absoluteHeadingDeg = null + this.pitchDeg = null + this.rollDeg = null + this.motionReady = false + this.compassReady = false + } + + destroy(): void { + this.stop() + } + + startMotionSource(previousMessage: string): void { + if (typeof wx.startDeviceMotionListening !== 'function' || typeof wx.onDeviceMotionChange !== 'function') { + this.callbacks.onError(previousMessage) + return + } + + const callback = (result: WechatMiniprogram.OnDeviceMotionChangeCallbackResult) => { + if (typeof result.alpha !== 'number' || Number.isNaN(result.alpha)) { + return + } + + this.pitchDeg = typeof result.beta === 'number' && !Number.isNaN(result.beta) + ? result.beta * 180 / Math.PI + : null + this.rollDeg = typeof result.gamma === 'number' && !Number.isNaN(result.gamma) + ? result.gamma * 180 / Math.PI + : null + + const alphaDeg = result.alpha * 180 / Math.PI + this.applyAbsoluteHeading(normalizeHeadingDeg(360 - alphaDeg), 'motion') + } + + this.motionCallback = callback + wx.onDeviceMotionChange(callback) + wx.startDeviceMotionListening({ + interval: 'ui', + success: () => { + this.motionReady = true + this.listening = true + this.source = 'motion' + }, + fail: (res) => { + this.detachMotionCallback() + const motionMessage = res && res.errMsg ? res.errMsg : 'startDeviceMotionListening failed' + this.callbacks.onError(`${previousMessage};${motionMessage}`) + }, + }) + } + + startCompassSource(): void { + const callback = (result: WechatMiniprogram.OnCompassChangeCallbackResult) => { + if (typeof result.direction !== 'number' || Number.isNaN(result.direction)) { + return + } + + this.applyAbsoluteHeading(normalizeHeadingDeg(result.direction), 'compass') + } + + this.compassCallback = callback + wx.onCompassChange(callback) + wx.startCompass({ + success: () => { + this.compassReady = true + this.listening = true + this.source = 'compass' + }, + fail: (res) => { + this.detachCompassCallback() + this.callbacks.onError(res && res.errMsg ? res.errMsg : 'startCompass failed') + }, + }) + } + + applyAbsoluteHeading(headingDeg: number, source: 'compass' | 'motion'): void { + if (this.absoluteHeadingDeg === null) { + this.absoluteHeadingDeg = headingDeg + } else { + this.absoluteHeadingDeg = interpolateHeadingDeg(this.absoluteHeadingDeg, headingDeg, ABSOLUTE_HEADING_CORRECTION) + } + + this.source = source + this.callbacks.onHeading(this.absoluteHeadingDeg) + } + + detachCallbacks(): void { + this.detachMotionCallback() + this.detachCompassCallback() + } + + detachMotionCallback(): void { + if (!this.motionCallback) { + return + } + + if (typeof wx.offDeviceMotionChange === 'function') { + wx.offDeviceMotionChange(this.motionCallback) + } + this.motionCallback = null + } + + detachCompassCallback(): void { + if (!this.compassCallback) { + return + } + + if (typeof wx.offCompassChange === 'function') { + wx.offCompassChange(this.compassCallback) + } + this.compassCallback = null + } +} + + + + diff --git a/miniprogram/engine/tile/tileStore.ts b/miniprogram/engine/tile/tileStore.ts new file mode 100644 index 0000000..23365d4 --- /dev/null +++ b/miniprogram/engine/tile/tileStore.ts @@ -0,0 +1,567 @@ +import { buildTileUrl, type TileItem } from '../../utils/tile' +import { getTilePersistentCache, type TilePersistentCache } from '../renderer/tilePersistentCache' + +const MAX_PARENT_FALLBACK_DEPTH = 2 +const MAX_CHILD_FALLBACK_DEPTH = 1 +const MAX_CONCURRENT_DOWNLOADS = 6 +const MAX_MEMORY_CACHE_SIZE = 240 +const ERROR_RETRY_DELAY_MS = 4000 + +export type TileStatus = 'idle' | 'loading' | 'ready' | 'error' + +export interface TileStoreEntry { + image: any + status: TileStatus + sourcePath: string + downloadTask: WechatMiniprogram.DownloadTask | null + priority: number + lastUsedAt: number + lastAttemptAt: number + lastVisibleKey: string +} + +export interface TileStoreStats { + visibleTileCount: number + readyTileCount: number + memoryTileCount: number + diskTileCount: number + memoryHitCount: number + diskHitCount: number + networkFetchCount: number +} + +export interface ParentFallbackTileSource { + entry: TileStoreEntry + zoom: number +} + +export interface ChildFallbackTileSource { + division: number + children: Array<{ + entry: TileStoreEntry + offsetX: number + offsetY: number + }> +} + +export interface TileStoreScene { + tileSource: string + zoom: number + viewportWidth: number + viewportHeight: number + translateX: number + translateY: number +} + +export interface TileStoreCallbacks { + onTileReady?: () => void + onTileError?: (message: string) => void +} + +function positiveModulo(value: number, divisor: number): number { + return ((value % divisor) + divisor) % divisor +} + +function getTilePriority(tile: TileItem, scene: TileStoreScene): number { + const viewportCenterX = scene.viewportWidth / 2 + scene.translateX + const viewportCenterY = scene.viewportHeight / 2 + scene.translateY + const tileCenterX = tile.leftPx + tile.sizePx / 2 + const tileCenterY = tile.topPx + tile.sizePx / 2 + const deltaX = tileCenterX - viewportCenterX + const deltaY = tileCenterY - viewportCenterY + + return deltaX * deltaX + deltaY * deltaY +} + +function bindImageLoad( + image: any, + src: string, + onReady: () => void, + onError: () => void, +): void { + image.onload = onReady + image.onerror = onError + image.src = src +} + +export class TileStore { + canvas: any + diskCache: TilePersistentCache + tileCache: Map + pendingUrls: string[] + pendingSet: Set + pendingQueueDirty: boolean + activeDownloadCount: number + destroyed: boolean + memoryHitCount: number + diskHitCount: number + networkFetchCount: number + onTileReady?: () => void + onTileError?: (message: string) => void + + constructor(callbacks?: TileStoreCallbacks) { + this.canvas = null + this.diskCache = getTilePersistentCache() + this.tileCache = new Map() + this.pendingUrls = [] + this.pendingSet = new Set() + this.pendingQueueDirty = false + this.activeDownloadCount = 0 + this.destroyed = false + this.memoryHitCount = 0 + this.diskHitCount = 0 + this.networkFetchCount = 0 + this.onTileReady = callbacks && callbacks.onTileReady ? callbacks.onTileReady : undefined + this.onTileError = callbacks && callbacks.onTileError ? callbacks.onTileError : undefined + } + + attachCanvas(canvas: any): void { + this.canvas = canvas + } + + destroy(): void { + this.destroyed = true + this.tileCache.forEach((entry) => { + if (entry.downloadTask) { + entry.downloadTask.abort() + } + }) + this.pendingUrls = [] + this.pendingSet.clear() + this.pendingQueueDirty = false + this.activeDownloadCount = 0 + this.tileCache.clear() + this.canvas = null + } + + getReadyMemoryTileCount(): number { + let count = 0 + this.tileCache.forEach((entry) => { + if (entry.status === 'ready' && entry.image) { + count += 1 + } + }) + return count + } + + getStats(visibleTileCount: number, readyTileCount: number): TileStoreStats { + return { + visibleTileCount, + readyTileCount, + memoryTileCount: this.getReadyMemoryTileCount(), + diskTileCount: this.diskCache.getCount(), + memoryHitCount: this.memoryHitCount, + diskHitCount: this.diskHitCount, + networkFetchCount: this.networkFetchCount, + } + } + + getEntry(url: string): TileStoreEntry | undefined { + return this.tileCache.get(url) + } + + touchTile(url: string, priority: number, usedAt: number): TileStoreEntry { + let entry = this.tileCache.get(url) + if (!entry) { + entry = { + image: null, + status: 'idle', + sourcePath: '', + downloadTask: null, + priority, + lastUsedAt: usedAt, + lastAttemptAt: 0, + lastVisibleKey: '', + } + this.tileCache.set(url, entry) + return entry + } + + if (entry.priority !== priority && this.pendingSet.has(url)) { + this.pendingQueueDirty = true + } + + entry.priority = priority + entry.lastUsedAt = usedAt + return entry + } + + trimPendingQueue(protectedUrls: Set): void { + const nextPendingUrls: string[] = [] + for (const url of this.pendingUrls) { + if (!protectedUrls.has(url)) { + continue + } + nextPendingUrls.push(url) + } + + this.pendingUrls = nextPendingUrls + this.pendingSet = new Set(nextPendingUrls) + this.pendingQueueDirty = true + } + + queueTile(url: string): void { + if (this.pendingSet.has(url)) { + return + } + + const entry = this.tileCache.get(url) + if (!entry || entry.status === 'loading' || entry.status === 'ready') { + return + } + + this.pendingSet.add(url) + this.pendingUrls.push(url) + this.pendingQueueDirty = true + } + + queueVisibleTiles(tiles: TileItem[], scene: TileStoreScene, visibleKey: string): void { + const usedAt = Date.now() + const protectedUrls = new Set() + const parentPriorityMap = new Map() + const countedMemoryHits = new Set() + + for (const tile of tiles) { + const priority = getTilePriority(tile, scene) + const entry = this.touchTile(tile.url, priority, usedAt) + protectedUrls.add(tile.url) + + if (entry.status === 'ready' && entry.lastVisibleKey !== visibleKey && !countedMemoryHits.has(tile.url)) { + this.memoryHitCount += 1 + entry.lastVisibleKey = visibleKey + countedMemoryHits.add(tile.url) + } + + for (let depth = 1; depth <= MAX_PARENT_FALLBACK_DEPTH; depth += 1) { + const fallbackZoom = scene.zoom - depth + if (fallbackZoom < 0) { + break + } + + const scale = Math.pow(2, depth) + const fallbackX = Math.floor(tile.x / scale) + const fallbackY = Math.floor(tile.y / scale) + const fallbackUrl = buildTileUrl(scene.tileSource, fallbackZoom, fallbackX, fallbackY) + const fallbackPriority = priority / (depth + 1) + const existingPriority = parentPriorityMap.get(fallbackUrl) + + if (typeof existingPriority !== 'number' || fallbackPriority < existingPriority) { + parentPriorityMap.set(fallbackUrl, fallbackPriority) + } + } + } + + parentPriorityMap.forEach((priority, url) => { + const entry = this.touchTile(url, priority, usedAt) + protectedUrls.add(url) + if (entry.status === 'ready' && entry.lastVisibleKey !== visibleKey && !countedMemoryHits.has(url)) { + this.memoryHitCount += 1 + entry.lastVisibleKey = visibleKey + countedMemoryHits.add(url) + } + }) + + this.trimPendingQueue(protectedUrls) + + parentPriorityMap.forEach((_priority, url) => { + const entry = this.tileCache.get(url) + if (!entry) { + return + } + + if (entry.status === 'idle' || (entry.status === 'error' && usedAt - entry.lastAttemptAt > ERROR_RETRY_DELAY_MS)) { + if (entry.status === 'error') { + entry.status = 'idle' + } + this.queueTile(url) + } + }) + + for (const tile of tiles) { + const entry = this.tileCache.get(tile.url) + if (!entry) { + continue + } + + if (entry.status === 'idle' || (entry.status === 'error' && usedAt - entry.lastAttemptAt > ERROR_RETRY_DELAY_MS)) { + if (entry.status === 'error') { + entry.status = 'idle' + } + this.queueTile(tile.url) + } + } + + this.pruneMemoryCache(protectedUrls) + this.pumpTileQueue() + } + + pumpTileQueue(): void { + if (this.destroyed || !this.canvas) { + return + } + + if (this.pendingQueueDirty && this.pendingUrls.length > 1) { + this.pendingUrls.sort((leftUrl, rightUrl) => { + const leftEntry = this.tileCache.get(leftUrl) + const rightEntry = this.tileCache.get(rightUrl) + const leftPriority = leftEntry ? leftEntry.priority : Number.MAX_SAFE_INTEGER + const rightPriority = rightEntry ? rightEntry.priority : Number.MAX_SAFE_INTEGER + return leftPriority - rightPriority + }) + this.pendingQueueDirty = false + } + + while (this.activeDownloadCount < MAX_CONCURRENT_DOWNLOADS && this.pendingUrls.length) { + const url = this.pendingUrls.shift() as string + this.pendingSet.delete(url) + + const entry = this.tileCache.get(url) + if (!entry || entry.status === 'loading' || entry.status === 'ready') { + continue + } + + this.startTileDownload(url, entry) + } + } + + startTileDownload(url: string, entry: TileStoreEntry): void { + if (this.destroyed || !this.canvas) { + return + } + + entry.status = 'loading' + entry.lastAttemptAt = Date.now() + this.activeDownloadCount += 1 + + let finished = false + const finish = () => { + if (finished) { + return + } + + finished = true + entry.downloadTask = null + this.activeDownloadCount = Math.max(0, this.activeDownloadCount - 1) + this.pumpTileQueue() + } + + const markReady = () => { + entry.status = 'ready' + finish() + if (this.onTileReady) { + this.onTileReady() + } + } + + const markError = (message: string) => { + entry.status = 'error' + finish() + if (this.onTileError) { + this.onTileError(`${message}: ${url}`) + } + } + + const loadLocalImage = (localPath: string, fromPersistentCache: boolean) => { + if (this.destroyed || !this.canvas) { + finish() + return + } + + const localImage = this.canvas.createImage() + entry.image = localImage + entry.sourcePath = localPath + + bindImageLoad( + localImage, + localPath, + () => { + this.diskCache.markReady(url, localPath) + markReady() + }, + () => { + this.diskCache.remove(url) + if (fromPersistentCache) { + downloadToPersistentPath() + return + } + markError('瓦片本地载入失败') + }, + ) + } + + const tryRemoteImage = () => { + if (this.destroyed || !this.canvas) { + finish() + return + } + + const remoteImage = this.canvas.createImage() + entry.image = remoteImage + entry.sourcePath = url + + bindImageLoad( + remoteImage, + url, + markReady, + () => markError('瓦片远程载入失败'), + ) + } + + const downloadToPersistentPath = () => { + this.networkFetchCount += 1 + const filePath = this.diskCache.getTargetPath(url) + const task = wx.downloadFile({ + url, + filePath, + success: (res) => { + if (this.destroyed) { + finish() + return + } + + const resolvedPath = res.filePath || filePath || res.tempFilePath + if (res.statusCode !== 200 || !resolvedPath) { + tryRemoteImage() + return + } + + loadLocalImage(resolvedPath, false) + }, + fail: () => { + tryRemoteImage() + }, + }) + + entry.downloadTask = task + } + + const cachedPath = this.diskCache.getCachedPath(url) + if (cachedPath) { + this.diskHitCount += 1 + loadLocalImage(cachedPath, true) + return + } + + downloadToPersistentPath() + } + + pruneMemoryCache(protectedUrls: Set): void { + if (this.tileCache.size <= MAX_MEMORY_CACHE_SIZE) { + return + } + + const removableEntries: Array<{ url: string; lastUsedAt: number; priority: number }> = [] + this.tileCache.forEach((entry, url) => { + if (protectedUrls.has(url) || this.pendingSet.has(url) || entry.status === 'loading') { + return + } + + removableEntries.push({ + url, + lastUsedAt: entry.lastUsedAt, + priority: entry.priority, + }) + }) + + removableEntries.sort((leftEntry, rightEntry) => { + if (leftEntry.lastUsedAt !== rightEntry.lastUsedAt) { + return leftEntry.lastUsedAt - rightEntry.lastUsedAt + } + return rightEntry.priority - leftEntry.priority + }) + + while (this.tileCache.size > MAX_MEMORY_CACHE_SIZE && removableEntries.length) { + const nextEntry = removableEntries.shift() as { url: string } + this.tileCache.delete(nextEntry.url) + } + } + + findParentFallback(tile: TileItem, scene: TileStoreScene): ParentFallbackTileSource | null { + for (let depth = 1; depth <= MAX_PARENT_FALLBACK_DEPTH; depth += 1) { + const fallbackZoom = scene.zoom - depth + if (fallbackZoom < 0) { + return null + } + + const scale = Math.pow(2, depth) + const fallbackX = Math.floor(tile.x / scale) + const fallbackY = Math.floor(tile.y / scale) + const fallbackUrl = buildTileUrl(scene.tileSource, fallbackZoom, fallbackX, fallbackY) + const fallbackEntry = this.tileCache.get(fallbackUrl) + + if (fallbackEntry && fallbackEntry.status === 'ready' && fallbackEntry.image) { + return { + entry: fallbackEntry, + zoom: fallbackZoom, + } + } + } + + return null + } + + getParentFallbackSlice(tile: TileItem, scene: TileStoreScene): { + entry: TileStoreEntry + sourceX: number + sourceY: number + sourceWidth: number + sourceHeight: number + } | null { + const fallback = this.findParentFallback(tile, scene) + if (!fallback) { + return null + } + + const zoomDelta = scene.zoom - fallback.zoom + const division = Math.pow(2, zoomDelta) + const imageWidth = fallback.entry.image.width || 256 + const imageHeight = fallback.entry.image.height || 256 + const sourceWidth = imageWidth / division + const sourceHeight = imageHeight / division + const offsetX = positiveModulo(tile.x, division) + const offsetY = positiveModulo(tile.y, division) + + return { + entry: fallback.entry, + sourceX: offsetX * sourceWidth, + sourceY: offsetY * sourceHeight, + sourceWidth, + sourceHeight, + } + } + + getChildFallback(tile: TileItem, scene: TileStoreScene): ChildFallbackTileSource | null { + for (let depth = 1; depth <= MAX_CHILD_FALLBACK_DEPTH; depth += 1) { + const childZoom = scene.zoom + depth + const division = Math.pow(2, depth) + const children: ChildFallbackTileSource['children'] = [] + + for (let offsetY = 0; offsetY < division; offsetY += 1) { + for (let offsetX = 0; offsetX < division; offsetX += 1) { + const childX = tile.x * division + offsetX + const childY = tile.y * division + offsetY + const childUrl = buildTileUrl(scene.tileSource, childZoom, childX, childY) + const childEntry = this.tileCache.get(childUrl) + if (!childEntry || childEntry.status !== 'ready' || !childEntry.image) { + continue + } + + children.push({ + entry: childEntry, + offsetX, + offsetY, + }) + } + } + + if (children.length) { + return { + division, + children, + } + } + } + + return null + } +} diff --git a/miniprogram/pages/index/index.json b/miniprogram/pages/index/index.json new file mode 100644 index 0000000..b55b5a2 --- /dev/null +++ b/miniprogram/pages/index/index.json @@ -0,0 +1,4 @@ +{ + "usingComponents": { + } +} \ No newline at end of file diff --git a/miniprogram/pages/index/index.ts b/miniprogram/pages/index/index.ts new file mode 100644 index 0000000..c7aaf97 --- /dev/null +++ b/miniprogram/pages/index/index.ts @@ -0,0 +1,54 @@ +// index.ts +// 获取应用实例 +const app = getApp() +const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0' + +Component({ + data: { + motto: 'Hello World', + userInfo: { + avatarUrl: defaultAvatarUrl, + nickName: '', + }, + hasUserInfo: false, + canIUseGetUserProfile: wx.canIUse('getUserProfile'), + canIUseNicknameComp: wx.canIUse('input.type.nickname'), + }, + methods: { + // 事件处理函数 + bindViewTap() { + wx.navigateTo({ + url: '../logs/logs', + }) + }, + onChooseAvatar(e: any) { + const { avatarUrl } = e.detail + const { nickName } = this.data.userInfo + this.setData({ + "userInfo.avatarUrl": avatarUrl, + hasUserInfo: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl, + }) + }, + onInputChange(e: any) { + const nickName = e.detail.value + const { avatarUrl } = this.data.userInfo + this.setData({ + "userInfo.nickName": nickName, + hasUserInfo: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl, + }) + }, + getUserProfile() { + // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认,开发者妥善保管用户快速填写的头像昵称,避免重复弹窗 + wx.getUserProfile({ + desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 + success: (res) => { + console.log(res) + this.setData({ + userInfo: res.userInfo, + hasUserInfo: true + }) + } + }) + }, + }, +}) diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml new file mode 100644 index 0000000..0721ba0 --- /dev/null +++ b/miniprogram/pages/index/index.wxml @@ -0,0 +1,27 @@ + + + + + + + + 昵称 + + + + + + 请使用2.10.4及以上版本基础库 + + + + {{userInfo.nickName}} + + + + {{motto}} + + + diff --git a/miniprogram/pages/index/index.wxss b/miniprogram/pages/index/index.wxss new file mode 100644 index 0000000..1ebed4b --- /dev/null +++ b/miniprogram/pages/index/index.wxss @@ -0,0 +1,62 @@ +/**index.wxss**/ +page { + height: 100vh; + display: flex; + flex-direction: column; +} +.scrollarea { + flex: 1; + overflow-y: hidden; +} + +.userinfo { + display: flex; + flex-direction: column; + align-items: center; + color: #aaa; + width: 80%; +} + +.userinfo-avatar { + overflow: hidden; + width: 128rpx; + height: 128rpx; + margin: 20rpx; + border-radius: 50%; +} + +.usermotto { + margin-top: 200px; +} + +.avatar-wrapper { + padding: 0; + width: 56px !important; + border-radius: 8px; + margin-top: 40px; + margin-bottom: 40px; +} + +.avatar { + display: block; + width: 56px; + height: 56px; +} + +.nickname-wrapper { + display: flex; + width: 100%; + padding: 16px; + box-sizing: border-box; + border-top: .5px solid rgba(0, 0, 0, 0.1); + border-bottom: .5px solid rgba(0, 0, 0, 0.1); + color: black; +} + +.nickname-label { + width: 105px; +} + +.nickname-input { + flex: 1; +} diff --git a/miniprogram/pages/logs/logs.json b/miniprogram/pages/logs/logs.json new file mode 100644 index 0000000..b55b5a2 --- /dev/null +++ b/miniprogram/pages/logs/logs.json @@ -0,0 +1,4 @@ +{ + "usingComponents": { + } +} \ No newline at end of file diff --git a/miniprogram/pages/logs/logs.ts b/miniprogram/pages/logs/logs.ts new file mode 100644 index 0000000..dba5c0a --- /dev/null +++ b/miniprogram/pages/logs/logs.ts @@ -0,0 +1,21 @@ +// logs.ts +// const util = require('../../utils/util.js') +import { formatTime } from '../../utils/util' + +Component({ + data: { + logs: [], + }, + lifetimes: { + attached() { + this.setData({ + logs: (wx.getStorageSync('logs') || []).map((log: string) => { + return { + date: formatTime(new Date(log)), + timeStamp: log + } + }), + }) + } + }, +}) diff --git a/miniprogram/pages/logs/logs.wxml b/miniprogram/pages/logs/logs.wxml new file mode 100644 index 0000000..85cf1bf --- /dev/null +++ b/miniprogram/pages/logs/logs.wxml @@ -0,0 +1,6 @@ + + + + {{index + 1}}. {{log.date}} + + diff --git a/miniprogram/pages/logs/logs.wxss b/miniprogram/pages/logs/logs.wxss new file mode 100644 index 0000000..33f9d9e --- /dev/null +++ b/miniprogram/pages/logs/logs.wxss @@ -0,0 +1,16 @@ +page { + height: 100vh; + display: flex; + flex-direction: column; +} +.scrollarea { + flex: 1; + overflow-y: hidden; +} +.log-item { + margin-top: 20rpx; + text-align: center; +} +.log-item:last-child { + padding-bottom: env(safe-area-inset-bottom); +} diff --git a/miniprogram/pages/map/map.json b/miniprogram/pages/map/map.json new file mode 100644 index 0000000..d3aae06 --- /dev/null +++ b/miniprogram/pages/map/map.json @@ -0,0 +1,4 @@ +{ + "navigationBarTitleText": "地图", + "disableScroll": true +} diff --git a/miniprogram/pages/map/map.ts b/miniprogram/pages/map/map.ts new file mode 100644 index 0000000..049b18c --- /dev/null +++ b/miniprogram/pages/map/map.ts @@ -0,0 +1,173 @@ +import { MapEngine, type MapEngineStageRect, type MapEngineViewState } from '../../engine/map/mapEngine' + +const INTERNAL_BUILD_VERSION = 'map-build-58' + +let mapEngine: MapEngine | null = null + +function getFallbackStageRect(): MapEngineStageRect { + const systemInfo = wx.getSystemInfoSync() + const width = Math.max(320, systemInfo.windowWidth - 20) + const height = Math.max(280, Math.floor(systemInfo.windowHeight * 0.66)) + + return { + width, + height, + left: 10, + top: 0, + } +} + +Page({ + data: {} as MapEngineViewState, + + onLoad() { + mapEngine = new MapEngine(INTERNAL_BUILD_VERSION, { + onData: (patch) => { + this.setData(patch) + }, + }) + + this.setData(mapEngine.getInitialData()) + }, + + onReady() { + this.measureStageAndCanvas() + }, + + onUnload() { + if (mapEngine) { + mapEngine.destroy() + mapEngine = null + } + }, + + measureStageAndCanvas() { + const page = this + const applyStage = (rawRect?: Partial) => { + const fallbackRect = getFallbackStageRect() + const rect: MapEngineStageRect = { + width: rawRect && typeof rawRect.width === 'number' ? rawRect.width : fallbackRect.width, + height: rawRect && typeof rawRect.height === 'number' ? rawRect.height : fallbackRect.height, + left: rawRect && typeof rawRect.left === 'number' ? rawRect.left : fallbackRect.left, + top: rawRect && typeof rawRect.top === 'number' ? rawRect.top : fallbackRect.top, + } + + const currentEngine = mapEngine + if (!currentEngine) { + return + } + + currentEngine.setStage(rect) + + const canvasQuery = wx.createSelectorQuery().in(page) + canvasQuery.select('#mapCanvas').fields({ node: true, size: true }) + canvasQuery.exec((canvasRes) => { + const canvasRef = canvasRes[0] as any + if (!canvasRef || !canvasRef.node) { + page.setData({ + statusText: `WebGL 引擎初始化失败 (${INTERNAL_BUILD_VERSION})`, + }) + return + } + + const dpr = wx.getSystemInfoSync().pixelRatio || 1 + try { + currentEngine.attachCanvas(canvasRef.node, rect.width, rect.height, dpr) + } catch (error) { + page.setData({ + statusText: `WebGL 初始化失败 (${INTERNAL_BUILD_VERSION})`, + }) + } + }) + } + + const query = wx.createSelectorQuery().in(page) + query.select('.map-stage').boundingClientRect() + query.exec((res) => { + const rect = res[0] as WechatMiniprogram.BoundingClientRectCallbackResult | undefined + applyStage(rect) + }) + }, + + handleTouchStart(event: WechatMiniprogram.TouchEvent) { + if (mapEngine) { + mapEngine.handleTouchStart(event) + } + }, + + handleTouchMove(event: WechatMiniprogram.TouchEvent) { + if (mapEngine) { + mapEngine.handleTouchMove(event) + } + }, + + handleTouchEnd(event: WechatMiniprogram.TouchEvent) { + if (mapEngine) { + mapEngine.handleTouchEnd(event) + } + }, + + handleTouchCancel() { + if (mapEngine) { + mapEngine.handleTouchCancel() + } + }, + + handleRecenter() { + if (mapEngine) { + mapEngine.handleRecenter() + } + }, + + handleRotateStep() { + if (mapEngine) { + mapEngine.handleRotateStep() + } + }, + + handleRotationReset() { + if (mapEngine) { + mapEngine.handleRotationReset() + } + }, + + handleSetManualMode() { + if (mapEngine) { + mapEngine.handleSetManualMode() + } + }, + + handleSetNorthUpMode() { + if (mapEngine) { + mapEngine.handleSetNorthUpMode() + } + }, + + handleSetHeadingUpMode() { + if (mapEngine) { + mapEngine.handleSetHeadingUpMode() + } + }, + + handleAutoRotateCalibrate() { + if (mapEngine) { + mapEngine.handleAutoRotateCalibrate() + } + }, +}) + + + + + + + + + + + + + + + + diff --git a/miniprogram/pages/map/map.wxml b/miniprogram/pages/map/map.wxml new file mode 100644 index 0000000..87b8121 --- /dev/null +++ b/miniprogram/pages/map/map.wxml @@ -0,0 +1,152 @@ + + + + CMR MINI PROGRAM + {{mapName}} + + {{mapReadyText}} + + + + + + + + + + + + + WEBGL MAP ENGINE + North Up / Heading Up / Manual + + 地图北已经固定为正上方。现在支持手动旋转、北朝上、朝向朝上三种模式,并提供指北针用于校验朝向。 + + + + + + N + + + + {{sensorHeadingText}} + + + + + + + + Build + {{buildVersion}} + + + Renderer + {{renderMode}} + + + Projection + {{projectionMode}} + + + Heading Mode + {{orientationModeText}} + + + Sensor Heading + {{sensorHeadingText}} + + + North Ref + {{northReferenceText}} + + + Auto Source + {{autoRotateSourceText}} + + + Calibration + {{autoRotateCalibrationText}} + + + Tile URL + {{tileSource}} + + + Zoom + {{zoom}} + + + Rotation + {{rotationText}} + + + Center Tile + {{centerText}} + + + Tile Size + {{tileSizePx}}px + + + Visible Tiles + {{visibleTileCount}} + + + Ready Tiles + {{readyTileCount}} + + + Memory Tiles + {{memoryTileCount}} + + + Disk Tiles + {{diskTileCount}} + + + Cache Hit + {{cacheHitRateText}} + + + Disk Hits + {{diskHitCount}} + + + Net Fetches + {{networkFetchCount}} + + + Status + {{statusText}} + + + + 回到首屏 + 旋转归零 + + + 手动 + 北朝上 + 朝向朝上 + + + 按当前方向校准 + + + 旋转 +15° + + + diff --git a/miniprogram/pages/map/map.wxss b/miniprogram/pages/map/map.wxss new file mode 100644 index 0000000..1fa5164 --- /dev/null +++ b/miniprogram/pages/map/map.wxss @@ -0,0 +1,337 @@ +.page { + height: 100vh; + padding: 20rpx 20rpx 24rpx; + box-sizing: border-box; + display: flex; + flex-direction: column; + overflow: hidden; + background: + radial-gradient(circle at top left, #d8f3dc 0%, rgba(216, 243, 220, 0) 32%), + linear-gradient(180deg, #f7fbf2 0%, #eef6ea 100%); + color: #163020; +} + +.page__header { + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 16rpx; + flex-shrink: 0; +} + +.page__eyebrow { + font-size: 20rpx; + letter-spacing: 4rpx; + color: #5f7a65; +} + +.page__title { + margin-top: 8rpx; + font-size: 44rpx; + font-weight: 600; +} + +.page__badge { + padding: 10rpx 18rpx; + border-radius: 999rpx; + background: #163020; + color: #f7fbf2; + font-size: 22rpx; +} + +.map-stage-wrap { + position: relative; + width: 100%; + height: 66vh; + min-height: 520rpx; + max-height: 72vh; + flex-shrink: 0; + margin-bottom: 16rpx; +} + +.map-stage { + position: relative; + width: 100%; + height: 100%; + overflow: hidden; + border: 2rpx solid rgba(22, 48, 32, 0.08); + border-radius: 32rpx; + background: #dbeed4; + box-shadow: 0 18rpx 40rpx rgba(22, 48, 32, 0.08); +} + +.map-content { + position: absolute; + inset: 0; +} + +.map-canvas { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + display: block; +} + +.map-canvas--base { + z-index: 1; +} + +.map-stage__crosshair { + position: absolute; + left: 50%; + top: 50%; + width: 44rpx; + height: 44rpx; + transform: translate(-50%, -50%); + border: 3rpx solid rgba(255, 255, 255, 0.95); + border-radius: 50%; + box-shadow: 0 0 0 4rpx rgba(22, 48, 32, 0.2); + pointer-events: none; + z-index: 3; +} + +.map-stage__crosshair::before, +.map-stage__crosshair::after { + content: ''; + position: absolute; + background: rgba(255, 255, 255, 0.95); +} + +.map-stage__crosshair::before { + left: 50%; + top: -18rpx; + width: 2rpx; + height: 76rpx; + transform: translateX(-50%); +} + +.map-stage__crosshair::after { + left: -18rpx; + top: 50%; + width: 76rpx; + height: 2rpx; + transform: translateY(-50%); +} + +.map-stage__overlay { + position: absolute; + inset: 0; + display: flex; + align-items: flex-start; + justify-content: space-between; + padding: 24rpx; + box-sizing: border-box; + pointer-events: none; + z-index: 4; +} + +.overlay-card { + width: 68%; + padding: 22rpx; + border-radius: 24rpx; + background: rgba(247, 251, 242, 0.92); + box-shadow: 0 12rpx 30rpx rgba(22, 48, 32, 0.08); +} + +.overlay-card__label { + font-size: 20rpx; + letter-spacing: 3rpx; + color: #5f7a65; +} + +.overlay-card__title { + margin-top: 10rpx; + font-size: 34rpx; + font-weight: 600; +} + +.overlay-card__desc { + margin-top: 12rpx; + font-size: 24rpx; + line-height: 1.6; + color: #45624b; +} + +.compass-widget { + display: flex; + flex-direction: column; + align-items: center; + gap: 10rpx; +} + +.compass-widget__ring { + position: relative; + width: 108rpx; + height: 108rpx; + border-radius: 50%; + background: rgba(247, 251, 242, 0.94); + border: 2rpx solid rgba(22, 48, 32, 0.12); + box-shadow: 0 10rpx 24rpx rgba(22, 48, 32, 0.1); +} + +.compass-widget__north { + position: absolute; + left: 50%; + top: 10rpx; + transform: translateX(-50%); + font-size: 20rpx; + font-weight: 700; + color: #d62828; +} + +.compass-widget__needle { + position: absolute; + left: 50%; + top: 18rpx; + width: 4rpx; + height: 72rpx; + transform-origin: 50% 36rpx; + background: linear-gradient(180deg, #d62828 0%, #163020 100%); + border-radius: 999rpx; +} + +.compass-widget__center { + position: absolute; + left: 50%; + top: 50%; + width: 14rpx; + height: 14rpx; + transform: translate(-50%, -50%); + border-radius: 50%; + background: #163020; +} + +.compass-widget__label { + min-width: 92rpx; + padding: 6rpx 10rpx; + border-radius: 999rpx; + background: rgba(247, 251, 242, 0.94); + font-size: 20rpx; + text-align: center; + color: #163020; + box-shadow: 0 8rpx 18rpx rgba(22, 48, 32, 0.08); +} + +.info-panel { + flex: 1; + min-height: 0; + padding: 22rpx 20rpx 28rpx; + box-sizing: border-box; + border-radius: 28rpx; + background: rgba(255, 255, 255, 0.88); + box-shadow: 0 12rpx 32rpx rgba(22, 48, 32, 0.08); +} + +.info-panel__row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16rpx; + padding: 10rpx 0; + border-bottom: 1rpx solid rgba(22, 48, 32, 0.08); +} + +.info-panel__row--stack { + display: block; +} + +.info-panel__row:last-of-type { + border-bottom: none; +} + +.info-panel__label { + flex-shrink: 0; + font-size: 22rpx; + letter-spacing: 2rpx; + color: #5f7a65; + text-transform: uppercase; +} + +.info-panel__value { + font-size: 25rpx; + color: #163020; + text-align: right; + word-break: break-all; +} + +.info-panel__row--stack .info-panel__value { + display: block; + margin-top: 10rpx; + text-align: left; + color: #45624b; + line-height: 1.5; +} + +.info-panel__actions { + display: flex; + gap: 14rpx; + margin-top: 18rpx; +} + +.info-panel__actions--triple .info-panel__action { + font-size: 23rpx; +} + +.info-panel__action { + flex: 1; + min-width: 0; + border-radius: 999rpx; + background: #d7e8da; + color: #163020; + font-size: 26rpx; +} + +.info-panel__action--primary { + background: #2d6a4f; + color: #f7fbf2; +} + +.info-panel__action--secondary { + background: #eef6ea; + color: #45624b; +} + +.info-panel__action--active { + background: #2d6a4f; + color: #f7fbf2; +} + +.control-row { + display: flex; + gap: 14rpx; + margin-top: 18rpx; +} + +.control-row--triple .control-chip { + font-size: 23rpx; +} + +.control-chip { + flex: 1; + min-width: 0; + padding: 20rpx 16rpx; + border-radius: 999rpx; + background: #d7e8da; + color: #163020; + font-size: 26rpx; + text-align: center; + box-sizing: border-box; +} + +.control-chip--primary { + background: #2d6a4f; + color: #f7fbf2; +} + +.control-chip--secondary { + background: #eef6ea; + color: #45624b; +} + +.control-chip--active { + background: #2d6a4f; + color: #f7fbf2; +} + + diff --git a/miniprogram/utils/projection.ts b/miniprogram/utils/projection.ts new file mode 100644 index 0000000..cc106e4 --- /dev/null +++ b/miniprogram/utils/projection.ts @@ -0,0 +1,61 @@ +export interface LonLatPoint { + lon: number + lat: number +} + +export interface WebMercatorPoint { + x: number + y: number +} + +export interface WorldTilePoint { + x: number + y: number +} + +const MAX_LATITUDE = 85.05112878 +const EARTH_RADIUS = 6378137 + +function clampLatitude(lat: number): number { + return Math.max(-MAX_LATITUDE, Math.min(MAX_LATITUDE, lat)) +} + +export function lonLatToWebMercator(point: LonLatPoint): WebMercatorPoint { + const latitude = clampLatitude(point.lat) + const lonRad = point.lon * Math.PI / 180 + const latRad = latitude * Math.PI / 180 + + return { + x: EARTH_RADIUS * lonRad, + y: EARTH_RADIUS * Math.log(Math.tan(Math.PI / 4 + latRad / 2)), + } +} + +export function webMercatorToLonLat(point: WebMercatorPoint): LonLatPoint { + return { + lon: point.x / EARTH_RADIUS * 180 / Math.PI, + lat: (2 * Math.atan(Math.exp(point.y / EARTH_RADIUS)) - Math.PI / 2) * 180 / Math.PI, + } +} + +export function lonLatToWorldTile(point: LonLatPoint, zoom: number): WorldTilePoint { + const latitude = clampLatitude(point.lat) + const scale = Math.pow(2, zoom) + const latRad = latitude * Math.PI / 180 + + return { + x: (point.lon + 180) / 360 * scale, + y: (1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2 * scale, + } +} + +export function worldTileToLonLat(point: WorldTilePoint, zoom: number): LonLatPoint { + const scale = Math.pow(2, zoom) + const lon = point.x / scale * 360 - 180 + const latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * point.y / scale))) + + return { + lon, + lat: latRad * 180 / Math.PI, + } +} diff --git a/miniprogram/utils/tile.ts b/miniprogram/utils/tile.ts new file mode 100644 index 0000000..083634d --- /dev/null +++ b/miniprogram/utils/tile.ts @@ -0,0 +1,61 @@ +export interface TileItem { + key: string + url: string + x: number + y: number + leftPx: number + topPx: number + sizePx: number + isCenter: boolean +} + +const TILE_OVERLAP_PX = 2 + +export interface TileGridOptions { + urlTemplate: string + zoom: number + centerTileX: number + centerTileY: number + viewportWidth: number + viewportHeight: number + tileSize: number + overdraw: number +} + +export function buildTileUrl(template: string, z: number, x: number, y: number): string { + return template + .replace('{z}', String(z)) + .replace('{x}', String(x)) + .replace('{y}', String(y)) +} + +export function createTileGrid(options: TileGridOptions): TileItem[] { + const tiles: TileItem[] = [] + const halfWidth = options.viewportWidth / 2 + const halfHeight = options.viewportHeight / 2 + const horizontalRange = Math.ceil(halfWidth / options.tileSize) + options.overdraw + const verticalRange = Math.ceil(halfHeight / options.tileSize) + options.overdraw + + for (let dy = -verticalRange; dy <= verticalRange; dy += 1) { + for (let dx = -horizontalRange; dx <= horizontalRange; dx += 1) { + const x = options.centerTileX + dx + const y = options.centerTileY + dy + + const rawLeft = halfWidth + (dx - 0.5) * options.tileSize + const rawTop = halfHeight + (dy - 0.5) * options.tileSize + + tiles.push({ + key: `${options.zoom}-${x}-${y}`, + url: buildTileUrl(options.urlTemplate, options.zoom, x, y), + x, + y, + leftPx: Math.floor(rawLeft - TILE_OVERLAP_PX / 2), + topPx: Math.floor(rawTop - TILE_OVERLAP_PX / 2), + sizePx: Math.ceil(options.tileSize) + TILE_OVERLAP_PX, + isCenter: dx === 0 && dy === 0, + }) + } + } + + return tiles +} diff --git a/miniprogram/utils/util.ts b/miniprogram/utils/util.ts new file mode 100644 index 0000000..69a2e19 --- /dev/null +++ b/miniprogram/utils/util.ts @@ -0,0 +1,19 @@ +export const formatTime = (date: Date) => { + const year = date.getFullYear() + const month = date.getMonth() + 1 + const day = date.getDate() + const hour = date.getHours() + const minute = date.getMinutes() + const second = date.getSeconds() + + return ( + [year, month, day].map(formatNumber).join('/') + + ' ' + + [hour, minute, second].map(formatNumber).join(':') + ) +} + +const formatNumber = (n: number) => { + const s = n.toString() + return s[1] ? s : '0' + s +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..89a82fe --- /dev/null +++ b/package-lock.json @@ -0,0 +1,37 @@ +{ + "name": "miniprogram-ts-quickstart", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "miniprogram-ts-quickstart", + "version": "1.0.0", + "devDependencies": { + "miniprogram-api-typings": "^2.8.3-1", + "typescript": "^5.9.3" + } + }, + "node_modules/miniprogram-api-typings": { + "version": "2.12.0", + "resolved": "https://registry.npmmirror.com/miniprogram-api-typings/-/miniprogram-api-typings-2.12.0.tgz", + "integrity": "sha512-ibvbqeslVFur0IAvTxLMvsbtvVcMo6gwvOnj0YZHV7aeDLu091VQRrETT2QuiG9P6aZWRcxeNGJChRKVPCp9VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8191c54 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "miniprogram-ts-quickstart", + "version": "1.0.0", + "description": "", + "scripts": { + "typecheck": "tsc --noEmit -p tsconfig.json", + "typecheck:watch": "tsc --noEmit -p tsconfig.json --watch" + }, + "keywords": [], + "author": "", + "license": "", + "devDependencies": { + "miniprogram-api-typings": "^2.8.3-1", + "typescript": "^5.9.3" + } +} diff --git a/project.config.json b/project.config.json new file mode 100644 index 0000000..9496dbd --- /dev/null +++ b/project.config.json @@ -0,0 +1,50 @@ +{ + "description": "项目配置文件", + "miniprogramRoot": "miniprogram/", + "compileType": "miniprogram", + "setting": { + "useCompilerPlugins": [ + "typescript" + ], + "babelSetting": { + "ignore": [], + "disablePlugins": [], + "outputPath": "" + }, + "coverView": false, + "postcss": false, + "minified": false, + "enhance": false, + "showShadowRootInWxmlPanel": false, + "packNpmRelationList": [], + "ignoreUploadUnusedFiles": true, + "compileHotReLoad": false, + "skylineRenderEnable": true, + "es6": false, + "compileWorklet": false, + "uglifyFileName": false, + "uploadWithSourceMap": true, + "packNpmManually": false, + "minifyWXSS": true, + "minifyWXML": true, + "localPlugins": false, + "condition": false, + "swc": false, + "disableSWC": true, + "disableUseStrict": false + }, + "simulatorType": "wechat", + "simulatorPluginLibVersion": {}, + "condition": {}, + "srcMiniprogramRoot": "miniprogram/", + "editorSetting": { + "tabIndent": "insertSpaces", + "tabSize": 2 + }, + "libVersion": "trial", + "packOptions": { + "ignore": [], + "include": [] + }, + "appid": "wx9d42aa29805ded5d" +} \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..52b2ac4 --- /dev/null +++ b/readme.md @@ -0,0 +1,207 @@ +# CMR Mini Program(彩图奔跑小程序) + +## 📌 项目简介 + +CMR Mini Program 是一个基于微信小程序的定向运动平台,支持: + +- 自定义地图(非第三方地图) +- 基于瓦片(XYZ)渲染 +- TFW / GIS 数据定位 +- 控制点(打卡点)与路线系统 +- 适用于赛事、训练、互动地图等场景 + +本项目核心目标: + +> 构建一套完全自研的「轻量级地图引擎 + 定向运动系统」 + +--- + +## 🧠 技术架构 + +```text +GeoTIFF + TFW + ↓ +QGIS 处理 + ↓ +XYZ 瓦片(3857) + ↓ +小程序地图引擎 + ↓ +控制点 / 路线 / 游戏逻辑 +🗺️ 地图系统设计 +坐标体系 +类型 说明 +EPSG:4326 经纬度(WGS84) +EPSG:3857 Web Mercator(瓦片使用) +瓦片规则 +/{z}/{x}/{y}.png + +标准 XYZ 瓦片 + +支持本地或服务器部署 + +支持自定义地图(非在线地图) + +TFW 支持 + +支持读取: + +meta.tfw + +TFW 六参数: + +A 像素宽度 +D 旋转 +B 旋转 +E 像素高度(负) +C 左上角X +F 左上角Y + +自动识别: + +✅ 经纬度(EPSG:4326) + +✅ 米制坐标(EPSG:3857) + +并转换为地图中心点。 + +🏗️ 项目结构 +cmr-miniprogram/ +│ +├── app.js # 全局逻辑 +├── app.json # 全局配置 +├── app.wxss # 全局样式 +├── project.config.json # 项目配置 +├── sitemap.json # 索引配置 +│ +├── pages/ +│ ├── index/ # 首页 +│ └── map/ # 地图页(核心) +│ +├── components/ # 公共组件 +│ +├── utils/ +│ ├── projection.js # 坐标转换 +│ ├── tile.js # 瓦片计算 +│ ├── tfw.js # TFW解析 +│ +├── engine/ # 地图引擎(核心) +│ ├── renderer/ # 渲染 +│ ├── gesture/ # 手势处理 +│ ├── camera/ # 视图控制 +│ ├── overlay/ # 覆盖物(控制点等) +│ +└── assets/ + +👉 小程序项目结构由 app.js / app.json / pages 等组成,是官方标准结构 + +⚙️ 开发环境 +必备工具 + +微信开发者工具 + +VS Code(推荐) + +Node.js(LTS) + +开发模式 +VS Code → 写代码 +微信开发者工具 → 编译 / 运行 +🚀 功能模块规划 +1. 地图引擎(核心) + +瓦片加载(XYZ) + +经纬度 ↔ 屏幕坐标转换 + +拖拽 / 缩放 + +相机系统(center / zoom) + +2. 数据层 + +TFW 解析 + +meta.json(可选) + +控制点 JSON + +3. 覆盖物系统 + +控制点(打卡点) + +路线绘制 + +编号 / 标记 + +高亮 / 状态变化 + +4. 交互系统 + +点击点位 + +GPS 定位 + +判定是否到达控制点 + +5. 游戏逻辑 + +打卡顺序 + +计时 + +成绩记录 + +NPC / 问答扩展 + +📍 当前进度 + +✅ QGIS 瓦片生成 + +✅ XYZ 瓦片加载 + +✅ TFW 自动定位(支持 4326 / 3857) + +✅ 浏览器调试页 + +🚧 小程序地图引擎开发中 + +🧩 后续计划 + + 地图引擎小程序版(Canvas / View) + + 控制点系统 + + 路线规划 + + 离线地图支持 + + B端赛事管理系统 + + Web + 小程序统一架构 + +💡 核心设计理念 + +不依赖第三方地图(如高德/腾讯) + +全部地图数据可控 + +支持离线 / 私有部署 + +面向定向运动深度定制 + +📌 适用场景 + +定向运动赛事 + +校园活动 + +城市探索游戏 + +户外训练 + +教育互动地图 + +🧠 一句话总结 + +这是一个“自研地图引擎 + 定向运动系统”的小程序项目,而不是普通地图应用。 \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5a7a0f9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "strictNullChecks": true, + "noImplicitAny": true, + "module": "CommonJS", + "target": "ES2020", + "allowJs": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "noImplicitThis": true, + "noImplicitReturns": true, + "alwaysStrict": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strict": true, + "strictPropertyInitialization": true, + "lib": [ + "ES2020" + ], + "typeRoots": [ + "./typings" + ], + "skipLibCheck": true + }, + "include": [ + "./**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/typings/index.d.ts b/typings/index.d.ts new file mode 100644 index 0000000..3ee60c8 --- /dev/null +++ b/typings/index.d.ts @@ -0,0 +1,8 @@ +/// + +interface IAppOption { + globalData: { + userInfo?: WechatMiniprogram.UserInfo, + } + userInfoReadyCallback?: WechatMiniprogram.GetUserInfoSuccessCallback, +} \ No newline at end of file diff --git a/typings/types/index.d.ts b/typings/types/index.d.ts new file mode 100644 index 0000000..a5e8a7c --- /dev/null +++ b/typings/types/index.d.ts @@ -0,0 +1 @@ +/// diff --git a/typings/types/wx/index.d.ts b/typings/types/wx/index.d.ts new file mode 100644 index 0000000..db82722 --- /dev/null +++ b/typings/types/wx/index.d.ts @@ -0,0 +1,74 @@ +/*! ***************************************************************************** +Copyright (c) 2021 Tencent, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +***************************************************************************** */ + +/// +/// +/// +/// +/// +/// +/// + +declare namespace WechatMiniprogram { + type IAnyObject = Record + type Optional = F extends (arg: infer P) => infer R ? (arg?: P) => R : F + type OptionalInterface = { [K in keyof T]: Optional } + interface AsyncMethodOptionLike { + success?: (...args: any[]) => void + } + type PromisifySuccessResult< + P, + T extends AsyncMethodOptionLike + > = P extends { success: any } + ? void + : P extends { fail: any } + ? void + : P extends { complete: any } + ? void + : Promise>[0]> +} + +declare const console: WechatMiniprogram.Console +declare const wx: WechatMiniprogram.Wx +/** 引入模块。返回模块通过 `module.exports` 或 `exports` 暴露的接口。 */ +declare function require( + /** 需要引入模块文件相对于当前文件的相对路径,或 npm 模块名,或 npm 模块路径。不支持绝对路径 */ + module: string +): any +/** 引入插件。返回插件通过 `main` 暴露的接口。 */ +declare function requirePlugin( + /** 需要引入的插件的 alias */ + module: string +): any +/** 插件引入当前使用者小程序。返回使用者小程序通过 [插件配置中 `export` 暴露的接口](https://developers.weixin.qq.com/miniprogram/dev/framework/plugin/using.html#%E5%AF%BC%E5%87%BA%E5%88%B0%E6%8F%92%E4%BB%B6)。 + * + * 该接口只在插件中存在 + * + * 最低基础库: `2.11.1` */ +declare function requireMiniProgram(): any +/** 当前模块对象 */ +declare let module: { + /** 模块向外暴露的对象,使用 `require` 引用该模块时可以获取 */ + exports: any +} +/** `module.exports` 的引用 */ +declare let exports: any diff --git a/typings/types/wx/lib.wx.api.d.ts b/typings/types/wx/lib.wx.api.d.ts new file mode 100644 index 0000000..4c6047a --- /dev/null +++ b/typings/types/wx/lib.wx.api.d.ts @@ -0,0 +1,19671 @@ +/*! ***************************************************************************** +Copyright (c) 2021 Tencent, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +***************************************************************************** */ + +declare namespace WechatMiniprogram { + interface AccessFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail no such file or directory ${path}': 文件/目录不存在; + * - 'fail sdcard not mounted': Android sdcard 挂载失败; */ + errMsg: string + } + interface AccessOption { + /** 要判断是否存在的文件/目录路径 (本地路径) */ + path: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AccessCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: AccessFailCallback + /** 接口调用成功的回调函数 */ + success?: AccessSuccessCallback + } + /** 帐号信息 */ + interface AccountInfo { + /** 小程序帐号信息 */ + miniProgram: MiniProgram + /** 插件帐号信息(仅在插件中调用时包含这一项) */ + plugin: Plugin + } + interface AddCardOption { + /** 需要添加的卡券列表 */ + cardList: AddCardRequestInfo[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AddCardCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: AddCardFailCallback + /** 接口调用成功的回调函数 */ + success?: AddCardSuccessCallback + } + /** 需要添加的卡券列表 */ + interface AddCardRequestInfo { + /** 卡券的扩展参数。需将 CardExt 对象 JSON 序列化为**字符串**传入 */ + cardExt: string + /** 卡券 ID */ + cardId: string + } + /** 卡券添加结果列表 */ + interface AddCardResponseInfo { + /** 卡券的扩展参数,结构请参考下文 */ + cardExt: string + /** 用户领取到卡券的 ID */ + cardId: string + /** 加密 code,为用户领取到卡券的code加密后的字符串,解密请参照:[code 解码接口](https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1499332673_Unm7V) */ + code: string + /** 是否成功 */ + isSuccess: boolean + } + interface AddCardSuccessCallbackResult { + /** 卡券添加结果列表 */ + cardList: AddCardResponseInfo[] + errMsg: string + } + interface AddCustomLayerOption { + /** 个性化图层id */ + layerId: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AddCustomLayerCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: AddCustomLayerFailCallback + /** 接口调用成功的回调函数 */ + success?: AddCustomLayerSuccessCallback + } + interface AddGroundOverlayOption { + /** 图片覆盖的经纬度范围 */ + bounds: MapBounds + /** 图片图层 id */ + id: string + /** 图片路径,支持网络图片、临时路径、代码包路径 */ + src: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AddGroundOverlayCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: AddGroundOverlayFailCallback + /** 图层透明度 */ + opacity?: number + /** 接口调用成功的回调函数 */ + success?: AddGroundOverlaySuccessCallback + /** 是否可见 */ + visible?: boolean + /** 图层绘制顺序 */ + zIndex?: number + } + interface AddMarkersOption { + /** 同传入 map 组件的 marker 属性 */ + markers: any[] + /** 是否先清空地图上所有 marker */ + clear?: boolean + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AddMarkersCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: AddMarkersFailCallback + /** 接口调用成功的回调函数 */ + success?: AddMarkersSuccessCallback + } + interface AddPhoneCalendarOption { + /** 开始时间的 unix 时间戳 */ + startTime: number + /** 日历事件标题 */ + title: string + /** 是否提醒,默认 true */ + alarm?: boolean + /** 提醒提前量,单位秒,默认 0 表示开始时提醒 */ + alarmOffset?: number + /** 是否全天事件,默认 false */ + allDay?: boolean + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AddPhoneCalendarCompleteCallback + /** 事件说明 */ + description?: string + /** 结束时间的 unix 时间戳,默认与开始时间相同 */ + endTime?: string + /** 接口调用失败的回调函数 */ + fail?: AddPhoneCalendarFailCallback + /** 事件位置 */ + location?: string + /** 接口调用成功的回调函数 */ + success?: AddPhoneCalendarSuccessCallback + } + interface AddPhoneContactOption { + /** 名字 */ + firstName: string + /** 联系地址城市 */ + addressCity?: string + /** 联系地址国家 */ + addressCountry?: string + /** 联系地址邮政编码 */ + addressPostalCode?: string + /** 联系地址省份 */ + addressState?: string + /** 联系地址街道 */ + addressStreet?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AddPhoneContactCompleteCallback + /** 电子邮件 */ + email?: string + /** 接口调用失败的回调函数 */ + fail?: AddPhoneContactFailCallback + /** 住宅地址城市 */ + homeAddressCity?: string + /** 住宅地址国家 */ + homeAddressCountry?: string + /** 住宅地址邮政编码 */ + homeAddressPostalCode?: string + /** 住宅地址省份 */ + homeAddressState?: string + /** 住宅地址街道 */ + homeAddressStreet?: string + /** 住宅传真 */ + homeFaxNumber?: string + /** 住宅电话 */ + homePhoneNumber?: string + /** 公司电话 */ + hostNumber?: string + /** 姓氏 */ + lastName?: string + /** 中间名 */ + middleName?: string + /** 手机号 */ + mobilePhoneNumber?: string + /** 昵称 */ + nickName?: string + /** 公司 */ + organization?: string + /** 头像本地文件路径 */ + photoFilePath?: string + /** 备注 */ + remark?: string + /** 接口调用成功的回调函数 */ + success?: AddPhoneContactSuccessCallback + /** 职位 */ + title?: string + /** 网站 */ + url?: string + /** 微信号 */ + weChatNumber?: string + /** 工作地址城市 */ + workAddressCity?: string + /** 工作地址国家 */ + workAddressCountry?: string + /** 工作地址邮政编码 */ + workAddressPostalCode?: string + /** 工作地址省份 */ + workAddressState?: string + /** 工作地址街道 */ + workAddressStreet?: string + /** 工作传真 */ + workFaxNumber?: string + /** 工作电话 */ + workPhoneNumber?: string + } + interface AddPhoneRepeatCalendarOption { + /** 开始时间的 unix 时间戳 (1970年1月1日开始所经过的秒数) */ + startTime: number + /** 日历事件标题 */ + title: string + /** 是否提醒,默认 true */ + alarm?: boolean + /** 提醒提前量,单位秒,默认 0 表示开始时提醒 */ + alarmOffset?: number + /** 是否全天事件,默认 false */ + allDay?: boolean + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AddPhoneRepeatCalendarCompleteCallback + /** 事件说明 */ + description?: string + /** 结束时间的 unix 时间戳,默认与开始时间相同 */ + endTime?: string + /** 接口调用失败的回调函数 */ + fail?: AddPhoneRepeatCalendarFailCallback + /** 事件位置 */ + location?: string + /** 重复周期结束时间的 unix 时间戳,不填表示一直重复 */ + repeatEndTime?: number + /** 重复周期,默认 month 每月重复 */ + repeatInterval?: string + /** 接口调用成功的回调函数 */ + success?: AddPhoneRepeatCalendarSuccessCallback + } + interface AddServiceOption { + /** 描述service的Object */ + service: BLEPeripheralService + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AddServiceCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: AddServiceFailCallback + /** 接口调用成功的回调函数 */ + success?: AddServiceSuccessCallback + } + /** 广播自定义参数 */ + interface AdvertiseReqObj { + /** 当前Service是否可连接 */ + connectable?: boolean + /** 广播中deviceName字段,默认为空 */ + deviceName?: string + /** 广播的制造商信息, 仅安卓支持 */ + manufacturerData?: ManufacturerData[] + /** 要广播的serviceUuid列表 */ + serviceUuids?: string[] + } + /** animationData */ + interface AnimationExportResult { + actions: IAnyObject[] + } + /** 动画效果 */ + interface AnimationOption { + /** 动画变化时间,单位 ms */ + duration?: number + /** 动画变化方式 + * + * 可选值: + * - 'linear': 动画从头到尾的速度是相同的; + * - 'easeIn': 动画以低速开始; + * - 'easeOut': 动画以低速结束; + * - 'easeInOut': 动画以低速开始和结束; */ + timingFunc?: 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' + } + interface AppendFileFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail no such file or directory, open ${filePath}': 指定的 filePath 文件不存在; + * - 'fail illegal operation on a directory, open "${filePath}"': 指定的 filePath 是一个已经存在的目录; + * - 'fail permission denied, open ${dirPath}': 指定的 filePath 路径没有写权限; + * - 'fail sdcard not mounted': Android sdcard 挂载失败; */ + errMsg: string + } + interface AppendFileOption { + /** 要追加的文本或二进制数据 */ + data: string | ArrayBuffer + /** 要追加内容的文件路径 (本地路径) */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AppendFileCompleteCallback + /** 指定写入文件的字符编码 + * + * 可选值: + * - 'ascii': ; + * - 'base64': ; + * - 'binary': ; + * - 'hex': ; + * - 'ucs2': 以小端序读取; + * - 'ucs-2': 以小端序读取; + * - 'utf16le': 以小端序读取; + * - 'utf-16le': 以小端序读取; + * - 'utf-8': ; + * - 'utf8': ; + * - 'latin1': ; */ + encoding?: + | 'ascii' + | 'base64' + | 'binary' + | 'hex' + | 'ucs2' + | 'ucs-2' + | 'utf16le' + | 'utf-16le' + | 'utf-8' + | 'utf8' + | 'latin1' + /** 接口调用失败的回调函数 */ + fail?: AppendFileFailCallback + /** 接口调用成功的回调函数 */ + success?: AppendFileSuccessCallback + } + interface AuthPrivateMessageOption { + /** shareTicket。可以从 wx.onShow 中获取。详情 [shareTicket](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share.html) */ + shareTicket: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AuthPrivateMessageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: AuthPrivateMessageFailCallback + /** 接口调用成功的回调函数 */ + success?: AuthPrivateMessageSuccessCallback + } + interface AuthPrivateMessageSuccessCallbackResult { + /** 经过加密的activityId,解密后可得到原始的activityId。若解密后得到的activityId可以与开发者后台的活动id对应上则验证通过,否则表明valid字段不可靠(被篡改) 详细见[加密数据解密算法](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html) */ + encryptedData: string + /** 错误信息 */ + errMsg: string + /** 加密算法的初始向量,详细见[加密数据解密算法](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html) */ + iv: string + /** 验证是否通过 */ + valid: boolean + } + /** 用户授权设置信息,详情参考[权限](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/authorize.html) */ + interface AuthSetting { + /** 是否授权通讯地址,已取消此项授权,会默认返回true */ + 'scope.address'?: boolean + /** 是否授权摄像头,对应[[camera](https://developers.weixin.qq.com/miniprogram/dev/component/camera.html)](https://developers.weixin.qq.com/miniprogram/dev/component/camera.html) 组件 */ + 'scope.camera'?: boolean + /** 是否授权获取发票,已取消此项授权,会默认返回true */ + 'scope.invoice'?: boolean + /** 是否授权发票抬头,已取消此项授权,会默认返回true */ + 'scope.invoiceTitle'?: boolean + /** 是否授权录音功能,对应接口 [wx.startRecord](https://developers.weixin.qq.com/miniprogram/dev/api/media/recorder/wx.startRecord.html) */ + 'scope.record'?: boolean + /** 是否授权用户信息,对应接口 [wx.getUserInfo](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserInfo.html) */ + 'scope.userInfo'?: boolean + /** 是否授权地理位置,对应接口 [wx.getLocation](https://developers.weixin.qq.com/miniprogram/dev/api/location/wx.getLocation.html), [wx.chooseLocation](https://developers.weixin.qq.com/miniprogram/dev/api/location/wx.chooseLocation.html) */ + 'scope.userLocation'?: boolean + /** 是否授权微信运动步数,对应接口 [wx.getWeRunData](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/werun/wx.getWeRunData.html) */ + 'scope.werun'?: boolean + /** 是否授权保存到相册 [wx.saveImageToPhotosAlbum](https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html), [wx.saveVideoToPhotosAlbum](https://developers.weixin.qq.com/miniprogram/dev/api/media/video/wx.saveVideoToPhotosAlbum.html) */ + 'scope.writePhotosAlbum'?: boolean + } + interface AuthorizeForMiniProgramOption { + /** 需要获取权限的 scope,详见 [scope 列表]((authorize#scope-列表)) + * + * 可选值: + * - 'scope.record': ; + * - 'scope.writePhotosAlbum': ; + * - 'scope.camera': ; */ + scope: 'scope.record' | 'scope.writePhotosAlbum' | 'scope.camera' + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AuthorizeForMiniProgramCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: AuthorizeForMiniProgramFailCallback + /** 接口调用成功的回调函数 */ + success?: AuthorizeForMiniProgramSuccessCallback + } + interface AuthorizeOption { + /** 需要获取权限的 scope,详见 [scope 列表]((authorize#scope-列表)) */ + scope: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: AuthorizeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: AuthorizeFailCallback + /** 接口调用成功的回调函数 */ + success?: AuthorizeSuccessCallback + } + /** 设备特征值列表 */ + interface BLECharacteristic { + /** 该特征值支持的操作类型 */ + properties: BLECharacteristicProperties + /** 蓝牙设备特征值的 uuid */ + uuid: string + } + /** 该特征值支持的操作类型 */ + interface BLECharacteristicProperties { + /** 该特征值是否支持 indicate 操作 */ + indicate: boolean + /** 该特征值是否支持 notify 操作 */ + notify: boolean + /** 该特征值是否支持 read 操作 */ + read: boolean + /** 该特征值是否支持 write 操作 */ + write: boolean + } + interface BLEPeripheralServerCloseOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SocketTaskCloseCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SocketTaskCloseFailCallback + /** 接口调用成功的回调函数 */ + success?: SocketTaskCloseSuccessCallback + } + /** 描述service的Object */ + interface BLEPeripheralService { + /** characteristics列表 */ + characteristics: Characteristic[] + /** service 的 uuid */ + uuid: string + } + /** 设备服务列表 */ + interface BLEService { + /** 该服务是否为主服务 */ + isPrimary: boolean + /** 蓝牙设备服务的 uuid */ + uuid: string + } + /** BackgroundAudioManager 实例,可通过 [wx.getBackgroundAudioManager](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/wx.getBackgroundAudioManager.html) 获取。 +* +* **示例代码** +* +* +* ```js +const backgroundAudioManager = wx.getBackgroundAudioManager() + +backgroundAudioManager.title = '此时此刻' +backgroundAudioManager.epname = '此时此刻' +backgroundAudioManager.singer = '许巍' +backgroundAudioManager.coverImgUrl = 'http://y.gtimg.cn/music/photo_new/T002R300x300M000003rsKF44GyaSk.jpg?max_age=2592000' +// 设置了 src 之后会自动播放 +backgroundAudioManager.src = 'http://ws.stream.qqmusic.qq.com/M500001VfvsJ21xFqb.mp3?guid=ffffffff82def4af4b12b3cd9337d5e7&uin=346897220&vkey=6292F51E1E384E061FF02C31F716658E5C81F5594D561F2E88B854E81CAAB7806D5E4F103E55D33C16F3FAC506D1AB172DE8600B37E43FAD&fromtag=46' +``` */ + interface BackgroundAudioManager { + /** 音频已缓冲的时间,仅保证当前播放时间点到此时间点内容已缓冲。(只读) */ + buffered: number + /** 封面图 URL,用于做原生音频播放器背景图。原生音频播放器中的分享功能,分享出去的卡片配图及背景也将使用该图。 */ + coverImgUrl: string + /** 当前音频的播放位置(单位:s),只有在有合法 src 时返回。(只读) */ + currentTime: number + /** 当前音频的长度(单位:s),只有在有合法 src 时返回。(只读) */ + duration: number + /** 专辑名,原生音频播放器中的分享功能,分享出去的卡片简介,也将使用该值。 */ + epname: string + /** 当前是否暂停或停止。(只读) */ + paused: boolean + /** 播放速度。范围 0.5-2.0,默认为 1。(Android 需要 6 及以上版本) + * + * 最低基础库: `2.11.0` */ + playbackRate: number + /** 音频协议。默认值为 'http',设置 'hls' 可以支持播放 HLS 协议的直播音频。 + * + * 最低基础库: `1.9.94` */ + protocol: string + /** 歌手名,原生音频播放器中的分享功能,分享出去的卡片简介,也将使用该值。 */ + singer: string + /** 音频的数据源([2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 开始支持云文件ID)。默认为空字符串,**当设置了新的 src 时,会自动开始播放**,目前支持的格式有 m4a, aac, mp3, wav。 */ + src: string + /** 音频开始播放的位置(单位:s)。 */ + startTime: number + /** 音频标题,用于原生音频播放器音频标题(必填)。原生音频播放器中的分享功能,分享出去的卡片标题,也将使用该值。 */ + title: string + /** 页面链接,原生音频播放器中的分享功能,分享出去的卡片简介,也将使用该值。 */ + webUrl: string + } + interface BlueToothDevice { + /** 当前蓝牙设备的信号强度 */ + RSSI: number + /** 当前蓝牙设备的广播数据段中的 ManufacturerData 数据段。 */ + advertisData: ArrayBuffer + /** 当前蓝牙设备的广播数据段中的 ServiceUUIDs 数据段 */ + advertisServiceUUIDs: string[] + /** 用于区分设备的 id */ + deviceId: string + /** 当前蓝牙设备的广播数据段中的 LocalName 数据段 */ + localName: string + /** 蓝牙设备名称,某些设备可能没有 */ + name: string + /** 当前蓝牙设备的广播数据段中的 ServiceData 数据段 */ + serviceData: IAnyObject + } + /** 搜索到的设备列表 */ + interface BluetoothDeviceInfo { + /** 用于区分设备的 id */ + deviceId: string + /** 蓝牙设备名称,某些设备可能没有 */ + name: string + } + interface BlurOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: BlurCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: BlurFailCallback + /** 接口调用成功的回调函数 */ + success?: BlurSuccessCallback + } + interface BoundingClientRectCallbackResult { + /** 节点的下边界坐标 */ + bottom: number + /** 节点的 dataset */ + dataset: IAnyObject + /** 节点的高度 */ + height: number + /** 节点的 ID */ + id: string + /** 节点的左边界坐标 */ + left: number + /** 节点的右边界坐标 */ + right: number + /** 节点的上边界坐标 */ + top: number + /** 节点的宽度 */ + width: number + } + /** 目标边界 */ + interface BoundingClientRectResult { + /** 下边界 */ + bottom: number + /** 高度 */ + height: number + /** 左边界 */ + left: number + /** 右边界 */ + right: number + /** 上边界 */ + top: number + /** 宽度 */ + width: number + } + interface CameraContextStartRecordOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartRecordCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartRecordFailCallback + /** 接口调用成功的回调函数 */ + success?: CameraContextStartRecordSuccessCallback + /** 超过30s或页面 `onHide` 时会结束录像 */ + timeoutCallback?: StartRecordTimeoutCallback + } + interface CameraContextStopRecordOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopRecordCompleteCallback + /** 启动视频压缩,压缩效果同`chooseVideo` */ + compressed?: boolean + /** 接口调用失败的回调函数 */ + fail?: StopRecordFailCallback + /** 接口调用成功的回调函数 */ + success?: CameraContextStopRecordSuccessCallback + } + interface CameraFrameListenerStartOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartFailCallback + /** 接口调用成功的回调函数 */ + success?: StartSuccessCallback + } + /** Canvas 实例,可通过 [SelectorQuery](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.html) 获取。 + * + * **示例代码** + * + * + * + * 2D Canvas 示例 + * [在微信开发者工具中查看示例](https://developers.weixin.qq.com/s/SHfgCmmq7UcM) + * + * WebGL 示例 + * [在微信开发者工具中查看示例](https://developers.weixin.qq.com/s/qEGUOqmf7T8z) + * + * 最低基础库: `2.7.0` */ + interface Canvas { + /** 画布高度 */ + height: number + /** 画布宽度 */ + width: number + } + /** canvas 组件的绘图上下文。CanvasContext 是旧版的接口, 新版 Canvas 2D 接口与 Web 一致。 */ + interface CanvasContext { + /** 填充颜色。用法同 [CanvasContext.setFillStyle()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setFillStyle.html)。 + * + * 最低基础库: `1.9.90` */ + fillStyle: string | CanvasGradient + /** 当前字体样式的属性。符合 [CSS font 语法](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font) 的 DOMString 字符串,至少需要提供字体大小和字体族名。默认值为 10px sans-serif。 + * + * 最低基础库: `1.9.90` */ + font: string + /** 全局画笔透明度。范围 0-1,0 表示完全透明,1 表示完全不透明。 */ + globalAlpha: number + /** 在绘制新形状时应用的合成操作的类型。目前安卓版本只适用于 `fill` 填充块的合成,用于 `stroke` 线段的合成效果都是 `source-over`。 + * + * 目前支持的操作有 + * - 安卓:xor, source-over, source-atop, destination-out, lighter, overlay, darken, lighten, hard-light + * - iOS:xor, source-over, source-atop, destination-over, destination-out, lighter, multiply, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, saturation, luminosity + * + * 最低基础库: `1.9.90` */ + globalCompositeOperation: string + /** 线条的端点样式。用法同 [CanvasContext.setLineCap()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setLineCap.html)。 + * + * 最低基础库: `1.9.90` */ + lineCap: string + /** 虚线偏移量,初始值为0 + * + * 最低基础库: `1.9.90` */ + lineDashOffset: number + /** 线条的交点样式。用法同 [CanvasContext.setLineJoin()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setLineJoin.html)。 + * + * 可选值: + * - 'bevel': 斜角; + * - 'round': 圆角; + * - 'miter': 尖角; + * + * 最低基础库: `1.9.90` */ + lineJoin: 'bevel' | 'round' | 'miter' + /** 线条的宽度。用法同 [CanvasContext.setLineWidth()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setLineWidth.html)。 + * + * 最低基础库: `1.9.90` */ + lineWidth: number + /** 最大斜接长度。用法同 [CanvasContext.setMiterLimit()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setMiterLimit.html)。 + * + * 最低基础库: `1.9.90` */ + miterLimit: number + /** 阴影的模糊级别 + * + * 最低基础库: `1.9.90` */ + shadowBlur: number + /** 阴影的颜色 + * + * 最低基础库: `1.9.90` */ + shadowColor: number + /** 阴影相对于形状在水平方向的偏移 + * + * 最低基础库: `1.9.90` */ + shadowOffsetX: number + /** 阴影相对于形状在竖直方向的偏移 + * + * 最低基础库: `1.9.90` */ + shadowOffsetY: number + /** 边框颜色。用法同 [CanvasContext.setStrokeStyle()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setStrokeStyle.html)。 + * + * 最低基础库: `1.9.90` */ + strokeStyle: string | CanvasGradient + } + interface CanvasGetImageDataOption { + /** 画布标识,传入 [canvas](https://developers.weixin.qq.com/miniprogram/dev/component/canvas.html) 组件的 `canvas-id` 属性。 */ + canvasId: string + /** 将要被提取的图像数据矩形区域的高度 */ + height: number + /** 将要被提取的图像数据矩形区域的宽度 */ + width: number + /** 将要被提取的图像数据矩形区域的左上角横坐标 */ + x: number + /** 将要被提取的图像数据矩形区域的左上角纵坐标 */ + y: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CanvasGetImageDataCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CanvasGetImageDataFailCallback + /** 接口调用成功的回调函数 */ + success?: CanvasGetImageDataSuccessCallback + } + interface CanvasGetImageDataSuccessCallbackResult { + /** 图像像素点数据,一维数组,每四项表示一个像素点的 rgba */ + data: Uint8ClampedArray + /** 图像数据矩形的高度 */ + height: number + /** 图像数据矩形的宽度 */ + width: number + errMsg: string + } + interface CanvasPutImageDataOption { + /** 画布标识,传入 [canvas](https://developers.weixin.qq.com/miniprogram/dev/component/canvas.html) 组件的 canvas-id 属性。 */ + canvasId: string + /** 图像像素点数据,一维数组,每四项表示一个像素点的 rgba */ + data: Uint8ClampedArray + /** 源图像数据矩形区域的高度 */ + height: number + /** 源图像数据矩形区域的宽度 */ + width: number + /** 源图像数据在目标画布中的位置偏移量(x 轴方向的偏移量) */ + x: number + /** 源图像数据在目标画布中的位置偏移量(y 轴方向的偏移量) */ + y: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CanvasPutImageDataCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CanvasPutImageDataFailCallback + /** 接口调用成功的回调函数 */ + success?: CanvasPutImageDataSuccessCallback + } + interface CanvasToTempFilePathOption { + /** 画布标识,传入 [canvas](https://developers.weixin.qq.com/miniprogram/dev/component/canvas.html) 组件实例 (canvas type="2d" 时使用该属性)。 */ + canvas?: IAnyObject + /** 画布标识,传入 [canvas](https://developers.weixin.qq.com/miniprogram/dev/component/canvas.html) 组件的 canvas-id */ + canvasId?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CanvasToTempFilePathCompleteCallback + /** 输出的图片的高度 + * + * 最低基础库: `1.2.0` */ + destHeight?: number + /** 输出的图片的宽度 + * + * 最低基础库: `1.2.0` */ + destWidth?: number + /** 接口调用失败的回调函数 */ + fail?: CanvasToTempFilePathFailCallback + /** 目标文件的类型 + * + * 可选值: + * - 'jpg': jpg 图片; + * - 'png': png 图片; + * + * 最低基础库: `1.7.0` */ + fileType?: 'jpg' | 'png' + /** 指定的画布区域的高度 + * + * 最低基础库: `1.2.0` */ + height?: number + /** 图片的质量,目前仅对 jpg 有效。取值范围为 (0, 1],不在范围内时当作 1.0 处理。 + * + * 最低基础库: `1.7.0` */ + quality?: number + /** 接口调用成功的回调函数 */ + success?: CanvasToTempFilePathSuccessCallback + /** 指定的画布区域的宽度 + * + * 最低基础库: `1.2.0` */ + width?: number + /** 指定的画布区域的左上角横坐标 + * + * 最低基础库: `1.2.0` */ + x?: number + /** 指定的画布区域的左上角纵坐标 + * + * 最低基础库: `1.2.0` */ + y?: number + } + interface CanvasToTempFilePathSuccessCallbackResult { + /** 生成文件的临时路径 (本地路径) */ + tempFilePath: string + errMsg: string + } + /** characteristics列表 */ + interface Characteristic { + /** Characteristic 的 uuid */ + uuid: string + /** 描述符数据 */ + descriptors?: CharacteristicDescriptor[] + /** 特征值权限 */ + permission?: CharacteristicPermission + /** 特征值支持的操作 */ + properties?: CharacteristicProperties + /** 特征值对应的二进制值 */ + value?: ArrayBuffer + } + /** 描述符数据 */ + interface CharacteristicDescriptor { + /** Descriptor 的 uuid */ + uuid: string + /** 描述符的权限 */ + permission?: DescriptorPermission + /** 描述符数据 */ + value?: ArrayBuffer + } + /** 特征值权限 */ + interface CharacteristicPermission { + /** 加密读请求 */ + readEncryptionRequired?: boolean + /** 可读 */ + readable?: boolean + /** 加密写请求 */ + writeEncryptionRequired?: boolean + /** 可写 */ + writeable?: boolean + } + /** 特征值支持的操作 */ + interface CharacteristicProperties { + /** 回包 */ + indicate?: boolean + /** 订阅 */ + notify?: boolean + /** 读 */ + read?: boolean + /** 写 */ + write?: boolean + } + interface CheckIsOpenAccessibilityOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CheckIsOpenAccessibilityCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CheckIsOpenAccessibilityFailCallback + /** 接口调用成功的回调函数 */ + success?: CheckIsOpenAccessibilitySuccessCallback + } + interface CheckIsOpenAccessibilitySuccessCallbackOption { + /** iOS 上开启辅助功能旁白,安卓开启 talkback 时返回 true */ + open: boolean + } + interface CheckIsSoterEnrolledInDeviceOption { + /** 认证方式 + * + * 可选值: + * - 'fingerPrint': 指纹识别; + * - 'facial': 人脸识别; + * - 'speech': 声纹识别(暂未支持); */ + checkAuthMode: 'fingerPrint' | 'facial' | 'speech' + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CheckIsSoterEnrolledInDeviceCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CheckIsSoterEnrolledInDeviceFailCallback + /** 接口调用成功的回调函数 */ + success?: CheckIsSoterEnrolledInDeviceSuccessCallback + } + interface CheckIsSoterEnrolledInDeviceSuccessCallbackResult { + /** 错误信息 */ + errMsg: string + /** 是否已录入信息 */ + isEnrolled: boolean + } + interface CheckIsSupportSoterAuthenticationOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CheckIsSupportSoterAuthenticationCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CheckIsSupportSoterAuthenticationFailCallback + /** 接口调用成功的回调函数 */ + success?: CheckIsSupportSoterAuthenticationSuccessCallback + } + interface CheckIsSupportSoterAuthenticationSuccessCallbackResult { + /** 该设备支持的可被SOTER识别的生物识别方式 + * + * 可选值: + * - 'fingerPrint': 指纹识别; + * - 'facial': 人脸识别; + * - 'speech': 声纹识别(暂未支持); */ + supportMode: Array<'fingerPrint' | 'facial' | 'speech'> + errMsg: string + } + interface CheckSessionOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CheckSessionCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CheckSessionFailCallback + /** 接口调用成功的回调函数 */ + success?: CheckSessionSuccessCallback + } + interface ChooseAddressOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ChooseAddressCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ChooseAddressFailCallback + /** 接口调用成功的回调函数 */ + success?: ChooseAddressSuccessCallback + } + interface ChooseAddressSuccessCallbackResult { + /** 国标收货地址第二级地址 */ + cityName: string + /** 国标收货地址第三级地址 */ + countyName: string + /** 详细收货地址信息 */ + detailInfo: string + /** 错误信息 */ + errMsg: string + /** 收货地址国家码 */ + nationalCode: string + /** 邮编 */ + postalCode: string + /** 国标收货地址第一级地址 */ + provinceName: string + /** 收货人手机号码 */ + telNumber: string + /** 收货人姓名 */ + userName: string + } + /** 返回选择的文件的本地临时文件对象数组 */ + interface ChooseFile { + /** 选择的文件名称 */ + name: string + /** 本地临时文件路径 (本地路径) */ + path: string + /** 本地临时文件大小,单位 B */ + size: number + /** 选择的文件的会话发送时间,Unix时间戳,工具暂不支持此属性 */ + time: number + /** 选择的文件类型 + * + * 可选值: + * - 'video': 选择了视频文件; + * - 'image': 选择了图片文件; + * - 'file': 选择了除图片和视频的文件; */ + type: 'video' | 'image' | 'file' + } + interface ChooseImageOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ChooseImageCompleteCallback + /** 最多可以选择的图片张数 */ + count?: number + /** 接口调用失败的回调函数 */ + fail?: ChooseImageFailCallback + /** 所选的图片的尺寸 + * + * 可选值: + * - 'original': 原图; + * - 'compressed': 压缩图; */ + sizeType?: Array<'original' | 'compressed'> + /** 选择图片的来源 + * + * 可选值: + * - 'album': 从相册选图; + * - 'camera': 使用相机; */ + sourceType?: Array<'album' | 'camera'> + /** 接口调用成功的回调函数 */ + success?: ChooseImageSuccessCallback + } + interface ChooseImageSuccessCallbackResult { + /** 图片的本地临时文件路径列表 (本地路径) */ + tempFilePaths: string[] + /** 图片的本地临时文件列表 + * + * 最低基础库: `1.2.0` */ + tempFiles: ImageFile[] + errMsg: string + } + interface ChooseInvoiceOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ChooseInvoiceCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ChooseInvoiceFailCallback + /** 接口调用成功的回调函数 */ + success?: ChooseInvoiceSuccessCallback + } + interface ChooseInvoiceSuccessCallbackResult { + /** 用户选中的发票信息,格式为一个 JSON 字符串,包含三个字段: card_id:所选发票卡券的 cardId,encrypt_code:所选发票卡券的加密 code,报销方可以通过 cardId 和 encryptCode 获得报销发票的信息,app_id: 发票方的 appId。 */ + invoiceInfo: string + errMsg: string + } + interface ChooseInvoiceTitleOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ChooseInvoiceTitleCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ChooseInvoiceTitleFailCallback + /** 接口调用成功的回调函数 */ + success?: ChooseInvoiceTitleSuccessCallback + } + interface ChooseInvoiceTitleSuccessCallbackResult { + /** 银行账号 */ + bankAccount: string + /** 银行名称 */ + bankName: string + /** 单位地址 */ + companyAddress: string + /** 错误信息 */ + errMsg: string + /** 抬头税号 */ + taxNumber: string + /** 手机号码 */ + telephone: string + /** 抬头名称 */ + title: string + /** 抬头类型 + * + * 可选值: + * - 0: 单位; + * - 1: 个人; */ + type: 0 | 1 + } + interface ChooseLocationOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ChooseLocationCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ChooseLocationFailCallback + /** 目标地纬度 + * + * 最低基础库: `2.9.0` */ + latitude?: number + /** 目标地经度 + * + * 最低基础库: `2.9.0` */ + longitude?: number + /** 接口调用成功的回调函数 */ + success?: ChooseLocationSuccessCallback + } + interface ChooseLocationSuccessCallbackResult { + /** 详细地址 */ + address: string + /** 纬度,浮点数,范围为-90~90,负数表示南纬。使用 gcj02 国测局坐标系 */ + latitude: string + /** 经度,浮点数,范围为-180~180,负数表示西经。使用 gcj02 国测局坐标系 */ + longitude: string + /** 位置名称 */ + name: string + errMsg: string + } + interface ChooseMediaOption { + /** 仅在 sourceType 为 camera 时生效,使用前置或后置摄像头 + * + * 可选值: + * - 'back': 使用后置摄像头; + * - 'front': 使用前置摄像头; */ + camera?: 'back' | 'front' + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ChooseMediaCompleteCallback + /** 最多可以选择的文件个数 */ + count?: number + /** 接口调用失败的回调函数 */ + fail?: ChooseMediaFailCallback + /** 拍摄视频最长拍摄时间,单位秒。时间范围为 3s 至 30s 之间 */ + maxDuration?: number + /** 文件类型 + * + * 可选值: + * - 'image': 只能拍摄图片或从相册选择图片; + * - 'video': 只能拍摄视频或从相册选择视频; */ + mediaType?: Array<'image' | 'video'> + /** 仅对 mediaType 为 image 时有效,是否压缩所选文件 */ + sizeType?: string[] + /** 图片和视频选择的来源 + * + * 可选值: + * - 'album': 从相册选择; + * - 'camera': 使用相机拍摄; */ + sourceType?: Array<'album' | 'camera'> + /** 接口调用成功的回调函数 */ + success?: ChooseMediaSuccessCallback + } + interface ChooseMediaSuccessCallbackResult { + /** 本地临时文件列表 */ + tempFiles: MediaFile[] + /** 文件类型,有效值有 image 、video */ + type: string + errMsg: string + } + interface ChooseMessageFileOption { + /** 最多可以选择的文件个数,可以 0~100 */ + count: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ChooseMessageFileCompleteCallback + /** 根据文件拓展名过滤,仅 type==file 时有效。每一项都不能是空字符串。默认不过滤。 + * + * 最低基础库: `2.6.0` */ + extension?: string[] + /** 接口调用失败的回调函数 */ + fail?: ChooseMessageFileFailCallback + /** 接口调用成功的回调函数 */ + success?: ChooseMessageFileSuccessCallback + /** 所选的文件的类型 + * + * 可选值: + * - 'all': 从所有文件选择; + * - 'video': 只能选择视频文件; + * - 'image': 只能选择图片文件; + * - 'file': 可以选择除了图片和视频之外的其它的文件; */ + type?: 'all' | 'video' | 'image' | 'file' + } + interface ChooseMessageFileSuccessCallbackResult { + /** 返回选择的文件的本地临时文件对象数组 */ + tempFiles: ChooseFile[] + errMsg: string + } + interface ChooseVideoOption { + /** 默认拉起的是前置或者后置摄像头。部分 Android 手机下由于系统 ROM 不支持无法生效 + * + * 可选值: + * - 'back': 默认拉起后置摄像头; + * - 'front': 默认拉起前置摄像头; */ + camera?: 'back' | 'front' + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ChooseVideoCompleteCallback + /** 是否压缩所选择的视频文件 + * + * 最低基础库: `1.6.0` */ + compressed?: boolean + /** 接口调用失败的回调函数 */ + fail?: ChooseVideoFailCallback + /** 拍摄视频最长拍摄时间,单位秒 */ + maxDuration?: number + /** 视频选择的来源 + * + * 可选值: + * - 'album': 从相册选择视频; + * - 'camera': 使用相机拍摄视频; */ + sourceType?: Array<'album' | 'camera'> + /** 接口调用成功的回调函数 */ + success?: ChooseVideoSuccessCallback + } + interface ChooseVideoSuccessCallbackResult { + /** 选定视频的时间长度 */ + duration: number + /** 返回选定视频的高度 */ + height: number + /** 选定视频的数据量大小 */ + size: number + /** 选定视频的临时文件路径 (本地路径) */ + tempFilePath: string + /** 返回选定视频的宽度 */ + width: number + errMsg: string + } + interface ClearOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ClearCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ClearFailCallback + /** 接口调用成功的回调函数 */ + success?: ClearSuccessCallback + } + interface ClearStorageOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ClearStorageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ClearStorageFailCallback + /** 接口调用成功的回调函数 */ + success?: ClearStorageSuccessCallback + } + interface CloseBLEConnectionOption { + /** 用于区分设备的 id */ + deviceId: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CloseBLEConnectionCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CloseBLEConnectionFailCallback + /** 接口调用成功的回调函数 */ + success?: CloseBLEConnectionSuccessCallback + } + interface CloseBluetoothAdapterOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CloseBluetoothAdapterCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CloseBluetoothAdapterFailCallback + /** 接口调用成功的回调函数 */ + success?: CloseBluetoothAdapterSuccessCallback + } + interface CloseSocketOption { + /** 一个数字值表示关闭连接的状态号,表示连接被关闭的原因。 */ + code?: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CloseSocketCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CloseSocketFailCallback + /** 一个可读的字符串,表示连接被关闭的原因。这个字符串必须是不长于 123 字节的 UTF-8 文本(不是字符)。 */ + reason?: string + /** 接口调用成功的回调函数 */ + success?: CloseSocketSuccessCallback + } + /** 颜色。可以用以下几种方式来表示 canvas 中使用的颜色: + * + * - RGB 颜色: 如 `'rgb(255, 0, 0)'` + * - RGBA 颜色:如 `'rgba(255, 0, 0, 0.3)'` + * - 16 进制颜色: 如 `'#FF0000'` + * - 预定义的颜色: 如 `'red'` + * + * 其中预定义颜色有以下148个: + * *注意**: Color Name 大小写不敏感 + * + * | Color Name | HEX | + * | -------------------- | ------- | + * | AliceBlue | #F0F8FF | + * | AntiqueWhite | #FAEBD7 | + * | Aqua | #00FFFF | + * | Aquamarine | #7FFFD4 | + * | Azure | #F0FFFF | + * | Beige | #F5F5DC | + * | Bisque | #FFE4C4 | + * | Black | #000000 | + * | BlanchedAlmond | #FFEBCD | + * | Blue | #0000FF | + * | BlueViolet | #8A2BE2 | + * | Brown | #A52A2A | + * | BurlyWood | #DEB887 | + * | CadetBlue | #5F9EA0 | + * | Chartreuse | #7FFF00 | + * | Chocolate | #D2691E | + * | Coral | #FF7F50 | + * | CornflowerBlue | #6495ED | + * | Cornsilk | #FFF8DC | + * | Crimson | #DC143C | + * | Cyan | #00FFFF | + * | DarkBlue | #00008B | + * | DarkCyan | #008B8B | + * | DarkGoldenRod | #B8860B | + * | DarkGray | #A9A9A9 | + * | DarkGrey | #A9A9A9 | + * | DarkGreen | #006400 | + * | DarkKhaki | #BDB76B | + * | DarkMagenta | #8B008B | + * | DarkOliveGreen | #556B2F | + * | DarkOrange | #FF8C00 | + * | DarkOrchid | #9932CC | + * | DarkRed | #8B0000 | + * | DarkSalmon | #E9967A | + * | DarkSeaGreen | #8FBC8F | + * | DarkSlateBlue | #483D8B | + * | DarkSlateGray | #2F4F4F | + * | DarkSlateGrey | #2F4F4F | + * | DarkTurquoise | #00CED1 | + * | DarkViolet | #9400D3 | + * | DeepPink | #FF1493 | + * | DeepSkyBlue | #00BFFF | + * | DimGray | #696969 | + * | DimGrey | #696969 | + * | DodgerBlue | #1E90FF | + * | FireBrick | #B22222 | + * | FloralWhite | #FFFAF0 | + * | ForestGreen | #228B22 | + * | Fuchsia | #FF00FF | + * | Gainsboro | #DCDCDC | + * | GhostWhite | #F8F8FF | + * | Gold | #FFD700 | + * | GoldenRod | #DAA520 | + * | Gray | #808080 | + * | Grey | #808080 | + * | Green | #008000 | + * | GreenYellow | #ADFF2F | + * | HoneyDew | #F0FFF0 | + * | HotPink | #FF69B4 | + * | IndianRed | #CD5C5C | + * | Indigo | #4B0082 | + * | Ivory | #FFFFF0 | + * | Khaki | #F0E68C | + * | Lavender | #E6E6FA | + * | LavenderBlush | #FFF0F5 | + * | LawnGreen | #7CFC00 | + * | LemonChiffon | #FFFACD | + * | LightBlue | #ADD8E6 | + * | LightCoral | #F08080 | + * | LightCyan | #E0FFFF | + * | LightGoldenRodYellow | #FAFAD2 | + * | LightGray | #D3D3D3 | + * | LightGrey | #D3D3D3 | + * | LightGreen | #90EE90 | + * | LightPink | #FFB6C1 | + * | LightSalmon | #FFA07A | + * | LightSeaGreen | #20B2AA | + * | LightSkyBlue | #87CEFA | + * | LightSlateGray | #778899 | + * | LightSlateGrey | #778899 | + * | LightSteelBlue | #B0C4DE | + * | LightYellow | #FFFFE0 | + * | Lime | #00FF00 | + * | LimeGreen | #32CD32 | + * | Linen | #FAF0E6 | + * | Magenta | #FF00FF | + * | Maroon | #800000 | + * | MediumAquaMarine | #66CDAA | + * | MediumBlue | #0000CD | + * | MediumOrchid | #BA55D3 | + * | MediumPurple | #9370DB | + * | MediumSeaGreen | #3CB371 | + * | MediumSlateBlue | #7B68EE | + * | MediumSpringGreen | #00FA9A | + * | MediumTurquoise | #48D1CC | + * | MediumVioletRed | #C71585 | + * | MidnightBlue | #191970 | + * | MintCream | #F5FFFA | + * | MistyRose | #FFE4E1 | + * | Moccasin | #FFE4B5 | + * | NavajoWhite | #FFDEAD | + * | Navy | #000080 | + * | OldLace | #FDF5E6 | + * | Olive | #808000 | + * | OliveDrab | #6B8E23 | + * | Orange | #FFA500 | + * | OrangeRed | #FF4500 | + * | Orchid | #DA70D6 | + * | PaleGoldenRod | #EEE8AA | + * | PaleGreen | #98FB98 | + * | PaleTurquoise | #AFEEEE | + * | PaleVioletRed | #DB7093 | + * | PapayaWhip | #FFEFD5 | + * | PeachPuff | #FFDAB9 | + * | Peru | #CD853F | + * | Pink | #FFC0CB | + * | Plum | #DDA0DD | + * | PowderBlue | #B0E0E6 | + * | Purple | #800080 | + * | RebeccaPurple | #663399 | + * | Red | #FF0000 | + * | RosyBrown | #BC8F8F | + * | RoyalBlue | #4169E1 | + * | SaddleBrown | #8B4513 | + * | Salmon | #FA8072 | + * | SandyBrown | #F4A460 | + * | SeaGreen | #2E8B57 | + * | SeaShell | #FFF5EE | + * | Sienna | #A0522D | + * | Silver | #C0C0C0 | + * | SkyBlue | #87CEEB | + * | SlateBlue | #6A5ACD | + * | SlateGray | #708090 | + * | SlateGrey | #708090 | + * | Snow | #FFFAFA | + * | SpringGreen | #00FF7F | + * | SteelBlue | #4682B4 | + * | Tan | #D2B48C | + * | Teal | #008080 | + * | Thistle | #D8BFD8 | + * | Tomato | #FF6347 | + * | Turquoise | #40E0D0 | + * | Violet | #EE82EE | + * | Wheat | #F5DEB3 | + * | White | #FFFFFF | + * | WhiteSmoke | #F5F5F5 | + * | Yellow | #FFFF00 | + * | YellowGreen | #9ACD32 | */ + interface Color {} + interface CompressImageOption { + /** 图片路径,图片的路径,支持本地路径、代码包路径 */ + src: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CompressImageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CompressImageFailCallback + /** 压缩质量,范围0~100,数值越小,质量越低,压缩率越高(仅对jpg有效)。 */ + quality?: number + /** 接口调用成功的回调函数 */ + success?: CompressImageSuccessCallback + } + interface CompressImageSuccessCallbackResult { + /** 压缩后图片的临时文件路径 (本地路径) */ + tempFilePath: string + errMsg: string + } + interface CompressVideoOption { + /** 码率,单位 kbps */ + bitrate: number + /** 帧率 */ + fps: number + /** 压缩质量 + * + * 可选值: + * - 'low': 低; + * - 'medium': 中; + * - 'high': 高; */ + quality: 'low' | 'medium' | 'high' + /** 相对于原视频的分辨率比例,取值范围(0, 1] */ + resolution: number + /** 视频文件路径,可以是临时文件路径也可以是永久文件路径 */ + src: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CompressVideoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CompressVideoFailCallback + /** 接口调用成功的回调函数 */ + success?: CompressVideoSuccessCallback + } + interface CompressVideoSuccessCallbackResult { + /** 压缩后的大小,单位 kB */ + size: string + /** 压缩后的临时文件地址 */ + tempFilePath: string + errMsg: string + } + interface ConnectOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ConnectCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ConnectFailCallback + /** 接口调用成功的回调函数 */ + success?: ConnectSuccessCallback + } + interface ConnectSocketOption { + /** 开发者服务器 wss 接口地址 */ + url: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ConnectSocketCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ConnectSocketFailCallback + /** HTTP Header,Header 中不能设置 Referer */ + header?: IAnyObject + /** 是否开启压缩扩展 + * + * 最低基础库: `2.8.0` */ + perMessageDeflate?: boolean + /** 子协议数组 + * + * 最低基础库: `1.4.0` */ + protocols?: string[] + /** 接口调用成功的回调函数 */ + success?: ConnectSocketSuccessCallback + /** 建立 TCP 连接的时候的 TCP_NODELAY 设置 + * + * 最低基础库: `2.4.0` */ + tcpNoDelay?: boolean + /** 超时时间,单位为毫秒 + * + * 最低基础库: `2.10.0` */ + timeout?: number + } + interface ConnectWifiOption { + /** Wi-Fi 设备 SSID */ + SSID: string + /** Wi-Fi 设备密码 */ + password: string + /** Wi-Fi 设备 BSSID */ + BSSID?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ConnectWifiCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ConnectWifiFailCallback + /** 跳转到系统设置页进行连接,仅安卓生效 + * + * 最低基础库: `2.12.0` */ + maunal?: boolean + /** 接口调用成功的回调函数 */ + success?: ConnectWifiSuccessCallback + } + interface ContextCallbackResult { + /** 节点对应的 Context 对象 */ + context: IAnyObject + } + interface CopyFileFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail permission denied, copyFile ${srcPath} -> ${destPath}': 指定目标文件路径没有写权限; + * - 'fail no such file or directory, copyFile ${srcPath} -> ${destPath}': 源文件不存在,或目标文件路径的上层目录不存在; + * - 'fail the maximum size of the file storage limit is exceeded': 存储空间不足; + * - 'fail sdcard not mounted': Android sdcard 挂载失败; */ + errMsg: string + } + interface CopyFileOption { + /** 目标文件路径,支持本地路径 */ + destPath: string + /** 源文件路径,支持本地路径 */ + srcPath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CopyFileCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CopyFileFailCallback + /** 接口调用成功的回调函数 */ + success?: CopyFileSuccessCallback + } + interface CreateBLEConnectionOption { + /** 用于区分设备的 id */ + deviceId: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CreateBLEConnectionCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CreateBLEConnectionFailCallback + /** 接口调用成功的回调函数 */ + success?: CreateBLEConnectionSuccessCallback + /** 超时时间,单位ms,不填表示不会超时 */ + timeout?: number + } + interface CreateBLEPeripheralServerOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: CreateBLEPeripheralServerCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: CreateBLEPeripheralServerFailCallback + /** 接口调用成功的回调函数 */ + success?: CreateBLEPeripheralServerSuccessCallback + } + interface CreateBLEPeripheralServerSuccessCallbackResult { + /** [BLEPeripheralServer](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.html) + * + * 外围设备的服务端。 */ + server: BLEPeripheralServer + errMsg: string + } + /** 选项 */ + interface CreateIntersectionObserverOption { + /** 初始的相交比例,如果调用时检测到的相交比例与这个值不相等且达到阈值,则会触发一次监听器的回调函数。 */ + initialRatio?: number + /** 是否同时观测多个目标节点(而非一个),如果设为 true ,observe 的 targetSelector 将选中多个节点(注意:同时选中过多节点将影响渲染性能) + * + * 最低基础库: `2.0.0` */ + observeAll?: boolean + /** 一个数值数组,包含所有阈值。 */ + thresholds?: number[] + } + interface CreateInterstitialAdOption { + /** 广告单元 id */ + adUnitId: string + } + interface CreateMediaRecorderOption { + /** 指定录制的时长(s),到达自动停止。最大 7200,最小 5 */ + duration?: number + /** 视频 fps */ + fps?: number + /** 视频关键帧间隔 */ + gop?: number + /** 视频比特率(kbps),最小值 600,最大值 3000 */ + videoBitsPerSecond?: number + } + interface CreateRewardedVideoAdOption { + /** 广告单元 id */ + adUnitId: string + /** 是否启用多例模式,默认为false + * + * 最低基础库: `2.8.0` */ + multiton?: boolean + } + /** 可选参数 */ + interface CreateWorkerOption { + /** 是否使用实验worker。在iOS下,实验worker的JS运行效率比非实验worker提升近十倍,如需在worker内进行重度计算的建议开启此选项。 + * + * 最低基础库: `2.13.0` */ + useExperimentalWorker?: boolean + } + /** 弹幕内容 */ + interface Danmu { + /** 弹幕文字 */ + text: string + /** 弹幕颜色 */ + color?: string + } + /** 可选的字体描述符 */ + interface DescOption { + /** 字体样式,可选值为 normal / italic / oblique */ + style?: string + /** 设置小型大写字母的字体显示文本,可选值为 normal / small-caps / inherit */ + variant?: string + /** 字体粗细,可选值为 normal / bold / 100 / 200../ 900 */ + weight?: string + } + /** 描述符的权限 */ + interface DescriptorPermission { + /** 读 */ + read?: boolean + /** 写 */ + write?: boolean + } + /** 指定 marker 移动到的目标点 */ + interface DestinationOption { + /** 纬度 */ + latitude: number + /** 经度 */ + longitude: number + } + interface DisableAlertBeforeUnloadOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: DisableAlertBeforeUnloadCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: DisableAlertBeforeUnloadFailCallback + /** 接口调用成功的回调函数 */ + success?: DisableAlertBeforeUnloadSuccessCallback + } + interface DownloadFileOption { + /** 下载资源的 url */ + url: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: DownloadFileCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: DownloadFileFailCallback + /** 指定文件下载后存储的路径 (本地路径) + * + * 最低基础库: `1.8.0` */ + filePath?: string + /** HTTP 请求的 Header,Header 中不能设置 Referer */ + header?: IAnyObject + /** 接口调用成功的回调函数 */ + success?: DownloadFileSuccessCallback + /** 超时时间,单位为毫秒 + * + * 最低基础库: `2.10.0` */ + timeout?: number + } + interface DownloadFileSuccessCallbackResult { + /** 用户文件路径 (本地路径)。传入 filePath 时会返回,跟传入的 filePath 一致 */ + filePath: string + /** 网络请求过程中一些调试信息 + * + * 最低基础库: `2.10.4` */ + profile: DownloadProfile + /** 开发者服务器返回的 HTTP 状态码 */ + statusCode: number + /** 临时文件路径 (本地路径)。没传入 filePath 指定文件存储路径时会返回,下载后的文件会存储到一个临时文件 */ + tempFilePath: string + errMsg: string + } + /** 网络请求过程中一些调试信息 + * + * 最低基础库: `2.10.4` */ + interface DownloadProfile { + /** SSL建立完成的时间,如果不是安全连接,则值为 0 */ + SSLconnectionEnd: number + /** SSL建立连接的时间,如果不是安全连接,则值为 0 */ + SSLconnectionStart: number + /** HTTP(TCP) 完成建立连接的时间(完成握手),如果是持久连接,则与 fetchStart 值相等。注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接完成的时间。注意这里握手结束,包括安全连接建立完成、SOCKS 授权通过 */ + connectEnd: number + /** HTTP(TCP) 开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等。注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接开始的时间 */ + connectStart: number + /** DNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 */ + domainLookupEnd: number + /** DNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 */ + domainLookupStart: number + /** 评估当前网络下载的kbps */ + downstreamThroughputKbpsEstimate: number + /** 评估的网络状态 slow 2g/2g/3g/4g */ + estimate_nettype: string + /** 组件准备好使用 HTTP 请求抓取资源的时间,这发生在检查本地缓存之前 */ + fetchStart: number + /** 协议层根据多个请求评估当前网络的 rtt(仅供参考) */ + httpRttEstimate: number + /** 当前请求的IP */ + peerIP: string + /** 当前请求的端口 */ + port: number + /** 收到字节数 */ + receivedBytedCount: number + /** 最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内部的重定向才算,否则值为 0 */ + redirectEnd: number + /** 第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为 0 */ + redirectStart: number + /** HTTP请求读取真实文档结束的时间 */ + requestEnd: number + /** HTTP请求读取真实文档开始的时间(完成建立连接),包括从本地读取缓存。连接错误重连时,这里显示的也是新建立连接的时间 */ + requestStart: number + /** HTTP 响应全部接收完成的时间(获取到最后一个字节),包括从本地读取缓存 */ + responseEnd: number + /** HTTP 开始接收响应的时间(获取到第一个字节),包括从本地读取缓存 */ + responseStart: number + /** 当次请求连接过程中实时 rtt */ + rtt: number + /** 发送的字节数 */ + sendBytesCount: number + /** 是否复用连接 */ + socketReused: boolean + /** 当前网络的实际下载kbps */ + throughputKbps: number + /** 传输层根据多个请求评估的当前网络的 rtt(仅供参考) */ + transportRttEstimate: number + } + interface DownloadTaskOnProgressUpdateCallbackResult { + /** 下载进度百分比 */ + progress: number + /** 预期需要下载的数据总长度,单位 Bytes */ + totalBytesExpectedToWrite: number + /** 已经下载的数据长度,单位 Bytes */ + totalBytesWritten: number + } + interface EnableAlertBeforeUnloadOption { + /** 询问对话框内容 */ + message: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: EnableAlertBeforeUnloadCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: EnableAlertBeforeUnloadFailCallback + /** 接口调用成功的回调函数 */ + success?: EnableAlertBeforeUnloadSuccessCallback + } + interface ExitFullScreenOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ExitFullScreenCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ExitFullScreenFailCallback + /** 接口调用成功的回调函数 */ + success?: ExitFullScreenSuccessCallback + } + interface ExitPictureInPictureOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ExitPictureInPictureCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ExitPictureInPictureFailCallback + /** 接口调用成功的回调函数 */ + success?: ExitPictureInPictureSuccessCallback + } + interface ExitVoIPChatOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ExitVoIPChatCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ExitVoIPChatFailCallback + /** 接口调用成功的回调函数 */ + success?: ExitVoIPChatSuccessCallback + } + interface ExtractDataSourceOption { + /** 视频源地址,只支持本地文件 */ + source: string + } + interface Fields { + /** 指定样式名列表,返回节点对应样式名的当前值 + * + * 最低基础库: `2.1.0` */ + computedStyle?: string[] + /** 是否返回节点对应的 Context 对象 + * + * 最低基础库: `2.4.2` */ + context?: boolean + /** 是否返回节点 dataset */ + dataset?: boolean + /** 是否返回节点 id */ + id?: boolean + /** 是否返回节点 mark */ + mark?: boolean + /** 是否返回节点对应的 Node 实例 + * + * 最低基础库: `2.7.0` */ + node?: boolean + /** 指定属性名列表,返回节点对应属性名的当前属性值(只能获得组件文档中标注的常规属性值,id class style 和事件绑定的属性值不可获取) */ + properties?: string[] + /** 是否返回节点布局位置(`left` `right` `top` `bottom`) */ + rect?: boolean + /** 否 是否返回节点的 `scrollLeft` `scrollTop`,节点必须是 `scroll-view` 或者 `viewport` */ + scrollOffset?: boolean + /** 是否返回节点尺寸(`width` `height`) */ + size?: boolean + } + interface FileItem { + /** 文件保存时的时间戳,从1970/01/01 08:00:00 到当前时间的秒数 */ + createTime: number + /** 文件路径 (本地路径) */ + filePath: string + /** 本地文件大小,以字节为单位 */ + size: number + } + interface FileSystemManagerGetFileInfoOption { + /** 要读取的文件路径 (本地路径) */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetFileInfoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: FileSystemManagerGetFileInfoFailCallback + /** 接口调用成功的回调函数 */ + success?: FileSystemManagerGetFileInfoSuccessCallback + } + interface FileSystemManagerGetFileInfoSuccessCallbackResult { + /** 文件大小,以字节为单位 */ + size: number + errMsg: string + } + interface FileSystemManagerGetSavedFileListOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetSavedFileListCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetSavedFileListFailCallback + /** 接口调用成功的回调函数 */ + success?: FileSystemManagerGetSavedFileListSuccessCallback + } + interface FileSystemManagerGetSavedFileListSuccessCallbackResult { + /** 文件数组 */ + fileList: FileItem[] + errMsg: string + } + interface FileSystemManagerRemoveSavedFileOption { + /** 需要删除的文件路径 (本地路径) */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RemoveSavedFileCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: FileSystemManagerRemoveSavedFileFailCallback + /** 接口调用成功的回调函数 */ + success?: RemoveSavedFileSuccessCallback + } + interface FileSystemManagerSaveFileOption { + /** 临时存储文件路径 (本地路径) */ + tempFilePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SaveFileCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: FileSystemManagerSaveFileFailCallback + /** 要存储的文件路径 (本地路径) */ + filePath?: string + /** 接口调用成功的回调函数 */ + success?: SaveFileSuccessCallback + } + /** 打开的文件信息数组,只有从聊天素材场景打开(scene为1173)才会携带该参数 */ + interface ForwardMaterials { + /** 文件名 */ + name: string + /** 文件路径(如果是webview则是url) */ + path: string + /** 文件大小 */ + size: number + /** 文件的mimetype类型 */ + type: string + } + /** 视频帧数据,若取不到则返回 null。当缓冲区为空的时候可能暂停取不到数据。 */ + interface FrameDataOptions { + /** 帧数据 */ + data: ArrayBuffer + /** 帧数据高度 */ + height: number + /** 帧原始 dts */ + pkDts: number + /** 帧原始 pts */ + pkPts: number + /** 帧数据宽度 */ + width: number + } + interface FromScreenLocationOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: FromScreenLocationCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: FromScreenLocationFailCallback + /** 接口调用成功的回调函数 */ + success?: FromScreenLocationSuccessCallback + } + interface GetAtqaOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetAtqaCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetAtqaFailCallback + /** 接口调用成功的回调函数 */ + success?: GetAtqaSuccessCallback + } + interface GetAtqaSuccessCallbackResult { + /** 返回 ATQA/SENS_RES 数据 */ + atqa: ArrayBuffer + errMsg: string + } + interface GetAvailableAudioSourcesOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetAvailableAudioSourcesCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetAvailableAudioSourcesFailCallback + /** 接口调用成功的回调函数 */ + success?: GetAvailableAudioSourcesSuccessCallback + } + interface GetAvailableAudioSourcesSuccessCallbackResult { + /** 支持的音频输入源列表,可在 [RecorderManager.start()](https://developers.weixin.qq.com/miniprogram/dev/api/media/recorder/RecorderManager.start.html) 接口中使用。返回值定义参考 https://developer.android.com/reference/kotlin/android/media/MediaRecorder.AudioSource + * + * 可选值: + * - 'auto': 自动设置,默认使用手机麦克风,插上耳麦后自动切换使用耳机麦克风,所有平台适用; + * - 'buildInMic': 手机麦克风,仅限 iOS; + * - 'headsetMic': 耳机麦克风,仅限 iOS; + * - 'mic': 麦克风(没插耳麦时是手机麦克风,插耳麦时是耳机麦克风),仅限 Android; + * - 'camcorder': 同 mic,适用于录制音视频内容,仅限 Android; + * - 'voice_communication': 同 mic,适用于实时沟通,仅限 Android; + * - 'voice_recognition': 同 mic,适用于语音识别,仅限 Android; */ + audioSources: Array< + | 'auto' + | 'buildInMic' + | 'headsetMic' + | 'mic' + | 'camcorder' + | 'voice_communication' + | 'voice_recognition' + > + errMsg: string + } + interface GetBLEDeviceCharacteristicsOption { + /** 蓝牙设备 id */ + deviceId: string + /** 蓝牙服务 uuid,需要使用 `getBLEDeviceServices` 获取 */ + serviceId: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetBLEDeviceCharacteristicsCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetBLEDeviceCharacteristicsFailCallback + /** 接口调用成功的回调函数 */ + success?: GetBLEDeviceCharacteristicsSuccessCallback + } + interface GetBLEDeviceCharacteristicsSuccessCallbackResult { + /** 设备特征值列表 */ + characteristics: BLECharacteristic[] + errMsg: string + } + interface GetBLEDeviceRSSIOption { + /** 蓝牙设备 id */ + deviceId: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetBLEDeviceRSSICompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetBLEDeviceRSSIFailCallback + /** 接口调用成功的回调函数 */ + success?: GetBLEDeviceRSSISuccessCallback + } + interface GetBLEDeviceRSSISuccessCallbackResult { + /** 信号强度 */ + RSSI: number + errMsg: string + } + interface GetBLEDeviceServicesOption { + /** 蓝牙设备 id */ + deviceId: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetBLEDeviceServicesCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetBLEDeviceServicesFailCallback + /** 接口调用成功的回调函数 */ + success?: GetBLEDeviceServicesSuccessCallback + } + interface GetBLEDeviceServicesSuccessCallbackResult { + /** 设备服务列表 */ + services: BLEService[] + errMsg: string + } + interface GetBackgroundAudioPlayerStateOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetBackgroundAudioPlayerStateCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetBackgroundAudioPlayerStateFailCallback + /** 接口调用成功的回调函数 */ + success?: GetBackgroundAudioPlayerStateSuccessCallback + } + interface GetBackgroundAudioPlayerStateSuccessCallbackResult { + /** 选定音频的播放位置(单位:s),只有在音乐播放中时返回 */ + currentPosition: number + /** 歌曲数据链接,只有在音乐播放中时返回 */ + dataUrl: string + /** 音频的下载进度百分比,只有在音乐播放中时返回 */ + downloadPercent: number + /** 选定音频的长度(单位:s),只有在音乐播放中时返回 */ + duration: number + /** 播放状态 + * + * 可选值: + * - 0: 暂停中; + * - 1: 播放中; + * - 2: 没有音乐播放; */ + status: 0 | 1 | 2 + errMsg: string + } + interface GetBackgroundFetchDataOption { + /** 取值为 periodic */ + fetchType: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetBackgroundFetchDataCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetBackgroundFetchDataFailCallback + /** 接口调用成功的回调函数 */ + success?: GetBackgroundFetchDataSuccessCallback + } + interface GetBackgroundFetchTokenOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetBackgroundFetchTokenCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetBackgroundFetchTokenFailCallback + /** 接口调用成功的回调函数 */ + success?: GetBackgroundFetchTokenSuccessCallback + } + interface GetBatteryInfoOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetBatteryInfoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetBatteryInfoFailCallback + /** 接口调用成功的回调函数 */ + success?: GetBatteryInfoSuccessCallback + } + interface GetBatteryInfoSuccessCallbackResult { + /** 是否正在充电中 */ + isCharging: boolean + /** 设备电量,范围 1 - 100 */ + level: string + errMsg: string + } + interface GetBatteryInfoSyncResult { + /** 是否正在充电中 */ + isCharging: boolean + /** 设备电量,范围 1 - 100 */ + level: string + } + interface GetBeaconsOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetBeaconsCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetBeaconsFailCallback + /** 接口调用成功的回调函数 */ + success?: GetBeaconsSuccessCallback + } + interface GetBeaconsSuccessCallbackResult { + /** iBeacon 设备列表 */ + beacons: IBeaconInfo[] + errMsg: string + } + interface GetBluetoothAdapterStateOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetBluetoothAdapterStateCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetBluetoothAdapterStateFailCallback + /** 接口调用成功的回调函数 */ + success?: GetBluetoothAdapterStateSuccessCallback + } + interface GetBluetoothAdapterStateSuccessCallbackResult { + /** 蓝牙适配器是否可用 */ + available: boolean + /** 是否正在搜索设备 */ + discovering: boolean + errMsg: string + } + interface GetBluetoothDevicesOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetBluetoothDevicesCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetBluetoothDevicesFailCallback + /** 接口调用成功的回调函数 */ + success?: GetBluetoothDevicesSuccessCallback + } + interface GetBluetoothDevicesSuccessCallbackResult { + /** uuid 对应的的已连接设备列表 */ + devices: BlueToothDevice[] + errMsg: string + } + interface GetCenterLocationOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetCenterLocationCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetCenterLocationFailCallback + /** 接口调用成功的回调函数 */ + success?: GetCenterLocationSuccessCallback + } + interface GetCenterLocationSuccessCallbackResult { + /** 纬度 */ + latitude: number + /** 经度 */ + longitude: number + errMsg: string + } + interface GetClipboardDataOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetClipboardDataCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetClipboardDataFailCallback + /** 接口调用成功的回调函数 */ + success?: GetClipboardDataSuccessCallback + } + interface GetClipboardDataSuccessCallbackOption { + /** 剪贴板的内容 */ + data: string + } + interface GetConnectedBluetoothDevicesOption { + /** 蓝牙设备主 service 的 uuid 列表 */ + services: string[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetConnectedBluetoothDevicesCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetConnectedBluetoothDevicesFailCallback + /** 接口调用成功的回调函数 */ + success?: GetConnectedBluetoothDevicesSuccessCallback + } + interface GetConnectedBluetoothDevicesSuccessCallbackResult { + /** 搜索到的设备列表 */ + devices: BluetoothDeviceInfo[] + errMsg: string + } + interface GetConnectedWifiOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetConnectedWifiCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetConnectedWifiFailCallback + /** 接口调用成功的回调函数 */ + success?: GetConnectedWifiSuccessCallback + } + interface GetConnectedWifiSuccessCallbackResult { + /** [WifiInfo](https://developers.weixin.qq.com/miniprogram/dev/api/device/wifi/WifiInfo.html) + * + * Wi-Fi 信息 */ + wifi: WifiInfo + errMsg: string + } + interface GetContentsOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetContentsCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetContentsFailCallback + /** 接口调用成功的回调函数 */ + success?: GetContentsSuccessCallback + } + interface GetContentsSuccessCallbackResult { + /** 表示内容的delta对象 */ + delta: IAnyObject + /** 带标签的HTML内容 */ + html: string + /** 纯文本内容 */ + text: string + errMsg: string + } + interface GetExtConfigOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetExtConfigCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetExtConfigFailCallback + /** 接口调用成功的回调函数 */ + success?: GetExtConfigSuccessCallback + } + interface GetExtConfigSuccessCallbackResult { + /** 第三方平台自定义的数据 */ + extConfig: IAnyObject + errMsg: string + } + interface GetFileInfoFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail file not exist': 指定的 filePath 找不到文件; */ + errMsg: string + } + interface GetGroupEnterInfoOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetGroupEnterInfoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetGroupEnterInfoFailCallback + /** 接口调用成功的回调函数 */ + success?: GetGroupEnterInfoSuccessCallback + } + interface GetGroupEnterInfoSuccessCallbackResult { + /** 敏感数据对应的云 ID,开通[云开发](https://developers.weixin.qq.com/miniprogram/dev/wxcloud/basis/getting-started.html)的小程序才会返回,可通过云调用直接获取开放数据,详细见[云调用直接获取开放数据](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html#method-cloud) + * + * 最低基础库: `2.7.0` */ + cloudID: string + /** 包括敏感数据在内的完整转发信息的加密数据,详细见[加密数据解密算法](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html) */ + encryptedData: string + /** 错误信息 */ + errMsg: string + /** 加密算法的初始向量,详细见[加密数据解密算法](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html) */ + iv: string + } + interface GetHCEStateOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetHCEStateCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetHCEStateFailCallback + /** 接口调用成功的回调函数 */ + success?: GetHCEStateSuccessCallback + } + interface GetHistoricalBytesOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetHistoricalBytesCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetHistoricalBytesFailCallback + /** 接口调用成功的回调函数 */ + success?: GetHistoricalBytesSuccessCallback + } + interface GetHistoricalBytesSuccessCallbackResult { + /** 返回历史二进制数据 */ + histBytes: ArrayBuffer + errMsg: string + } + interface GetImageInfoOption { + /** 图片的路径,支持网络路径、本地路径、代码包路径 */ + src: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetImageInfoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetImageInfoFailCallback + /** 接口调用成功的回调函数 */ + success?: GetImageInfoSuccessCallback + } + interface GetImageInfoSuccessCallbackResult { + /** 图片原始高度,单位px。不考虑旋转。 */ + height: number + /** [拍照时设备方向](http://sylvana.net/jpegcrop/exif_orientation.html) + * + * 可选值: + * - 'up': 默认方向(手机横持拍照),对应 Exif 中的 1。或无 orientation 信息。; + * - 'up-mirrored': 同 up,但镜像翻转,对应 Exif 中的 2; + * - 'down': 旋转180度,对应 Exif 中的 3; + * - 'down-mirrored': 同 down,但镜像翻转,对应 Exif 中的 4; + * - 'left-mirrored': 同 left,但镜像翻转,对应 Exif 中的 5; + * - 'right': 顺时针旋转90度,对应 Exif 中的 6; + * - 'right-mirrored': 同 right,但镜像翻转,对应 Exif 中的 7; + * - 'left': 逆时针旋转90度,对应 Exif 中的 8; + * + * 最低基础库: `1.9.90` */ + orientation: + | 'up' + | 'up-mirrored' + | 'down' + | 'down-mirrored' + | 'left-mirrored' + | 'right' + | 'right-mirrored' + | 'left' + /** 图片的本地路径 */ + path: string + /** 图片格式 + * + * 最低基础库: `1.9.90` */ + type: string + /** 图片原始宽度,单位px。不考虑旋转。 */ + width: number + errMsg: string + } + interface GetLocationOption { + /** 传入 true 会返回高度信息,由于获取高度需要较高精确度,会减慢接口返回速度 + * + * 最低基础库: `1.6.0` */ + altitude?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetLocationCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetLocationFailCallback + /** 高精度定位超时时间(ms),指定时间内返回最高精度,该值3000ms以上高精度定位才有效果 + * + * 最低基础库: `2.9.0` */ + highAccuracyExpireTime?: number + /** 开启高精度定位 + * + * 最低基础库: `2.9.0` */ + isHighAccuracy?: boolean + /** 接口调用成功的回调函数 */ + success?: GetLocationSuccessCallback + /** wgs84 返回 gps 坐标,gcj02 返回可用于 wx.openLocation 的坐标 */ + type?: string + } + interface GetLocationSuccessCallbackResult { + /** 位置的精确度 */ + accuracy: number + /** 高度,单位 m + * + * 最低基础库: `1.2.0` */ + altitude: number + /** 水平精度,单位 m + * + * 最低基础库: `1.2.0` */ + horizontalAccuracy: number + /** 纬度,范围为 -90~90,负数表示南纬 */ + latitude: number + /** 经度,范围为 -180~180,负数表示西经 */ + longitude: number + /** 速度,单位 m/s */ + speed: number + /** 垂直精度,单位 m(Android 无法获取,返回 0) + * + * 最低基础库: `1.2.0` */ + verticalAccuracy: number + errMsg: string + } + interface GetLogManagerOption { + /** 取值为0/1,取值为0表示是否会把 `App`、`Page` 的生命周期函数和 `wx` 命名空间下的函数调用写入日志,取值为1则不会。默认值是 0 + * + * 最低基础库: `2.3.2` */ + level?: number + } + interface GetMaxTransceiveLengthOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetMaxTransceiveLengthCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetMaxTransceiveLengthFailCallback + /** 接口调用成功的回调函数 */ + success?: GetMaxTransceiveLengthSuccessCallback + } + interface GetMaxTransceiveLengthSuccessCallbackResult { + /** 最大传输长度 */ + length: number + errMsg: string + } + interface GetNetworkTypeOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetNetworkTypeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetNetworkTypeFailCallback + /** 接口调用成功的回调函数 */ + success?: GetNetworkTypeSuccessCallback + } + interface GetNetworkTypeSuccessCallbackResult { + /** 网络类型 + * + * 可选值: + * - 'wifi': wifi 网络; + * - '2g': 2g 网络; + * - '3g': 3g 网络; + * - '4g': 4g 网络; + * - '5g': 5g 网络; + * - 'unknown': Android 下不常见的网络类型; + * - 'none': 无网络; */ + networkType: 'wifi' | '2g' | '3g' | '4g' | '5g' | 'unknown' | 'none' + errMsg: string + } + interface GetRandomValuesOption { + /** 整数,生成随机数的字节数,最大 1048576 */ + length: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetRandomValuesCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetRandomValuesFailCallback + /** 接口调用成功的回调函数 */ + success?: GetRandomValuesSuccessCallback + } + interface GetRandomValuesSuccessCallbackResult { + /** 随机数内容,长度为传入的字节数 */ + randomValues: ArrayBuffer + errMsg: string + } + interface GetRegionOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetRegionCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetRegionFailCallback + /** 接口调用成功的回调函数 */ + success?: GetRegionSuccessCallback + } + interface GetRegionSuccessCallbackResult { + /** 东北角经纬度 */ + northeast: MapPostion + /** 西南角经纬度 */ + southwest: MapPostion + errMsg: string + } + interface GetRotateOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetRotateCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetRotateFailCallback + /** 接口调用成功的回调函数 */ + success?: GetRotateSuccessCallback + } + interface GetRotateSuccessCallbackResult { + /** 旋转角 */ + rotate: number + errMsg: string + } + interface GetSakOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetSakCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetSakFailCallback + /** 接口调用成功的回调函数 */ + success?: GetSakSuccessCallback + } + interface GetSakSuccessCallbackResult { + /** 返回 SAK/SEL_RES 数据 */ + sak: number + errMsg: string + } + interface GetSavedFileInfoOption { + /** 文件路径 (本地路径) */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetSavedFileInfoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetSavedFileInfoFailCallback + /** 接口调用成功的回调函数 */ + success?: GetSavedFileInfoSuccessCallback + } + interface GetSavedFileInfoSuccessCallbackResult { + /** 文件保存时的时间戳,从1970/01/01 08:00:00 到该时刻的秒数 */ + createTime: number + /** 文件大小,单位 B */ + size: number + errMsg: string + } + interface GetScaleOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetScaleCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetScaleFailCallback + /** 接口调用成功的回调函数 */ + success?: GetScaleSuccessCallback + } + interface GetScaleSuccessCallbackResult { + /** 缩放值 */ + scale: number + errMsg: string + } + interface GetScreenBrightnessOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetScreenBrightnessCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetScreenBrightnessFailCallback + /** 接口调用成功的回调函数 */ + success?: GetScreenBrightnessSuccessCallback + } + interface GetScreenBrightnessSuccessCallbackOption { + /** 屏幕亮度值,范围 0 ~ 1,0 最暗,1 最亮 */ + value: number + } + interface GetSelectedTextRangeOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetSelectedTextRangeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetSelectedTextRangeFailCallback + /** 接口调用成功的回调函数 */ + success?: GetSelectedTextRangeSuccessCallback + } + interface GetSelectedTextRangeSuccessCallbackResult { + /** 输入框光标结束位置 */ + end: number + /** 输入框光标起始位置 */ + start: number + errMsg: string + } + interface GetSelectionTextOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetSelectionTextCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetSelectionTextFailCallback + /** 接口调用成功的回调函数 */ + success?: GetSelectionTextSuccessCallback + } + interface GetSelectionTextSuccessCallbackResult { + /** 纯文本内容 */ + text: string + errMsg: string + } + interface GetSettingOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetSettingCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetSettingFailCallback + /** 接口调用成功的回调函数 */ + success?: GetSettingSuccessCallback + /** 是否同时获取用户订阅消息的订阅状态,默认不获取。注意:withSubscriptions 只返回用户勾选过订阅面板中的“总是保持以上选择,不再询问”的订阅消息。 + * + * 最低基础库: `2.10.1` */ + withSubscriptions?: boolean + } + interface GetSettingSuccessCallbackResult { + /** [AuthSetting](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/setting/AuthSetting.html) + * + * 用户授权结果 */ + authSetting: AuthSetting + /** [SubscriptionsSetting](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/setting/SubscriptionsSetting.html) + * + * 用户订阅消息设置,接口参数`withSubscriptions`值为`true`时才会返回。 + * + * 最低基础库: `2.10.1` */ + subscriptionsSetting: SubscriptionsSetting + /** [AuthSetting](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/setting/AuthSetting.html) + * + * 在插件中调用时,当前宿主小程序的用户授权结果 */ + miniprogramAuthSetting?: AuthSetting + errMsg: string + } + interface GetShareInfoOption { + /** shareTicket */ + shareTicket: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetShareInfoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetShareInfoFailCallback + /** 接口调用成功的回调函数 */ + success?: GetShareInfoSuccessCallback + /** 超时时间,单位 ms + * + * 最低基础库: `1.9.90` */ + timeout?: number + } + interface GetSkewOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetSkewCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetSkewFailCallback + /** 接口调用成功的回调函数 */ + success?: GetSkewSuccessCallback + } + interface GetSkewSuccessCallbackResult { + /** 倾斜角 */ + skew: number + errMsg: string + } + interface GetStorageInfoOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetStorageInfoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetStorageInfoFailCallback + /** 接口调用成功的回调函数 */ + success?: GetStorageInfoSuccessCallback + } + interface GetStorageInfoSuccessCallbackOption { + /** 当前占用的空间大小, 单位 KB */ + currentSize: number + /** 当前 storage 中所有的 key */ + keys: string[] + /** 限制的空间大小,单位 KB */ + limitSize: number + } + interface GetStorageInfoSyncOption { + /** 当前占用的空间大小, 单位 KB */ + currentSize: number + /** 当前 storage 中所有的 key */ + keys: string[] + /** 限制的空间大小,单位 KB */ + limitSize: number + } + interface GetStorageOption { + /** 本地缓存中指定的 key */ + key: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetStorageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetStorageFailCallback + /** 接口调用成功的回调函数 */ + success?: GetStorageSuccessCallback + } + interface GetStorageSuccessCallbackResult { + /** key对应的内容 */ + data: T + errMsg: string + } + interface GetSystemInfoAsyncOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetSystemInfoAsyncCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetSystemInfoAsyncFailCallback + /** 接口调用成功的回调函数 */ + success?: GetSystemInfoAsyncSuccessCallback + } + interface GetSystemInfoOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetSystemInfoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetSystemInfoFailCallback + /** 接口调用成功的回调函数 */ + success?: GetSystemInfoSuccessCallback + } + interface GetUserInfoOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetUserInfoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetUserInfoFailCallback + /** 显示用户信息的语言 + * + * 可选值: + * - 'en': 英文; + * - 'zh_CN': 简体中文; + * - 'zh_TW': 繁体中文; */ + lang?: 'en' | 'zh_CN' | 'zh_TW' + /** 接口调用成功的回调函数 */ + success?: GetUserInfoSuccessCallback + /** 是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。 */ + withCredentials?: boolean + } + interface GetUserInfoSuccessCallbackResult { + /** 敏感数据对应的云 ID,开通[云开发](https://developers.weixin.qq.com/miniprogram/dev/wxcloud/basis/getting-started.html)的小程序才会返回,可通过云调用直接获取开放数据,详细见[云调用直接获取开放数据](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html#method-cloud) + * + * 最低基础库: `2.7.0` */ + cloudID: string + /** 包括敏感数据在内的完整用户信息的加密数据,详见 [用户数据的签名验证和加解密]((signature#加密数据解密算法)) */ + encryptedData: string + /** 加密算法的初始向量,详见 [用户数据的签名验证和加解密]((signature#加密数据解密算法)) */ + iv: string + /** 不包括敏感信息的原始数据字符串,用于计算签名 */ + rawData: string + /** 使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见 [用户数据的签名验证和加解密](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html) */ + signature: string + /** [UserInfo](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/UserInfo.html) + * + * 用户信息对象,不包含 openid 等敏感信息 */ + userInfo: UserInfo + errMsg: string + } + interface GetUserProfileOption { + /** 声明获取用户个人信息后的用途,不超过30个字符 */ + desc: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetUserProfileCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetUserProfileFailCallback + /** 显示用户信息的语言 + * + * 可选值: + * - 'en': 英文; + * - 'zh_CN': 简体中文; + * - 'zh_TW': 繁体中文; */ + lang?: 'en' | 'zh_CN' | 'zh_TW' + /** 接口调用成功的回调函数 */ + success?: GetUserProfileSuccessCallback + } + interface GetUserProfileSuccessCallbackResult { + /** [UserInfo](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/UserInfo.html) + * + * 用户信息对象 */ + userInfo: UserInfo + errMsg: string + } + interface GetVideoInfoOption { + /** 视频文件路径,可以是临时文件路径也可以是永久文件路径 */ + src: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetVideoInfoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetVideoInfoFailCallback + /** 接口调用成功的回调函数 */ + success?: GetVideoInfoSuccessCallback + } + interface GetVideoInfoSuccessCallbackResult { + /** 视频码率,单位 kbps */ + bitrate: number + /** 视频长度 */ + duration: number + /** 视频帧率 */ + fps: number + /** 视频的长,单位 px */ + height: number + /** 画面方向 + * + * 可选值: + * - 'up': 默认; + * - 'down': 180度旋转; + * - 'left': 逆时针旋转90度; + * - 'right': 顺时针旋转90度; + * - 'up-mirrored': 同up,但水平翻转; + * - 'down-mirrored': 同down,但水平翻转; + * - 'left-mirrored': 同left,但垂直翻转; + * - 'right-mirrored': 同right,但垂直翻转; */ + orientation: + | 'up' + | 'down' + | 'left' + | 'right' + | 'up-mirrored' + | 'down-mirrored' + | 'left-mirrored' + | 'right-mirrored' + /** 视频大小,单位 kB */ + size: number + /** 视频格式 */ + type: string + /** 视频的宽,单位 px */ + width: number + errMsg: string + } + interface GetWeRunDataOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetWeRunDataCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetWeRunDataFailCallback + /** 接口调用成功的回调函数 */ + success?: GetWeRunDataSuccessCallback + } + interface GetWeRunDataSuccessCallbackResult { + /** 敏感数据对应的云 ID,开通云开发的小程序才会返回,可通过云调用直接获取开放数据,详细见[云调用直接获取开放数据](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html#method-cloud) + * + * 最低基础库: `2.7.0` */ + cloudID: string + /** 包括敏感数据在内的完整用户信息的加密数据,详细见[加密数据解密算法](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html)。解密后得到的数据结构见后文 */ + encryptedData: string + /** 加密算法的初始向量,详细见[加密数据解密算法](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html) */ + iv: string + errMsg: string + } + interface GetWifiListOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetWifiListCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetWifiListFailCallback + /** 接口调用成功的回调函数 */ + success?: GetWifiListSuccessCallback + } + interface HideHomeButtonOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: HideHomeButtonCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: HideHomeButtonFailCallback + /** 接口调用成功的回调函数 */ + success?: HideHomeButtonSuccessCallback + } + interface HideKeyboardOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: HideKeyboardCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: HideKeyboardFailCallback + /** 接口调用成功的回调函数 */ + success?: HideKeyboardSuccessCallback + } + interface HideLoadingOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: HideLoadingCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: HideLoadingFailCallback + /** 接口调用成功的回调函数 */ + success?: HideLoadingSuccessCallback + } + interface HideNavigationBarLoadingOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: HideNavigationBarLoadingCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: HideNavigationBarLoadingFailCallback + /** 接口调用成功的回调函数 */ + success?: HideNavigationBarLoadingSuccessCallback + } + interface HideShareMenuOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: HideShareMenuCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: HideShareMenuFailCallback + /** 本接口为 Beta 版本,暂只在 Android 平台支持。需要隐藏的转发按钮名称列表,默认['shareAppMessage', 'shareTimeline']。按钮名称合法值包含 "shareAppMessage"、"shareTimeline" 两种 + * + * 最低基础库: `2.11.3` */ + menus?: string[] + /** 接口调用成功的回调函数 */ + success?: HideShareMenuSuccessCallback + } + interface HideTabBarOption { + /** 是否需要动画效果 */ + animation?: boolean + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: HideTabBarCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: HideTabBarFailCallback + /** 接口调用成功的回调函数 */ + success?: HideTabBarSuccessCallback + } + interface HideTabBarRedDotOption { + /** tabBar 的哪一项,从左边算起 */ + index: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: HideTabBarRedDotCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: HideTabBarRedDotFailCallback + /** 接口调用成功的回调函数 */ + success?: HideTabBarRedDotSuccessCallback + } + interface HideToastOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: HideToastCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: HideToastFailCallback + /** 接口调用成功的回调函数 */ + success?: HideToastSuccessCallback + } + interface IBeaconInfo { + /** iBeacon 设备的距离 */ + accuracy: number + /** iBeacon 设备的主 id */ + major: string + /** iBeacon 设备的次 id */ + minor: string + /** 表示设备距离的枚举值 */ + proximity: number + /** 表示设备的信号强度 */ + rssi: number + /** iBeacon 设备广播的 uuid */ + uuid: string + } + /** 图片对象 + * + * 最低基础库: `2.7.0` */ + interface Image { + /** 图片的真实高度 */ + height: number + /** 图片加载发生错误后触发的回调函数 */ + onerror: (...args: any[]) => any + /** 图片加载完成后触发的回调函数 */ + onload: (...args: any[]) => any + /** 图片的 URL。v2.11.0 起支持传递 base64 Data URI */ + src: string + /** 图片的真实宽度 */ + width: number + } + /** ImageData 对象 + * + * 最低基础库: `2.9.0` */ + interface ImageData { + /** 一维数组,包含以 RGBA 顺序的数据,数据使用 0 至 255(包含)的整数表示 */ + data: Uint8ClampedArray + /** 使用像素描述 ImageData 的实际高度 */ + height: number + /** 使用像素描述 ImageData 的实际宽度 */ + width: number + } + /** 图片的本地临时文件列表 + * + * 最低基础库: `1.2.0` */ + interface ImageFile { + /** 本地临时文件路径 (本地路径) */ + path: string + /** 本地临时文件大小,单位 B */ + size: number + } + interface IncludePointsOption { + /** 要显示在可视区域内的坐标点列表 */ + points: MapPostion[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: IncludePointsCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: IncludePointsFailCallback + /** 坐标点形成的矩形边缘到地图边缘的距离,单位像素。格式为[上,右,下,左],安卓上只能识别数组第一项,上下左右的padding一致。开发者工具暂不支持padding参数。 */ + padding?: number[] + /** 接口调用成功的回调函数 */ + success?: IncludePointsSuccessCallback + } + interface InitMarkerClusterOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: InitMarkerClusterCompleteCallback + /** 启用默认的聚合样式 */ + enableDefaultStyle?: boolean + /** 接口调用失败的回调函数 */ + fail?: InitMarkerClusterFailCallback + /** 聚合算法的可聚合距离,即距离小于该值的点会聚合至一起,以像素为单位 */ + gridSize?: boolean + /** 接口调用成功的回调函数 */ + success?: InitMarkerClusterSuccessCallback + /** 点击已经聚合的标记点时是否实现聚合分离 */ + zoomOnClick?: boolean + } + /** InnerAudioContext 实例,可通过 [wx.createInnerAudioContext](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/wx.createInnerAudioContext.html) 接口获取实例。注意,音频播放过程中,可能被系统中断,可通过 [wx.onAudioInterruptionBegin](https://developers.weixin.qq.com/miniprogram/dev/api/base/app/app-event/wx.onAudioInterruptionBegin.html)、[wx.onAudioInterruptionEnd](https://developers.weixin.qq.com/miniprogram/dev/api/base/app/app-event/wx.onAudioInterruptionEnd.html)事件来处理这种情况。 +* +* **支持格式** +* +* +* | 格式 | iOS | Android | +* | ---- | ---- | ------- | +* | flac | x | √ | +* | m4a | √ | √ | +* | ogg | x | √ | +* | ape | x | √ | +* | amr | x | √ | +* | wma | x | √ | +* | wav | √ | √ | +* | mp3 | √ | √ | +* | mp4 | x | √ | +* | aac | √ | √ | +* | aiff | √ | x | +* | caf | √ | x | +* +* **示例代码** +* +* +* ```js +const innerAudioContext = wx.createInnerAudioContext() +innerAudioContext.autoplay = true +innerAudioContext.src = 'http://ws.stream.qqmusic.qq.com/M500001VfvsJ21xFqb.mp3?guid=ffffffff82def4af4b12b3cd9337d5e7&uin=346897220&vkey=6292F51E1E384E061FF02C31F716658E5C81F5594D561F2E88B854E81CAAB7806D5E4F103E55D33C16F3FAC506D1AB172DE8600B37E43FAD&fromtag=46' +innerAudioContext.onPlay(() => { + console.log('开始播放') +}) +innerAudioContext.onError((res) => { + console.log(res.errMsg) + console.log(res.errCode) +}) +``` */ + interface InnerAudioContext { + /** 是否自动开始播放,默认为 `false` */ + autoplay: boolean + /** 音频缓冲的时间点,仅保证当前播放时间点到此时间点内容已缓冲(只读) */ + buffered: number + /** 当前音频的播放位置(单位 s)。只有在当前有合法的 src 时返回,时间保留小数点后 6 位(只读) */ + currentTime: number + /** 当前音频的长度(单位 s)。只有在当前有合法的 src 时返回(只读) */ + duration: number + /** 是否循环播放,默认为 `false` */ + loop: boolean + /** 是否遵循系统静音开关,默认为 `true`。当此参数为 `false` 时,即使用户打开了静音开关,也能继续发出声音。从 2.3.0 版本开始此参数不生效,使用 [wx.setInnerAudioOption](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/wx.setInnerAudioOption.html) 接口统一设置。 */ + obeyMuteSwitch: boolean + /** 当前是是否暂停或停止状态(只读) */ + paused: boolean + /** 播放速度。范围 0.5-2.0,默认为 1。(Android 需要 6 及以上版本) + * + * 最低基础库: `2.11.0` */ + playbackRate: number + /** 音频资源的地址,用于直接播放。[2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 开始支持云文件ID */ + src: string + /** 开始播放的位置(单位:s),默认为 0 */ + startTime: number + /** 音量。范围 0~1。默认为 1 + * + * 最低基础库: `1.9.90` */ + volume: number + } + interface InnerAudioContextOnErrorCallbackResult { + /** + * + * 可选值: + * - 10001: 系统错误; + * - 10002: 网络错误; + * - 10003: 文件错误; + * - 10004: 格式错误; + * - -1: 未知错误; */ + errCode: 10001 | 10002 | 10003 | 10004 | -1 + errMsg: string + } + interface InsertDividerOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: InsertDividerCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: InsertDividerFailCallback + /** 接口调用成功的回调函数 */ + success?: InsertDividerSuccessCallback + } + interface InsertImageOption { + /** 图片地址,仅支持 http(s)、base64、云图片(2.8.0)、临时文件(2.8.3)。 */ + src: string + /** 图像无法显示时的替代文本 */ + alt?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: InsertImageCompleteCallback + /** data 被序列化为 name=value;name1=value2 的格式挂在属性 data-custom 上 */ + data?: IAnyObject + /** 添加到图片 img 标签上的类名 */ + extClass?: string + /** 接口调用失败的回调函数 */ + fail?: InsertImageFailCallback + /** 图片高度 (pixels/百分比) */ + height?: string + /** 接口调用成功的回调函数 */ + success?: InsertImageSuccessCallback + /** 图片宽度(pixels/百分比) */ + width?: string + } + interface InsertTextOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: InsertTextCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: InsertTextFailCallback + /** 接口调用成功的回调函数 */ + success?: InsertTextSuccessCallback + /** 文本内容 */ + text?: string + } + interface IntersectionObserverObserveCallbackResult { + /** 目标边界 */ + boundingClientRect: BoundingClientRectResult + /** 相交比例 */ + intersectionRatio: number + /** 相交区域的边界 */ + intersectionRect: IntersectionRectResult + /** 参照区域的边界 */ + relativeRect: RelativeRectResult + /** 相交检测时的时间戳 */ + time: number + } + /** 相交区域的边界 */ + interface IntersectionRectResult { + /** 下边界 */ + bottom: number + /** 高度 */ + height: number + /** 左边界 */ + left: number + /** 右边界 */ + right: number + /** 上边界 */ + top: number + /** 宽度 */ + width: number + } + interface InterstitialAdOnErrorCallbackResult { + /** 错误码 + * + * 可选值: + * - 1000: 后端接口调用失败; + * - 1001: 参数错误; + * - 1002: 广告单元无效; + * - 1003: 内部错误; + * - 1004: 无合适的广告; + * - 1005: 广告组件审核中; + * - 1006: 广告组件被驳回; + * - 1007: 广告组件被封禁; + * - 1008: 广告单元已关闭; */ + errCode: 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 + /** 错误信息 */ + errMsg: string + } + interface IsConnectedOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: IsConnectedCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: IsConnectedFailCallback + /** 接口调用成功的回调函数 */ + success?: IsConnectedSuccessCallback + } + interface JoinVoIPChatOption { + /** 小游戏内此房间/群聊的 ID。同一时刻传入相同 groupId 的用户会进入到同个实时语音房间。 */ + groupId: string + /** 验证所需的随机字符串 */ + nonceStr: string + /** 签名,用于验证小游戏的身份 */ + signature: string + /** 验证所需的时间戳 */ + timeStamp: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: JoinVoIPChatCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: JoinVoIPChatFailCallback + /** 静音设置 */ + muteConfig?: MuteConfig + /** 房间类型 + * + * 可选值: + * - 'voice': 音频房间,用于语音通话; + * - 'video': 视频房间,结合 [voip-room](https://developers.weixin.qq.com/miniprogram/dev/component/voip-room.html) 组件可显示成员画面; */ + roomType?: 'voice' | 'video' + /** 接口调用成功的回调函数 */ + success?: JoinVoIPChatSuccessCallback + } + interface JoinVoIPChatSuccessCallbackResult { + /** 错误码 */ + errCode: number + /** 调用结果 */ + errMsg: string + /** 在此通话中的成员 openId 名单 */ + openIdList: string[] + } + /** 启动参数 */ + interface LaunchOptionsApp { + /** 打开的文件信息数组,只有从聊天素材场景打开(scene为1173)才会携带该参数 */ + forwardMaterials: ForwardMaterials[] + /** 启动小程序的路径 (代码包路径) */ + path: string + /** 启动小程序的 query 参数 */ + query: IAnyObject + /** 来源信息。从另一个小程序、公众号或 App 进入小程序时返回。否则返回 `{}`。(参见后文注意) */ + referrerInfo: ReferrerInfo + /** 启动小程序的[场景值](https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/scene.html) */ + scene: number + /** shareTicket,详见[获取更多转发信息](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share.html) */ + shareTicket?: string + } + interface LivePlayerContextRequestFullScreenOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RequestFullScreenCompleteCallback + /** 设置全屏时的方向 + * + * 可选值: + * - 0: 正常竖向; + * - 90: 屏幕逆时针90度; + * - -90: 屏幕顺时针90度; */ + direction?: 0 | 90 | -90 + /** 接口调用失败的回调函数 */ + fail?: RequestFullScreenFailCallback + /** 接口调用成功的回调函数 */ + success?: RequestFullScreenSuccessCallback + } + interface LivePlayerContextSnapshotOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SnapshotCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SnapshotFailCallback + /** 图片的质量,默认原图。有效值为 raw、compressed + * + * 最低基础库: `2.10.0` */ + quality?: string + /** 接口调用成功的回调函数 */ + success?: LivePlayerContextSnapshotSuccessCallback + } + interface LivePlayerContextSnapshotSuccessCallbackResult { + /** 图片的高度 */ + height: string + /** 图片文件的临时路径 (本地路径) */ + tempImagePath: string + /** 图片的宽度 */ + width: string + errMsg: string + } + interface LivePusherContextSnapshotOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SnapshotCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SnapshotFailCallback + /** 图片的质量,默认原图。有效值为 raw、compressed + * + * 最低基础库: `2.10.0` */ + quality?: string + /** 接口调用成功的回调函数 */ + success?: LivePusherContextSnapshotSuccessCallback + } + interface LivePusherContextSnapshotSuccessCallbackResult { + /** 图片的高度 */ + height: string + /** 图片文件的临时路径 */ + tempImagePath: string + /** 图片的宽度 */ + width: string + errMsg: string + } + interface LoadFontFaceCompleteCallbackResult { + /** 加载字体结果 */ + status: string + } + interface LoadFontFaceOption { + /** 定义的字体名称 */ + family: string + /** 字体资源的地址。建议格式为 TTF 和 WOFF,WOFF2 在低版本的iOS上会不兼容。 */ + source: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: LoadFontFaceCompleteCallback + /** 可选的字体描述符 */ + desc?: DescOption + /** 接口调用失败的回调函数 */ + fail?: LoadFontFaceFailCallback + /** 是否全局生效 + * + * 最低基础库: `2.10.0` */ + global?: boolean + /** 字体作用范围,可选值为 webview / native,默认 webview,设置 native 可在 Canvas 2D 下使用 */ + scopes?: any[] + /** 接口调用成功的回调函数 */ + success?: LoadFontFaceSuccessCallback + } + interface LoginOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: LoginCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: LoginFailCallback + /** 接口调用成功的回调函数 */ + success?: LoginSuccessCallback + /** 超时时间,单位ms + * + * 最低基础库: `1.9.90` */ + timeout?: number + } + interface LoginSuccessCallbackResult { + /** 用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 [auth.code2Session](https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html),使用 code 换取 openid 和 session_key 等信息 */ + code: string + errMsg: string + } + interface MakeBluetoothPairOption { + /** 蓝牙设备 id */ + deviceId: string + /** pin 码,Base64 格式。 */ + pin: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: MakeBluetoothPairCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: MakeBluetoothPairFailCallback + /** 接口调用成功的回调函数 */ + success?: MakeBluetoothPairSuccessCallback + /** 超时时间 */ + timeout?: number + } + interface MakePhoneCallOption { + /** 需要拨打的电话号码 */ + phoneNumber: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: MakePhoneCallCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: MakePhoneCallFailCallback + /** 接口调用成功的回调函数 */ + success?: MakePhoneCallSuccessCallback + } + /** 广播的制造商信息, 仅安卓支持 */ + interface ManufacturerData { + /** 制造商ID,0x 开头的十六进制 */ + manufacturerId: string + /** 制造商信息 */ + manufacturerSpecificData?: ArrayBuffer + } + /** 图片覆盖的经纬度范围 */ + interface MapBounds { + /** 东北角经纬度 */ + northeast: MapPostion + /** 西南角经纬度 */ + southwest: MapPostion + } + interface MapPostion { + /** 纬度 */ + latitude: number + /** 经度 */ + longitude: number + } + /** 用来扩展(或收缩)参照节点布局区域的边界 */ + interface Margins { + /** 节点布局区域的下边界 */ + bottom?: number + /** 节点布局区域的左边界 */ + left?: number + /** 节点布局区域的右边界 */ + right?: number + /** 节点布局区域的上边界 */ + top?: number + } + /** MediaAudioPlayer 实例,可通过 [wx.createMediaAudioPlayer](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/wx.createMediaAudioPlayer.html) 接口获取实例。 */ + interface MediaAudioPlayer { + /** 音量。范围 0~1。默认为 1 */ + volume: number + } + /** 本地临时文件列表 */ + interface MediaFile { + /** 视频的时间长度 */ + duration: number + /** 视频的高度 */ + height: number + /** 本地临时文件大小,单位 B */ + size: number + /** 本地临时文件路径 (本地路径) */ + tempFilePath: string + /** 视频缩略图临时文件路径 */ + thumbTempFilePath: string + /** 视频的宽度 */ + width: number + } + interface MediaQueryObserverObserveCallbackResult { + /** 页面的当前状态是否满足所指定的 media query */ + matches: boolean + } + /** 需要预览的资源列表 */ + interface MediaSource { + /** 图片或视频的地址 */ + url: string + /** 视频的封面图片 */ + poster?: string + /** 资源的类型,默认为图片 + * + * 可选值: + * - 'image': 图片; + * - 'video': 视频; */ + type?: 'image' | 'video' + } + /** 可通过 [MediaContainer.extractDataSource](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/MediaContainer.extractDataSource.html) 返回。 + * + * [MediaTrack](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/MediaTrack.html) 音频或视频轨道,可以对轨道进行一些操作 + * + * 最低基础库: `2.9.0` */ + interface MediaTrack { + /** 轨道长度,只读 */ + duration: number + /** 轨道类型,只读 + * + * 可选值: + * - 'audio': 音频轨道; + * - 'video': 视频轨道; */ + kind: 'audio' | 'video' + /** 音量,音频轨道下有效,可写 */ + volume: number + } + /** 小程序帐号信息 */ + interface MiniProgram { + /** 小程序 appId */ + appId: string + /** 小程序版本 + * + * 可选值: + * - 'develop': 开发版; + * - 'trial': 体验版; + * - 'release': 正式版; + * + * 最低基础库: `2.10.0` */ + envVersion: 'develop' | 'trial' | 'release' + /** 线上小程序版本号 + * + * 最低基础库: `2.10.2` */ + version: string + } + interface MkdirFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail no such file or directory ${dirPath}': 上级目录不存在; + * - 'fail permission denied, open ${dirPath}': 指定的 filePath 路径没有写权限; + * - 'fail file already exists ${dirPath}': 有同名文件或目录; + * - 'fail sdcard not mounted': Android sdcard 挂载失败; */ + errMsg: string + } + interface MkdirOption { + /** 创建的目录路径 (本地路径) */ + dirPath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: MkdirCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: MkdirFailCallback + /** 是否在递归创建该目录的上级目录后再创建该目录。如果对应的上级目录已经存在,则不创建该上级目录。如 dirPath 为 a/b/c/d 且 recursive 为 true,将创建 a 目录,再在 a 目录下创建 b 目录,以此类推直至创建 a/b/c 目录下的 d 目录。 + * + * 最低基础库: `2.3.0` */ + recursive?: boolean + /** 接口调用成功的回调函数 */ + success?: MkdirSuccessCallback + } + interface MoveAlongOption { + /** 平滑移动的时间 */ + duration: number + /** 指定 marker */ + markerId: number + /** 移动路径的坐标串,坐标点格式 `{longitude, latitude}` */ + path: any[] + /** 根据路径方向自动改变 marker 的旋转角度 */ + autoRotate?: boolean + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: MoveAlongCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: MoveAlongFailCallback + /** 接口调用成功的回调函数 */ + success?: MoveAlongSuccessCallback + } + interface MoveToLocationOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: MoveToLocationCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: MoveToLocationFailCallback + /** 纬度 + * + * 最低基础库: `2.8.0` */ + latitude?: number + /** 经度 + * + * 最低基础库: `2.8.0` */ + longitude?: number + /** 接口调用成功的回调函数 */ + success?: MoveToLocationSuccessCallback + } + /** 静音设置 */ + interface MuteConfig { + /** 是否静音耳机 */ + muteEarphone?: boolean + /** 是否静音麦克风 */ + muteMicrophone?: boolean + } + interface MuteOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: MuteCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: MuteFailCallback + /** 接口调用成功的回调函数 */ + success?: MuteSuccessCallback + } + /** + * + * 最低基础库: `2.11.2` */ + interface NFCAdapter { + /** 标签类型枚举 */ + tech: TechType + } + interface NavigateBackMiniProgramOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: NavigateBackMiniProgramCompleteCallback + /** 需要返回给上一个小程序的数据,上一个小程序可在 `App.onShow` 中获取到这份数据。 [详情](https://developers.weixin.qq.com/miniprogram/dev/reference/api/App.html)。 */ + extraData?: IAnyObject + /** 接口调用失败的回调函数 */ + fail?: NavigateBackMiniProgramFailCallback + /** 接口调用成功的回调函数 */ + success?: NavigateBackMiniProgramSuccessCallback + } + interface NavigateBackOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: NavigateBackCompleteCallback + /** 返回的页面数,如果 delta 大于现有页面数,则返回到首页。 */ + delta?: number + /** 接口调用失败的回调函数 */ + fail?: NavigateBackFailCallback + /** 接口调用成功的回调函数 */ + success?: NavigateBackSuccessCallback + } + interface NavigateToMiniProgramOption { + /** 要打开的小程序 appId */ + appId: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: NavigateToMiniProgramCompleteCallback + /** 要打开的小程序版本。仅在当前小程序为开发版或体验版时此参数有效。如果当前小程序是正式版,则打开的小程序必定是正式版。 + * + * 可选值: + * - 'develop': 开发版; + * - 'trial': 体验版; + * - 'release': 正式版; */ + envVersion?: 'develop' | 'trial' | 'release' + /** 需要传递给目标小程序的数据,目标小程序可在 `App.onLaunch`,`App.onShow` 中获取到这份数据。如果跳转的是小游戏,可以在 [wx.onShow](#)、[wx.getLaunchOptionsSync](https://developers.weixin.qq.com/miniprogram/dev/api/base/app/life-cycle/wx.getLaunchOptionsSync.html) 中可以获取到这份数据数据。 */ + extraData?: IAnyObject + /** 接口调用失败的回调函数 */ + fail?: NavigateToMiniProgramFailCallback + /** 打开的页面路径,如果为空则打开首页。path 中 ? 后面的部分会成为 query,在小程序的 `App.onLaunch`、`App.onShow` 和 `Page.onLoad` 的回调函数或小游戏的 [wx.onShow](#) 回调函数、[wx.getLaunchOptionsSync](https://developers.weixin.qq.com/miniprogram/dev/api/base/app/life-cycle/wx.getLaunchOptionsSync.html) 中可以获取到 query 数据。对于小游戏,可以只传入 query 部分,来实现传参效果,如:传入 "?foo=bar"。 */ + path?: string + /** 接口调用成功的回调函数 */ + success?: NavigateToMiniProgramSuccessCallback + } + interface NavigateToOption { + /** 需要跳转的应用内非 tabBar 的页面的路径 (代码包路径), 路径后可以带参数。参数与路径之间使用 `?` 分隔,参数键与参数值用 `=` 相连,不同参数用 `&` 分隔;如 'path?key=value&key2=value2' */ + url: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: NavigateToCompleteCallback + /** 页面间通信接口,用于监听被打开页面发送到当前页面的数据。基础库 2.7.3 开始支持。 */ + events?: IAnyObject + /** 接口调用失败的回调函数 */ + fail?: NavigateToFailCallback + /** 接口调用成功的回调函数 */ + success?: NavigateToSuccessCallback + } + interface NavigateToSuccessCallbackResult { + /** [EventChannel](https://developers.weixin.qq.com/miniprogram/dev/api/route/EventChannel.html) + * + * 和被打开页面进行通信 */ + eventChannel: EventChannel + errMsg: string + } + interface NdefCloseOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: NdefCloseCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: NdefCloseFailCallback + /** 接口调用成功的回调函数 */ + success?: NdefCloseSuccessCallback + } + interface NodeCallbackResult { + /** 节点对应的 Node 实例 */ + node: IAnyObject + } + interface NotifyBLECharacteristicValueChangeOption { + /** 蓝牙特征值的 uuid */ + characteristicId: string + /** 蓝牙设备 id */ + deviceId: string + /** 蓝牙特征值对应服务的 uuid */ + serviceId: string + /** 是否启用 notify */ + state: boolean + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: NotifyBLECharacteristicValueChangeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: NotifyBLECharacteristicValueChangeFailCallback + /** 接口调用成功的回调函数 */ + success?: NotifyBLECharacteristicValueChangeSuccessCallback + } + /** media query 描述符 */ + interface ObserveDescriptor { + /** 页面高度( px 为单位) */ + height: number + /** 页面最大高度( px 为单位) */ + maxHeight: number + /** 页面最大宽度( px 为单位) */ + maxWidth: number + /** 页面最小高度( px 为单位) */ + minHeight: number + /** 页面最小宽度( px 为单位) */ + minWidth: number + /** 屏幕方向( `landscape` 或 `portrait` ) */ + orientation: string + /** 页面宽度( px 为单位) */ + width: number + } + interface OnAccelerometerChangeCallbackResult { + /** X 轴 */ + x: number + /** Y 轴 */ + y: number + /** Z 轴 */ + z: number + } + interface OnAppShowCallbackResult { + /** 打开的文件信息数组,只有从聊天素材场景打开(scene为1173)才会携带该参数 */ + forwardMaterials: ForwardMaterials[] + /** 小程序切前台的路径 (代码包路径) */ + path: string + /** 小程序切前台的 query 参数 */ + query: IAnyObject + /** 来源信息。从另一个小程序、公众号或 App 进入小程序时返回。否则返回 `{}`。(参见后文注意) */ + referrerInfo: ReferrerInfo + /** 小程序切前台的[场景值](https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/scene.html) */ + scene: number + /** shareTicket,详见[获取更多转发信息](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share.html) */ + shareTicket?: string + } + interface OnBLECharacteristicValueChangeCallbackResult { + /** 蓝牙特征值的 uuid */ + characteristicId: string + /** 蓝牙设备 id */ + deviceId: string + /** 蓝牙特征值对应服务的 uuid */ + serviceId: string + /** 特征值最新的值 */ + value: ArrayBuffer + } + interface OnBLEConnectionStateChangeCallbackResult { + /** 是否处于已连接状态 */ + connected: boolean + /** 蓝牙设备ID */ + deviceId: string + } + interface OnBLEPeripheralConnectionStateChangedCallbackResult { + /** 连接目前状态 */ + connected: boolean + /** 连接状态变化的设备 id */ + deviceId: string + /** server 的 uuid */ + serverId: string + } + interface OnBackgroundFetchDataCallbackResult { + /** 缓存数据类别 (periodic) */ + fetchType: string + /** 缓存数据 */ + fetchedData: string + /** 客户端拿到缓存数据的时间戳 */ + timeStamp: number + } + interface OnBeaconServiceChangeCallbackResult { + /** 服务目前是否可用 */ + available: boolean + /** 目前是否处于搜索状态 */ + discovering: boolean + } + interface OnBeaconUpdateCallbackResult { + /** 当前搜寻到的所有 iBeacon 设备列表 */ + beacons: IBeaconInfo[] + } + interface OnBluetoothAdapterStateChangeCallbackResult { + /** 蓝牙适配器是否可用 */ + available: boolean + /** 蓝牙适配器是否处于搜索状态 */ + discovering: boolean + } + interface OnBluetoothDeviceFoundCallbackResult { + /** 新搜索到的设备列表 */ + devices: BlueToothDevice[] + } + interface OnCameraFrameCallbackResult { + /** 图像像素点数据,一维数组,每四项表示一个像素点的 rgba */ + data: ArrayBuffer + /** 图像数据矩形的高度 */ + height: number + /** 图像数据矩形的宽度 */ + width: number + } + interface OnCharacteristicReadRequestCallbackResult { + /** 唯一标识码,调用 writeCharacteristicValue 时使用 */ + callbackId: number + /** characteristic对应的uuid */ + characteristicId: string + /** service对应的uuid */ + serviceId: string + } + interface OnCharacteristicSubscribedCallbackResult { + /** characteristic对应的uuid */ + characteristicId: string + /** service对应的uuid */ + serviceId: string + } + interface OnCharacteristicWriteRequestCallbackResult { + /** 唯一标识码,调用 writeCharacteristicValue 时使用 */ + callbackId: number + /** characteristic对应的uuid */ + characteristicId: string + /** service对应的uuid */ + serviceId: string + /** 请求写入的特征值数据 */ + value: ArrayBuffer + } + interface OnCheckForUpdateCallbackResult { + /** 是否有新版本 */ + hasUpdate: boolean + } + interface OnCompassChangeCallbackResult { + /** 精度 + * + * 最低基础库: `2.4.0` */ + accuracy: number | string + /** 面对的方向度数 */ + direction: number + } + interface OnCopyUrlCallbackResult { + /** 用短链打开小程序时当前页面携带的查询字符串。小程序中使用时,应在进入页面时调用 `wx.onCopyUrl` 自定义 `query`,退出页面时调用 `wx.offCopyUrl`,防止影响其它页面。 */ + query: string + } + interface OnDeviceMotionChangeCallbackResult { + /** 当 手机坐标 X/Y 和 地球 X/Y 重合时,绕着 Z 轴转动的夹角为 alpha,范围值为 [0, 2*PI)。逆时针转动为正。 */ + alpha: number + /** 当手机坐标 Y/Z 和地球 Y/Z 重合时,绕着 X 轴转动的夹角为 beta。范围值为 [-1*PI, PI) 。顶部朝着地球表面转动为正。也有可能朝着用户为正。 */ + beta: number + /** 当手机 X/Z 和地球 X/Z 重合时,绕着 Y 轴转动的夹角为 gamma。范围值为 [-1*PI/2, PI/2)。右边朝着地球表面转动为正。 */ + gamma: number + } + interface OnDiscoveredCallbackResult { + /** NdefMessage 数组,消息格式为 {id: ArrayBuffer, type: ArrayBuffer, payload: ArrayBuffer} */ + messages: any[] + /** tech 数组,用于匹配NFC卡片具体可以使用什么标准(NfcA等实例)处理 */ + techs: any[] + } + interface OnFrameRecordedCallbackResult { + /** 录音分片数据 */ + frameBuffer: ArrayBuffer + /** 当前帧是否正常录音结束前的最后一帧 */ + isLastFrame: boolean + } + interface OnGetWifiListCallbackResult { + /** Wi-Fi 列表数据 */ + wifiList: WifiInfo[] + } + interface OnGyroscopeChangeCallbackResult { + /** x 轴的角速度 */ + x: number + /** y 轴的角速度 */ + y: number + /** z 轴的角速度 */ + z: number + } + interface OnHCEMessageCallbackResult { + /** `messageType=1` 时 ,客户端接收到 NFC 设备的指令 */ + data: ArrayBuffer + /** 消息类型 + * + * 可选值: + * - 1: HCE APDU Command类型,小程序需对此指令进行处理,并调用 sendHCEMessage 接口返回处理指令; + * - 2: 设备离场事件类型; */ + messageType: 1 | 2 + /** `messageType=2` 时,原因 */ + reason: number + } + interface OnHeadersReceivedCallbackResult { + /** 开发者服务器返回的 HTTP Response Header */ + header: IAnyObject + } + interface OnKeyboardHeightChangeCallbackResult { + /** 键盘高度 */ + height: number + } + interface OnLocalServiceFoundCallbackResult { + /** 服务的 ip 地址 */ + ip: string + /** 服务的端口 */ + port: number + /** 服务的名称 */ + serviceName: string + /** 服务的类型 */ + serviceType: string + } + interface OnLocalServiceLostCallbackResult { + /** 服务的名称 */ + serviceName: string + /** 服务的类型 */ + serviceType: string + } + interface OnLocationChangeCallbackResult { + /** 位置的精确度 */ + accuracy: number + /** 高度,单位 m + * + * 最低基础库: `1.2.0` */ + altitude: number + /** 水平精度,单位 m + * + * 最低基础库: `1.2.0` */ + horizontalAccuracy: number + /** 纬度,范围为 -90~90,负数表示南纬 */ + latitude: number + /** 经度,范围为 -180~180,负数表示西经 */ + longitude: number + /** 速度,单位 m/s */ + speed: number + /** 垂直精度,单位 m(Android 无法获取,返回 0) + * + * 最低基础库: `1.2.0` */ + verticalAccuracy: number + } + interface OnMemoryWarningCallbackResult { + /** 内存告警等级,只有 Android 才有,对应系统宏定义 + * + * 可选值: + * - 5: TRIM_MEMORY_RUNNING_MODERATE; + * - 10: TRIM_MEMORY_RUNNING_LOW; + * - 15: TRIM_MEMORY_RUNNING_CRITICAL; */ + level: 5 | 10 | 15 + } + interface OnNetworkStatusChangeCallbackResult { + /** 当前是否有网络连接 */ + isConnected: boolean + /** 网络类型 + * + * 可选值: + * - 'wifi': wifi 网络; + * - '2g': 2g 网络; + * - '3g': 3g 网络; + * - '4g': 4g 网络; + * - 'unknown': Android 下不常见的网络类型; + * - 'none': 无网络; */ + networkType: 'wifi' | '2g' | '3g' | '4g' | 'unknown' | 'none' + } + interface OnOpenCallbackResult { + /** 连接成功的 HTTP 响应 Header + * + * 最低基础库: `2.0.0` */ + header: IAnyObject + /** 网络请求过程中一些调试信息 + * + * 最低基础库: `2.10.4` */ + profile: SocketProfile + } + interface OnPageNotFoundCallbackResult { + /** 是否本次启动的首个页面(例如从分享等入口进来,首个页面是开发者配置的分享页面) */ + isEntryPage: boolean + /** 不存在页面的路径 (代码包路径) */ + path: string + /** 打开不存在页面的 query 参数 */ + query: IAnyObject + } + interface OnSocketOpenCallbackResult { + /** 连接成功的 HTTP 响应 Header + * + * 最低基础库: `2.0.0` */ + header: IAnyObject + } + interface OnStopCallbackResult { + /** 录音总时长,单位:ms */ + duration: number + /** 录音文件大小,单位:Byte */ + fileSize: number + /** 录音文件的临时路径 (本地路径) */ + tempFilePath: string + } + interface OnThemeChangeCallbackResult { + /** 系统当前的主题,取值为`light`或`dark` + * + * 可选值: + * - 'dark': 深色主题; + * - 'light': 浅色主题; */ + theme: 'dark' | 'light' + } + interface OnUnhandledRejectionCallbackResult { + /** 被拒绝的 Promise 对象 */ + promise: Promise + /** 拒绝原因,一般是一个 Error 对象 */ + reason: string + } + interface OnVoIPChatInterruptedCallbackResult { + /** 错误码 */ + errCode: number + /** 调用结果(错误原因) */ + errMsg: string + } + interface OnVoIPChatMembersChangedCallbackResult { + /** 错误码 */ + errCode: number + /** 调用结果 */ + errMsg: string + /** 还在实时语音通话中的成员 openId 名单 */ + openIdList: string[] + } + interface OnVoIPChatSpeakersChangedCallbackResult { + /** 错误码 */ + errCode: number + /** 调用结果(错误原因) */ + errMsg: string + /** 还在实时语音通话中的成员 openId 名单 */ + openIdList: string[] + } + interface OnVoIPVideoMembersChangedCallbackResult { + /** 错误码 */ + errCode: number + /** 调用结果 */ + errMsg: string + /** 开启视频的成员名单 */ + openIdList: string[] + } + interface OnWifiConnectedCallbackResult { + /** [WifiInfo](https://developers.weixin.qq.com/miniprogram/dev/api/device/wifi/WifiInfo.html) + * + * Wi-Fi 信息 */ + wifi: WifiInfo + } + interface OnWindowResizeCallbackResult { + size: Size + } + interface OpenBluetoothAdapterOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: OpenBluetoothAdapterCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: OpenBluetoothAdapterFailCallback + /** 蓝牙模式,可作为主/从设备,仅 iOS 需要。 + * + * 可选值: + * - 'central': 主机模式; + * - 'peripheral': 从机模式; + * + * 最低基础库: `2.10.0` */ + mode?: 'central' | 'peripheral' + /** 接口调用成功的回调函数 */ + success?: OpenBluetoothAdapterSuccessCallback + } + interface OpenCardOption { + /** 需要打开的卡券列表 */ + cardList: OpenCardRequestInfo[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: OpenCardCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: OpenCardFailCallback + /** 接口调用成功的回调函数 */ + success?: OpenCardSuccessCallback + } + /** 需要打开的卡券列表 */ + interface OpenCardRequestInfo { + /** 卡券 ID */ + cardId: string + /** 由 [wx.addCard](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/card/wx.addCard.html) 的返回对象中的加密 code 通过解密后得到,解密请参照:[code 解码接口](https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1499332673_Unm7V) */ + code: string + } + interface OpenDocumentOption { + /** 文件路径 (本地路径) ,可通过 downloadFile 获得 */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: OpenDocumentCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: OpenDocumentFailCallback + /** 文件类型,指定文件类型打开文件 + * + * 可选值: + * - 'doc': doc 格式; + * - 'docx': docx 格式; + * - 'xls': xls 格式; + * - 'xlsx': xlsx 格式; + * - 'ppt': ppt 格式; + * - 'pptx': pptx 格式; + * - 'pdf': pdf 格式; + * + * 最低基础库: `1.4.0` */ + fileType?: 'doc' | 'docx' | 'xls' | 'xlsx' | 'ppt' | 'pptx' | 'pdf' + /** 是否显示右上角菜单 + * + * 最低基础库: `2.11.0` */ + showMenu?: boolean + /** 接口调用成功的回调函数 */ + success?: OpenDocumentSuccessCallback + } + interface OpenLocationOption { + /** 纬度,范围为-90~90,负数表示南纬。使用 gcj02 国测局坐标系 */ + latitude: number + /** 经度,范围为-180~180,负数表示西经。使用 gcj02 国测局坐标系 */ + longitude: number + /** 地址的详细说明 */ + address?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: OpenLocationCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: OpenLocationFailCallback + /** 位置名 */ + name?: string + /** 缩放比例,范围5~18 */ + scale?: number + /** 接口调用成功的回调函数 */ + success?: OpenLocationSuccessCallback + } + interface OpenMapAppOption { + /** 目的地名称 */ + destination: string + /** 目的地纬度 */ + latitude: number + /** 目的地经度 */ + longitude: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: OpenMapAppCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: OpenMapAppFailCallback + /** 接口调用成功的回调函数 */ + success?: OpenMapAppSuccessCallback + } + interface OpenSettingOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: OpenSettingCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: OpenSettingFailCallback + /** 接口调用成功的回调函数 */ + success?: OpenSettingSuccessCallback + /** 是否同时获取用户订阅消息的订阅状态,默认不获取。注意:withSubscriptions 只返回用户勾选过订阅面板中的“总是保持以上选择,不再询问”的订阅消息。 + * + * 最低基础库: `2.10.3` */ + withSubscriptions?: boolean + } + interface OpenSettingSuccessCallbackResult { + /** [AuthSetting](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/setting/AuthSetting.html) + * + * 用户授权结果 */ + authSetting: AuthSetting + /** [SubscriptionsSetting](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/setting/SubscriptionsSetting.html) + * + * 用户订阅消息设置,接口参数`withSubscriptions`值为`true`时才会返回。 + * + * 最低基础库: `2.10.3` */ + subscriptionsSetting: SubscriptionsSetting + errMsg: string + } + interface OpenVideoEditorOption { + /** 视频源的路径,只支持本地路径 */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: OpenVideoEditorCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: OpenVideoEditorFailCallback + /** 接口调用成功的回调函数 */ + success?: OpenVideoEditorSuccessCallback + } + interface OpenVideoEditorSuccessCallbackResult { + /** 剪辑后生成的视频文件的时长,单位毫秒(ms) */ + duration: number + /** 剪辑后生成的视频文件大小,单位字节数(byte) */ + size: number + /** 编辑后生成的视频文件的临时路径 */ + tempFilePath: string + /** 编辑后生成的缩略图文件的临时路径 */ + tempThumbPath: string + errMsg: string + } + interface PageScrollToOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: PageScrollToCompleteCallback + /** 滚动动画的时长,单位 ms */ + duration?: number + /** 接口调用失败的回调函数 */ + fail?: PageScrollToFailCallback + /** 滚动到页面的目标位置,单位 px */ + scrollTop?: number + /** 选择器 + * + * 最低基础库: `2.7.3` */ + selector?: string + /** 接口调用成功的回调函数 */ + success?: PageScrollToSuccessCallback + } + /** Canvas 2D API 的接口 Path2D 用来声明路径,此路径稍后会被CanvasRenderingContext2D 对象使用。CanvasRenderingContext2D 接口的 路径方法 也存在于 Path2D 这个接口中,允许你在 canvas 中根据需要创建可以保留并重用的路径。 + * + * 最低基础库: `2.11.0` */ + interface Path2D {} + interface PauseBGMOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: PauseBGMCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: PauseBGMFailCallback + /** 接口调用成功的回调函数 */ + success?: PauseBGMSuccessCallback + } + interface PauseBackgroundAudioOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: PauseBackgroundAudioCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: PauseBackgroundAudioFailCallback + /** 接口调用成功的回调函数 */ + success?: PauseBackgroundAudioSuccessCallback + } + interface PauseOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: PauseCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: PauseFailCallback + /** 接口调用成功的回调函数 */ + success?: PauseSuccessCallback + } + interface PauseVoiceOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: PauseVoiceCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: PauseVoiceFailCallback + /** 接口调用成功的回调函数 */ + success?: PauseVoiceSuccessCallback + } + /** PerformanceObserver 对象, 用于监听性能相关事件 + * + * 最低基础库: `2.11.0` */ + interface PerformanceObserver { + /** 获取当前支持的所有性能指标类型 */ + supportedEntryTypes: any[] + } + interface PlayBGMOption { + /** 加入背景混音的资源地址 */ + url: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: PlayBGMCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: PlayBGMFailCallback + /** 接口调用成功的回调函数 */ + success?: PlayBGMSuccessCallback + } + interface PlayBackgroundAudioOption { + /** 音乐链接,目前支持的格式有 m4a, aac, mp3, wav */ + dataUrl: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: PlayBackgroundAudioCompleteCallback + /** 封面URL */ + coverImgUrl?: string + /** 接口调用失败的回调函数 */ + fail?: PlayBackgroundAudioFailCallback + /** 接口调用成功的回调函数 */ + success?: PlayBackgroundAudioSuccessCallback + /** 音乐标题 */ + title?: string + } + interface PlayOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: PlayCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: PlayFailCallback + /** 接口调用成功的回调函数 */ + success?: PlaySuccessCallback + } + interface PlayVoiceOption { + /** 需要播放的语音文件的文件路径 (本地路径) */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: PlayVoiceCompleteCallback + /** 指定播放时长,到达指定的播放时长后会自动停止播放,单位:秒 + * + * 最低基础库: `1.6.0` */ + duration?: number + /** 接口调用失败的回调函数 */ + fail?: PlayVoiceFailCallback + /** 接口调用成功的回调函数 */ + success?: PlayVoiceSuccessCallback + } + /** 插件帐号信息(仅在插件中调用时包含这一项) */ + interface Plugin { + /** 插件 appId */ + appId: string + /** 插件版本号 */ + version: string + } + interface PreviewImageOption { + /** 需要预览的图片链接列表。[2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起支持云文件ID。 */ + urls: string[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: PreviewImageCompleteCallback + /** 当前显示图片的链接 */ + current?: string + /** 接口调用失败的回调函数 */ + fail?: PreviewImageFailCallback + /** 是否显示长按菜单 + * + * 最低基础库: `2.13.0` */ + showmenu?: boolean + /** 接口调用成功的回调函数 */ + success?: PreviewImageSuccessCallback + } + interface PreviewMediaOption { + /** 需要预览的资源列表 */ + sources: MediaSource[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: PreviewMediaCompleteCallback + /** 当前显示的资源序号 */ + current?: number + /** 接口调用失败的回调函数 */ + fail?: PreviewMediaFailCallback + /** 是否显示长按菜单 + * + * 最低基础库: `2.13.0` */ + showmenu?: boolean + /** 接口调用成功的回调函数 */ + success?: PreviewMediaSuccessCallback + } + interface ReLaunchOption { + /** 需要跳转的应用内页面路径 (代码包路径),路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 'path?key=value&key2=value2' */ + url: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ReLaunchCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ReLaunchFailCallback + /** 接口调用成功的回调函数 */ + success?: ReLaunchSuccessCallback + } + interface ReadBLECharacteristicValueOption { + /** 蓝牙特征值的 uuid */ + characteristicId: string + /** 蓝牙设备 id */ + deviceId: string + /** 蓝牙特征值对应服务的 uuid */ + serviceId: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ReadBLECharacteristicValueCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ReadBLECharacteristicValueFailCallback + /** 接口调用成功的回调函数 */ + success?: ReadBLECharacteristicValueSuccessCallback + } + interface ReadFileFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail no such file or directory, open ${filePath}': 指定的 filePath 所在目录不存在; + * - 'fail permission denied, open ${dirPath}': 指定的 filePath 路径没有读权限; + * - 'fail sdcard not mounted': Android sdcard 挂载失败; */ + errMsg: string + } + interface ReadFileOption { + /** 要读取的文件的路径 (本地路径) */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ReadFileCompleteCallback + /** 指定读取文件的字符编码,如果不传 encoding,则以 ArrayBuffer 格式读取文件的二进制内容 + * + * 可选值: + * - 'ascii': ; + * - 'base64': ; + * - 'binary': ; + * - 'hex': ; + * - 'ucs2': 以小端序读取; + * - 'ucs-2': 以小端序读取; + * - 'utf16le': 以小端序读取; + * - 'utf-16le': 以小端序读取; + * - 'utf-8': ; + * - 'utf8': ; + * - 'latin1': ; */ + encoding?: + | 'ascii' + | 'base64' + | 'binary' + | 'hex' + | 'ucs2' + | 'ucs-2' + | 'utf16le' + | 'utf-16le' + | 'utf-8' + | 'utf8' + | 'latin1' + /** 接口调用失败的回调函数 */ + fail?: ReadFileFailCallback + /** 指定文件的长度,如果不指定,则读到文件末尾。有效范围:[1, fileLength]。单位:byte + * + * 最低基础库: `2.10.0` */ + length?: number + /** 从文件指定位置开始读,如果不指定,则从文件头开始读。读取的范围应该是左闭右开区间 [position, position+length)。有效范围:[0, fileLength - 1]。单位:byte + * + * 最低基础库: `2.10.0` */ + position?: number + /** 接口调用成功的回调函数 */ + success?: ReadFileSuccessCallback + } + interface ReadFileSuccessCallbackResult { + /** 文件内容 */ + data: string | ArrayBuffer + errMsg: string + } + interface ReaddirFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail no such file or directory ${dirPath}': 目录不存在; + * - 'fail not a directory ${dirPath}': dirPath 不是目录; + * - 'fail permission denied, open ${dirPath}': 指定的 filePath 路径没有读权限; + * - 'fail sdcard not mounted': Android sdcard 挂载失败; */ + errMsg: string + } + interface ReaddirOption { + /** 要读取的目录路径 (本地路径) */ + dirPath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ReaddirCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ReaddirFailCallback + /** 接口调用成功的回调函数 */ + success?: ReaddirSuccessCallback + } + interface ReaddirSuccessCallbackResult { + /** 指定目录下的文件名数组。 */ + files: string[] + errMsg: string + } + interface RecorderManagerStartOption { + /** 指定录音的音频输入源,可通过 [wx.getAvailableAudioSources()](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/wx.getAvailableAudioSources.html) 获取当前可用的音频源 + * + * 可选值: + * - 'auto': 自动设置,默认使用手机麦克风,插上耳麦后自动切换使用耳机麦克风,所有平台适用; + * - 'buildInMic': 手机麦克风,仅限 iOS; + * - 'headsetMic': 有线耳机麦克风,仅限 iOS; + * - 'mic': 麦克风(没插耳麦时是手机麦克风,插耳麦时是耳机麦克风),仅限 Android; + * - 'camcorder': 同 mic,适用于录制音视频内容,仅限 Android; + * - 'voice_communication': 同 mic,适用于实时沟通,仅限 Android; + * - 'voice_recognition': 同 mic,适用于语音识别,仅限 Android; + * + * 最低基础库: `2.1.0` */ + audioSource?: + | 'auto' + | 'buildInMic' + | 'headsetMic' + | 'mic' + | 'camcorder' + | 'voice_communication' + | 'voice_recognition' + /** 录音的时长,单位 ms,最大值 600000(10 分钟) */ + duration?: number + /** 编码码率,有效值见下表格 */ + encodeBitRate?: number + /** 音频格式 + * + * 可选值: + * - 'mp3': mp3 格式; + * - 'aac': aac 格式; + * - 'wav': wav 格式; + * - 'PCM': pcm 格式; */ + format?: 'mp3' | 'aac' | 'wav' | 'PCM' + /** 指定帧大小,单位 KB。传入 frameSize 后,每录制指定帧大小的内容后,会回调录制的文件内容,不指定则不会回调。暂仅支持 mp3 格式。 */ + frameSize?: number + /** 录音通道数 + * + * 可选值: + * - 1: 1 个通道; + * - 2: 2 个通道; */ + numberOfChannels?: 1 | 2 + /** 采样率 + * + * 可选值: + * - 8000: 8000 采样率; + * - 11025: 11025 采样率; + * - 12000: 12000 采样率; + * - 16000: 16000 采样率; + * - 22050: 22050 采样率; + * - 24000: 24000 采样率; + * - 32000: 32000 采样率; + * - 44100: 44100 采样率; + * - 48000: 48000 采样率; */ + sampleRate?: + | 8000 + | 11025 + | 12000 + | 16000 + | 22050 + | 24000 + | 32000 + | 44100 + | 48000 + } + /** 菜单按钮的布局位置信息 */ + interface Rect { + /** 下边界坐标,单位:px */ + bottom: number + /** 高度,单位:px */ + height: number + /** 左边界坐标,单位:px */ + left: number + /** 右边界坐标,单位:px */ + right: number + /** 上边界坐标,单位:px */ + top: number + /** 宽度,单位:px */ + width: number + } + interface RedirectToOption { + /** 需要跳转的应用内非 tabBar 的页面的路径 (代码包路径), 路径后可以带参数。参数与路径之间使用 `?` 分隔,参数键与参数值用 `=` 相连,不同参数用 `&` 分隔;如 'path?key=value&key2=value2' */ + url: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RedirectToCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RedirectToFailCallback + /** 接口调用成功的回调函数 */ + success?: RedirectToSuccessCallback + } + interface RedoOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RedoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RedoFailCallback + /** 接口调用成功的回调函数 */ + success?: RedoSuccessCallback + } + /** 来源信息。从另一个小程序、公众号或 App 进入小程序时返回。否则返回 `{}`。(参见后文注意) */ + interface ReferrerInfo { + /** 来源小程序、公众号或 App 的 appId */ + appId: string + /** 来源小程序传过来的数据,scene=1037或1038时支持 */ + extraData: IAnyObject + } + /** 参照区域的边界 */ + interface RelativeRectResult { + /** 下边界 */ + bottom: number + /** 左边界 */ + left: number + /** 右边界 */ + right: number + /** 上边界 */ + top: number + } + /** 消息来源的结构化信息 */ + interface RemoteInfo { + /** 发送消息的 socket 的地址 */ + address: string + /** 使用的协议族,为 IPv4 或者 IPv6 */ + family: string + /** 端口号 */ + port: number + /** message 的大小,单位:字节 */ + size: number + } + interface RemoveCustomLayerOption { + /** 个性化图层id */ + layerId: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RemoveCustomLayerCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RemoveCustomLayerFailCallback + /** 接口调用成功的回调函数 */ + success?: RemoveCustomLayerSuccessCallback + } + interface RemoveFormatOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RemoveFormatCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RemoveFormatFailCallback + /** 接口调用成功的回调函数 */ + success?: RemoveFormatSuccessCallback + } + interface RemoveGroundOverlayOption { + /** 图片图层 id */ + id: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RemoveGroundOverlayCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RemoveGroundOverlayFailCallback + /** 接口调用成功的回调函数 */ + success?: RemoveGroundOverlaySuccessCallback + } + interface RemoveMarkersOption { + /** marker 的 id 集合。 */ + markerIds: any[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RemoveMarkersCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RemoveMarkersFailCallback + /** 接口调用成功的回调函数 */ + success?: RemoveMarkersSuccessCallback + } + interface RemoveSavedFileFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail file not exist': 指定的 tempFilePath 找不到文件; */ + errMsg: string + } + interface RemoveServiceOption { + /** service 的 uuid */ + serviceId: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RemoveServiceCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RemoveServiceFailCallback + /** 接口调用成功的回调函数 */ + success?: RemoveServiceSuccessCallback + } + interface RemoveStorageOption { + /** 本地缓存中指定的 key */ + key: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RemoveStorageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RemoveStorageFailCallback + /** 接口调用成功的回调函数 */ + success?: RemoveStorageSuccessCallback + } + interface RemoveTabBarBadgeOption { + /** tabBar 的哪一项,从左边算起 */ + index: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RemoveTabBarBadgeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RemoveTabBarBadgeFailCallback + /** 接口调用成功的回调函数 */ + success?: RemoveTabBarBadgeSuccessCallback + } + interface RenameFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail permission denied, rename ${oldPath} -> ${newPath}': 指定源文件或目标文件没有写权限; + * - 'fail no such file or directory, rename ${oldPath} -> ${newPath}': 源文件不存在,或目标文件路径的上层目录不存在; */ + errMsg: string + } + interface RenameOption { + /** 新文件路径,支持本地路径 */ + newPath: string + /** 源文件路径,支持本地路径 */ + oldPath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RenameCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RenameFailCallback + /** 接口调用成功的回调函数 */ + success?: RenameSuccessCallback + } + /** Canvas 绘图上下文。 + * + * **** + * + * - 通过 Canvas.getContext('2d') 接口可以获取 CanvasRenderingContext2D 对象,实现了 [HTML Canvas 2D Context](https://www.w3.org/TR/2dcontext/) 定义的属性、方法。 + * - 通过 Canvas.getContext('webgl') 或 OffscreenCanvas.getContext('webgl') 接口可以获取 WebGLRenderingContext 对象,实现了 [WebGL 1.0](https://www.khronos.org/registry/webgl/specs/latest/1.0/) 定义的所有属性、方法、常量。 + * - CanvasRenderingContext2D 的 drawImage 方法 2.10.0 起支持传入通过 [SelectorQuery](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.html) 获取的 video 对象 + * + * **示例代码** + * + * + * + * video 画到 2D Canvas 示例 + * [在微信开发者工具中查看示例](https://developers.weixin.qq.com/s/tJTak7mU7sfX) */ + interface RenderingContext {} + interface RequestOption< + T extends string | IAnyObject | ArrayBuffer = + | string + | IAnyObject + | ArrayBuffer + > { + /** 开发者服务器接口地址 */ + url: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RequestCompleteCallback + /** 请求的参数 */ + data?: string | IAnyObject | ArrayBuffer + /** 返回的数据格式 + * + * 可选值: + * - 'json': 返回的数据为 JSON,返回后会对返回的数据进行一次 JSON.parse; + * - '其他': 不对返回的内容进行 JSON.parse; */ + dataType?: 'json' | '其他' + /** 开启 cache + * + * 最低基础库: `2.10.4` */ + enableCache?: boolean + /** 开启 http2 + * + * 最低基础库: `2.10.4` */ + enableHttp2?: boolean + /** 开启 quic + * + * 最低基础库: `2.10.4` */ + enableQuic?: boolean + /** 接口调用失败的回调函数 */ + fail?: RequestFailCallback + /** 设置请求的 header,header 中不能设置 Referer。 + * + * `content-type` 默认为 `application/json` */ + header?: IAnyObject + /** HTTP 请求方法 + * + * 可选值: + * - 'OPTIONS': HTTP 请求 OPTIONS; + * - 'GET': HTTP 请求 GET; + * - 'HEAD': HTTP 请求 HEAD; + * - 'POST': HTTP 请求 POST; + * - 'PUT': HTTP 请求 PUT; + * - 'DELETE': HTTP 请求 DELETE; + * - 'TRACE': HTTP 请求 TRACE; + * - 'CONNECT': HTTP 请求 CONNECT; */ + method?: + | 'OPTIONS' + | 'GET' + | 'HEAD' + | 'POST' + | 'PUT' + | 'DELETE' + | 'TRACE' + | 'CONNECT' + /** 响应的数据类型 + * + * 可选值: + * - 'text': 响应的数据为文本; + * - 'arraybuffer': 响应的数据为 ArrayBuffer; + * + * 最低基础库: `1.7.0` */ + responseType?: 'text' | 'arraybuffer' + /** 接口调用成功的回调函数 */ + success?: RequestSuccessCallback + /** 超时时间,单位为毫秒 + * + * 最低基础库: `2.10.0` */ + timeout?: number + } + interface RequestPaymentOption { + /** 随机字符串,长度为32个字符以下 */ + nonceStr: string + /** 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=*** */ + package: string + /** 签名,具体见微信支付文档 */ + paySign: string + /** 时间戳,从 1970 年 1 月 1 日 00:00:00 至今的秒数,即当前的时间 */ + timeStamp: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RequestPaymentCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RequestPaymentFailCallback + /** 签名算法,应与后台下单时的值一致 + * + * 可选值: + * - 'MD5': 仅在 v2 版本接口适用; + * - 'HMAC-SHA256': 仅在 v2 版本接口适用; + * - 'RSA': 仅在 v3 版本接口适用; */ + signType?: 'MD5' | 'HMAC-SHA256' | 'RSA' + /** 接口调用成功的回调函数 */ + success?: RequestPaymentSuccessCallback + } + interface RequestPictureInPictureOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RequestPictureInPictureCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RequestPictureInPictureFailCallback + /** 接口调用成功的回调函数 */ + success?: RequestPictureInPictureSuccessCallback + } + /** 网络请求过程中一些调试信息 + * + * 最低基础库: `2.10.4` */ + interface RequestProfile { + /** SSL建立完成的时间,如果不是安全连接,则值为 0 */ + SSLconnectionEnd: number + /** SSL建立连接的时间,如果不是安全连接,则值为 0 */ + SSLconnectionStart: number + /** HTTP(TCP) 完成建立连接的时间(完成握手),如果是持久连接,则与 fetchStart 值相等。注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接完成的时间。注意这里握手结束,包括安全连接建立完成、SOCKS 授权通过 */ + connectEnd: number + /** HTTP(TCP) 开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等。注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接开始的时间 */ + connectStart: number + /** DNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 */ + domainLookupEnd: number + /** DNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 */ + domainLookupStart: number + /** 评估当前网络下载的kbps */ + downstreamThroughputKbpsEstimate: number + /** 评估的网络状态 slow 2g/2g/3g/4g */ + estimate_nettype: string + /** 组件准备好使用 HTTP 请求抓取资源的时间,这发生在检查本地缓存之前 */ + fetchStart: number + /** 协议层根据多个请求评估当前网络的 rtt(仅供参考) */ + httpRttEstimate: number + /** 当前请求的IP */ + peerIP: string + /** 当前请求的端口 */ + port: number + /** 收到字节数 */ + receivedBytedCount: number + /** 最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内部的重定向才算,否则值为 0 */ + redirectEnd: number + /** 第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为 0 */ + redirectStart: number + /** HTTP请求读取真实文档结束的时间 */ + requestEnd: number + /** HTTP请求读取真实文档开始的时间(完成建立连接),包括从本地读取缓存。连接错误重连时,这里显示的也是新建立连接的时间 */ + requestStart: number + /** HTTP 响应全部接收完成的时间(获取到最后一个字节),包括从本地读取缓存 */ + responseEnd: number + /** HTTP 开始接收响应的时间(获取到第一个字节),包括从本地读取缓存 */ + responseStart: number + /** 当次请求连接过程中实时 rtt */ + rtt: number + /** 发送的字节数 */ + sendBytesCount: number + /** 是否复用连接 */ + socketReused: boolean + /** 当前网络的实际下载kbps */ + throughputKbps: number + /** 传输层根据多个请求评估的当前网络的 rtt(仅供参考) */ + transportRttEstimate: number + } + interface RequestSubscribeMessageFailCallbackResult { + /** 接口调用失败错误码 */ + errCode: number + /** 接口调用失败错误信息 */ + errMsg: string + } + interface RequestSubscribeMessageOption { + /** 需要订阅的消息模板的id的集合,一次调用最多可订阅3条消息(注意:iOS客户端7.0.6版本、Android客户端7.0.7版本之后的一次性订阅/长期订阅才支持多个模板消息,iOS客户端7.0.5版本、Android客户端7.0.6版本之前的一次订阅只支持一个模板消息)消息模板id在[微信公众平台(mp.weixin.qq.com)-功能-订阅消息]中配置。每个tmplId对应的模板标题需要不相同,否则会被过滤。 */ + tmplIds: any[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RequestSubscribeMessageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RequestSubscribeMessageFailCallback + /** 接口调用成功的回调函数 */ + success?: RequestSubscribeMessageSuccessCallback + } + interface RequestSubscribeMessageSuccessCallbackResult { + /** [TEMPLATE_ID]是动态的键,即模板id,值包括'accept'、'reject'、'ban'、'filter'。'accept'表示用户同意订阅该条id对应的模板消息,'reject'表示用户拒绝订阅该条id对应的模板消息,'ban'表示已被后台封禁,'filter'表示该模板因为模板标题同名被后台过滤。例如 { errMsg: "requestSubscribeMessage:ok", zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: "accept"} 表示用户同意订阅zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE这条消息 */ + [TEMPLATE_ID: string]: string + /** 接口调用成功时errMsg值为'requestSubscribeMessage:ok' */ + errMsg: string + } + interface RequestSuccessCallbackResult< + T extends string | IAnyObject | ArrayBuffer = + | string + | IAnyObject + | ArrayBuffer + > { + /** 开发者服务器返回的 cookies,格式为字符串数组 + * + * 最低基础库: `2.10.0` */ + cookies: string[] + /** 开发者服务器返回的数据 */ + data: T + /** 开发者服务器返回的 HTTP Response Header + * + * 最低基础库: `1.2.0` */ + header: IAnyObject + /** 网络请求过程中一些调试信息 + * + * 最低基础库: `2.10.4` */ + profile: RequestProfile + /** 开发者服务器返回的 HTTP 状态码 */ + statusCode: number + errMsg: string + } + interface ResumeBGMOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ResumeBGMCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ResumeBGMFailCallback + /** 接口调用成功的回调函数 */ + success?: ResumeBGMSuccessCallback + } + interface ResumeOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ResumeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ResumeFailCallback + /** 接口调用成功的回调函数 */ + success?: ResumeSuccessCallback + } + interface RewardedVideoAdOnCloseCallbackResult { + /** 视频是否是在用户完整观看的情况下被关闭的 + * + * 最低基础库: `2.1.0` */ + isEnded: boolean + } + interface RewardedVideoAdOnErrorCallbackResult { + /** 错误码 + * + * 可选值: + * - 1000: 后端接口调用失败; + * - 1001: 参数错误; + * - 1002: 广告单元无效; + * - 1003: 内部错误; + * - 1004: 无合适的广告; + * - 1005: 广告组件审核中; + * - 1006: 广告组件被驳回; + * - 1007: 广告组件被封禁; + * - 1008: 广告单元已关闭; + * + * 最低基础库: `2.2.2` */ + errCode: 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 + /** 错误信息 */ + errMsg: string + } + interface RmdirFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail no such file or directory ${dirPath}': 目录不存在; + * - 'fail directory not empty': 目录不为空; + * - 'fail permission denied, open ${dirPath}': 指定的 dirPath 路径没有写权限; + * - 'fail sdcard not mounted': Android sdcard 挂载失败; */ + errMsg: string + } + interface RmdirOption { + /** 要删除的目录路径 (本地路径) */ + dirPath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RmdirCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: RmdirFailCallback + /** 是否递归删除目录。如果为 true,则删除该目录和该目录下的所有子目录以及文件。 + * + * 最低基础库: `2.3.0` */ + recursive?: boolean + /** 接口调用成功的回调函数 */ + success?: RmdirSuccessCallback + } + /** 在竖屏正方向下的安全区域 + * + * 最低基础库: `2.7.0` */ + interface SafeArea { + /** 安全区域右下角纵坐标 */ + bottom: number + /** 安全区域的高度,单位逻辑像素 */ + height: number + /** 安全区域左上角横坐标 */ + left: number + /** 安全区域右下角横坐标 */ + right: number + /** 安全区域左上角纵坐标 */ + top: number + /** 安全区域的宽度,单位逻辑像素 */ + width: number + } + interface SaveFileFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail tempFilePath file not exist': 指定的 tempFilePath 找不到文件; + * - 'fail permission denied, open "${filePath}"': 指定的 filePath 路径没有写权限; + * - 'fail no such file or directory "${dirPath}"': 上级目录不存在; + * - 'fail the maximum size of the file storage limit is exceeded': 存储空间不足; + * - 'fail sdcard not mounted': Android sdcard 挂载失败; */ + errMsg: string + } + interface SaveFileSuccessCallbackResult { + /** 存储后的文件路径 (本地路径) */ + savedFilePath: string + errMsg: string + } + interface SaveFileToDiskOption { + /** 待保存文件路径 */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SaveFileToDiskCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SaveFileToDiskFailCallback + /** 接口调用成功的回调函数 */ + success?: SaveFileToDiskSuccessCallback + } + interface SaveImageToPhotosAlbumOption { + /** 图片文件路径,可以是临时文件路径或永久文件路径 (本地路径) ,不支持网络路径 */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SaveImageToPhotosAlbumCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SaveImageToPhotosAlbumFailCallback + /** 接口调用成功的回调函数 */ + success?: SaveImageToPhotosAlbumSuccessCallback + } + interface SaveVideoToPhotosAlbumOption { + /** 视频文件路径,可以是临时文件路径也可以是永久文件路径 (本地路径) */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SaveVideoToPhotosAlbumCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SaveVideoToPhotosAlbumFailCallback + /** 接口调用成功的回调函数 */ + success?: SaveVideoToPhotosAlbumSuccessCallback + } + interface ScanCodeOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ScanCodeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ScanCodeFailCallback + /** 是否只能从相机扫码,不允许从相册选择图片 + * + * 最低基础库: `1.2.0` */ + onlyFromCamera?: boolean + /** 扫码类型 + * + * 可选值: + * - 'barCode': 一维码; + * - 'qrCode': 二维码; + * - 'datamatrix': Data Matrix 码; + * - 'pdf417': PDF417 条码; + * + * 最低基础库: `1.7.0` */ + scanType?: Array<'barCode' | 'qrCode' | 'datamatrix' | 'pdf417'> + /** 接口调用成功的回调函数 */ + success?: ScanCodeSuccessCallback + } + interface ScanCodeSuccessCallbackResult { + /** 所扫码的字符集 */ + charSet: string + /** 当所扫的码为当前小程序二维码时,会返回此字段,内容为二维码携带的 path */ + path: string + /** 原始数据,base64编码 */ + rawData: string + /** 所扫码的内容 */ + result: string + /** 所扫码的类型 + * + * 可选值: + * - 'QR_CODE': 二维码; + * - 'AZTEC': 一维码; + * - 'CODABAR': 一维码; + * - 'CODE_39': 一维码; + * - 'CODE_93': 一维码; + * - 'CODE_128': 一维码; + * - 'DATA_MATRIX': 二维码; + * - 'EAN_8': 一维码; + * - 'EAN_13': 一维码; + * - 'ITF': 一维码; + * - 'MAXICODE': 一维码; + * - 'PDF_417': 二维码; + * - 'RSS_14': 一维码; + * - 'RSS_EXPANDED': 一维码; + * - 'UPC_A': 一维码; + * - 'UPC_E': 一维码; + * - 'UPC_EAN_EXTENSION': 一维码; + * - 'WX_CODE': 二维码; + * - 'CODE_25': 一维码; */ + scanType: + | 'QR_CODE' + | 'AZTEC' + | 'CODABAR' + | 'CODE_39' + | 'CODE_93' + | 'CODE_128' + | 'DATA_MATRIX' + | 'EAN_8' + | 'EAN_13' + | 'ITF' + | 'MAXICODE' + | 'PDF_417' + | 'RSS_14' + | 'RSS_EXPANDED' + | 'UPC_A' + | 'UPC_E' + | 'UPC_EAN_EXTENSION' + | 'WX_CODE' + | 'CODE_25' + errMsg: string + } + interface ScrollOffsetCallbackResult { + /** 节点的 dataset */ + dataset: IAnyObject + /** 节点的 ID */ + id: string + /** 节点的水平滚动位置 */ + scrollLeft: number + /** 节点的竖直滚动位置 */ + scrollTop: number + } + interface ScrollToOption { + /** 是否启用滚动动画 */ + animated?: boolean + /** 滚动动画时长 */ + duration?: number + /** 左边界距离 */ + left?: number + /** 顶部距离 */ + top?: number + /** 初始速度 */ + velocity?: number + } + /** 增强 ScrollView 实例 + * + * 最低基础库: `2.14.4` */ + interface ScrollViewContext { + /** 设置滚动边界弹性 (仅在 iOS 下生效) */ + bounces: boolean + /** 取消滚动惯性 (仅在 iOS 下生效) */ + decelerationDisabled: boolean + /** 设置滚动减速速率 */ + fastDeceleration: boolean + /** 分页滑动开关 */ + pagingEnabled: boolean + /** 滚动开关 */ + scrollEnabled: boolean + /** 设置是否显示滚动条 */ + showScrollbar: boolean + } + interface SeekBackgroundAudioOption { + /** 音乐位置,单位:秒 */ + position: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SeekBackgroundAudioCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SeekBackgroundAudioFailCallback + /** 接口调用成功的回调函数 */ + success?: SeekBackgroundAudioSuccessCallback + } + interface SendHCEMessageOption { + /** 二进制数据 */ + data: ArrayBuffer + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SendHCEMessageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SendHCEMessageFailCallback + /** 接口调用成功的回调函数 */ + success?: SendHCEMessageSuccessCallback + } + interface SendMessageOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SendMessageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SendMessageFailCallback + /** 接口调用成功的回调函数 */ + success?: SendMessageSuccessCallback + } + interface SendSocketMessageOption { + /** 需要发送的内容 */ + data: string | ArrayBuffer + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SendSocketMessageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SendSocketMessageFailCallback + /** 接口调用成功的回调函数 */ + success?: SendSocketMessageSuccessCallback + } + interface SetBGMVolumeOption { + /** 音量大小,范围是 0-1 */ + volume: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetBGMVolumeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetBGMVolumeFailCallback + /** 接口调用成功的回调函数 */ + success?: SetBGMVolumeSuccessCallback + } + interface SetBLEMTUOption { + /** 用于区分设备的 id */ + deviceId: string + /** 最大传输单元(22,512) 区间内,单位 bytes */ + mtu: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetBLEMTUCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetBLEMTUFailCallback + /** 接口调用成功的回调函数 */ + success?: SetBLEMTUSuccessCallback + } + interface SetBackgroundColorOption { + /** 窗口的背景色,必须为十六进制颜色值 */ + backgroundColor?: string + /** 底部窗口的背景色,必须为十六进制颜色值,仅 iOS 支持 */ + backgroundColorBottom?: string + /** 顶部窗口的背景色,必须为十六进制颜色值,仅 iOS 支持 */ + backgroundColorTop?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetBackgroundColorCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetBackgroundColorFailCallback + /** 接口调用成功的回调函数 */ + success?: SetBackgroundColorSuccessCallback + } + interface SetBackgroundFetchTokenOption { + /** 自定义的登录态 */ + token: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetBackgroundFetchTokenCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetBackgroundFetchTokenFailCallback + /** 接口调用成功的回调函数 */ + success?: SetBackgroundFetchTokenSuccessCallback + } + interface SetBackgroundTextStyleOption { + /** 下拉背景字体、loading 图的样式。 + * + * 可选值: + * - 'dark': dark 样式; + * - 'light': light 样式; */ + textStyle: 'dark' | 'light' + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetBackgroundTextStyleCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetBackgroundTextStyleFailCallback + /** 接口调用成功的回调函数 */ + success?: SetBackgroundTextStyleSuccessCallback + } + interface SetCenterOffsetOption { + /** 偏移量,两位数组 */ + offset: number[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetCenterOffsetCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetCenterOffsetFailCallback + /** 接口调用成功的回调函数 */ + success?: SetCenterOffsetSuccessCallback + } + interface SetClipboardDataOption { + /** 剪贴板的内容 */ + data: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetClipboardDataCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetClipboardDataFailCallback + /** 接口调用成功的回调函数 */ + success?: SetClipboardDataSuccessCallback + } + interface SetContentsOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetContentsCompleteCallback + /** 表示内容的delta对象 */ + delta?: IAnyObject + /** 接口调用失败的回调函数 */ + fail?: SetContentsFailCallback + /** 带标签的HTML内容 */ + html?: string + /** 接口调用成功的回调函数 */ + success?: SetContentsSuccessCallback + } + interface SetEnableDebugOption { + /** 是否打开调试 */ + enableDebug: boolean + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetEnableDebugCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetEnableDebugFailCallback + /** 接口调用成功的回调函数 */ + success?: SetEnableDebugSuccessCallback + } + interface SetInnerAudioOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetInnerAudioOptionCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetInnerAudioOptionFailCallback + /** 是否与其他音频混播,设置为 true 之后,不会终止其他应用或微信内的音乐 */ + mixWithOther?: boolean + /** (仅在 iOS 生效)是否遵循静音开关,设置为 false 之后,即使是在静音模式下,也能播放声音 */ + obeyMuteSwitch?: boolean + /** true 代表用扬声器播放,false 代表听筒播放,默认值为 true。 */ + speakerOn?: boolean + /** 接口调用成功的回调函数 */ + success?: SetInnerAudioOptionSuccessCallback + } + interface SetKeepScreenOnOption { + /** 是否保持屏幕常亮 */ + keepScreenOn: boolean + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetKeepScreenOnCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetKeepScreenOnFailCallback + /** 接口调用成功的回调函数 */ + success?: SetKeepScreenOnSuccessCallback + } + interface SetMICVolumeOption { + /** 音量大小,范围是 0.0-1.0 */ + volume: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetMICVolumeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetMICVolumeFailCallback + /** 接口调用成功的回调函数 */ + success?: SetMICVolumeSuccessCallback + } + interface SetNavigationBarColorOption { + /** 背景颜色值,有效值为十六进制颜色 */ + backgroundColor: string + /** 前景颜色值,包括按钮、标题、状态栏的颜色,仅支持 #ffffff 和 #000000 */ + frontColor: string + /** 动画效果 */ + animation?: AnimationOption + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetNavigationBarColorCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetNavigationBarColorFailCallback + /** 接口调用成功的回调函数 */ + success?: SetNavigationBarColorSuccessCallback + } + interface SetNavigationBarTitleOption { + /** 页面标题 */ + title: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetNavigationBarTitleCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetNavigationBarTitleFailCallback + /** 接口调用成功的回调函数 */ + success?: SetNavigationBarTitleSuccessCallback + } + interface SetScreenBrightnessOption { + /** 屏幕亮度值,范围 0 ~ 1。0 最暗,1 最亮 */ + value: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetScreenBrightnessCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetScreenBrightnessFailCallback + /** 接口调用成功的回调函数 */ + success?: SetScreenBrightnessSuccessCallback + } + interface SetStorageOption { + /** 需要存储的内容。只支持原生类型、Date、及能够通过`JSON.stringify`序列化的对象。 */ + data: T + /** 本地缓存中指定的 key */ + key: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetStorageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetStorageFailCallback + /** 接口调用成功的回调函数 */ + success?: SetStorageSuccessCallback + } + interface SetTabBarBadgeOption { + /** tabBar 的哪一项,从左边算起 */ + index: number + /** 显示的文本,超过 4 个字符则显示成 ... */ + text: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetTabBarBadgeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetTabBarBadgeFailCallback + /** 接口调用成功的回调函数 */ + success?: SetTabBarBadgeSuccessCallback + } + interface SetTabBarItemOption { + /** tabBar 的哪一项,从左边算起 */ + index: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetTabBarItemCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetTabBarItemFailCallback + /** 图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效 */ + iconPath?: string + /** 选中时的图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效 */ + selectedIconPath?: string + /** 接口调用成功的回调函数 */ + success?: SetTabBarItemSuccessCallback + /** tab 上的按钮文字 */ + text?: string + } + interface SetTabBarStyleOption { + /** tab 的背景色,HexColor */ + backgroundColor?: string + /** tabBar上边框的颜色, 仅支持 black/white */ + borderStyle?: string + /** tab 上的文字默认颜色,HexColor */ + color?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetTabBarStyleCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetTabBarStyleFailCallback + /** tab 上的文字选中时的颜色,HexColor */ + selectedColor?: string + /** 接口调用成功的回调函数 */ + success?: SetTabBarStyleSuccessCallback + } + interface SetTimeoutOption { + /** 设置超时时间 (ms) */ + timeout: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetTimeoutCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetTimeoutFailCallback + /** 接口调用成功的回调函数 */ + success?: SetTimeoutSuccessCallback + } + interface SetTopBarTextOption { + /** 置顶栏文字 */ + text: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetTopBarTextCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetTopBarTextFailCallback + /** 接口调用成功的回调函数 */ + success?: SetTopBarTextSuccessCallback + } + interface SetWifiListOption { + /** 提供预设的 Wi-Fi 信息列表 */ + wifiList: WifiData[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetWifiListCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetWifiListFailCallback + /** 接口调用成功的回调函数 */ + success?: SetWifiListSuccessCallback + } + interface SetWindowSizeOption { + /** 窗口高度,以像素为单位 */ + height: number + /** 窗口宽度,以像素为单位 */ + width: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetWindowSizeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetWindowSizeFailCallback + /** 接口调用成功的回调函数 */ + success?: SetWindowSizeSuccessCallback + } + interface SetZoomOption { + /** 缩放级别,范围[1, maxZoom]。zoom 可取小数,精确到小数后一位。maxZoom 可在 bindinitdone 返回值中获取。 */ + zoom: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SetZoomCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SetZoomFailCallback + /** 接口调用成功的回调函数 */ + success?: SetZoomSuccessCallback + } + interface SetZoomSuccessCallbackResult { + /** 实际设置的缩放级别。由于系统限制,某些机型可能无法设置成指定值,会改用最接近的可设值。 */ + zoom: number + errMsg: string + } + interface ShowActionSheetOption { + /** 按钮的文字数组,数组长度最大为 6 */ + itemList: string[] + /** 警示文案 + * + * 最低基础库: `2.14.0` */ + alertText?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ShowActionSheetCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ShowActionSheetFailCallback + /** 按钮的文字颜色 */ + itemColor?: string + /** 接口调用成功的回调函数 */ + success?: ShowActionSheetSuccessCallback + } + interface ShowActionSheetSuccessCallbackResult { + /** 用户点击的按钮序号,从上到下的顺序,从0开始 */ + tapIndex: number + errMsg: string + } + interface ShowLoadingOption { + /** 提示的内容 */ + title: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ShowLoadingCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ShowLoadingFailCallback + /** 是否显示透明蒙层,防止触摸穿透 */ + mask?: boolean + /** 接口调用成功的回调函数 */ + success?: ShowLoadingSuccessCallback + } + interface ShowModalOption { + /** 取消按钮的文字颜色,必须是 16 进制格式的颜色字符串 */ + cancelColor?: string + /** 取消按钮的文字,最多 4 个字符 */ + cancelText?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ShowModalCompleteCallback + /** 确认按钮的文字颜色,必须是 16 进制格式的颜色字符串 */ + confirmColor?: string + /** 确认按钮的文字,最多 4 个字符 */ + confirmText?: string + /** 提示的内容,editable 为 true 时,会输入框默认文本 */ + content?: string + /** 是否显示输入框 + * + * 最低基础库: `2.15.0` */ + editable?: boolean + /** 接口调用失败的回调函数 */ + fail?: ShowModalFailCallback + /** 输入框提示文本 + * + * 最低基础库: `2.15.0` */ + placeholderText?: string + /** 是否显示取消按钮 */ + showCancel?: boolean + /** 接口调用成功的回调函数 */ + success?: ShowModalSuccessCallback + /** 提示的标题 */ + title?: string + } + interface ShowModalSuccessCallbackResult { + /** 为 true 时,表示用户点击了取消(用于 Android 系统区分点击蒙层关闭还是点击取消按钮关闭) + * + * 最低基础库: `1.1.0` */ + cancel: boolean + /** 为 true 时,表示用户点击了确定按钮 */ + confirm: boolean + /** editable 为 true 时,用户输入的文本 */ + content: string + errMsg: string + } + interface ShowNavigationBarLoadingOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ShowNavigationBarLoadingCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ShowNavigationBarLoadingFailCallback + /** 接口调用成功的回调函数 */ + success?: ShowNavigationBarLoadingSuccessCallback + } + interface ShowRedPackageOption { + /** 封面地址 */ + url: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ShowRedPackageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ShowRedPackageFailCallback + /** 接口调用成功的回调函数 */ + success?: ShowRedPackageSuccessCallback + } + interface ShowShareImageMenuOption { + /** 要分享的图片地址,必须为本地路径或临时路径 */ + path: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ShowShareImageMenuCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ShowShareImageMenuFailCallback + /** 接口调用成功的回调函数 */ + success?: ShowShareImageMenuSuccessCallback + } + interface ShowShareMenuOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ShowShareMenuCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ShowShareMenuFailCallback + /** 本接口为 Beta 版本,暂只在 Android 平台支持。需要显示的转发按钮名称列表,默认['shareAppMessage']。按钮名称合法值包含 "shareAppMessage"、"shareTimeline" 两种 + * + * 最低基础库: `2.11.3` */ + menus?: string[] + /** 接口调用成功的回调函数 */ + success?: ShowShareMenuSuccessCallback + /** 是否使用带 shareTicket 的转发[详情](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share.html) */ + withShareTicket?: boolean + } + interface ShowTabBarOption { + /** 是否需要动画效果 */ + animation?: boolean + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ShowTabBarCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ShowTabBarFailCallback + /** 接口调用成功的回调函数 */ + success?: ShowTabBarSuccessCallback + } + interface ShowTabBarRedDotOption { + /** tabBar 的哪一项,从左边算起 */ + index: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ShowTabBarRedDotCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ShowTabBarRedDotFailCallback + /** 接口调用成功的回调函数 */ + success?: ShowTabBarRedDotSuccessCallback + } + interface ShowToastOption { + /** 提示的内容 */ + title: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ShowToastCompleteCallback + /** 提示的延迟时间 */ + duration?: number + /** 接口调用失败的回调函数 */ + fail?: ShowToastFailCallback + /** 图标 + * + * 可选值: + * - 'success': 显示成功图标,此时 title 文本最多显示 7 个汉字长度; + * - 'error': 显示失败图标,此时 title 文本最多显示 7 个汉字长度; + * - 'loading': 显示加载图标,此时 title 文本最多显示 7 个汉字长度; + * - 'none': 不显示图标,此时 title 文本最多可显示两行,[1.9.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html)及以上版本支持; */ + icon?: 'success' | 'error' | 'loading' | 'none' + /** 自定义图标的本地路径,image 的优先级高于 icon + * + * 最低基础库: `1.1.0` */ + image?: string + /** 是否显示透明蒙层,防止触摸穿透 */ + mask?: boolean + /** 接口调用成功的回调函数 */ + success?: ShowToastSuccessCallback + } + interface Size { + /** 变化后的窗口高度,单位 px */ + windowHeight: number + /** 变化后的窗口宽度,单位 px */ + windowWidth: number + } + /** 网络请求过程中一些调试信息 + * + * 最低基础库: `2.10.4` */ + interface SocketProfile { + /** 完成建立连接的时间(完成握手),如果是持久连接,则与 fetchStart 值相等。注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接完成的时间。注意这里握手结束,包括安全连接建立完成、SOCKS 授权通过 */ + connectEnd: number + /** 开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等。注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接开始的时间 */ + connectStart: number + /** 上层请求到返回的耗时 */ + cost: number + /** DNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 */ + domainLookupEnd: number + /** DNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 */ + domainLookupStart: number + /** 组件准备好使用 SOCKET 建立请求的时间,这发生在检查本地缓存之前 */ + fetchStart: number + /** 握手耗时 */ + handshakeCost: number + /** 单次连接的耗时,包括 connect ,tls */ + rtt: number + } + interface SocketTaskCloseOption { + /** 一个数字值表示关闭连接的状态号,表示连接被关闭的原因。 */ + code?: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SocketTaskCloseCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SocketTaskCloseFailCallback + /** 一个可读的字符串,表示连接被关闭的原因。这个字符串必须是不长于 123 字节的 UTF-8 文本(不是字符)。 */ + reason?: string + /** 接口调用成功的回调函数 */ + success?: SocketTaskCloseSuccessCallback + } + interface SocketTaskOnCloseCallbackResult { + /** 一个数字值表示关闭连接的状态号,表示连接被关闭的原因。 */ + code: number + /** 一个可读的字符串,表示连接被关闭的原因。 */ + reason: string + } + interface SocketTaskOnMessageCallbackResult { + /** 服务器返回的消息 */ + data: string | ArrayBuffer + } + interface SocketTaskSendOption { + /** 需要发送的内容 */ + data: string | ArrayBuffer + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SendCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SendFailCallback + /** 接口调用成功的回调函数 */ + success?: SendSuccessCallback + } + interface StartAccelerometerOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartAccelerometerCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartAccelerometerFailCallback + /** 监听加速度数据回调函数的执行频率 + * + * 可选值: + * - 'game': 适用于更新游戏的回调频率,在 20ms/次 左右; + * - 'ui': 适用于更新 UI 的回调频率,在 60ms/次 左右; + * - 'normal': 普通的回调频率,在 200ms/次 左右; + * + * 最低基础库: `2.1.0` */ + interval?: 'game' | 'ui' | 'normal' + /** 接口调用成功的回调函数 */ + success?: StartAccelerometerSuccessCallback + } + interface StartAdvertisingObject { + /** 广播自定义参数 */ + advertiseRequest: AdvertiseReqObj + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartAdvertisingCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartAdvertisingFailCallback + /** 广播功率 + * + * 可选值: + * - 'low': 功率低; + * - 'medium': 功率适中; + * - 'high': 功率高; */ + powerLevel?: 'low' | 'medium' | 'high' + /** 接口调用成功的回调函数 */ + success?: StartAdvertisingSuccessCallback + } + interface StartBeaconDiscoveryOption { + /** iBeacon 设备广播的 uuid 列表 */ + uuids: string[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartBeaconDiscoveryCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartBeaconDiscoveryFailCallback + /** 是否校验蓝牙开关,仅在 iOS 下有效 */ + ignoreBluetoothAvailable?: boolean + /** 接口调用成功的回调函数 */ + success?: StartBeaconDiscoverySuccessCallback + } + interface StartBluetoothDevicesDiscoveryOption { + /** 是否允许重复上报同一设备。如果允许重复上报,则 [wx.onBlueToothDeviceFound](#) 方法会多次上报同一设备,但是 RSSI 值会有不同。 */ + allowDuplicatesKey?: boolean + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartBluetoothDevicesDiscoveryCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartBluetoothDevicesDiscoveryFailCallback + /** 上报设备的间隔。0 表示找到新设备立即上报,其他数值根据传入的间隔上报。 */ + interval?: number + /** 扫描模式,越高扫描越快,也越耗电, 仅安卓 7.0.12 及以上支持。 + * + * 可选值: + * - 'low': 低; + * - 'medium': 中; + * - 'high': 高; */ + powerLevel?: 'low' | 'medium' | 'high' + /** 要搜索的蓝牙设备主 service 的 uuid 列表。某些蓝牙设备会广播自己的主 service 的 uuid。如果设置此参数,则只搜索广播包有对应 uuid 的主服务的蓝牙设备。建议主要通过该参数过滤掉周边不需要处理的其他蓝牙设备。 */ + services?: string[] + /** 接口调用成功的回调函数 */ + success?: StartBluetoothDevicesDiscoverySuccessCallback + } + interface StartCompassOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartCompassCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartCompassFailCallback + /** 接口调用成功的回调函数 */ + success?: StartCompassSuccessCallback + } + interface StartDeviceMotionListeningOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartDeviceMotionListeningCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartDeviceMotionListeningFailCallback + /** 监听设备方向的变化回调函数的执行频率 + * + * 可选值: + * - 'game': 适用于更新游戏的回调频率,在 20ms/次 左右; + * - 'ui': 适用于更新 UI 的回调频率,在 60ms/次 左右; + * - 'normal': 普通的回调频率,在 200ms/次 左右; */ + interval?: 'game' | 'ui' | 'normal' + /** 接口调用成功的回调函数 */ + success?: StartDeviceMotionListeningSuccessCallback + } + interface StartDiscoveryOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartDiscoveryCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartDiscoveryFailCallback + /** 接口调用成功的回调函数 */ + success?: StartDiscoverySuccessCallback + } + interface StartGyroscopeOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartGyroscopeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartGyroscopeFailCallback + /** 监听陀螺仪数据回调函数的执行频率 + * + * 可选值: + * - 'game': 适用于更新游戏的回调频率,在 20ms/次 左右; + * - 'ui': 适用于更新 UI 的回调频率,在 60ms/次 左右; + * - 'normal': 普通的回调频率,在 200ms/次 左右; */ + interval?: 'game' | 'ui' | 'normal' + /** 接口调用成功的回调函数 */ + success?: StartGyroscopeSuccessCallback + } + interface StartHCEOption { + /** 需要注册到系统的 AID 列表 */ + aid_list: string[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartHCECompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartHCEFailCallback + /** 接口调用成功的回调函数 */ + success?: StartHCESuccessCallback + } + interface StartLocalServiceDiscoveryFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'invalid param': serviceType 为空; + * - 'scan task already exist': 在当前 startLocalServiceDiscovery 发起的搜索未停止的情况下,再次调用 startLocalServiceDiscovery; */ + errMsg: string + } + interface StartLocalServiceDiscoveryOption { + /** 要搜索的服务类型 */ + serviceType: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartLocalServiceDiscoveryCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartLocalServiceDiscoveryFailCallback + /** 接口调用成功的回调函数 */ + success?: StartLocalServiceDiscoverySuccessCallback + } + interface StartLocationUpdateBackgroundOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartLocationUpdateBackgroundCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartLocationUpdateBackgroundFailCallback + /** 接口调用成功的回调函数 */ + success?: StartLocationUpdateBackgroundSuccessCallback + } + interface StartLocationUpdateOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartLocationUpdateCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartLocationUpdateFailCallback + /** 接口调用成功的回调函数 */ + success?: StartLocationUpdateSuccessCallback + } + interface StartPreviewOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartPreviewCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartPreviewFailCallback + /** 接口调用成功的回调函数 */ + success?: StartPreviewSuccessCallback + } + interface StartPullDownRefreshOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartPullDownRefreshCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartPullDownRefreshFailCallback + /** 接口调用成功的回调函数 */ + success?: StartPullDownRefreshSuccessCallback + } + interface StartRecordSuccessCallbackResult { + /** 录音文件的临时路径 (本地路径) */ + tempFilePath: string + errMsg: string + } + interface StartRecordTimeoutCallbackResult { + /** 封面图片文件的临时路径 (本地路径) */ + tempThumbPath: string + /** 视频的文件的临时路径 (本地路径) */ + tempVideoPath: string + } + interface StartSoterAuthenticationOption { + /** 挑战因子。挑战因子为调用者为此次生物鉴权准备的用于签名的字符串关键识别信息,将作为 `resultJSON` 的一部分,供调用者识别本次请求。例如:如果场景为请求用户对某订单进行授权确认,则可以将订单号填入此参数。 */ + challenge: string + /** 请求使用的可接受的生物认证方式 + * + * 可选值: + * - 'fingerPrint': 指纹识别; + * - 'facial': 人脸识别; + * - 'speech': 声纹识别(暂未支持); */ + requestAuthModes: Array<'fingerPrint' | 'facial' | 'speech'> + /** 验证描述,即识别过程中显示在界面上的对话框提示内容 */ + authContent?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartSoterAuthenticationCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartSoterAuthenticationFailCallback + /** 接口调用成功的回调函数 */ + success?: StartSoterAuthenticationSuccessCallback + } + interface StartSoterAuthenticationSuccessCallbackResult { + /** 生物认证方式 */ + authMode: string + /** 错误码 */ + errCode: number + /** 错误信息 */ + errMsg: string + /** 在设备安全区域(TEE)内获得的本机安全信息(如TEE名称版本号等以及防重放参数)以及本次认证信息(仅Android支持,本次认证的指纹ID)。具体说明见下文 */ + resultJSON: string + /** 用SOTER安全密钥对 `resultJSON` 的签名(SHA256 with RSA/PSS, saltlen=20) */ + resultJSONSignature: string + } + interface StartWifiOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartWifiCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartWifiFailCallback + /** 接口调用成功的回调函数 */ + success?: StartWifiSuccessCallback + } + interface StatFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail permission denied, open ${path}': 指定的 path 路径没有读权限; + * - 'fail no such file or directory ${path}': 文件不存在; + * - 'fail sdcard not mounted': Android sdcard 挂载失败; */ + errMsg: string + } + interface StatOption { + /** 文件/目录路径 (本地路径) */ + path: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StatCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StatFailCallback + /** 是否递归获取目录下的每个文件的 Stats 信息 + * + * 最低基础库: `2.3.0` */ + recursive?: boolean + /** 接口调用成功的回调函数 */ + success?: StatSuccessCallback + } + interface StatSuccessCallbackResult { + /** [Stats](https://developers.weixin.qq.com/miniprogram/dev/api/file/Stats.html)|Object + * + * 当 recursive 为 false 时,res.stats 是一个 Stats 对象。当 recursive 为 true 且 path 是一个目录的路径时,res.stats 是一个 Object,key 以 path 为根路径的相对路径,value 是该路径对应的 Stats 对象。 */ + stats: Stats | IAnyObject + errMsg: string + } + /** 描述文件状态的对象 */ + interface Stats { + /** 文件最近一次被存取或被执行的时间,UNIX 时间戳,对应 POSIX stat.st_atime */ + lastAccessedTime: number + /** 文件最后一次被修改的时间,UNIX 时间戳,对应 POSIX stat.st_mtime */ + lastModifiedTime: number + /** 文件的类型和存取的权限,对应 POSIX stat.st_mode */ + mode: string + /** 文件大小,单位:B,对应 POSIX stat.st_size */ + size: number + } + interface StepOption { + /** 动画延迟时间,单位 ms */ + delay?: number + /** 动画持续时间,单位 ms */ + duration?: number + /** 动画的效果 + * + * 可选值: + * - 'linear': 动画从头到尾的速度是相同的; + * - 'ease': 动画以低速开始,然后加快,在结束前变慢; + * - 'ease-in': 动画以低速开始; + * - 'ease-in-out': 动画以低速开始和结束; + * - 'ease-out': 动画以低速结束; + * - 'step-start': 动画第一帧就跳至结束状态直到结束; + * - 'step-end': 动画一直保持开始状态,最后一帧跳到结束状态; */ + timingFunction?: + | 'linear' + | 'ease' + | 'ease-in' + | 'ease-in-out' + | 'ease-out' + | 'step-start' + | 'step-end' + transformOrigin?: string + } + interface StopAccelerometerOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopAccelerometerCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopAccelerometerFailCallback + /** 接口调用成功的回调函数 */ + success?: StopAccelerometerSuccessCallback + } + interface StopAdvertisingOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopAdvertisingCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopAdvertisingFailCallback + /** 接口调用成功的回调函数 */ + success?: StopAdvertisingSuccessCallback + } + interface StopBGMOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopBGMCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopBGMFailCallback + /** 接口调用成功的回调函数 */ + success?: StopBGMSuccessCallback + } + interface StopBackgroundAudioOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopBackgroundAudioCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopBackgroundAudioFailCallback + /** 接口调用成功的回调函数 */ + success?: StopBackgroundAudioSuccessCallback + } + interface StopBeaconDiscoveryOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopBeaconDiscoveryCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopBeaconDiscoveryFailCallback + /** 接口调用成功的回调函数 */ + success?: StopBeaconDiscoverySuccessCallback + } + interface StopBluetoothDevicesDiscoveryOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopBluetoothDevicesDiscoveryCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopBluetoothDevicesDiscoveryFailCallback + /** 接口调用成功的回调函数 */ + success?: StopBluetoothDevicesDiscoverySuccessCallback + } + interface StopCompassOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopCompassCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopCompassFailCallback + /** 接口调用成功的回调函数 */ + success?: StopCompassSuccessCallback + } + interface StopDeviceMotionListeningOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopDeviceMotionListeningCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopDeviceMotionListeningFailCallback + /** 接口调用成功的回调函数 */ + success?: StopDeviceMotionListeningSuccessCallback + } + interface StopDiscoveryOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopDiscoveryCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopDiscoveryFailCallback + /** 接口调用成功的回调函数 */ + success?: StopDiscoverySuccessCallback + } + interface StopGyroscopeOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopGyroscopeCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopGyroscopeFailCallback + /** 接口调用成功的回调函数 */ + success?: StopGyroscopeSuccessCallback + } + interface StopHCEOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopHCECompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopHCEFailCallback + /** 接口调用成功的回调函数 */ + success?: StopHCESuccessCallback + } + interface StopLocalServiceDiscoveryFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'task not found': 在当前没有处在搜索服务中的情况下调用 stopLocalServiceDiscovery; */ + errMsg: string + } + interface StopLocalServiceDiscoveryOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopLocalServiceDiscoveryCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopLocalServiceDiscoveryFailCallback + /** 接口调用成功的回调函数 */ + success?: StopLocalServiceDiscoverySuccessCallback + } + interface StopLocationUpdateOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopLocationUpdateCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopLocationUpdateFailCallback + /** 接口调用成功的回调函数 */ + success?: StopLocationUpdateSuccessCallback + } + interface StopOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopFailCallback + /** 接口调用成功的回调函数 */ + success?: StopSuccessCallback + } + interface StopPreviewOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopPreviewCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopPreviewFailCallback + /** 接口调用成功的回调函数 */ + success?: StopPreviewSuccessCallback + } + interface StopPullDownRefreshOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopPullDownRefreshCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopPullDownRefreshFailCallback + /** 接口调用成功的回调函数 */ + success?: StopPullDownRefreshSuccessCallback + } + interface StopRecordSuccessCallbackResult { + /** 封面图片文件的临时路径 (本地路径) */ + tempThumbPath: string + /** 视频的文件的临时路径 (本地路径) */ + tempVideoPath: string + errMsg: string + } + interface StopVoiceOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopVoiceCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopVoiceFailCallback + /** 接口调用成功的回调函数 */ + success?: StopVoiceSuccessCallback + } + interface StopWifiOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopWifiCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopWifiFailCallback + /** 接口调用成功的回调函数 */ + success?: StopWifiSuccessCallback + } + interface SubscribeVoIPVideoMembersOption { + /** 订阅的成员列表 */ + openIdList: string[] + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SubscribeVoIPVideoMembersCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SubscribeVoIPVideoMembersFailCallback + /** 接口调用成功的回调函数 */ + success?: SubscribeVoIPVideoMembersSuccessCallback + } + /** 订阅消息设置 +* +* **示例代码** +* +* +* ```javascript +wx.getSetting({ + withSubscriptions: true, + success (res) { + console.log(res.authSetting) + // res.authSetting = { + // "scope.userInfo": true, + // "scope.userLocation": true + // } + console.log(res.subscriptionsSetting) + // res.subscriptionsSetting = { + // mainSwitch: true, // 订阅消息总开关 + // itemSettings: { // 每一项开关 + // SYS_MSG_TYPE_INTERACTIVE: 'accept', // 小游戏系统订阅消息 + // SYS_MSG_TYPE_RANK: 'accept' + // zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: 'reject', // 普通一次性订阅消息 + // ke_OZC_66gZxALLcsuI7ilCJSP2OJ2vWo2ooUPpkWrw: 'ban', + // } + // } + } +}) +``` */ + interface SubscriptionsSetting { + /** 订阅消息总开关,true为开启,false为关闭 */ + mainSwitch: boolean + /** 每一项订阅消息的订阅状态。itemSettings对象的键为**一次性订阅消息的模板id**或**系统订阅消息的类型**,值为'accept'、'reject'、'ban'中的其中一种。'accept'表示用户同意订阅这条消息,'reject'表示用户拒绝订阅这条消息,'ban'表示已被后台封禁。一次性订阅消息使用方法详见 [wx.requestSubscribeMessage](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/subscribe-message/wx.requestSubscribeMessage.html),永久订阅消息(仅小游戏可用)使用方法详见[wx.requestSubscribeSystemMessage](/minigame/dev/api/open-api/subscribe-message/wx.requestSubscribeSystemMessage.html) + * ## 注意事项 + * - itemSettings 只返回用户勾选过订阅面板中的“总是保持以上选择,不再询问”的订阅消息。 */ + itemSettings?: IAnyObject + } + interface SwitchCameraOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SwitchCameraCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SwitchCameraFailCallback + /** 接口调用成功的回调函数 */ + success?: SwitchCameraSuccessCallback + } + interface SwitchTabOption { + /** 需要跳转的 tabBar 页面的路径 (代码包路径)(需在 app.json 的 [tabBar](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#tabbar) 字段定义的页面),路径后不能带参数。 */ + url: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SwitchTabCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: SwitchTabFailCallback + /** 接口调用成功的回调函数 */ + success?: SwitchTabSuccessCallback + } + interface SystemInfo { + /** 客户端基础库版本 + * + * 最低基础库: `1.1.0` */ + SDKVersion: string + /** 允许微信使用相册的开关(仅 iOS 有效) + * + * 最低基础库: `2.6.0` */ + albumAuthorized: boolean + /** 设备性能等级(仅 Android)。取值为:-2 或 0(该设备无法运行小游戏),-1(性能未知),>=1(设备性能值,该值越高,设备性能越好,目前最高不到50) + * + * 最低基础库: `1.8.0` */ + benchmarkLevel: number + /** 蓝牙的系统开关 + * + * 最低基础库: `2.6.0` */ + bluetoothEnabled: boolean + /** 设备品牌 + * + * 最低基础库: `1.5.0` */ + brand: string + /** 允许微信使用摄像头的开关 + * + * 最低基础库: `2.6.0` */ + cameraAuthorized: boolean + /** 设备方向 + * + * 可选值: + * - 'portrait': 竖屏; + * - 'landscape': 横屏; */ + deviceOrientation: 'portrait' | 'landscape' + /** 是否已打开调试。可通过右上角菜单或 [wx.setEnableDebug](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/wx.setEnableDebug.html) 打开调试。 + * + * 最低基础库: `2.15.0` */ + enableDebug: boolean + /** 用户字体大小(单位px)。以微信客户端「我-设置-通用-字体大小」中的设置为准 + * + * 最低基础库: `1.5.0` */ + fontSizeSetting: number + /** 微信设置的语言 */ + language: string + /** 允许微信使用定位的开关 + * + * 最低基础库: `2.6.0` */ + locationAuthorized: boolean + /** 地理位置的系统开关 + * + * 最低基础库: `2.6.0` */ + locationEnabled: boolean + /** `true` 表示模糊定位,`false` 表示精确定位,仅 iOS 支持 */ + locationReducedAccuracy: boolean + /** 允许微信使用麦克风的开关 + * + * 最低基础库: `2.6.0` */ + microphoneAuthorized: boolean + /** 设备型号。新机型刚推出一段时间会显示unknown,微信会尽快进行适配。 */ + model: string + /** 允许微信通知带有提醒的开关(仅 iOS 有效) + * + * 最低基础库: `2.6.0` */ + notificationAlertAuthorized: boolean + /** 允许微信通知的开关 + * + * 最低基础库: `2.6.0` */ + notificationAuthorized: boolean + /** 允许微信通知带有标记的开关(仅 iOS 有效) + * + * 最低基础库: `2.6.0` */ + notificationBadgeAuthorized: boolean + /** 允许微信通知带有声音的开关(仅 iOS 有效) + * + * 最低基础库: `2.6.0` */ + notificationSoundAuthorized: boolean + /** 设备像素比 */ + pixelRatio: number + /** 客户端平台 */ + platform: string + /** 在竖屏正方向下的安全区域 + * + * 最低基础库: `2.7.0` */ + safeArea: SafeArea + /** 屏幕高度,单位px + * + * 最低基础库: `1.1.0` */ + screenHeight: number + /** 屏幕宽度,单位px + * + * 最低基础库: `1.1.0` */ + screenWidth: number + /** 状态栏的高度,单位px + * + * 最低基础库: `1.9.0` */ + statusBarHeight: number + /** 操作系统及版本 */ + system: string + /** 微信版本号 */ + version: string + /** Wi-Fi 的系统开关 + * + * 最低基础库: `2.6.0` */ + wifiEnabled: boolean + /** 可使用窗口高度,单位px */ + windowHeight: number + /** 可使用窗口宽度,单位px */ + windowWidth: number + /** 系统当前主题,取值为`light`或`dark`,全局配置`"darkmode":true`时才能获取,否则为 undefined (不支持小游戏) + * + * 可选值: + * - 'dark': 深色主题; + * - 'light': 浅色主题; + * + * 最低基础库: `2.11.0` */ + theme?: 'dark' | 'light' + } + interface TakePhotoOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: TakePhotoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: TakePhotoFailCallback + /** 成像质量 + * + * 可选值: + * - 'high': 高质量; + * - 'normal': 普通质量; + * - 'low': 低质量; */ + quality?: 'high' | 'normal' | 'low' + /** 接口调用成功的回调函数 */ + success?: TakePhotoSuccessCallback + } + interface TakePhotoSuccessCallbackResult { + /** 照片文件的临时路径 (本地路径),安卓是jpg图片格式,ios是png */ + tempImagePath: string + errMsg: string + } + /** 标签类型枚举 */ + interface TechType { + /** 对应IsoDep实例,实例支持ISO-DEP (ISO 14443-4)标准的读写 */ + isoDep: string + /** 对应MifareClassic实例,实例支持MIFARE Classic标签的读写 */ + mifareClassic: string + /** 对应MifareUltralight实例,实例支持MIFARE Ultralight标签的读写 */ + mifareUltralight: string + /** 对应Ndef实例,实例支持对NDEF格式的NFC标签上的NDEF数据的读写 */ + ndef: string + /** 对应NfcA实例,实例支持NFC-A (ISO 14443-3A)标准的读写 */ + nfcA: string + /** 对应NfcB实例,实例支持NFC-B (ISO 14443-3B)标准的读写 */ + nfcB: string + /** 对应NfcF实例,实例支持NFC-F (JIS 6319-4)标准的读写 */ + nfcF: string + /** 对应NfcV实例,实例支持NFC-V (ISO 15693)标准的读写 */ + nfcV: string + } + interface TextMetrics { + /** 文本的宽度 */ + width: number + } + interface ToScreenLocationOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ToScreenLocationCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ToScreenLocationFailCallback + /** 接口调用成功的回调函数 */ + success?: ToScreenLocationSuccessCallback + } + interface ToScreenLocationSuccessCallbackResult { + /** x 坐标值 */ + x: number + /** y 坐标值 */ + y: number + errMsg: string + } + interface ToggleTorchOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: ToggleTorchCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: ToggleTorchFailCallback + /** 接口调用成功的回调函数 */ + success?: ToggleTorchSuccessCallback + } + interface TransceiveOption { + /** 需要传递的二进制数据 */ + data: ArrayBuffer + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: TransceiveCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: TransceiveFailCallback + /** 接口调用成功的回调函数 */ + success?: TransceiveSuccessCallback + } + interface TransceiveSuccessCallbackResult { + data: ArrayBuffer + errMsg: string + } + interface TranslateMarkerOption { + /** 移动过程中是否自动旋转 marker */ + autoRotate: boolean + /** 指定 marker 移动到的目标点 */ + destination: DestinationOption + /** 指定 marker */ + markerId: number + /** marker 的旋转角度 */ + rotate: number + /** 动画结束回调函数 */ + animationEnd?: (...args: any[]) => any + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: TranslateMarkerCompleteCallback + /** 动画持续时长,平移与旋转分别计算 */ + duration?: number + /** 接口调用失败的回调函数 */ + fail?: TranslateMarkerFailCallback + /** 平移和旋转同时进行 + * + * 最低基础库: `2.13.0` */ + moveWithRotate?: boolean + /** 接口调用成功的回调函数 */ + success?: TranslateMarkerSuccessCallback + } + interface UDPSocketOnErrorCallbackResult { + /** 错误信息 */ + errMsg: string + } + interface UDPSocketOnMessageCallbackResult { + /** 收到的消息 */ + message: ArrayBuffer + /** 消息来源的结构化信息 */ + remoteInfo: RemoteInfo + } + interface UDPSocketSendOption { + /** 要发消息的地址。在基础库 2.9.3 及之前版本可以是一个和本机同网段的 IP 地址,也可以是在安全域名列表内的域名地址;在基础库 2.9.4 及之后版本,可以是任意 IP 和域名 */ + address: string + /** 要发送的数据 */ + message: string | ArrayBuffer + /** 要发送消息的端口号 */ + port: number + /** 发送数据的长度,仅当 message 为 ArrayBuffer 类型时有效 */ + length?: number + /** 发送数据的偏移量,仅当 message 为 ArrayBuffer 类型时有效 */ + offset?: number + } + interface UndoOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: UndoCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: UndoFailCallback + /** 接口调用成功的回调函数 */ + success?: UndoSuccessCallback + } + interface UnlinkFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail permission denied, open ${path}': 指定的 path 路径没有读权限; + * - 'fail no such file or directory ${path}': 文件不存在; + * - 'fail operation not permitted, unlink ${filePath}': 传入的 filePath 是一个目录; + * - 'fail sdcard not mounted': Android sdcard 挂载失败; */ + errMsg: string + } + interface UnlinkOption { + /** 要删除的文件路径 (本地路径) */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: UnlinkCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: UnlinkFailCallback + /** 接口调用成功的回调函数 */ + success?: UnlinkSuccessCallback + } + interface UnzipFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail permission denied, unzip ${zipFilePath} -> ${destPath}': 指定目标文件路径没有写权限; + * - 'fail no such file or directory, unzip ${zipFilePath} -> "${destPath}': 源文件不存在,或目标文件路径的上层目录不存在; */ + errMsg: string + } + interface UnzipOption { + /** 目标目录路径, 支持本地路径 */ + targetPath: string + /** 源文件路径,支持本地路径, 只可以是 zip 压缩文件 */ + zipFilePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: UnzipCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: UnzipFailCallback + /** 接口调用成功的回调函数 */ + success?: UnzipSuccessCallback + } + /** 参数列表 */ + interface UpdatableMessageFrontEndParameter { + /** 参数名 */ + name: string + /** 参数值 */ + value: string + } + /** 动态消息的模板信息 + * + * 最低基础库: `2.4.0` */ + interface UpdatableMessageFrontEndTemplateInfo { + /** 参数列表 */ + parameterList: UpdatableMessageFrontEndParameter[] + } + interface UpdateGroundOverlayOption { + /** 图片覆盖的经纬度范围 */ + bounds: MapBounds + /** 图片图层 id */ + id: string + /** 图片路径,支持网络图片、临时路径、代码包路径 */ + src: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: UpdateGroundOverlayCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: UpdateGroundOverlayFailCallback + /** 图层透明度 */ + opacity?: number + /** 接口调用成功的回调函数 */ + success?: UpdateGroundOverlaySuccessCallback + /** 是否可见 */ + visible?: boolean + /** 图层绘制顺序 */ + zIndex?: number + } + interface UpdateShareMenuOption { + /** 动态消息的 activityId。通过 [updatableMessage.createActivityId](https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/updatable-message/updatableMessage.createActivityId.html) 接口获取 + * + * 最低基础库: `2.4.0` */ + activityId?: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: UpdateShareMenuCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: UpdateShareMenuFailCallback + /** 是否是私密消息。详见 [小程序私密消息](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share/private-message.html) + * + * 最低基础库: `2.13.0` */ + isPrivateMessage?: boolean + /** 是否是动态消息,详见[动态消息](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share/updatable-message.html) + * + * 最低基础库: `2.4.0` */ + isUpdatableMessage?: boolean + /** 接口调用成功的回调函数 */ + success?: UpdateShareMenuSuccessCallback + /** 动态消息的模板信息 + * + * 最低基础库: `2.4.0` */ + templateInfo?: UpdatableMessageFrontEndTemplateInfo + /** 群待办消息的id,通过toDoActivityId可以把多个群待办消息聚合为同一个。通过 [updatableMessage.createActivityId](https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/updatable-message/updatableMessage.createActivityId.html) 接口获取。详见[群待办消息](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share.html) + * + * 最低基础库: `2.11.0` */ + toDoActivityId?: string + /** 是否使用带 shareTicket 的转发[详情](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share.html) */ + withShareTicket?: boolean + } + interface UpdateVoIPChatMuteConfigOption { + /** 静音设置 */ + muteConfig: MuteConfig + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: UpdateVoIPChatMuteConfigCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: UpdateVoIPChatMuteConfigFailCallback + /** 接口调用成功的回调函数 */ + success?: UpdateVoIPChatMuteConfigSuccessCallback + } + interface UpdateWeChatAppOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: UpdateWeChatAppCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: UpdateWeChatAppFailCallback + /** 接口调用成功的回调函数 */ + success?: UpdateWeChatAppSuccessCallback + } + interface UploadFileOption { + /** 要上传文件资源的路径 (本地路径) */ + filePath: string + /** 文件对应的 key,开发者在服务端可以通过这个 key 获取文件的二进制内容 */ + name: string + /** 开发者服务器地址 */ + url: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: UploadFileCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: UploadFileFailCallback + /** HTTP 请求中其他额外的 form data */ + formData?: IAnyObject + /** HTTP 请求 Header,Header 中不能设置 Referer */ + header?: IAnyObject + /** 接口调用成功的回调函数 */ + success?: UploadFileSuccessCallback + /** 超时时间,单位为毫秒 + * + * 最低基础库: `2.10.0` */ + timeout?: number + } + interface UploadFileSuccessCallbackResult { + /** 开发者服务器返回的数据 */ + data: string + /** 开发者服务器返回的 HTTP 状态码 */ + statusCode: number + errMsg: string + } + interface UploadTaskOnProgressUpdateCallbackResult { + /** 上传进度百分比 */ + progress: number + /** 预期需要上传的数据总长度,单位 Bytes */ + totalBytesExpectedToSend: number + /** 已经上传的数据长度,单位 Bytes */ + totalBytesSent: number + } + /** 用户信息 */ + interface UserInfo { + /** 用户头像图片的 URL。URL 最后一个数值代表正方形头像大小(有 0、46、64、96、132 数值可选,0 代表 640x640 的正方形头像,46 表示 46x46 的正方形头像,剩余数值以此类推。默认132),用户没有头像时该项为空。若用户更换头像,原有头像 URL 将失效。 */ + avatarUrl: string + /** 用户所在城市 */ + city: string + /** 用户所在国家 */ + country: string + /** 用户性别 + * + * 可选值: + * - 0: 未知; + * - 1: 男性; + * - 2: 女性; */ + gender: 0 | 1 | 2 + /** 显示 country,province,city 所用的语言 + * + * 可选值: + * - 'en': 英文; + * - 'zh_CN': 简体中文; + * - 'zh_TW': 繁体中文; */ + language: 'en' | 'zh_CN' | 'zh_TW' + /** 用户昵称 */ + nickName: string + /** 用户所在省份 */ + province: string + } + interface VibrateLongOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: VibrateLongCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: VibrateLongFailCallback + /** 接口调用成功的回调函数 */ + success?: VibrateLongSuccessCallback + } + interface VibrateShortOption { + /** 震动强度类型,有效值为:heavy、medium、light + * + * 最低基础库: `2.13.0` */ + type: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: VibrateShortCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: VibrateShortFailCallback + /** 接口调用成功的回调函数 */ + success?: VibrateShortSuccessCallback + } + interface VideoContextRequestFullScreenOption { + /** 设置全屏时视频的方向,不指定则根据宽高比自动判断。 + * + * 可选值: + * - 0: 正常竖向; + * - 90: 屏幕逆时针90度; + * - -90: 屏幕顺时针90度; + * + * 最低基础库: `1.7.0` */ + direction?: 0 | 90 | -90 + } + interface VideoDecoderStartOption { + /** 需要解码的视频源文件。基础库 2.13.0 以下的版本只支持本地路径。 2.13.0 开始支持 http:// 和 https:// 协议的远程路径。 */ + source: string + /** 解码模式。0:按 pts 解码;1:以最快速度解码 */ + mode?: number + } + /** 提供预设的 Wi-Fi 信息列表 */ + interface WifiData { + /** Wi-Fi 的 BSSID */ + BSSID?: string + /** Wi-Fi 的 SSID */ + SSID?: string + /** Wi-Fi 设备密码 */ + password?: string + } + /** Wifi 信息 */ + interface WifiInfo { + /** Wi-Fi 的 BSSID */ + BSSID: string + /** Wi-Fi 的 SSID */ + SSID: string + /** Wi-Fi 频段单位 MHz + * + * 最低基础库: `2.12.0` */ + frequency: number + /** Wi-Fi 是否安全 */ + secure: boolean + /** Wi-Fi 信号强度 */ + signalStrength: number + } + interface WorkerOnMessageCallbackResult { + /** 主线程/Worker 线程向当前线程发送的消息 */ + message: IAnyObject + } + interface WriteBLECharacteristicValueOption { + /** 蓝牙特征值的 uuid */ + characteristicId: string + /** 蓝牙设备 id */ + deviceId: string + /** 蓝牙特征值对应服务的 uuid */ + serviceId: string + /** 蓝牙设备特征值对应的二进制值 */ + value: ArrayBuffer + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: WriteBLECharacteristicValueCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: WriteBLECharacteristicValueFailCallback + /** 接口调用成功的回调函数 */ + success?: WriteBLECharacteristicValueSuccessCallback + } + interface WriteCharacteristicValueObject { + /** characteristic对应的uuid */ + characteristicId: string + /** 是否需要通知主机value已更新 */ + needNotify: boolean + /** service 的 uuid */ + serviceId: string + /** 特征值对应的二进制值 */ + value: ArrayBuffer + /** 可选,处理回包时使用 */ + callbackId?: number + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: WriteCharacteristicValueCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: WriteCharacteristicValueFailCallback + /** 接口调用成功的回调函数 */ + success?: WriteCharacteristicValueSuccessCallback + } + interface WriteFileFailCallbackResult { + /** 错误信息 + * + * 可选值: + * - 'fail no such file or directory, open ${filePath}': 指定的 filePath 所在目录不存在; + * - 'fail permission denied, open ${dirPath}': 指定的 filePath 路径没有写权限; + * - 'fail the maximum size of the file storage limit is exceeded': 存储空间不足; + * - 'fail sdcard not mounted': Android sdcard 挂载失败; */ + errMsg: string + } + interface WriteFileOption { + /** 要写入的文本或二进制数据 */ + data: string | ArrayBuffer + /** 要写入的文件路径 (本地路径) */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: WriteFileCompleteCallback + /** 指定写入文件的字符编码 + * + * 可选值: + * - 'ascii': ; + * - 'base64': ; + * - 'binary': ; + * - 'hex': ; + * - 'ucs2': 以小端序读取; + * - 'ucs-2': 以小端序读取; + * - 'utf16le': 以小端序读取; + * - 'utf-16le': 以小端序读取; + * - 'utf-8': ; + * - 'utf8': ; + * - 'latin1': ; */ + encoding?: + | 'ascii' + | 'base64' + | 'binary' + | 'hex' + | 'ucs2' + | 'ucs-2' + | 'utf16le' + | 'utf-16le' + | 'utf-8' + | 'utf8' + | 'latin1' + /** 接口调用失败的回调函数 */ + fail?: WriteFileFailCallback + /** 接口调用成功的回调函数 */ + success?: WriteFileSuccessCallback + } + interface WriteNdefMessageOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: WriteNdefMessageCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: WriteNdefMessageFailCallback + /** 二进制对象数组, 需要指明 id, type 以及 payload (均为 ArrayBuffer 类型) */ + records?: any[] + /** 接口调用成功的回调函数 */ + success?: WriteNdefMessageSuccessCallback + /** text 数组 */ + texts?: any[] + /** uri 数组 */ + uris?: any[] + } + interface WxGetFileInfoOption { + /** 本地文件路径 (本地路径) */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetFileInfoCompleteCallback + /** 计算文件摘要的算法 + * + * 可选值: + * - 'md5': md5 算法; + * - 'sha1': sha1 算法; */ + digestAlgorithm?: 'md5' | 'sha1' + /** 接口调用失败的回调函数 */ + fail?: WxGetFileInfoFailCallback + /** 接口调用成功的回调函数 */ + success?: WxGetFileInfoSuccessCallback + } + interface WxGetFileInfoSuccessCallbackResult { + /** 按照传入的 digestAlgorithm 计算得出的的文件摘要 */ + digest: string + /** 文件大小,以字节为单位 */ + size: number + errMsg: string + } + interface WxGetSavedFileListOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: GetSavedFileListCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: GetSavedFileListFailCallback + /** 接口调用成功的回调函数 */ + success?: WxGetSavedFileListSuccessCallback + } + interface WxGetSavedFileListSuccessCallbackResult { + /** 文件数组,每一项是一个 FileItem */ + fileList: FileItem[] + errMsg: string + } + interface WxRemoveSavedFileOption { + /** 需要删除的文件路径 (本地路径) */ + filePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: RemoveSavedFileCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: WxRemoveSavedFileFailCallback + /** 接口调用成功的回调函数 */ + success?: RemoveSavedFileSuccessCallback + } + interface WxSaveFileOption { + /** 需要保存的文件的临时路径 (本地路径) */ + tempFilePath: string + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: SaveFileCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: WxSaveFileFailCallback + /** 接口调用成功的回调函数 */ + success?: SaveFileSuccessCallback + } + interface WxStartRecordOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StartRecordCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StartRecordFailCallback + /** 接口调用成功的回调函数 */ + success?: WxStartRecordSuccessCallback + } + interface WxStopRecordOption { + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: StopRecordCompleteCallback + /** 接口调用失败的回调函数 */ + fail?: StopRecordFailCallback + /** 接口调用成功的回调函数 */ + success?: WxStopRecordSuccessCallback + } + interface Animation { + /** [Object Animation.export()](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.export.html) + * + * 导出动画队列。**export 方法每次调用后会清掉之前的动画操作。** */ + export(): AnimationExportResult + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.backgroundColor(string value)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.backgroundColor.html) + * + * 设置背景色 */ + backgroundColor( + /** 颜色值 */ + value: string + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.bottom(number|string value)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.bottom.html) + * + * 设置 bottom 值 */ + bottom( + /** 长度值,如果传入 number 则默认使用 px,可传入其他自定义单位的长度值 */ + value: number | string + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.height(number|string value)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.height.html) + * + * 设置高度 */ + height( + /** 长度值,如果传入 number 则默认使用 px,可传入其他自定义单位的长度值 */ + value: number | string + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.left(number|string value)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.left.html) + * + * 设置 left 值 */ + left( + /** 长度值,如果传入 number 则默认使用 px,可传入其他自定义单位的长度值 */ + value: number | string + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.matrix()](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.matrix.html) + * + * 同 [transform-function matrix](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix) */ + matrix(): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.matrix3d()](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.matrix3d.html) + * + * 同 [transform-function matrix3d](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix3d) */ + matrix3d(): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.opacity(number value)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.opacity.html) + * + * 设置透明度 */ + opacity( + /** 透明度,范围 0-1 */ + value: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.right(number|string value)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.right.html) + * + * 设置 right 值 */ + right( + /** 长度值,如果传入 number 则默认使用 px,可传入其他自定义单位的长度值 */ + value: number | string + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.rotate(number angle)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.rotate.html) + * + * 从原点顺时针旋转一个角度 */ + rotate( + /** 旋转的角度。范围 [-180, 180] */ + angle: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.rotate3d(number x, number y, number z, number angle)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.rotate3d.html) + * + * 从 固定 轴顺时针旋转一个角度 */ + rotate3d( + /** 旋转轴的 x 坐标 */ + x: number, + /** 旋转轴的 y 坐标 */ + y: number, + /** 旋转轴的 z 坐标 */ + z: number, + /** 旋转的角度。范围 [-180, 180] */ + angle: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.rotateX(number angle)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.rotateX.html) + * + * 从 X 轴顺时针旋转一个角度 */ + rotateX( + /** 旋转的角度。范围 [-180, 180] */ + angle: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.rotateY(number angle)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.rotateY.html) + * + * 从 Y 轴顺时针旋转一个角度 */ + rotateY( + /** 旋转的角度。范围 [-180, 180] */ + angle: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.rotateZ(number angle)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.rotateZ.html) + * + * 从 Z 轴顺时针旋转一个角度 */ + rotateZ( + /** 旋转的角度。范围 [-180, 180] */ + angle: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.scale(number sx, number sy)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.scale.html) + * + * 缩放 */ + scale( + /** 当仅有 sx 参数时,表示在 X 轴、Y 轴同时缩放sx倍数 */ + sx: number, + /** 在 Y 轴缩放 sy 倍数 */ + sy?: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.scale3d(number sx, number sy, number sz)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.scale3d.html) + * + * 缩放 */ + scale3d( + /** x 轴的缩放倍数 */ + sx: number, + /** y 轴的缩放倍数 */ + sy: number, + /** z 轴的缩放倍数 */ + sz: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.scaleX(number scale)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.scaleX.html) + * + * 缩放 X 轴 */ + scaleX( + /** X 轴的缩放倍数 */ + scale: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.scaleY(number scale)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.scaleY.html) + * + * 缩放 Y 轴 */ + scaleY( + /** Y 轴的缩放倍数 */ + scale: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.scaleZ(number scale)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.scaleZ.html) + * + * 缩放 Z 轴 */ + scaleZ( + /** Z 轴的缩放倍数 */ + scale: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.skew(number ax, number ay)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.skew.html) + * + * 对 X、Y 轴坐标进行倾斜 */ + skew( + /** 对 X 轴坐标倾斜的角度,范围 [-180, 180] */ + ax: number, + /** 对 Y 轴坐标倾斜的角度,范围 [-180, 180] */ + ay: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.skewX(number angle)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.skewX.html) + * + * 对 X 轴坐标进行倾斜 */ + skewX( + /** 倾斜的角度,范围 [-180, 180] */ + angle: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.skewY(number angle)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.skewY.html) + * + * 对 Y 轴坐标进行倾斜 */ + skewY( + /** 倾斜的角度,范围 [-180, 180] */ + angle: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.step(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.step.html) + * + * 表示一组动画完成。可以在一组动画中调用任意多个动画方法,一组动画中的所有动画会同时开始,一组动画完成后才会进行下一组动画。 */ + step(option?: StepOption): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.top(number|string value)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.top.html) + * + * 设置 top 值 */ + top( + /** 长度值,如果传入 number 则默认使用 px,可传入其他自定义单位的长度值 */ + value: number | string + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.translate(number tx, number ty)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.translate.html) + * + * 平移变换 */ + translate( + /** 当仅有该参数时表示在 X 轴偏移 tx,单位 px */ + tx?: number, + /** 在 Y 轴平移的距离,单位为 px */ + ty?: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.translate3d(number tx, number ty, number tz)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.translate3d.html) + * + * 对 xyz 坐标进行平移变换 */ + translate3d( + /** 在 X 轴平移的距离,单位为 px */ + tx?: number, + /** 在 Y 轴平移的距离,单位为 px */ + ty?: number, + /** 在 Z 轴平移的距离,单位为 px */ + tz?: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.translateX(number translation)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.translateX.html) + * + * 对 X 轴平移 */ + translateX( + /** 在 X 轴平移的距离,单位为 px */ + translation: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.translateY(number translation)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.translateY.html) + * + * 对 Y 轴平移 */ + translateY( + /** 在 Y 轴平移的距离,单位为 px */ + translation: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.translateZ(number translation)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.translateZ.html) + * + * 对 Z 轴平移 */ + translateZ( + /** 在 Z 轴平移的距离,单位为 px */ + translation: number + ): Animation + /** [[Animation](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.html) Animation.width(number|string value)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/Animation.width.html) + * + * 设置宽度 */ + width( + /** 长度值,如果传入 number 则默认使用 px,可传入其他自定义单位的长度值 */ + value: number | string + ): Animation + } + interface AudioContext { + /** [AudioContext.pause()](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/AudioContext.pause.html) + * + * 暂停音频。 */ + pause(): void + /** [AudioContext.play()](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/AudioContext.play.html) + * + * 播放音频。 */ + play(): void + /** [AudioContext.seek(number position)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/AudioContext.seek.html) + * + * 跳转到指定位置。 */ + seek( + /** 跳转位置,单位 s */ + position: number + ): void + /** [AudioContext.setSrc(string src)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/AudioContext.setSrc.html) + * + * 设置音频地址 */ + setSrc( + /** 音频地址 */ + src: string + ): void + } + interface BLEPeripheralServer { + /** [BLEPeripheralServer.addService(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.addService.html) + * + * 添加服务。 + * + * 最低基础库: `2.10.3` */ + addService(option: AddServiceOption): void + /** [BLEPeripheralServer.close(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.close.html) + * + * 关闭当前服务端。 + * + * 最低基础库: `2.10.3` */ + close(option?: BLEPeripheralServerCloseOption): void + /** [BLEPeripheralServer.offCharacteristicReadRequest(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.offCharacteristicReadRequest.html) + * + * 取消监听已连接的设备请求读当前外围设备的特征值事件 + * + * 最低基础库: `2.10.3` */ + offCharacteristicReadRequest( + /** 已连接的设备请求读当前外围设备的特征值事件的回调函数 */ + callback?: OffCharacteristicReadRequestCallback + ): void + /** [BLEPeripheralServer.offCharacteristicSubscribed(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.offCharacteristicSubscribed.html) + * + * 取消监听特征值订阅事件 + * + * 最低基础库: `2.13.0` */ + offCharacteristicSubscribed( + /** 特征值订阅事件的回调函数 */ + callback?: OffCharacteristicSubscribedCallback + ): void + /** [BLEPeripheralServer.offCharacteristicUnsubscribed(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.offCharacteristicUnsubscribed.html) + * + * 取消监听取消特征值订阅事件 + * + * 最低基础库: `2.13.0` */ + offCharacteristicUnsubscribed( + /** 取消特征值订阅事件的回调函数 */ + callback?: OffCharacteristicUnsubscribedCallback + ): void + /** [BLEPeripheralServer.offCharacteristicWriteRequest(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.offCharacteristicWriteRequest.html) + * + * 取消监听已连接的设备请求写当前外围设备的特征值事件 + * + * 最低基础库: `2.10.3` */ + offCharacteristicWriteRequest( + /** 已连接的设备请求写当前外围设备的特征值事件的回调函数 */ + callback?: OffCharacteristicWriteRequestCallback + ): void + /** [BLEPeripheralServer.onCharacteristicReadRequest(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.onCharacteristicReadRequest.html) + * + * 监听已连接的设备请求读当前外围设备的特征值事件。收到该消息后需要立刻调用 `writeCharacteristicValue` 写回数据,否则主机不会收到响应。 + * + * 最低基础库: `2.10.3` */ + onCharacteristicReadRequest( + /** 已连接的设备请求读当前外围设备的特征值事件的回调函数 */ + callback: OnCharacteristicReadRequestCallback + ): void + /** [BLEPeripheralServer.onCharacteristicSubscribed(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.onCharacteristicSubscribed.html) + * + * 监听特征值订阅事件,仅 iOS 支持。 + * + * 最低基础库: `2.13.0` */ + onCharacteristicSubscribed( + /** 特征值订阅事件的回调函数 */ + callback: OnCharacteristicSubscribedCallback + ): void + /** [BLEPeripheralServer.onCharacteristicUnsubscribed(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.onCharacteristicUnsubscribed.html) + * + * 监听取消特征值订阅事件,仅 iOS 支持。 + * + * 最低基础库: `2.13.0` */ + onCharacteristicUnsubscribed( + /** 取消特征值订阅事件的回调函数 */ + callback: OnCharacteristicUnsubscribedCallback + ): void + /** [BLEPeripheralServer.onCharacteristicWriteRequest(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.onCharacteristicWriteRequest.html) + * + * 监听已连接的设备请求写当前外围设备的特征值事件。收到该消息后需要立刻调用 `writeCharacteristicValue` 写回数据,否则主机不会收到响应。 + * + * 最低基础库: `2.10.3` */ + onCharacteristicWriteRequest( + /** 已连接的设备请求写当前外围设备的特征值事件的回调函数 */ + callback: OnCharacteristicWriteRequestCallback + ): void + /** [BLEPeripheralServer.removeService(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.removeService.html) + * + * 移除服务。 + * + * 最低基础库: `2.10.3` */ + removeService(option: RemoveServiceOption): void + /** [BLEPeripheralServer.startAdvertising(Object Object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.startAdvertising.html) + * + * 开始广播本地创建的外围设备。 + * + * 最低基础库: `2.10.3` */ + startAdvertising(Object: StartAdvertisingObject): void + /** [BLEPeripheralServer.stopAdvertising(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.stopAdvertising.html) + * + * 停止广播。 + * + * 最低基础库: `2.10.3` */ + stopAdvertising(option?: StopAdvertisingOption): void + /** [BLEPeripheralServer.writeCharacteristicValue(Object Object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-peripheral/BLEPeripheralServer.writeCharacteristicValue.html) + * + * 往指定特征值写入数据,并通知已连接的主机,从机的特征值已发生变化,该接口会处理是走回包还是走订阅。 + * + * 最低基础库: `2.10.3` */ + writeCharacteristicValue(Object: WriteCharacteristicValueObject): void + } + interface BackgroundAudioError { + /** 错误信息 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | 10001 | | 系统错误 | + * | 10002 | | 网络错误 | + * | 10003 | | 文件错误,请检查是否responseheader是否缺少Content-Length | + * | 10004 | | 格式错误 | + * | -1 | | 未知错误 | */ errMsg: string + /** 错误码 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | 10001 | | 系统错误 | + * | 10002 | | 网络错误 | + * | 10003 | | 文件错误,请检查是否responseheader是否缺少Content-Length | + * | 10004 | | 格式错误 | + * | -1 | | 未知错误 | */ errCode: number + } + interface BackgroundAudioManager { + /** [BackgroundAudioManager.onCanplay(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onCanplay.html) + * + * 监听背景音频进入可播放状态事件。 但不保证后面可以流畅播放 */ + onCanplay( + /** 背景音频进入可播放状态事件的回调函数 */ + callback: OnCanplayCallback + ): void + /** [BackgroundAudioManager.onEnded(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onEnded.html) + * + * 监听背景音频自然播放结束事件 */ + onEnded( + /** 背景音频自然播放结束事件的回调函数 */ + callback: OnEndedCallback + ): void + /** [BackgroundAudioManager.onError(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onError.html) + * + * 监听背景音频播放错误事件 */ + onError( + /** 背景音频播放错误事件的回调函数 */ + callback: BackgroundAudioManagerOnErrorCallback + ): void + /** [BackgroundAudioManager.onNext(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onNext.html) + * + * 监听用户在系统音乐播放面板点击下一曲事件(仅iOS) */ + onNext( + /** 用户在系统音乐播放面板点击下一曲事件的回调函数 */ + callback: OnNextCallback + ): void + /** [BackgroundAudioManager.onPause(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onPause.html) + * + * 监听背景音频暂停事件 */ + onPause( + /** 背景音频暂停事件的回调函数 */ + callback: OnPauseCallback + ): void + /** [BackgroundAudioManager.onPlay(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onPlay.html) + * + * 监听背景音频播放事件 */ + onPlay( + /** 背景音频播放事件的回调函数 */ + callback: OnPlayCallback + ): void + /** [BackgroundAudioManager.onPrev(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onPrev.html) + * + * 监听用户在系统音乐播放面板点击上一曲事件(仅iOS) */ + onPrev( + /** 用户在系统音乐播放面板点击上一曲事件的回调函数 */ + callback: OnPrevCallback + ): void + /** [BackgroundAudioManager.onSeeked(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onSeeked.html) + * + * 监听背景音频完成跳转操作事件 */ + onSeeked( + /** 背景音频完成跳转操作事件的回调函数 */ + callback: OnSeekedCallback + ): void + /** [BackgroundAudioManager.onSeeking(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onSeeking.html) + * + * 监听背景音频开始跳转操作事件 */ + onSeeking( + /** 背景音频开始跳转操作事件的回调函数 */ + callback: OnSeekingCallback + ): void + /** [BackgroundAudioManager.onStop(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onStop.html) + * + * 监听背景音频停止事件 */ + onStop( + /** 背景音频停止事件的回调函数 */ + callback: InnerAudioContextOnStopCallback + ): void + /** [BackgroundAudioManager.onTimeUpdate(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onTimeUpdate.html) + * + * 监听背景音频播放进度更新事件,只有小程序在前台时会回调。 */ + onTimeUpdate( + /** 背景音频播放进度更新事件的回调函数 */ + callback: OnTimeUpdateCallback + ): void + /** [BackgroundAudioManager.onWaiting(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.onWaiting.html) + * + * 监听音频加载中事件。当音频因为数据不足,需要停下来加载时会触发 */ + onWaiting( + /** 音频加载中事件的回调函数 */ + callback: OnWaitingCallback + ): void + /** [BackgroundAudioManager.pause()](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.pause.html) + * + * 暂停音乐 */ + pause(): void + /** [BackgroundAudioManager.play()](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.play.html) + * + * 播放音乐 */ + play(): void + /** [BackgroundAudioManager.seek(number currentTime)](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.seek.html) + * + * 跳转到指定位置 */ + seek( + /** 跳转的位置,单位 s。精确到小数点后 3 位,即支持 ms 级别精确度 */ + currentTime: number + ): void + /** [BackgroundAudioManager.stop()](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.stop.html) + * + * 停止音乐 */ + stop(): void + } + interface BluetoothError { + /** 错误信息 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | 0 | ok | 正常 | + * | -1 | already connet | 已连接 | + * | 10000 | not init | 未初始化蓝牙适配器 | + * | 10001 | not available | 当前蓝牙适配器不可用 | + * | 10002 | no device | 没有找到指定设备 | + * | 10003 | connection fail | 连接失败 | + * | 10004 | no service | 没有找到指定服务 | + * | 10005 | no characteristic | 没有找到指定特征值 | + * | 10006 | no connection | 当前连接已断开 | + * | 10007 | property not support | 当前特征值不支持此操作 | + * | 10008 | system error | 其余所有系统上报的异常 | + * | 10009 | system not support | Android 系统特有,系统版本低于 4.3 不支持 BLE | + * | 10012 | operate time out | 连接超时 | + * | 10013 | invalid_data | 连接 deviceId 为空或者是格式不正确 | */ errMsg: string + /** 错误码 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | 0 | ok | 正常 | + * | -1 | already connet | 已连接 | + * | 10000 | not init | 未初始化蓝牙适配器 | + * | 10001 | not available | 当前蓝牙适配器不可用 | + * | 10002 | no device | 没有找到指定设备 | + * | 10003 | connection fail | 连接失败 | + * | 10004 | no service | 没有找到指定服务 | + * | 10005 | no characteristic | 没有找到指定特征值 | + * | 10006 | no connection | 当前连接已断开 | + * | 10007 | property not support | 当前特征值不支持此操作 | + * | 10008 | system error | 其余所有系统上报的异常 | + * | 10009 | system not support | Android 系统特有,系统版本低于 4.3 不支持 BLE | + * | 10012 | operate time out | 连接超时 | + * | 10013 | invalid_data | 连接 deviceId 为空或者是格式不正确 | */ errCode: number + } + interface CameraContext { + /** [CameraContext.setZoom(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/camera/CameraContext.setZoom.html) + * + * 设置缩放级别 + * + * 最低基础库: `2.10.0` */ + setZoom(option: SetZoomOption): void + /** [CameraContext.startRecord(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/camera/CameraContext.startRecord.html) + * + * 开始录像 */ + startRecord(option: CameraContextStartRecordOption): void + /** [CameraContext.stopRecord(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/camera/CameraContext.stopRecord.html) + * + * 结束录像 */ + stopRecord(option: CameraContextStopRecordOption): void + /** [CameraContext.takePhoto(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/camera/CameraContext.takePhoto.html) + * + * 拍摄照片 */ + takePhoto(option: TakePhotoOption): void + /** [[CameraFrameListener](https://developers.weixin.qq.com/miniprogram/dev/api/media/camera/CameraFrameListener.html) CameraContext.onCameraFrame(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/camera/CameraContext.onCameraFrame.html) +* +* 获取 Camera 实时帧数据 +* +* **** +* +* 注: 使用该接口需同时在 [camera](https://developers.weixin.qq.com/miniprogram/dev/component/camera.html) 组件属性中指定 frame-size。 +* +* **示例代码** +* +* +* ```js +const context = wx.createCameraContext() +const listener = context.onCameraFrame((frame) => { + console.log(frame.data instanceof ArrayBuffer, frame.width, frame.height) +}) +listener.start() +``` +* +* 最低基础库: `2.7.0` */ + onCameraFrame( + /** 回调函数 */ + callback: OnCameraFrameCallback + ): CameraFrameListener + } + interface CameraFrameListener { + /** [CameraFrameListener.start(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/camera/CameraFrameListener.start.html) + * + * 开始监听帧数据 */ + start(option?: CameraFrameListenerStartOption): void + /** [CameraFrameListener.stop(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/camera/CameraFrameListener.stop.html) + * + * 停止监听帧数据 */ + stop(option?: StopOption): void + } + interface Canvas { + /** [Canvas.cancelAnimationFrame(number requestID)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Canvas.cancelAnimationFrame.html) + * + * 取消由 requestAnimationFrame 添加到计划中的动画帧请求。支持在 2D Canvas 和 WebGL Canvas 下使用, 但不支持混用 2D 和 WebGL 的方法。 + * + * 最低基础库: `2.7.0` */ + cancelAnimationFrame(requestID: number): void + /** [[ImageData](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/ImageData.html) Canvas.createImageData()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Canvas.createImageData.html) + * + * 创建一个 ImageData 对象。仅支持在 2D Canvas 中使用。 + * + * 最低基础库: `2.9.0` */ + createImageData(): ImageData + /** [[Image](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Image.html) Canvas.createImage()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Canvas.createImage.html) + * + * 创建一个图片对象。 支持在 2D Canvas 和 WebGL Canvas 下使用, 但不支持混用 2D 和 WebGL 的方法。 + * + * 最低基础库: `2.7.0` */ + createImage(): Image + /** [[Path2D](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Path2D.html) Canvas.createPath2D([Path2D](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Path2D.html) path)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Canvas.createPath2D.html) + * + * 创建 Path2D 对象 + * + * 最低基础库: `2.11.0` */ + createPath2D( + /** [Path2D](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Path2D.html) + * + * */ + path: Path2D + ): Path2D + /** [[RenderingContext](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/RenderingContext.html) Canvas.getContext(string contextType)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Canvas.getContext.html) + * + * 该方法返回 Canvas 的绘图上下文 + * + * **** + * + * 支持获取 2D 和 WebGL 绘图上下文 + * + * 最低基础库: `2.7.0` */ + getContext(contextType: string): any + /** [number Canvas.requestAnimationFrame(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Canvas.requestAnimationFrame.html) + * + * 在下次进行重绘时执行。 支持在 2D Canvas 和 WebGL Canvas 下使用, 但不支持混用 2D 和 WebGL 的方法。 + * + * 最低基础库: `2.7.0` */ + requestAnimationFrame( + /** 执行的 callback */ + callback: (...args: any[]) => any + ): number + /** [string Canvas.toDataURL(string type, number encoderOptions)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Canvas.toDataURL.html) + * + * 返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。 + * + * 最低基础库: `2.11.0` */ + toDataURL( + /** 图片格式,默认为 image/png */ + type: string, + /** 在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。 */ + encoderOptions: number + ): string + } + interface CanvasContext { + /** [CanvasContext.arc(number x, number y, number r, number sAngle, number eAngle, boolean counterclockwise)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.arc.html) +* +* 创建一条弧线。 +* +* - 创建一个圆可以指定起始弧度为 0,终止弧度为 2 * Math.PI。 +* - 用 `stroke` 或者 `fill` 方法来在 `canvas` 中画弧线。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +// Draw coordinates +ctx.arc(100, 75, 50, 0, 2 * Math.PI) +ctx.setFillStyle('#EEEEEE') +ctx.fill() + +ctx.beginPath() +ctx.moveTo(40, 75) +ctx.lineTo(160, 75) +ctx.moveTo(100, 15) +ctx.lineTo(100, 135) +ctx.setStrokeStyle('#AAAAAA') +ctx.stroke() + +ctx.setFontSize(12) +ctx.setFillStyle('black') +ctx.fillText('0', 165, 78) +ctx.fillText('0.5*PI', 83, 145) +ctx.fillText('1*PI', 15, 78) +ctx.fillText('1.5*PI', 83, 10) + +// Draw points +ctx.beginPath() +ctx.arc(100, 75, 2, 0, 2 * Math.PI) +ctx.setFillStyle('lightgreen') +ctx.fill() + +ctx.beginPath() +ctx.arc(100, 25, 2, 0, 2 * Math.PI) +ctx.setFillStyle('blue') +ctx.fill() + +ctx.beginPath() +ctx.arc(150, 75, 2, 0, 2 * Math.PI) +ctx.setFillStyle('red') +ctx.fill() + +// Draw arc +ctx.beginPath() +ctx.arc(100, 75, 50, 0, 1.5 * Math.PI) +ctx.setStrokeStyle('#333333') +ctx.stroke() + +ctx.draw() +``` +* +* ![](@program/dev/image/canvas/arc.png) +* +* 针对 arc(100, 75, 50, 0, 1.5 * Math.PI)的三个关键坐标如下: +* +* - 绿色: 圆心 (100, 75) +* - 红色: 起始弧度 (0) +* - 蓝色: 终止弧度 (1.5 * Math.PI) */ + arc( + /** 圆心的 x 坐标 */ + x: number, + /** 圆心的 y 坐标 */ + y: number, + /** 圆的半径 */ + r: number, + /** 起始弧度,单位弧度(在3点钟方向) */ + sAngle: number, + /** 终止弧度 */ + eAngle: number, + /** 弧度的方向是否是逆时针 */ + counterclockwise?: boolean + ): void + /** [CanvasContext.arcTo(number x1, number y1, number x2, number y2, number radius)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.arcTo.html) + * + * 根据控制点和半径绘制圆弧路径。 + * + * 最低基础库: `1.9.90` */ + arcTo( + /** 第一个控制点的 x 轴坐标 */ + x1: number, + /** 第一个控制点的 y 轴坐标 */ + y1: number, + /** 第二个控制点的 x 轴坐标 */ + x2: number, + /** 第二个控制点的 y 轴坐标 */ + y2: number, + /** 圆弧的半径 */ + radius: number + ): void + /** [CanvasContext.beginPath()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.beginPath.html) +* +* 开始创建一个路径。需要调用 `fill` 或者 `stroke` 才会使用路径进行填充或描边 +* +* - 在最开始的时候相当于调用了一次 `beginPath`。 +* - 同一个路径内的多次 `setFillStyle`、`setStrokeStyle`、`setLineWidth`等设置,以最后一次设置为准。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +// begin path +ctx.rect(10, 10, 100, 30) +ctx.setFillStyle('yellow') +ctx.fill() + +// begin another path +ctx.beginPath() +ctx.rect(10, 40, 100, 30) + +// only fill this rect, not in current path +ctx.setFillStyle('blue') +ctx.fillRect(10, 70, 100, 30) + +ctx.rect(10, 100, 100, 30) + +// it will fill current path +ctx.setFillStyle('red') +ctx.fill() +ctx.draw() +``` +* +* ![](@program/dev/image/canvas/fill-path.png) */ + beginPath(): void + /** [CanvasContext.bezierCurveTo(number cp1x, number cp1y, number cp2x, number cp2y, number x, number y)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.bezierCurveTo.html) +* +* 创建三次方贝塞尔曲线路径。曲线的起始点为路径中前一个点。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +// Draw points +ctx.beginPath() +ctx.arc(20, 20, 2, 0, 2 * Math.PI) +ctx.setFillStyle('red') +ctx.fill() + +ctx.beginPath() +ctx.arc(200, 20, 2, 0, 2 * Math.PI) +ctx.setFillStyle('lightgreen') +ctx.fill() + +ctx.beginPath() +ctx.arc(20, 100, 2, 0, 2 * Math.PI) +ctx.arc(200, 100, 2, 0, 2 * Math.PI) +ctx.setFillStyle('blue') +ctx.fill() + +ctx.setFillStyle('black') +ctx.setFontSize(12) + +// Draw guides +ctx.beginPath() +ctx.moveTo(20, 20) +ctx.lineTo(20, 100) +ctx.lineTo(150, 75) + +ctx.moveTo(200, 20) +ctx.lineTo(200, 100) +ctx.lineTo(70, 75) +ctx.setStrokeStyle('#AAAAAA') +ctx.stroke() + +// Draw quadratic curve +ctx.beginPath() +ctx.moveTo(20, 20) +ctx.bezierCurveTo(20, 100, 200, 100, 200, 20) +ctx.setStrokeStyle('black') +ctx.stroke() + +ctx.draw() +``` +* +* ![](@program/dev/image/canvas/bezier-curve.png) +* +* 针对 moveTo(20, 20) bezierCurveTo(20, 100, 200, 100, 200, 20) 的三个关键坐标如下: +* +* - 红色:起始点(20, 20) +* - 蓝色:两个控制点(20, 100) (200, 100) +* - 绿色:终止点(200, 20) */ + bezierCurveTo( + /** 第一个贝塞尔控制点的 x 坐标 */ + cp1x: number, + /** 第一个贝塞尔控制点的 y 坐标 */ + cp1y: number, + /** 第二个贝塞尔控制点的 x 坐标 */ + cp2x: number, + /** 第二个贝塞尔控制点的 y 坐标 */ + cp2y: number, + /** 结束点的 x 坐标 */ + x: number, + /** 结束点的 y 坐标 */ + y: number + ): void + /** [CanvasContext.clearRect(number x, number y, number width, number height)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.clearRect.html) +* +* 清除画布上在该矩形区域内的内容 +* +* **示例代码** +* +* +* clearRect 并非画一个白色的矩形在地址区域,而是清空,为了有直观感受,对 canvas 加了一层背景色。 +* ```html +* +* ``` +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.setFillStyle('red') +ctx.fillRect(0, 0, 150, 200) +ctx.setFillStyle('blue') +ctx.fillRect(150, 0, 150, 200) +ctx.clearRect(10, 10, 150, 75) +ctx.draw() +``` +* ![](@program/dev/image/canvas/clear-rect.png) */ + clearRect( + /** 矩形路径左上角的横坐标 */ + x: number, + /** 矩形路径左上角的纵坐标 */ + y: number, + /** 矩形路径的宽度 */ + width: number, + /** 矩形路径的高度 */ + height: number + ): void + /** [CanvasContext.clip()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.clip.html) +* +* 从原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。可以在使用 `clip` 方法前通过使用 `save` 方法对当前画布区域进行保存,并在以后的任意时间通过`restore`方法对其进行恢复。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +wx.downloadFile({ + url: 'http://is5.mzstatic.com/image/thumb/Purple128/v4/75/3b/90/753b907c-b7fb-5877-215a-759bd73691a4/source/50x50bb.jpg', + success: function(res) { + ctx.save() + ctx.beginPath() + ctx.arc(50, 50, 25, 0, 2*Math.PI) + ctx.clip() + ctx.drawImage(res.tempFilePath, 25, 25) + ctx.restore() + ctx.draw() + } +}) +``` +* ![](@program/dev/image/canvas/clip.png) +* +* 最低基础库: `1.6.0` */ + clip(): void + /** [CanvasContext.closePath()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.closePath.html) +* +* 关闭一个路径。会连接起点和终点。如果关闭路径后没有调用 `fill` 或者 `stroke` 并开启了新的路径,那之前的路径将不会被渲染。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.moveTo(10, 10) +ctx.lineTo(100, 10) +ctx.lineTo(100, 100) +ctx.closePath() +ctx.stroke() +ctx.draw() +``` +* ![](@program/dev/image/canvas/close-line.png) +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +// begin path +ctx.rect(10, 10, 100, 30) +ctx.closePath() + +// begin another path +ctx.beginPath() +ctx.rect(10, 40, 100, 30) + +// only fill this rect, not in current path +ctx.setFillStyle('blue') +ctx.fillRect(10, 70, 100, 30) + +ctx.rect(10, 100, 100, 30) + +// it will fill current path +ctx.setFillStyle('red') +ctx.fill() +ctx.draw() +``` +* +* ![](@program/dev/image/canvas/close-path.png) */ + closePath(): void + /** [CanvasContext.createPattern(string image, string repetition)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.createPattern.html) + * + * 对指定的图像创建模式的方法,可在指定的方向上重复元图像 + * + * 最低基础库: `1.9.90` */ + createPattern( + /** 重复的图像源,支持代码包路径和本地临时路径 (本地路径) */ + image: string, + /** 如何重复图像 + * + * 参数 repetition 可选值: + * - 'repeat': 水平竖直方向都重复; + * - 'repeat-x': 水平方向重复; + * - 'repeat-y': 竖直方向重复; + * - 'no-repeat': 不重复; */ + repetition: 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat' + ): void + /** [CanvasContext.draw(boolean reserve, function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.draw.html) +* +* 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中。 +* +* **示例代码** +* +* +* 第二次 draw() reserve 为 true。所以保留了上一次的绘制结果,在上下文设置的 fillStyle 'red' 也变成了默认的 'black'。 +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +ctx.setFillStyle('red') +ctx.fillRect(10, 10, 150, 100) +ctx.draw() +ctx.fillRect(50, 50, 150, 100) +ctx.draw(true) +``` +* ![](@program/dev/image/canvas/reserve.png) +* +* **示例代码** +* +* +* 第二次 draw() reserve 为 false。所以没有保留了上一次的绘制结果和在上下文设置的 fillStyle 'red'。 +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +ctx.setFillStyle('red') +ctx.fillRect(10, 10, 150, 100) +ctx.draw() +ctx.fillRect(50, 50, 150, 100) +ctx.draw() +``` +* ![](@program/dev/image/canvas/un-reserve.png) */ + draw( + /** 本次绘制是否接着上一次绘制。即 reserve 参数为 false,则在本次调用绘制之前 native 层会先清空画布再继续绘制;若 reserve 参数为 true,则保留当前画布上的内容,本次调用 drawCanvas 绘制的内容覆盖在上面,默认 false。 */ + reserve?: boolean, + /** 绘制完成后执行的回调函数 */ + callback?: (...args: any[]) => any + ): void + /** [CanvasContext.drawImage(string imageResource, number sx, number sy, number sWidth, number sHeight, number dx, number dy, number dWidth, number dHeight)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html) +* +* 绘制图像到画布 +* +* **示例代码** +* +* +* +* 有三个版本的写法: +* +* - drawImage(imageResource, dx, dy) +* - drawImage(imageResource, dx, dy, dWidth, dHeight) +* - drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) 从 1.9.0 起支持 +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +wx.chooseImage({ + success: function(res){ + ctx.drawImage(res.tempFilePaths[0], 0, 0, 150, 100) + ctx.draw() + } +}) + +``` +* ![](@program/dev/image/canvas/draw-image.png) */ + drawImage( + /** 所要绘制的图片资源(网络图片要通过 getImageInfo / downloadFile 先下载) */ + imageResource: string, + /** imageResource的左上角在目标 canvas 上 x 轴的位置 */ + dx: number, + /** imageResource的左上角在目标 canvas 上 y 轴的位置 */ + dy: number + ): void + /** [CanvasContext.drawImage(string imageResource, number sx, number sy, number sWidth, number sHeight, number dx, number dy, number dWidth, number dHeight)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html) +* +* 绘制图像到画布 +* +* **示例代码** +* +* +* +* 有三个版本的写法: +* +* - drawImage(imageResource, dx, dy) +* - drawImage(imageResource, dx, dy, dWidth, dHeight) +* - drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) 从 1.9.0 起支持 +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +wx.chooseImage({ + success: function(res){ + ctx.drawImage(res.tempFilePaths[0], 0, 0, 150, 100) + ctx.draw() + } +}) + +``` +* ![](@program/dev/image/canvas/draw-image.png) */ + drawImage( + /** 所要绘制的图片资源(网络图片要通过 getImageInfo / downloadFile 先下载) */ + imageResource: string, + /** imageResource的左上角在目标 canvas 上 x 轴的位置 */ + dx: number, + /** imageResource的左上角在目标 canvas 上 y 轴的位置 */ + dy: number, + /** 在目标画布上绘制imageResource的宽度,允许对绘制的imageResource进行缩放 */ + dWidth: number, + /** 在目标画布上绘制imageResource的高度,允许对绘制的imageResource进行缩放 */ + dHeight: number + ): void + /** [CanvasContext.drawImage(string imageResource, number sx, number sy, number sWidth, number sHeight, number dx, number dy, number dWidth, number dHeight)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html) +* +* 绘制图像到画布 +* +* **示例代码** +* +* +* +* 有三个版本的写法: +* +* - drawImage(imageResource, dx, dy) +* - drawImage(imageResource, dx, dy, dWidth, dHeight) +* - drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) 从 1.9.0 起支持 +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +wx.chooseImage({ + success: function(res){ + ctx.drawImage(res.tempFilePaths[0], 0, 0, 150, 100) + ctx.draw() + } +}) + +``` +* ![](@program/dev/image/canvas/draw-image.png) */ + drawImage( + /** 所要绘制的图片资源(网络图片要通过 getImageInfo / downloadFile 先下载) */ + imageResource: string, + /** 需要绘制到画布中的,imageResource的矩形(裁剪)选择框的左上角 x 坐标 */ + sx: number, + /** 需要绘制到画布中的,imageResource的矩形(裁剪)选择框的左上角 y 坐标 */ + sy: number, + /** 需要绘制到画布中的,imageResource的矩形(裁剪)选择框的宽度 */ + sWidth: number, + /** 需要绘制到画布中的,imageResource的矩形(裁剪)选择框的高度 */ + sHeight: number, + /** imageResource的左上角在目标 canvas 上 x 轴的位置 */ + dx: number, + /** imageResource的左上角在目标 canvas 上 y 轴的位置 */ + dy: number, + /** 在目标画布上绘制imageResource的宽度,允许对绘制的imageResource进行缩放 */ + dWidth: number, + /** 在目标画布上绘制imageResource的高度,允许对绘制的imageResource进行缩放 */ + dHeight: number + ): void + /** [CanvasContext.fill()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.fill.html) +* +* 对当前路径中的内容进行填充。默认的填充色为黑色。 +* +* **示例代码** +* +* +* +* 如果当前路径没有闭合,fill() 方法会将起点和终点进行连接,然后填充。 +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.moveTo(10, 10) +ctx.lineTo(100, 10) +ctx.lineTo(100, 100) +ctx.fill() +ctx.draw() +``` +* +* fill() 填充的的路径是从 beginPath() 开始计算,但是不会将 fillRect() 包含进去。 +* +* ![](@program/dev/image/canvas/fill-line.png) +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +// begin path +ctx.rect(10, 10, 100, 30) +ctx.setFillStyle('yellow') +ctx.fill() + +// begin another path +ctx.beginPath() +ctx.rect(10, 40, 100, 30) + +// only fill this rect, not in current path +ctx.setFillStyle('blue') +ctx.fillRect(10, 70, 100, 30) + +ctx.rect(10, 100, 100, 30) + +// it will fill current path +ctx.setFillStyle('red') +ctx.fill() +ctx.draw() +``` +* +* ![](@program/dev/image/canvas/fill-path.png) */ + fill(): void + /** [CanvasContext.fillRect(number x, number y, number width, number height)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.fillRect.html) +* +* 填充一个矩形。用 [`setFillStyle`](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setFillStyle.html) 设置矩形的填充色,如果没设置默认是黑色。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.setFillStyle('red') +ctx.fillRect(10, 10, 150, 75) +ctx.draw() +``` +* ![](@program/dev/image/canvas/fill-rect.png) */ + fillRect( + /** 矩形路径左上角的横坐标 */ + x: number, + /** 矩形路径左上角的纵坐标 */ + y: number, + /** 矩形路径的宽度 */ + width: number, + /** 矩形路径的高度 */ + height: number + ): void + /** [CanvasContext.fillText(string text, number x, number y, number maxWidth)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.fillText.html) +* +* 在画布上绘制被填充的文本 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +ctx.setFontSize(20) +ctx.fillText('Hello', 20, 20) +ctx.fillText('MINA', 100, 100) + +ctx.draw() +``` +* ![](@program/dev/image/canvas/text.png) */ + fillText( + /** 在画布上输出的文本 */ + text: string, + /** 绘制文本的左上角 x 坐标位置 */ + x: number, + /** 绘制文本的左上角 y 坐标位置 */ + y: number, + /** 需要绘制的最大宽度,可选 */ + maxWidth?: number + ): void + /** [CanvasContext.lineTo(number x, number y)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.lineTo.html) +* +* 增加一个新点,然后创建一条从上次指定点到目标点的线。用 `stroke` 方法来画线条 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.moveTo(10, 10) +ctx.rect(10, 10, 100, 50) +ctx.lineTo(110, 60) +ctx.stroke() +ctx.draw() +``` +* ![](@program/dev/image/canvas/line-to.png) */ + lineTo( + /** 目标位置的 x 坐标 */ + x: number, + /** 目标位置的 y 坐标 */ + y: number + ): void + /** [CanvasContext.moveTo(number x, number y)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.moveTo.html) +* +* 把路径移动到画布中的指定点,不创建线条。用 `stroke` 方法来画线条 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.moveTo(10, 10) +ctx.lineTo(100, 10) + +ctx.moveTo(10, 50) +ctx.lineTo(100, 50) +ctx.stroke() +ctx.draw() +``` +* ![](@program/dev/image/canvas/move-to.png) */ + moveTo( + /** 目标位置的 x 坐标 */ + x: number, + /** 目标位置的 y 坐标 */ + y: number + ): void + /** [CanvasContext.quadraticCurveTo(number cpx, number cpy, number x, number y)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.quadraticCurveTo.html) +* +* 创建二次贝塞尔曲线路径。曲线的起始点为路径中前一个点。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +// Draw points +ctx.beginPath() +ctx.arc(20, 20, 2, 0, 2 * Math.PI) +ctx.setFillStyle('red') +ctx.fill() + +ctx.beginPath() +ctx.arc(200, 20, 2, 0, 2 * Math.PI) +ctx.setFillStyle('lightgreen') +ctx.fill() + +ctx.beginPath() +ctx.arc(20, 100, 2, 0, 2 * Math.PI) +ctx.setFillStyle('blue') +ctx.fill() + +ctx.setFillStyle('black') +ctx.setFontSize(12) + +// Draw guides +ctx.beginPath() +ctx.moveTo(20, 20) +ctx.lineTo(20, 100) +ctx.lineTo(200, 20) +ctx.setStrokeStyle('#AAAAAA') +ctx.stroke() + +// Draw quadratic curve +ctx.beginPath() +ctx.moveTo(20, 20) +ctx.quadraticCurveTo(20, 100, 200, 20) +ctx.setStrokeStyle('black') +ctx.stroke() + +ctx.draw() +``` +* +* ![](@program/dev/image/canvas/quadratic-curve-to.png) +* +* 针对 moveTo(20, 20) quadraticCurveTo(20, 100, 200, 20) 的三个关键坐标如下: +* +* - 红色:起始点(20, 20) +* - 蓝色:控制点(20, 100) +* - 绿色:终止点(200, 20) */ + quadraticCurveTo( + /** 贝塞尔控制点的 x 坐标 */ + cpx: number, + /** 贝塞尔控制点的 y 坐标 */ + cpy: number, + /** 结束点的 x 坐标 */ + x: number, + /** 结束点的 y 坐标 */ + y: number + ): void + /** [CanvasContext.rect(number x, number y, number width, number height)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.rect.html) +* +* 创建一个矩形路径。需要用 [`fill`](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.fill.html) 或者 [`stroke`](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.stroke.html) 方法将矩形真正的画到 `canvas` 中 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.rect(10, 10, 150, 75) +ctx.setFillStyle('red') +ctx.fill() +ctx.draw() +``` +* ![](@program/dev/image/canvas/fill-rect.png) */ + rect( + /** 矩形路径左上角的横坐标 */ + x: number, + /** 矩形路径左上角的纵坐标 */ + y: number, + /** 矩形路径的宽度 */ + width: number, + /** 矩形路径的高度 */ + height: number + ): void + /** [CanvasContext.restore()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.restore.html) +* +* 恢复之前保存的绘图上下文。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +// save the default fill style +ctx.save() +ctx.setFillStyle('red') +ctx.fillRect(10, 10, 150, 100) + +// restore to the previous saved state +ctx.restore() +ctx.fillRect(50, 50, 150, 100) + +ctx.draw() +``` +* ![](@program/dev/image/canvas/save-restore.png) */ + restore(): void + /** [CanvasContext.rotate(number rotate)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.rotate.html) +* +* 以原点为中心顺时针旋转当前坐标轴。多次调用旋转的角度会叠加。原点可以用 `translate` 方法修改。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +ctx.strokeRect(100, 10, 150, 100) +ctx.rotate(20 * Math.PI / 180) +ctx.strokeRect(100, 10, 150, 100) +ctx.rotate(20 * Math.PI / 180) +ctx.strokeRect(100, 10, 150, 100) + +ctx.draw() +``` +* ![](@program/dev/image/canvas/rotate.png) */ + rotate( + /** 旋转角度,以弧度计 degrees * Math.PI/180;degrees 范围为 0-360 */ + rotate: number + ): void + /** [CanvasContext.save()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.save.html) +* +* 保存绘图上下文。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +// save the default fill style +ctx.save() +ctx.setFillStyle('red') +ctx.fillRect(10, 10, 150, 100) + +// restore to the previous saved state +ctx.restore() +ctx.fillRect(50, 50, 150, 100) + +ctx.draw() +``` +* ![](@program/dev/image/canvas/save-restore.png) */ + save(): void + /** [CanvasContext.scale(number scaleWidth, number scaleHeight)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.scale.html) +* +* 在调用后,之后创建的路径其横纵坐标会被缩放。多次调用倍数会相乘。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +ctx.strokeRect(10, 10, 25, 15) +ctx.scale(2, 2) +ctx.strokeRect(10, 10, 25, 15) +ctx.scale(2, 2) +ctx.strokeRect(10, 10, 25, 15) + +ctx.draw() +``` +* ![](@program/dev/image/canvas/scale.png) */ + scale( + /** 横坐标缩放的倍数 (1 = 100%,0.5 = 50%,2 = 200%) */ + scaleWidth: number, + /** 纵坐标轴缩放的倍数 (1 = 100%,0.5 = 50%,2 = 200%) */ + scaleHeight: number + ): void + /** [CanvasContext.setFillStyle(string|[CanvasGradient](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasGradient.html) color)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setFillStyle.html) +* +* 设置填充色。 +* +* **代码示例** +* +* +* ```js +const ctx = wx.createCanvasContext('myCanvas') +ctx.setFillStyle('red') +ctx.fillRect(10, 10, 150, 75) +ctx.draw() +``` +* ![](@program/dev/image/canvas/fill-rect.png) +* @deprecated 基础库版本 [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起已废弃,请使用 [CanvasContext.fillStyle](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html) 替换 +* */ + setFillStyle( + /** 填充的颜色,默认颜色为 black。 */ + color: string | CanvasGradient + ): void + /** [CanvasContext.setFontSize(number fontSize)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setFontSize.html) +* +* 设置字体的字号 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +ctx.setFontSize(20) +ctx.fillText('20', 20, 20) +ctx.setFontSize(30) +ctx.fillText('30', 40, 40) +ctx.setFontSize(40) +ctx.fillText('40', 60, 60) +ctx.setFontSize(50) +ctx.fillText('50', 90, 90) + +ctx.draw() +``` +* ![](@program/dev/image/canvas/font-size.png) +* @deprecated 基础库版本 [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起已废弃,请使用 [CanvasContext.font](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html) 替换 +* */ + setFontSize( + /** 字体的字号 */ + fontSize: number + ): void + /** [CanvasContext.setGlobalAlpha(number alpha)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setGlobalAlpha.html) +* +* 设置全局画笔透明度。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +ctx.setFillStyle('red') +ctx.fillRect(10, 10, 150, 100) +ctx.setGlobalAlpha(0.2) +ctx.setFillStyle('blue') +ctx.fillRect(50, 50, 150, 100) +ctx.setFillStyle('yellow') +ctx.fillRect(100, 100, 150, 100) + +ctx.draw() +``` +* ![](@program/dev/image/canvas/global-alpha.png) +* @deprecated 基础库版本 [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起已废弃,请使用 [CanvasContext.globalAlpha](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html) 替换 +* */ + setGlobalAlpha( + /** 透明度。范围 0-1,0 表示完全透明,1 表示完全不透明。 */ + alpha: number + ): void + /** [CanvasContext.setLineCap(string lineCap)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setLineCap.html) +* +* 设置线条的端点样式 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.beginPath() +ctx.moveTo(10, 10) +ctx.lineTo(150, 10) +ctx.stroke() + +ctx.beginPath() +ctx.setLineCap('butt') +ctx.setLineWidth(10) +ctx.moveTo(10, 30) +ctx.lineTo(150, 30) +ctx.stroke() + +ctx.beginPath() +ctx.setLineCap('round') +ctx.setLineWidth(10) +ctx.moveTo(10, 50) +ctx.lineTo(150, 50) +ctx.stroke() + +ctx.beginPath() +ctx.setLineCap('square') +ctx.setLineWidth(10) +ctx.moveTo(10, 70) +ctx.lineTo(150, 70) +ctx.stroke() + +ctx.draw() +``` +* ![](@program/dev/image/canvas/line-cap.png) +* @deprecated 基础库版本 [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起已废弃,请使用 [CanvasContext.lineCap](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html) 替换 +* */ + setLineCap( + /** 线条的结束端点样式 + * + * 参数 lineCap 可选值: + * - 'butt': 向线条的每个末端添加平直的边缘。; + * - 'round': 向线条的每个末端添加圆形线帽。; + * - 'square': 向线条的每个末端添加正方形线帽。; */ + lineCap: 'butt' | 'round' | 'square' + ): void + /** [CanvasContext.setLineDash(Array.<number> pattern, number offset)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setLineDash.html) +* +* 设置虚线样式。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +ctx.setLineDash([10, 20], 5); + +ctx.beginPath(); +ctx.moveTo(0,100); +ctx.lineTo(400, 100); +ctx.stroke(); + +ctx.draw() +``` +* ![](@program/dev/image/canvas/set-line-dash.png) +* +* 最低基础库: `1.6.0` +* @deprecated 基础库版本 [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起已废弃,请使用 [CanvasContext.lineDashOffset](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html) 替换 +* */ + setLineDash( + /** 一组描述交替绘制线段和间距(坐标空间单位)长度的数字 */ + pattern: number[], + /** 虚线偏移量 */ + offset: number + ): void + /** [CanvasContext.setLineJoin(string lineJoin)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setLineJoin.html) +* +* 设置线条的交点样式 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.beginPath() +ctx.moveTo(10, 10) +ctx.lineTo(100, 50) +ctx.lineTo(10, 90) +ctx.stroke() + +ctx.beginPath() +ctx.setLineJoin('bevel') +ctx.setLineWidth(10) +ctx.moveTo(50, 10) +ctx.lineTo(140, 50) +ctx.lineTo(50, 90) +ctx.stroke() + +ctx.beginPath() +ctx.setLineJoin('round') +ctx.setLineWidth(10) +ctx.moveTo(90, 10) +ctx.lineTo(180, 50) +ctx.lineTo(90, 90) +ctx.stroke() + +ctx.beginPath() +ctx.setLineJoin('miter') +ctx.setLineWidth(10) +ctx.moveTo(130, 10) +ctx.lineTo(220, 50) +ctx.lineTo(130, 90) +ctx.stroke() + +ctx.draw() +``` +* ![](@program/dev/image/canvas/line-join.png) +* @deprecated 基础库版本 [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起已废弃,请使用 [CanvasContext.lineJoin](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html) 替换 +* */ + setLineJoin( + /** 线条的结束交点样式 + * + * 参数 lineJoin 可选值: + * - 'bevel': 斜角; + * - 'round': 圆角; + * - 'miter': 尖角; */ + lineJoin: 'bevel' | 'round' | 'miter' + ): void + /** [CanvasContext.setLineWidth(number lineWidth)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setLineWidth.html) +* +* 设置线条的宽度 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.beginPath() +ctx.moveTo(10, 10) +ctx.lineTo(150, 10) +ctx.stroke() + +ctx.beginPath() +ctx.setLineWidth(5) +ctx.moveTo(10, 30) +ctx.lineTo(150, 30) +ctx.stroke() + +ctx.beginPath() +ctx.setLineWidth(10) +ctx.moveTo(10, 50) +ctx.lineTo(150, 50) +ctx.stroke() + +ctx.beginPath() +ctx.setLineWidth(15) +ctx.moveTo(10, 70) +ctx.lineTo(150, 70) +ctx.stroke() + +ctx.draw() +``` +* +* ![](@program/dev/image/canvas/line-width.png) +* @deprecated 基础库版本 [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起已废弃,请使用 [CanvasContext.lineWidth](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html) 替换 +* */ + setLineWidth( + /** 线条的宽度,单位px */ + lineWidth: number + ): void + /** [CanvasContext.setMiterLimit(number miterLimit)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setMiterLimit.html) +* +* 设置最大斜接长度。斜接长度指的是在两条线交汇处内角和外角之间的距离。当 [CanvasContext.setLineJoin()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setLineJoin.html) 为 miter 时才有效。超过最大倾斜长度的,连接处将以 lineJoin 为 bevel 来显示。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.beginPath() +ctx.setLineWidth(10) +ctx.setLineJoin('miter') +ctx.setMiterLimit(1) +ctx.moveTo(10, 10) +ctx.lineTo(100, 50) +ctx.lineTo(10, 90) +ctx.stroke() + +ctx.beginPath() +ctx.setLineWidth(10) +ctx.setLineJoin('miter') +ctx.setMiterLimit(2) +ctx.moveTo(50, 10) +ctx.lineTo(140, 50) +ctx.lineTo(50, 90) +ctx.stroke() + +ctx.beginPath() +ctx.setLineWidth(10) +ctx.setLineJoin('miter') +ctx.setMiterLimit(3) +ctx.moveTo(90, 10) +ctx.lineTo(180, 50) +ctx.lineTo(90, 90) +ctx.stroke() + +ctx.beginPath() +ctx.setLineWidth(10) +ctx.setLineJoin('miter') +ctx.setMiterLimit(4) +ctx.moveTo(130, 10) +ctx.lineTo(220, 50) +ctx.lineTo(130, 90) +ctx.stroke() + +ctx.draw() +``` +* ![](@program/dev/image/canvas/miter-limit.png) +* @deprecated 基础库版本 [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起已废弃,请使用 [CanvasContext.miterLimit](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html) 替换 +* */ + setMiterLimit( + /** 最大斜接长度 */ + miterLimit: number + ): void + /** [CanvasContext.setShadow(number offsetX, number offsetY, number blur, string color)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setShadow.html) +* +* 设定阴影样式。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.setFillStyle('red') +ctx.setShadow(10, 50, 50, 'blue') +ctx.fillRect(10, 10, 150, 75) +ctx.draw() +``` +* ![](@program/dev/image/canvas/shadow.png) +* @deprecated 基础库版本 [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起已废弃,请使用 [CanvasContext.shadowOffsetX|CanvasContext.shadowOffsetY|CanvasContext.shadowColor|CanvasContext.shadowBlur](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html) 替换 +* */ + setShadow( + /** 阴影相对于形状在水平方向的偏移,默认值为 0。 */ + offsetX: number, + /** 阴影相对于形状在竖直方向的偏移,默认值为 0。 */ + offsetY: number, + /** 阴影的模糊级别,数值越大越模糊。范围 0- 100。,默认值为 0。 */ + blur: number, + /** 阴影的颜色。默认值为 black。 */ + color: string + ): void + /** [CanvasContext.setStrokeStyle(string|[CanvasGradient](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasGradient.html) color)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setStrokeStyle.html) +* +* 设置描边颜色。 +* +* **代码示例** +* +* +* ```js +const ctx = wx.createCanvasContext('myCanvas') +ctx.setStrokeStyle('red') +ctx.strokeRect(10, 10, 150, 75) +ctx.draw() +``` +* ![](@program/dev/image/canvas/stroke-rect.png) +* @deprecated 基础库版本 [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起已废弃,请使用 [CanvasContext.strokeStyle](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html) 替换 +* */ + setStrokeStyle( + /** 描边的颜色,默认颜色为 black。 */ + color: string | CanvasGradient + ): void + /** [CanvasContext.setTextAlign(string align)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setTextAlign.html) +* +* 设置文字的对齐 +* +* **示例代码** +* +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +ctx.setStrokeStyle('red') +ctx.moveTo(150, 20) +ctx.lineTo(150, 170) +ctx.stroke() + +ctx.setFontSize(15) +ctx.setTextAlign('left') +ctx.fillText('textAlign=left', 150, 60) + +ctx.setTextAlign('center') +ctx.fillText('textAlign=center', 150, 80) + +ctx.setTextAlign('right') +ctx.fillText('textAlign=right', 150, 100) + +ctx.draw() +``` +* +* ![](@program/dev/image/canvas/set-text-align.png) +* +* 最低基础库: `1.1.0` */ + setTextAlign( + /** 文字的对齐方式 + * + * 参数 align 可选值: + * - 'left': 左对齐; + * - 'center': 居中对齐; + * - 'right': 右对齐; */ + align: 'left' | 'center' | 'right' + ): void + /** [CanvasContext.setTextBaseline(string textBaseline)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setTextBaseline.html) +* +* 设置文字的竖直对齐 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +ctx.setStrokeStyle('red') +ctx.moveTo(5, 75) +ctx.lineTo(295, 75) +ctx.stroke() + +ctx.setFontSize(20) + +ctx.setTextBaseline('top') +ctx.fillText('top', 5, 75) + +ctx.setTextBaseline('middle') +ctx.fillText('middle', 50, 75) + +ctx.setTextBaseline('bottom') +ctx.fillText('bottom', 120, 75) + +ctx.setTextBaseline('normal') +ctx.fillText('normal', 200, 75) + +ctx.draw() +``` +* ![](@program/dev/image/canvas/set-text-baseline.png) +* +* 最低基础库: `1.4.0` */ + setTextBaseline( + /** 文字的竖直对齐方式 + * + * 参数 textBaseline 可选值: + * - 'top': 顶部对齐; + * - 'bottom': 底部对齐; + * - 'middle': 居中对齐; + * - 'normal': ; */ + textBaseline: 'top' | 'bottom' | 'middle' | 'normal' + ): void + /** [CanvasContext.setTransform(number scaleX, number skewX, number skewY, number scaleY, number translateX, number translateY)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setTransform.html) + * + * 使用矩阵重新设置(覆盖)当前变换的方法 + * + * 最低基础库: `1.9.90` */ + setTransform( + /** 水平缩放 */ + scaleX: number, + /** 水平倾斜 */ + skewX: number, + /** 垂直倾斜 */ + skewY: number, + /** 垂直缩放 */ + scaleY: number, + /** 水平移动 */ + translateX: number, + /** 垂直移动 */ + translateY: number + ): void + /** [CanvasContext.stroke()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.stroke.html) +* +* 画出当前路径的边框。默认颜色色为黑色。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.moveTo(10, 10) +ctx.lineTo(100, 10) +ctx.lineTo(100, 100) +ctx.stroke() +ctx.draw() +``` +* ![](@program/dev/image/canvas/stroke-line.png) +* +* stroke() 描绘的的路径是从 beginPath() 开始计算,但是不会将 strokeRect() 包含进去。 +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +// begin path +ctx.rect(10, 10, 100, 30) +ctx.setStrokeStyle('yellow') +ctx.stroke() + +// begin another path +ctx.beginPath() +ctx.rect(10, 40, 100, 30) + +// only stoke this rect, not in current path +ctx.setStrokeStyle('blue') +ctx.strokeRect(10, 70, 100, 30) + +ctx.rect(10, 100, 100, 30) + +// it will stroke current path +ctx.setStrokeStyle('red') +ctx.stroke() +ctx.draw() +``` +* +* ![](@program/dev/image/canvas/stroke-path.png) */ + stroke(): void + /** [CanvasContext.strokeRect(number x, number y, number width, number height)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.strokeRect.html) +* +* 画一个矩形(非填充)。 用 [`setStrokeStyle`](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.setStrokeStyle.html) 设置矩形线条的颜色,如果没设置默认是黑色。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') +ctx.setStrokeStyle('red') +ctx.strokeRect(10, 10, 150, 75) +ctx.draw() +``` +* ![](@program/dev/image/canvas/stroke-rect.png) */ + strokeRect( + /** 矩形路径左上角的横坐标 */ + x: number, + /** 矩形路径左上角的纵坐标 */ + y: number, + /** 矩形路径的宽度 */ + width: number, + /** 矩形路径的高度 */ + height: number + ): void + /** [CanvasContext.strokeText(string text, number x, number y, number maxWidth)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.strokeText.html) + * + * 给定的 (x, y) 位置绘制文本描边的方法 + * + * 最低基础库: `1.9.90` */ + strokeText( + /** 要绘制的文本 */ + text: string, + /** 文本起始点的 x 轴坐标 */ + x: number, + /** 文本起始点的 y 轴坐标 */ + y: number, + /** 需要绘制的最大宽度,可选 */ + maxWidth?: number + ): void + /** [CanvasContext.transform(number scaleX, number skewX, number skewY, number scaleY, number translateX, number translateY)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.transform.html) + * + * 使用矩阵多次叠加当前变换的方法 + * + * 最低基础库: `1.9.90` */ + transform( + /** 水平缩放 */ + scaleX: number, + /** 水平倾斜 */ + skewX: number, + /** 垂直倾斜 */ + skewY: number, + /** 垂直缩放 */ + scaleY: number, + /** 水平移动 */ + translateX: number, + /** 垂直移动 */ + translateY: number + ): void + /** [CanvasContext.translate(number x, number y)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.translate.html) +* +* 对当前坐标系的原点 (0, 0) 进行变换。默认的坐标系原点为页面左上角。 +* +* **示例代码** +* +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +ctx.strokeRect(10, 10, 150, 100) +ctx.translate(20, 20) +ctx.strokeRect(10, 10, 150, 100) +ctx.translate(20, 20) +ctx.strokeRect(10, 10, 150, 100) + +ctx.draw() +``` +* +* ![](@program/dev/image/canvas/translate.png) */ + translate( + /** 水平坐标平移量 */ + x: number, + /** 竖直坐标平移量 */ + y: number + ): void + /** [Object CanvasContext.measureText(string text)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.measureText.html) + * + * 测量文本尺寸信息。目前仅返回文本宽度。同步接口。 + * + * 最低基础库: `1.9.90` */ + measureText( + /** 要测量的文本 */ + text: string + ): TextMetrics + /** [[CanvasGradient](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasGradient.html) CanvasContext.createCircularGradient(number x, number y, number r)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.createCircularGradient.html) +* +* 创建一个圆形的渐变颜色。起点在圆心,终点在圆环。返回的`CanvasGradient`对象需要使用 [CanvasGradient.addColorStop()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasGradient.addColorStop.html) 来指定渐变点,至少要两个。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +// Create circular gradient +const grd = ctx.createCircularGradient(75, 50, 50) +grd.addColorStop(0, 'red') +grd.addColorStop(1, 'white') + +// Fill with gradient +ctx.setFillStyle(grd) +ctx.fillRect(10, 10, 150, 80) +ctx.draw() +``` +* ![](@program/dev/image/canvas/circular-gradient.png) */ + createCircularGradient( + /** 圆心的 x 坐标 */ + x: number, + /** 圆心的 y 坐标 */ + y: number, + /** 圆的半径 */ + r: number + ): CanvasGradient + /** [[CanvasGradient](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasGradient.html) CanvasContext.createLinearGradient(number x0, number y0, number x1, number y1)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.createLinearGradient.html) +* +* 创建一个线性的渐变颜色。返回的`CanvasGradient`对象需要使用 [CanvasGradient.addColorStop()](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasGradient.addColorStop.html) 来指定渐变点,至少要两个。 +* +* **示例代码** +* +* +* ```javascript +const ctx = wx.createCanvasContext('myCanvas') + +// Create linear gradient +const grd = ctx.createLinearGradient(0, 0, 200, 0) +grd.addColorStop(0, 'red') +grd.addColorStop(1, 'white') + +// Fill with gradient +ctx.setFillStyle(grd) +ctx.fillRect(10, 10, 150, 80) +ctx.draw() +``` +* ![](@program/dev/image/canvas/linear-gradient.png) */ + createLinearGradient( + /** 起点的 x 坐标 */ + x0: number, + /** 起点的 y 坐标 */ + y0: number, + /** 终点的 x 坐标 */ + x1: number, + /** 终点的 y 坐标 */ + y1: number + ): CanvasGradient + } + interface CanvasGradient { + /** [CanvasGradient.addColorStop(number stop, string color)](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasGradient.addColorStop.html) +* +* 添加颜色的渐变点。小于最小 stop 的部分会按最小 stop 的 color 来渲染,大于最大 stop 的部分会按最大 stop 的 color 来渲染 +* +* **示例代码** +* +* +* ```js +const ctx = wx.createCanvasContext('myCanvas') + +// Create circular gradient +const grd = ctx.createLinearGradient(30, 10, 120, 10) +grd.addColorStop(0, 'red') +grd.addColorStop(0.16, 'orange') +grd.addColorStop(0.33, 'yellow') +grd.addColorStop(0.5, 'green') +grd.addColorStop(0.66, 'cyan') +grd.addColorStop(0.83, 'blue') +grd.addColorStop(1, 'purple') + +// Fill with gradient +ctx.setFillStyle(grd) +ctx.fillRect(10, 10, 150, 80) +ctx.draw() +``` +* ![](@program/dev/image/canvas/color-stop.png) */ + addColorStop( + /** 表示渐变中开始与结束之间的位置,范围 0-1。 */ + stop: number, + /** 渐变点的颜色。 */ + color: string + ): void + } + interface Console { + /** [console.debug()](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/console.debug.html) + * + * 向调试面板中打印 debug 日志 */ + debug( + /** 日志内容,可以有任意多个。 */ + ...args: any[] + ): void + /** [console.error()](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/console.error.html) + * + * 向调试面板中打印 error 日志 */ + error( + /** 日志内容,可以有任意多个。 */ + ...args: any[] + ): void + /** [console.group(string label)](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/console.group.html) + * + * 在调试面板中创建一个新的分组。随后输出的内容都会被添加一个缩进,表示该内容属于当前分组。调用 [console.groupEnd](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/console.groupEnd.html)之后分组结束。 + * + * **注意** + * + * + * 仅在工具中有效,在 vConsole 中为空函数实现。 */ + group( + /** 分组标记,可选。 */ + label?: string + ): void + /** [console.groupEnd()](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/console.groupEnd.html) + * + * 结束由 [console.group](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/console.group.html) 创建的分组 + * + * **注意** + * + * + * 仅在工具中有效,在 vConsole 中为空函数实现。 */ + groupEnd(): void + /** [console.info()](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/console.info.html) + * + * 向调试面板中打印 info 日志 */ + info( + /** 日志内容,可以有任意多个。 */ + ...args: any[] + ): void + /** [console.log()](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/console.log.html) + * + * 向调试面板中打印 log 日志 */ + log( + /** 日志内容,可以有任意多个。 */ + ...args: any[] + ): void + /** [console.warn()](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/console.warn.html) + * + * 向调试面板中打印 warn 日志 */ + warn( + /** 日志内容,可以有任意多个。 */ + ...args: any[] + ): void + } + interface DownloadTask { + /** [DownloadTask.abort()](https://developers.weixin.qq.com/miniprogram/dev/api/network/download/DownloadTask.abort.html) + * + * 中断下载任务 + * + * 最低基础库: `1.4.0` */ + abort(): void + /** [DownloadTask.offHeadersReceived(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/network/download/DownloadTask.offHeadersReceived.html) + * + * 取消监听 HTTP Response Header 事件 + * + * 最低基础库: `2.1.0` */ + offHeadersReceived( + /** HTTP Response Header 事件的回调函数 */ + callback?: OffHeadersReceivedCallback + ): void + /** [DownloadTask.offProgressUpdate(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/network/download/DownloadTask.offProgressUpdate.html) + * + * 取消监听下载进度变化事件 + * + * 最低基础库: `2.1.0` */ + offProgressUpdate( + /** 下载进度变化事件的回调函数 */ + callback?: DownloadTaskOffProgressUpdateCallback + ): void + /** [DownloadTask.onHeadersReceived(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/network/download/DownloadTask.onHeadersReceived.html) + * + * 监听 HTTP Response Header 事件。会比请求完成事件更早 + * + * 最低基础库: `2.1.0` */ + onHeadersReceived( + /** HTTP Response Header 事件的回调函数 */ + callback: OnHeadersReceivedCallback + ): void + /** [DownloadTask.onProgressUpdate(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/network/download/DownloadTask.onProgressUpdate.html) + * + * 监听下载进度变化事件 + * + * 最低基础库: `1.4.0` */ + onProgressUpdate( + /** 下载进度变化事件的回调函数 */ + callback: DownloadTaskOnProgressUpdateCallback + ): void + } + interface EditorContext { + /** [EditorContext.blur(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.blur.html) + * + * 编辑器失焦,同时收起键盘。 + * + * 最低基础库: `2.8.3` */ + blur(option?: BlurOption): void + /** [EditorContext.clear(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.clear.html) + * + * 清空编辑器内容 + * + * 最低基础库: `2.7.0` */ + clear(option?: ClearOption): void + /** [EditorContext.format(string name, string value)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.format.html) + * + * 修改样式 + * + * **** + * + * ## 支持设置的样式列表 + * | name | value | verson | + * | --------------------------------------------------------- | ------------------------------- | ------ | + * | bold | | 2.7.0 | + * | italic | | 2.7.0 | + * | underline | | 2.7.0 | + * | strike | | 2.7.0 | + * | ins | | 2.7.0 | + * | script | sub / super | 2.7.0 | + * | header | H1 / H2 / h3 / H4 / h5 / H6 | 2.7.0 | + * | align | left / center / right / justify | 2.7.0 | + * | direction | rtl | 2.7.0 | + * | indent | -1 / +1 | 2.7.0 | + * | list | ordered / bullet / check | 2.7.0 | + * | color | hex color | 2.7.0 | + * | backgroundColor | hex color | 2.7.0 | + * | margin/marginTop/marginBottom/marginLeft/marginRight | css style | 2.7.0 | + * | padding/paddingTop/paddingBottom/paddingLeft/paddingRight | css style | 2.7.0 | + * | font/fontSize/fontStyle/fontVariant/fontWeight/fontFamily | css style | 2.7.0 | + * | lineHeight | css style | 2.7.0 | + * | letterSpacing | css style | 2.7.0 | + * | textDecoration | css style | 2.7.0 | + * | textIndent | css style | 2.8.0 | + * | wordWrap | css style | 2.10.2 | + * | wordBreak | css style | 2.10.2 | + * | whiteSpace | css style | 2.10.2 | + * + * 对已经应用样式的选区设置会取消样式。css style 表示 css 中规定的允许值。 + * + * 最低基础库: `2.7.0` */ + format( + /** 属性 */ + name: string, + /** 值 */ + value?: string + ): void + /** [EditorContext.getContents(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.getContents.html) + * + * 获取编辑器内容 + * + * 最低基础库: `2.7.0` */ + getContents(option?: GetContentsOption): void + /** [EditorContext.getSelectionText(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.getSelectionText.html) + * + * 获取编辑器已选区域内的纯文本内容。当编辑器失焦或未选中一段区间时,返回内容为空。 + * + * 最低基础库: `2.10.2` */ + getSelectionText(option?: GetSelectionTextOption): void + /** [EditorContext.insertDivider(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.insertDivider.html) + * + * 插入分割线 + * + * 最低基础库: `2.7.0` */ + insertDivider(option?: InsertDividerOption): void + /** [EditorContext.insertImage(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.insertImage.html) +* +* 插入图片。 +* +* 地址为临时文件时,获取的编辑器html格式内容中 标签增加属性 data-local,delta 格式内容中图片 attributes 属性增加 data-local 字段,该值为传入的临时文件地址。 +* +* 开发者可选择在提交阶段上传图片到服务器,获取到网络地址后进行替换。替换时对于html内容应替换掉 的 src 值,对于 delta 内容应替换掉 `insert { image: abc }` 值。 +* +* **示例代码** +* +* +* ```javascript +this.editorCtx.insertImage({ + src: 'xx', + width: '100px', + height: '50px', + extClass: className +}) +``` +* +* 最低基础库: `2.7.0` */ + insertImage(option: InsertImageOption): void + /** [EditorContext.insertText(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.insertText.html) + * + * 覆盖当前选区,设置一段文本 + * + * 最低基础库: `2.7.0` */ + insertText(option: InsertTextOption): void + /** [EditorContext.redo(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.redo.html) + * + * 恢复 + * + * 最低基础库: `2.7.0` */ + redo(option?: RedoOption): void + /** [EditorContext.removeFormat(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.removeFormat.html) + * + * 清除当前选区的样式 + * + * 最低基础库: `2.7.0` */ + removeFormat(option?: RemoveFormatOption): void + /** [EditorContext.scrollIntoView()](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.scrollIntoView.html) + * + * 使得编辑器光标处滚动到窗口可视区域内。 + * + * 最低基础库: `2.8.3` */ + scrollIntoView(): void + /** [EditorContext.setContents(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.setContents.html) + * + * 初始化编辑器内容,html和delta同时存在时仅delta生效 + * + * 最低基础库: `2.7.0` */ + setContents(option: SetContentsOption): void + /** [EditorContext.undo(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.undo.html) + * + * 撤销 + * + * 最低基础库: `2.7.0` */ + undo(option?: UndoOption): void + } + interface EntryList { + /** [Array EntryList.getEntries()](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/performance/EntryList.getEntries.html) + * + * 该方法返回当前列表中的所有性能数据 + * + * 最低基础库: `2.11.0` */ + getEntries(): any[] + /** [Array EntryList.getEntriesByName(string name, string entryType)](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/performance/EntryList.getEntriesByName.html) + * + * 获取当前列表中所有名称为 [name] 且类型为 [entryType] 的性能数据 + * + * 最低基础库: `2.11.0` */ + getEntriesByName(name: string, entryType?: string): any[] + /** [Array EntryList.getEntriesByType(string entryType)](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/performance/EntryList.getEntriesByType.html) + * + * 获取当前列表中所有类型为 [entryType] 的性能数据 + * + * 最低基础库: `2.11.0` */ + getEntriesByType(entryType: string): any[] + } + interface EventChannel { + /** [EventChannel.emit(string eventName, any args)](https://developers.weixin.qq.com/miniprogram/dev/api/route/EventChannel.emit.html) + * + * 触发一个事件 + * + * 最低基础库: `2.7.3` */ + emit( + /** 事件名称 */ + eventName: string, + /** 事件参数 */ + ...args: any + ): void + /** [EventChannel.off(string eventName, function fn)](https://developers.weixin.qq.com/miniprogram/dev/api/route/EventChannel.off.html) + * + * 取消监听一个事件。给出第二个参数时,只取消给出的监听函数,否则取消所有监听函数 + * + * 最低基础库: `2.7.3` */ + off( + /** 事件名称 */ + eventName: string, + /** 事件监听函数 */ + fn: EventCallback + ): void + /** [EventChannel.on(string eventName, function fn)](https://developers.weixin.qq.com/miniprogram/dev/api/route/EventChannel.on.html) + * + * 持续监听一个事件 + * + * 最低基础库: `2.7.3` */ + on( + /** 事件名称 */ + eventName: string, + /** 事件监听函数 */ + fn: EventCallback + ): void + /** [EventChannel.once(string eventName, function fn)](https://developers.weixin.qq.com/miniprogram/dev/api/route/EventChannel.once.html) + * + * 监听一个事件一次,触发后失效 + * + * 最低基础库: `2.7.3` */ + once( + /** 事件名称 */ + eventName: string, + /** 事件监听函数 */ + fn: EventCallback + ): void + } + interface FileSystemManager { + /** [Array.<string> FileSystemManager.readdirSync(string dirPath)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.readdirSync.html) + * + * [FileSystemManager.readdir](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.readdir.html) 的同步版本 */ + readdirSync( + /** 要读取的目录路径 (本地路径) */ + dirPath: string + ): string[] + /** [FileSystemManager.access(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.access.html) + * + * 判断文件/目录是否存在 */ + access(option: AccessOption): void + /** [FileSystemManager.accessSync(string path)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.accessSync.html) + * + * [FileSystemManager.access](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.access.html) 的同步版本 */ + accessSync( + /** 要判断是否存在的文件/目录路径 (本地路径) */ + path: string + ): void + /** [FileSystemManager.appendFile(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.appendFile.html) + * + * 在文件结尾追加内容 + * + * 最低基础库: `2.1.0` */ + appendFile(option: AppendFileOption): void + /** [FileSystemManager.appendFileSync(string filePath, string|ArrayBuffer data, string encoding)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.appendFileSync.html) + * + * [FileSystemManager.appendFile](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.appendFile.html) 的同步版本 + * + * 最低基础库: `2.1.0` */ + appendFileSync( + /** 要追加内容的文件路径 (本地路径) */ + filePath: string, + /** 要追加的文本或二进制数据 */ + data: string | ArrayBuffer, + /** 指定写入文件的字符编码 + * + * 参数 encoding 可选值: + * - 'ascii': ; + * - 'base64': ; + * - 'binary': ; + * - 'hex': ; + * - 'ucs2': 以小端序读取; + * - 'ucs-2': 以小端序读取; + * - 'utf16le': 以小端序读取; + * - 'utf-16le': 以小端序读取; + * - 'utf-8': ; + * - 'utf8': ; + * - 'latin1': ; */ + encoding?: + | 'ascii' + | 'base64' + | 'binary' + | 'hex' + | 'ucs2' + | 'ucs-2' + | 'utf16le' + | 'utf-16le' + | 'utf-8' + | 'utf8' + | 'latin1' + ): void + /** [FileSystemManager.copyFile(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.copyFile.html) + * + * 复制文件 */ + copyFile(option: CopyFileOption): void + /** [FileSystemManager.copyFileSync(string srcPath, string destPath)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.copyFileSync.html) + * + * [FileSystemManager.copyFile](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.copyFile.html) 的同步版本 */ + copyFileSync( + /** 源文件路径,支持本地路径 */ + srcPath: string, + /** 目标文件路径,支持本地路径 */ + destPath: string + ): void + /** [FileSystemManager.getFileInfo(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.getFileInfo.html) + * + * 获取该小程序下的 本地临时文件 或 本地缓存文件 信息 */ + getFileInfo(option: FileSystemManagerGetFileInfoOption): void + /** [FileSystemManager.getSavedFileList(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.getSavedFileList.html) + * + * 获取该小程序下已保存的本地缓存文件列表 */ + getSavedFileList(option?: FileSystemManagerGetSavedFileListOption): void + /** [FileSystemManager.mkdir(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.mkdir.html) + * + * 创建目录 */ + mkdir(option: MkdirOption): void + /** [FileSystemManager.mkdirSync(string dirPath, boolean recursive)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.mkdirSync.html) + * + * [FileSystemManager.mkdir](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.mkdir.html) 的同步版本 */ + mkdirSync( + /** 创建的目录路径 (本地路径) */ + dirPath: string, + /** 是否在递归创建该目录的上级目录后再创建该目录。如果对应的上级目录已经存在,则不创建该上级目录。如 dirPath 为 a/b/c/d 且 recursive 为 true,将创建 a 目录,再在 a 目录下创建 b 目录,以此类推直至创建 a/b/c 目录下的 d 目录。 + * + * 最低基础库: `2.3.0` */ + recursive?: boolean + ): void + /** [FileSystemManager.readFile(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.readFile.html) + * + * 读取本地文件内容 */ + readFile(option: ReadFileOption): void + /** [FileSystemManager.readdir(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.readdir.html) + * + * 读取目录内文件列表 */ + readdir(option: ReaddirOption): void + /** [FileSystemManager.removeSavedFile(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.removeSavedFile.html) + * + * 删除该小程序下已保存的本地缓存文件 */ + removeSavedFile(option: FileSystemManagerRemoveSavedFileOption): void + /** [FileSystemManager.rename(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.rename.html) + * + * 重命名文件。可以把文件从 oldPath 移动到 newPath */ + rename(option: RenameOption): void + /** [FileSystemManager.renameSync(string oldPath, string newPath)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.renameSync.html) + * + * [FileSystemManager.rename](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.rename.html) 的同步版本 */ + renameSync( + /** 源文件路径,支持本地路径 */ + oldPath: string, + /** 新文件路径,支持本地路径 */ + newPath: string + ): void + /** [FileSystemManager.rmdir(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.rmdir.html) + * + * 删除目录 */ + rmdir(option: RmdirOption): void + /** [FileSystemManager.rmdirSync(string dirPath, boolean recursive)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.rmdirSync.html) + * + * [FileSystemManager.rmdir](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.rmdir.html) 的同步版本 */ + rmdirSync( + /** 要删除的目录路径 (本地路径) */ + dirPath: string, + /** 是否递归删除目录。如果为 true,则删除该目录和该目录下的所有子目录以及文件。 + * + * 最低基础库: `2.3.0` */ + recursive?: boolean + ): void + /** [FileSystemManager.saveFile(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.saveFile.html) + * + * 保存临时文件到本地。此接口会移动临时文件,因此调用成功后,tempFilePath 将不可用。 */ + saveFile(option: FileSystemManagerSaveFileOption): void + /** [FileSystemManager.stat(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.stat.html) + * + * 获取文件 Stats 对象 */ + stat(option: StatOption): void + /** [FileSystemManager.unlink(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.unlink.html) + * + * 删除文件 */ + unlink(option: UnlinkOption): void + /** [FileSystemManager.unlinkSync(string filePath)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.unlinkSync.html) + * + * [FileSystemManager.unlink](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.unlink.html) 的同步版本 */ + unlinkSync( + /** 要删除的文件路径 (本地路径) */ + filePath: string + ): void + /** [FileSystemManager.unzip(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.unzip.html) + * + * 解压文件 */ + unzip(option: UnzipOption): void + /** [FileSystemManager.writeFile(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.writeFile.html) + * + * 写文件 */ + writeFile(option: WriteFileOption): void + /** [FileSystemManager.writeFileSync(string filePath, string|ArrayBuffer data, string encoding)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.writeFileSync.html) + * + * [FileSystemManager.writeFile](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.writeFile.html) 的同步版本 */ + writeFileSync( + /** 要写入的文件路径 (本地路径) */ + filePath: string, + /** 要写入的文本或二进制数据 */ + data: string | ArrayBuffer, + /** 指定写入文件的字符编码 + * + * 参数 encoding 可选值: + * - 'ascii': ; + * - 'base64': ; + * - 'binary': ; + * - 'hex': ; + * - 'ucs2': 以小端序读取; + * - 'ucs-2': 以小端序读取; + * - 'utf16le': 以小端序读取; + * - 'utf-16le': 以小端序读取; + * - 'utf-8': ; + * - 'utf8': ; + * - 'latin1': ; */ + encoding?: + | 'ascii' + | 'base64' + | 'binary' + | 'hex' + | 'ucs2' + | 'ucs-2' + | 'utf16le' + | 'utf-16le' + | 'utf-8' + | 'utf8' + | 'latin1' + ): void + /** [[Stats](https://developers.weixin.qq.com/miniprogram/dev/api/file/Stats.html)|Object FileSystemManager.statSync(string path, boolean recursive)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.statSync.html) + * + * [FileSystemManager.stat](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.stat.html) 的同步版本 */ + statSync( + /** 文件/目录路径 (本地路径) */ + path: string, + /** 是否递归获取目录下的每个文件的 Stats 信息 + * + * 最低基础库: `2.3.0` */ + recursive?: boolean + ): Stats | IAnyObject + /** [string FileSystemManager.saveFileSync(string tempFilePath, string filePath)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.saveFileSync.html) + * + * [FileSystemManager.saveFile](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.saveFile.html) 的同步版本 */ + saveFileSync( + /** 临时存储文件路径 (本地路径) */ + tempFilePath: string, + /** 要存储的文件路径 (本地路径) */ + filePath?: string + ): string + /** [string|ArrayBuffer FileSystemManager.readFileSync(string filePath, string encoding, number position, number length)](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.readFileSync.html) + * + * [FileSystemManager.readFile](https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.readFile.html) 的同步版本 */ + readFileSync( + /** 要读取的文件的路径 (本地路径) */ + filePath: string, + /** 指定读取文件的字符编码,如果不传 encoding,则以 ArrayBuffer 格式读取文件的二进制内容 + * + * 参数 encoding 可选值: + * - 'ascii': ; + * - 'base64': ; + * - 'binary': ; + * - 'hex': ; + * - 'ucs2': 以小端序读取; + * - 'ucs-2': 以小端序读取; + * - 'utf16le': 以小端序读取; + * - 'utf-16le': 以小端序读取; + * - 'utf-8': ; + * - 'utf8': ; + * - 'latin1': ; */ + encoding?: + | 'ascii' + | 'base64' + | 'binary' + | 'hex' + | 'ucs2' + | 'ucs-2' + | 'utf16le' + | 'utf-16le' + | 'utf-8' + | 'utf8' + | 'latin1', + /** 从文件指定位置开始读,如果不指定,则从文件头开始读。读取的范围应该是左闭右开区间 [position, position+length)。有效范围:[0, fileLength - 1]。单位:byte + * + * 最低基础库: `2.10.0` */ + position?: number, + /** 指定文件的长度,如果不指定,则读到文件末尾。有效范围:[1, fileLength]。单位:byte + * + * 最低基础库: `2.10.0` */ + length?: number + ): string | ArrayBuffer + } + interface GeneralCallbackResult { + errMsg: string + } + interface IBeaconError { + /** 错误信息 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | 0 | ok | 正常 | + * | 11000 | unsupport | 系统或设备不支持 | + * | 11001 | bluetooth service unavailable | 蓝牙服务不可用 | + * | 11002 | location service unavailable | 位置服务不可用 | + * | 11003 | already start | 已经开始搜索 | + * | 11004 | not startBeaconDiscovery | 还未开始搜索 | + * | 11005 | system error | 系统错误 | + * | 11006 | invalid data | 参数不正确 | */ errMsg: string + /** 错误码 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | 0 | ok | 正常 | + * | 11000 | unsupport | 系统或设备不支持 | + * | 11001 | bluetooth service unavailable | 蓝牙服务不可用 | + * | 11002 | location service unavailable | 位置服务不可用 | + * | 11003 | already start | 已经开始搜索 | + * | 11004 | not startBeaconDiscovery | 还未开始搜索 | + * | 11005 | system error | 系统错误 | + * | 11006 | invalid data | 参数不正确 | */ errCode: number + } + interface InnerAudioContext { + /** [InnerAudioContext.destroy()](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.destroy.html) + * + * 销毁当前实例 */ + destroy(): void + /** [InnerAudioContext.offCanplay(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.offCanplay.html) + * + * 取消监听音频进入可以播放状态的事件 + * + * 最低基础库: `1.9.0` */ + offCanplay( + /** 音频进入可以播放状态的事件的回调函数 */ + callback?: OffCanplayCallback + ): void + /** [InnerAudioContext.offEnded(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.offEnded.html) + * + * 取消监听音频自然播放至结束的事件 + * + * 最低基础库: `1.9.0` */ + offEnded( + /** 音频自然播放至结束的事件的回调函数 */ + callback?: OffEndedCallback + ): void + /** [InnerAudioContext.offError(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.offError.html) + * + * 取消监听音频播放错误事件 + * + * 最低基础库: `1.9.0` */ + offError( + /** 音频播放错误事件的回调函数 */ + callback?: InnerAudioContextOffErrorCallback + ): void + /** [InnerAudioContext.offPause(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.offPause.html) + * + * 取消监听音频暂停事件 + * + * 最低基础库: `1.9.0` */ + offPause( + /** 音频暂停事件的回调函数 */ + callback?: OffPauseCallback + ): void + /** [InnerAudioContext.offPlay(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.offPlay.html) + * + * 取消监听音频播放事件 + * + * 最低基础库: `1.9.0` */ + offPlay( + /** 音频播放事件的回调函数 */ + callback?: OffPlayCallback + ): void + /** [InnerAudioContext.offSeeked(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.offSeeked.html) + * + * 取消监听音频完成跳转操作的事件 + * + * 最低基础库: `1.9.0` */ + offSeeked( + /** 音频完成跳转操作的事件的回调函数 */ + callback?: OffSeekedCallback + ): void + /** [InnerAudioContext.offSeeking(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.offSeeking.html) + * + * 取消监听音频进行跳转操作的事件 + * + * 最低基础库: `1.9.0` */ + offSeeking( + /** 音频进行跳转操作的事件的回调函数 */ + callback?: OffSeekingCallback + ): void + /** [InnerAudioContext.offStop(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.offStop.html) + * + * 取消监听音频停止事件 + * + * 最低基础库: `1.9.0` */ + offStop( + /** 音频停止事件的回调函数 */ + callback?: OffStopCallback + ): void + /** [InnerAudioContext.offTimeUpdate(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.offTimeUpdate.html) + * + * 取消监听音频播放进度更新事件 + * + * 最低基础库: `1.9.0` */ + offTimeUpdate( + /** 音频播放进度更新事件的回调函数 */ + callback?: OffTimeUpdateCallback + ): void + /** [InnerAudioContext.offWaiting(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.offWaiting.html) + * + * 取消监听音频加载中事件 + * + * 最低基础库: `1.9.0` */ + offWaiting( + /** 音频加载中事件的回调函数 */ + callback?: OffWaitingCallback + ): void + /** [InnerAudioContext.onCanplay(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.onCanplay.html) + * + * 监听音频进入可以播放状态的事件。但不保证后面可以流畅播放 */ + onCanplay( + /** 音频进入可以播放状态的事件的回调函数 */ + callback: OnCanplayCallback + ): void + /** [InnerAudioContext.onEnded(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.onEnded.html) + * + * 监听音频自然播放至结束的事件 */ + onEnded( + /** 音频自然播放至结束的事件的回调函数 */ + callback: OnEndedCallback + ): void + /** [InnerAudioContext.onError(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.onError.html) + * + * 监听音频播放错误事件 + * + * **Tips** + * + * + * 1. errCode=100001 时,如若 errMsg 中有 INNERCODE -11828 ,请先检查 response header 是否缺少 Content-Length + * 2. errCode=100001 时,如若 errMsg 中有 systemErrCode:200333420,请检查文件编码格式和 fileExtension 是否一致 */ + onError( + /** 音频播放错误事件的回调函数 */ + callback: InnerAudioContextOnErrorCallback + ): void + /** [InnerAudioContext.onPause(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.onPause.html) + * + * 监听音频暂停事件 */ + onPause( + /** 音频暂停事件的回调函数 */ + callback: OnPauseCallback + ): void + /** [InnerAudioContext.onPlay(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.onPlay.html) + * + * 监听音频播放事件 */ + onPlay( + /** 音频播放事件的回调函数 */ + callback: OnPlayCallback + ): void + /** [InnerAudioContext.onSeeked(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.onSeeked.html) + * + * 监听音频完成跳转操作的事件 */ + onSeeked( + /** 音频完成跳转操作的事件的回调函数 */ + callback: OnSeekedCallback + ): void + /** [InnerAudioContext.onSeeking(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.onSeeking.html) + * + * 监听音频进行跳转操作的事件 */ + onSeeking( + /** 音频进行跳转操作的事件的回调函数 */ + callback: OnSeekingCallback + ): void + /** [InnerAudioContext.onStop(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.onStop.html) + * + * 监听音频停止事件 */ + onStop( + /** 音频停止事件的回调函数 */ + callback: InnerAudioContextOnStopCallback + ): void + /** [InnerAudioContext.onTimeUpdate(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.onTimeUpdate.html) + * + * 监听音频播放进度更新事件 */ + onTimeUpdate( + /** 音频播放进度更新事件的回调函数 */ + callback: OnTimeUpdateCallback + ): void + /** [InnerAudioContext.onWaiting(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.onWaiting.html) + * + * 监听音频加载中事件。当音频因为数据不足,需要停下来加载时会触发 */ + onWaiting( + /** 音频加载中事件的回调函数 */ + callback: OnWaitingCallback + ): void + /** [InnerAudioContext.pause()](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.pause.html) + * + * 暂停。暂停后的音频再播放会从暂停处开始播放 */ + pause(): void + /** [InnerAudioContext.play()](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.play.html) + * + * 播放 */ + play(): void + /** [InnerAudioContext.seek(number position)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.seek.html) + * + * 跳转到指定位置 */ + seek( + /** 跳转的时间,单位 s。精确到小数点后 3 位,即支持 ms 级别精确度 */ + position: number + ): void + /** [InnerAudioContext.stop()](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/InnerAudioContext.stop.html) + * + * 停止。停止后的音频再播放会从头开始播放。 */ + stop(): void + } + interface IntersectionObserver { + /** [IntersectionObserver.disconnect()](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.disconnect.html) + * + * 停止监听。回调函数将不再触发 */ + disconnect(): void + /** [IntersectionObserver.observe(string targetSelector, function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.observe.html) + * + * 指定目标节点并开始监听相交状态变化情况 */ + observe( + /** 选择器 */ + targetSelector: string, + /** 监听相交状态变化的回调函数 */ + callback: IntersectionObserverObserveCallback + ): void + /** [[IntersectionObserver](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.html) IntersectionObserver.relativeTo(string selector, Object margins)](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.relativeTo.html) + * + * 使用选择器指定一个节点,作为参照区域之一。 */ + relativeTo( + /** 选择器 */ + selector: string, + /** 用来扩展(或收缩)参照节点布局区域的边界 */ + margins?: Margins + ): IntersectionObserver + /** [[IntersectionObserver](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.html) IntersectionObserver.relativeToViewport(Object margins)](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.relativeToViewport.html) +* +* 指定页面显示区域作为参照区域之一 +* +* **示例代码** +* +* +* 下面的示例代码中,如果目标节点(用选择器 .target-class 指定)进入显示区域以下 100px 时,就会触发回调函数。 +* ```javascript +Page({ + onLoad: function(){ + wx.createIntersectionObserver().relativeToViewport({bottom: 100}).observe('.target-class', (res) => { + res.intersectionRatio // 相交区域占目标节点的布局区域的比例 + res.intersectionRect // 相交区域 + res.intersectionRect.left // 相交区域的左边界坐标 + res.intersectionRect.top // 相交区域的上边界坐标 + res.intersectionRect.width // 相交区域的宽度 + res.intersectionRect.height // 相交区域的高度 + }) + } +}) +``` */ + relativeToViewport( + /** 用来扩展(或收缩)参照节点布局区域的边界 */ + margins?: Margins + ): IntersectionObserver + } + interface InterstitialAd { + /** [InterstitialAd.destroy()](https://developers.weixin.qq.com/miniprogram/dev/api/ad/InterstitialAd.destroy.html) + * + * 销毁插屏广告实例。 + * + * 最低基础库: `2.8.0` */ + destroy(): void + /** [InterstitialAd.offClose(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/ad/InterstitialAd.offClose.html) + * + * 取消监听插屏广告关闭事件 */ + offClose( + /** 插屏广告关闭事件的回调函数 */ + callback?: UDPSocketOffCloseCallback + ): void + /** [InterstitialAd.offError(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/ad/InterstitialAd.offError.html) + * + * 取消监听插屏错误事件 */ + offError( + /** 插屏错误事件的回调函数 */ + callback?: InterstitialAdOffErrorCallback + ): void + /** [InterstitialAd.offLoad(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/ad/InterstitialAd.offLoad.html) + * + * 取消监听插屏广告加载事件 */ + offLoad( + /** 插屏广告加载事件的回调函数 */ + callback?: OffLoadCallback + ): void + /** [InterstitialAd.onClose(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/ad/InterstitialAd.onClose.html) + * + * 监听插屏广告关闭事件。 */ + onClose( + /** 插屏广告关闭事件的回调函数 */ + callback: UDPSocketOnCloseCallback + ): void + /** [InterstitialAd.onError(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/ad/InterstitialAd.onError.html) + * + * 监听插屏错误事件。 + * + * **错误码信息与解决方案表** + * + * + * 错误码是通过onError获取到的错误信息。调试期间,可以通过异常返回来捕获信息。 + * 在小程序发布上线之后,如果遇到异常问题,可以在[“运维中心“](https://mp.weixin.qq.com/)里面搜寻错误日志,还可以针对异常返回加上适当的监控信息。 + * + * | 代码 | 异常情况 | 理由 | 解决方案 | + * | ------ | -------------- | --------------- | -------------------------- | + * | 1000 | 后端错误调用失败 | 该项错误不是开发者的异常情况 | 一般情况下忽略一段时间即可恢复。 | + * | 1001 | 参数错误 | 使用方法错误 | 可以前往developers.weixin.qq.com确认具体教程(小程序和小游戏分别有各自的教程,可以在顶部选项中,“设计”一栏的右侧进行切换。| + * | 1002 | 广告单元无效 | 可能是拼写错误、或者误用了其他APP的广告ID | 请重新前往mp.weixin.qq.com确认广告位ID。 | + * | 1003 | 内部错误 | 该项错误不是开发者的异常情况 | 一般情况下忽略一段时间即可恢复。| + * | 1004 | 无适合的广告 | 广告不是每一次都会出现,这次没有出现可能是由于该用户不适合浏览广告 | 属于正常情况,且开发者需要针对这种情况做形态上的兼容。 | + * | 1005 | 广告组件审核中 | 你的广告正在被审核,无法展现广告 | 请前往mp.weixin.qq.com确认审核状态,且开发者需要针对这种情况做形态上的兼容。| + * | 1006 | 广告组件被驳回 | 你的广告审核失败,无法展现广告 | 请前往mp.weixin.qq.com确认审核状态,且开发者需要针对这种情况做形态上的兼容。| + * | 1007 | 广告组件被驳回 | 你的广告能力已经被封禁,封禁期间无法展现广告 | 请前往mp.weixin.qq.com确认小程序广告封禁状态。 | + * | 1008 | 广告单元已关闭 | 该广告位的广告能力已经被关闭 | 请前往mp.weixin.qq.com重新打开对应广告位的展现。| */ + onError( + /** 插屏错误事件的回调函数 */ + callback: InterstitialAdOnErrorCallback + ): void + /** [InterstitialAd.onLoad(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/ad/InterstitialAd.onLoad.html) + * + * 监听插屏广告加载事件。 */ + onLoad( + /** 插屏广告加载事件的回调函数 */ + callback: OnLoadCallback + ): void + /** [Promise InterstitialAd.load()](https://developers.weixin.qq.com/miniprogram/dev/api/ad/InterstitialAd.load.html) + * + * 加载插屏广告。 + * + * 最低基础库: `2.8.0` */ + load(): Promise + /** [Promise InterstitialAd.show()](https://developers.weixin.qq.com/miniprogram/dev/api/ad/InterstitialAd.show.html) + * + * 显示插屏广告。 + * + * **错误码信息表** + * + * + * 如果插屏广告显示失败,InterstitialAd.show() 方法会返回一个rejected Promise,开发者可以获取到错误码及对应的错误信息。 + * + * | 代码 | 异常情况 | 理由 | + * | ------ | -------------- | -------------------------- | + * | 2001 | 触发频率限制 | 小程序启动一定时间内不允许展示插屏广告 | + * | 2002 | 触发频率限制 | 距离小程序插屏广告或者激励视频广告上次播放时间间隔不足,不允许展示插屏广告 | + * | 2003 | 触发频率限制 | 当前正在播放激励视频广告或者插屏广告,不允许再次展示插屏广告 | + * | 2004 | 广告渲染失败 | 该项错误不是开发者的异常情况,或因小程序页面切换导致广告渲染失败 | + * | 2005 | 广告调用异常 | 插屏广告实例不允许跨页面调用 | */ + show(): Promise + } + interface IsoDep { + /** [IsoDep.close(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/IsoDep.close.html) + * + * 断开连接 + * + * 最低基础库: `2.11.2` */ + close(option?: NdefCloseOption): void + /** [IsoDep.connect(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/IsoDep.connect.html) + * + * 连接 NFC 标签 + * + * 最低基础库: `2.11.2` */ + connect(option?: ConnectOption): void + /** [IsoDep.getHistoricalBytes(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/IsoDep.getHistoricalBytes.html) + * + * 获取复位信息 + * + * 最低基础库: `2.11.2` */ + getHistoricalBytes(option?: GetHistoricalBytesOption): void + /** [IsoDep.getMaxTransceiveLength(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/IsoDep.getMaxTransceiveLength.html) + * + * 获取最大传输长度 + * + * 最低基础库: `2.11.2` */ + getMaxTransceiveLength(option?: GetMaxTransceiveLengthOption): void + /** [IsoDep.isConnected(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/IsoDep.isConnected.html) + * + * 检查是否已连接 + * + * 最低基础库: `2.11.2` */ + isConnected(option?: IsConnectedOption): void + /** [IsoDep.setTimeout(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/IsoDep.setTimeout.html) + * + * 设置超时时间 + * + * 最低基础库: `2.11.2` */ + setTimeout(option: SetTimeoutOption): void + /** [IsoDep.transceive(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/IsoDep.transceive.html) + * + * 发送数据 + * + * 最低基础库: `2.11.2` */ + transceive(option: TransceiveOption): void + } + interface JoinVoIPChatError { + /** 错误信息 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | -1 | 当前已在房间内 | | + * | -2 | 录音设备被占用,可能是当前正在使用微信内语音通话或系统通话 | | + * | -3 | 加入会话期间退出(可能是用户主动退出,或者退后台、来电等原因),因此加入失败 | | + * | -1000 | 系统错误 | | */ errMsg: string + /** 错误码 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | -1 | 当前已在房间内 | | + * | -2 | 录音设备被占用,可能是当前正在使用微信内语音通话或系统通话 | | + * | -3 | 加入会话期间退出(可能是用户主动退出,或者退后台、来电等原因),因此加入失败 | | + * | -1000 | 系统错误 | | */ errCode: number + } + interface LivePlayerContext { + /** [LivePlayerContext.exitFullScreen(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePlayerContext.exitFullScreen.html) + * + * 退出全屏 */ + exitFullScreen(option?: ExitFullScreenOption): void + /** [LivePlayerContext.exitPictureInPicture(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePlayerContext.exitPictureInPicture.html) + * + * 退出小窗,该方法可在任意页面调用 */ + exitPictureInPicture(option?: ExitPictureInPictureOption): void + /** [LivePlayerContext.mute(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePlayerContext.mute.html) + * + * 静音 */ + mute(option?: MuteOption): void + /** [LivePlayerContext.pause(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePlayerContext.pause.html) + * + * 暂停 + * + * 最低基础库: `1.9.90` */ + pause(option?: PauseOption): void + /** [LivePlayerContext.play(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePlayerContext.play.html) + * + * 播放 */ + play(option?: PlayOption): void + /** [LivePlayerContext.requestFullScreen(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePlayerContext.requestFullScreen.html) + * + * 进入全屏 */ + requestFullScreen( + option: LivePlayerContextRequestFullScreenOption + ): void + /** [LivePlayerContext.requestPictureInPicture(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePlayerContext.requestPictureInPicture.html) + * + * 进入小窗 + * + * 最低基础库: `2.15.0` */ + requestPictureInPicture(option?: RequestPictureInPictureOption): void + /** [LivePlayerContext.resume(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePlayerContext.resume.html) + * + * 恢复 + * + * 最低基础库: `1.9.90` */ + resume(option?: ResumeOption): void + /** [LivePlayerContext.snapshot(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePlayerContext.snapshot.html) + * + * 截图 + * + * 最低基础库: `2.7.1` */ + snapshot(option: LivePlayerContextSnapshotOption): void + /** [LivePlayerContext.stop(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePlayerContext.stop.html) + * + * 停止 */ + stop(option?: StopOption): void + } + interface LivePusherContext { + /** [LivePusherContext.pause(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.pause.html) + * + * 暂停推流 */ + pause(option?: PauseOption): void + /** [LivePusherContext.pauseBGM(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.pauseBGM.html) + * + * 暂停背景音 + * + * 最低基础库: `2.4.0` */ + pauseBGM(option?: PauseBGMOption): void + /** [LivePusherContext.playBGM(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.playBGM.html) + * + * 播放背景音 + * + * 最低基础库: `2.4.0` */ + playBGM(option: PlayBGMOption): void + /** [LivePusherContext.resume(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.resume.html) + * + * 恢复推流 */ + resume(option?: ResumeOption): void + /** [LivePusherContext.resumeBGM(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.resumeBGM.html) + * + * 恢复背景音 + * + * 最低基础库: `2.4.0` */ + resumeBGM(option?: ResumeBGMOption): void + /** [LivePusherContext.sendMessage(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.sendMessage.html) + * + * 发送SEI消息 + * + * 最低基础库: `2.10.0` */ + sendMessage(option?: SendMessageOption): void + /** [LivePusherContext.setBGMVolume(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.setBGMVolume.html) + * + * 设置背景音音量 + * + * 最低基础库: `2.4.0` */ + setBGMVolume(option: SetBGMVolumeOption): void + /** [LivePusherContext.setMICVolume(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.setMICVolume.html) + * + * 设置麦克风音量 + * + * 最低基础库: `2.10.0` */ + setMICVolume(option: SetMICVolumeOption): void + /** [LivePusherContext.snapshot(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.snapshot.html) + * + * 快照 + * + * 最低基础库: `1.9.90` */ + snapshot(option: LivePusherContextSnapshotOption): void + /** [LivePusherContext.start(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.start.html) + * + * 开始推流,同时开启摄像头预览 */ + start(option?: CameraFrameListenerStartOption): void + /** [LivePusherContext.startPreview(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.startPreview.html) + * + * 开启摄像头预览 + * + * 最低基础库: `2.7.0` */ + startPreview(option?: StartPreviewOption): void + /** [LivePusherContext.stop(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.stop.html) + * + * 停止推流,同时停止摄像头预览 */ + stop(option?: StopOption): void + /** [LivePusherContext.stopBGM(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.stopBGM.html) + * + * 停止背景音 + * + * 最低基础库: `2.4.0` */ + stopBGM(option?: StopBGMOption): void + /** [LivePusherContext.stopPreview(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.stopPreview.html) + * + * 关闭摄像头预览 + * + * 最低基础库: `2.7.0` */ + stopPreview(option?: StopPreviewOption): void + /** [LivePusherContext.switchCamera(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.switchCamera.html) + * + * 切换前后摄像头 */ + switchCamera(option?: SwitchCameraOption): void + /** [LivePusherContext.toggleTorch(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePusherContext.toggleTorch.html) + * + * 切换手电筒 + * + * 最低基础库: `2.1.0` */ + toggleTorch(option?: ToggleTorchOption): void + } + interface LogManager { + /** [LogManager.debug()](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/LogManager.debug.html) + * + * 写 debug 日志 */ + debug( + /** 日志内容,可以有任意多个。每次调用的参数的总大小不超过100Kb */ + ...args: any[] + ): void + /** [LogManager.info()](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/LogManager.info.html) + * + * 写 info 日志 */ + info( + /** 日志内容,可以有任意多个。每次调用的参数的总大小不超过100Kb */ + ...args: any[] + ): void + /** [LogManager.log()](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/LogManager.log.html) + * + * 写 log 日志 */ + log( + /** 日志内容,可以有任意多个。每次调用的参数的总大小不超过100Kb */ + ...args: any[] + ): void + /** [LogManager.warn()](https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/LogManager.warn.html) + * + * 写 warn 日志 */ + warn( + /** 日志内容,可以有任意多个。每次调用的参数的总大小不超过100Kb */ + ...args: any[] + ): void + } + interface MapContext { + /** [MapContext.addCustomLayer(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.addCustomLayer.html) + * + * 添加个性化图层。 + * + * 最低基础库: `2.12.0` */ + addCustomLayer(option: AddCustomLayerOption): void + /** [MapContext.addGroundOverlay(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.addGroundOverlay.html) + * + * 创建自定义图片图层,图片会随着地图缩放而缩放。 + * + * 最低基础库: `2.14.0` */ + addGroundOverlay(option: AddGroundOverlayOption): void + /** [MapContext.addMarkers(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.addMarkers.html) + * + * 添加 marker。 + * + * 最低基础库: `2.13.0` */ + addMarkers(option: AddMarkersOption): void + /** [MapContext.fromScreenLocation(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.fromScreenLocation.html) + * + * 获取屏幕上的点对应的经纬度,坐标原点为地图左上角。 + * + * 最低基础库: `2.14.0` */ + fromScreenLocation(option: FromScreenLocationOption): void + /** [MapContext.getCenterLocation(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.getCenterLocation.html) + * + * 获取当前地图中心的经纬度。返回的是 gcj02 坐标系,可以用于 [wx.openLocation()](https://developers.weixin.qq.com/miniprogram/dev/api/location/wx.openLocation.html) */ + getCenterLocation(option?: GetCenterLocationOption): void + /** [MapContext.getRegion(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.getRegion.html) + * + * 获取当前地图的视野范围 + * + * 最低基础库: `1.4.0` */ + getRegion(option?: GetRegionOption): void + /** [MapContext.getRotate(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.getRotate.html) + * + * 获取当前地图的旋转角 + * + * 最低基础库: `2.8.0` */ + getRotate(option?: GetRotateOption): void + /** [MapContext.getScale(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.getScale.html) + * + * 获取当前地图的缩放级别 + * + * 最低基础库: `1.4.0` */ + getScale(option?: GetScaleOption): void + /** [MapContext.getSkew(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.getSkew.html) + * + * 获取当前地图的倾斜角 + * + * 最低基础库: `2.8.0` */ + getSkew(option?: GetSkewOption): void + /** [MapContext.includePoints(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.includePoints.html) + * + * 缩放视野展示所有经纬度 + * + * 最低基础库: `1.2.0` */ + includePoints(option: IncludePointsOption): void + /** [MapContext.initMarkerCluster(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.initMarkerCluster.html) + * + * 初始化点聚合的配置,未调用时采用默认配置。 + * + * 最低基础库: `2.13.0` */ + initMarkerCluster(option: InitMarkerClusterOption): void + /** [MapContext.moveAlong(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.moveAlong.html) + * + * 沿指定路径移动 `marker`,用于轨迹回放等场景。动画完成时触发回调事件,若动画进行中,对同一 `marker` 再次调用 `moveAlong` 方法,前一次的动画将被打断。 + * + * 最低基础库: `2.13.0` */ + moveAlong(option: MoveAlongOption): void + /** [MapContext.moveToLocation(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.moveToLocation.html) + * + * 将地图中心移置当前定位点,此时需设置地图组件 show-location 为true。[2.8.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html) 起支持将地图中心移动到指定位置。 + * + * 最低基础库: `1.2.0` */ + moveToLocation(option?: MoveToLocationOption): void + /** [MapContext.on(string event, function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.on.html) +* +* 监听地图事件。 +* +* ### markerClusterCreate +* +* 缩放或拖动导致新的聚合簇产生时触发,仅返回新创建的聚合簇信息。 +* +* #### 返回参数 +* +* | 参数 | 类型 | 说明 | +* | --------- | ------ | --------- | +* | clusters | `Array<ClusterInfo>` | 聚合簇数据 | +* +* ### markerClusterClick +* +* 聚合簇的点击事件。 +* +* #### 返回参数 +* +* | 参数 | 类型 | 说明 | +* | --------- | ------------- | --------- | +* | cluster | ClusterInfo | 聚合簇 | +* +* +* #### ClusterInfo 结构 +* +* | 参数 | 类型 | 说明 | +* | ---------- | -------------------- | -------------------------- | +* | clusterId | Number | 聚合簇的 id | +* | center | LatLng | 聚合簇的坐标 | +* | markerIds | `Array<Number>` | 该聚合簇内的点标记数据数组 | +* +* **示例代码** +* +* +* +* ```js + MapContext.on('markerClusterCreate', (res) => {}) + MapContext.on('markerClusterClick', (res) => {}) +``` +* +* 最低基础库: `2.13.0` */ + on( + /** 事件名 + * + * 参数 event 可选值: + * - 'markerClusterCreate': ; + * - 'markerClusterClick': ; */ + event: 'markerClusterCreate' | 'markerClusterClick', + /** 事件的回调函数 */ + callback: (...args: any[]) => any + ): void + /** [MapContext.openMapApp(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.openMapApp.html) + * + * 拉起地图APP选择导航。 + * + * 最低基础库: `2.14.0` */ + openMapApp(option: OpenMapAppOption): void + /** [MapContext.removeCustomLayer(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.removeCustomLayer.html) + * + * 移除个性化图层。 + * + * 最低基础库: `2.12.0` */ + removeCustomLayer(option: RemoveCustomLayerOption): void + /** [MapContext.removeGroundOverlay(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.removeGroundOverlay.html) + * + * 移除自定义图片图层。 + * + * 最低基础库: `2.14.0` */ + removeGroundOverlay(option: RemoveGroundOverlayOption): void + /** [MapContext.removeMarkers(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.removeMarkers.html) + * + * 移除 marker。 + * + * 最低基础库: `2.13.0` */ + removeMarkers(option: RemoveMarkersOption): void + /** [MapContext.setCenterOffset(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.setCenterOffset.html) + * + * 设置地图中心点偏移,向后向下为增长,屏幕比例范围(0.25~0.75),默认偏移为[0.5, 0.5] + * + * 最低基础库: `2.10.0` */ + setCenterOffset(option: SetCenterOffsetOption): void + /** [MapContext.toScreenLocation(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.toScreenLocation.html) + * + * 获取经纬度对应的屏幕坐标,坐标原点为地图左上角。 + * + * 最低基础库: `2.14.0` */ + toScreenLocation(option: ToScreenLocationOption): void + /** [MapContext.translateMarker(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.translateMarker.html) + * + * 平移marker,带动画。 + * + * 最低基础库: `1.2.0` */ + translateMarker(option: TranslateMarkerOption): void + /** [MapContext.updateGroundOverlay(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.updateGroundOverlay.html) + * + * 更新自定义图片图层。 + * + * 最低基础库: `2.14.0` */ + updateGroundOverlay(option: UpdateGroundOverlayOption): void + } + interface MediaAudioPlayer { + /** [Promise MediaAudioPlayer.addAudioSource([VideoDecoder](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-decoder/VideoDecoder.html) source)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/MediaAudioPlayer.addAudioSource.html) + * + * 添加音频源 */ + addAudioSource( + /** [VideoDecoder](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-decoder/VideoDecoder.html) + * + * 视频解码器实例。作为音频源添加到音频播放器中 */ + source: VideoDecoder + ): Promise + /** [Promise MediaAudioPlayer.destroy()](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/MediaAudioPlayer.destroy.html) + * + * 销毁播放器 */ + destroy(): Promise + /** [Promise MediaAudioPlayer.removeAudioSource([VideoDecoder](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-decoder/VideoDecoder.html) source)](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/MediaAudioPlayer.removeAudioSource.html) + * + * 移除音频源 */ + removeAudioSource( + /** [VideoDecoder](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-decoder/VideoDecoder.html) + * + * 视频解码器实例 */ + source: VideoDecoder + ): Promise + /** [Promise MediaAudioPlayer.start()](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/MediaAudioPlayer.start.html) + * + * 启动播放器 */ + start(): Promise + /** [Promise MediaAudioPlayer.stop()](https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/MediaAudioPlayer.stop.html) + * + * 停止播放器 */ + stop(): Promise + } + interface MediaContainer { + /** [MediaContainer.addTrack([MediaTrack](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/MediaTrack.html) track)](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/MediaContainer.addTrack.html) + * + * 将音频或视频轨道添加到容器 + * + * 最低基础库: `2.9.0` */ + addTrack( + /** [MediaTrack](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/MediaTrack.html) + * + * 要添加的音频或视频轨道 */ + track: MediaTrack + ): void + /** [MediaContainer.destroy()](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/MediaContainer.destroy.html) + * + * 将容器销毁,释放资源 + * + * 最低基础库: `2.9.0` */ + destroy(): void + /** [MediaContainer.export()](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/MediaContainer.export.html) + * + * 将容器内的轨道合并并导出视频文件 + * + * 最低基础库: `2.9.0` */ + export(): void + /** [MediaContainer.extractDataSource(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/MediaContainer.extractDataSource.html) + * + * 将传入的视频源分离轨道。不会自动将轨道添加到待合成的容器里。 + * + * 最低基础库: `2.9.0` */ + extractDataSource(option: ExtractDataSourceOption): void + /** [MediaContainer.removeTrack([MediaTrack](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/MediaTrack.html) track)](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/MediaContainer.removeTrack.html) + * + * 将音频或视频轨道从容器中移除 + * + * 最低基础库: `2.9.0` */ + removeTrack( + /** [MediaTrack](https://developers.weixin.qq.com/miniprogram/dev/api/media/video-processing/MediaTrack.html) + * + * 要移除的音频或视频轨道 */ + track: MediaTrack + ): void + } + interface MediaQueryObserver { + /** [MediaQueryObserver.disconnect()](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/MediaQueryObserver.disconnect.html) + * + * 停止监听。回调函数将不再触发 */ + disconnect(): void + /** [MediaQueryObserver.observe(Object descriptor, function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/MediaQueryObserver.observe.html) + * + * 开始监听页面 media query 变化情况 */ + observe( + /** media query 描述符 */ + descriptor: ObserveDescriptor, + /** 监听 media query 状态变化的回调函数 */ + callback: MediaQueryObserverObserveCallback + ): void + } + interface MediaRecorder { + /** [MediaRecorder.destroy()](https://developers.weixin.qq.com/miniprogram/dev/api/media/media-recorder/MediaRecorder.destroy.html) + * + * 销毁录制器 + * + * 最低基础库: `2.11.0` */ + destroy(): void + /** [MediaRecorder.off(string eventName, function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/media-recorder/MediaRecorder.off.html) + * + * 取消监听录制事件。当对应事件触发时,该回调函数不再执行。 + * + * 最低基础库: `2.11.0` */ + off( + /** 事件名 */ + eventName: string, + /** 事件触发时执行的回调函数 */ + callback: (...args: any[]) => any + ): void + /** [MediaRecorder.on(string eventName, function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/media-recorder/MediaRecorder.on.html) + * + * 注册监听录制事件的回调函数。当对应事件触发时,回调函数会被执行。 + * + * 最低基础库: `2.11.0` */ + on( + /** 事件名 + * + * 参数 eventName 可选值: + * - 'start': 录制开始事件。; + * - 'stop': 录制结束事件。返回 {tempFilePath, duration, fileSize}; */ + eventName: 'start' | 'stop', + /** 事件触发时执行的回调函数 */ + callback: (...args: any[]) => any + ): void + /** [MediaRecorder.pause()](https://developers.weixin.qq.com/miniprogram/dev/api/media/media-recorder/MediaRecorder.pause.html) + * + * 暂停录制 + * + * 最低基础库: `2.11.0` */ + pause(): void + /** [MediaRecorder.requestFrame(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/media/media-recorder/MediaRecorder.requestFrame.html) + * + * 请求下一帧录制,在 callback 里完成一帧渲染后开始录制当前帧 + * + * 最低基础库: `2.11.0` */ + requestFrame(callback: (...args: any[]) => any): void + /** [MediaRecorder.resume()](https://developers.weixin.qq.com/miniprogram/dev/api/media/media-recorder/MediaRecorder.resume.html) + * + * 恢复录制 + * + * 最低基础库: `2.11.0` */ + resume(): void + /** [MediaRecorder.start()](https://developers.weixin.qq.com/miniprogram/dev/api/media/media-recorder/MediaRecorder.start.html) + * + * 开始录制 + * + * 最低基础库: `2.11.0` */ + start(): void + /** [MediaRecorder.stop()](https://developers.weixin.qq.com/miniprogram/dev/api/media/media-recorder/MediaRecorder.stop.html) + * + * 结束录制 + * + * 最低基础库: `2.11.0` */ + stop(): void + } + interface MifareClassic { + /** [MifareClassic.close(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareClassic.close.html) + * + * 断开连接 + * + * 最低基础库: `2.11.2` */ + close(option?: NdefCloseOption): void + /** [MifareClassic.connect(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareClassic.connect.html) + * + * 连接 NFC 标签 + * + * 最低基础库: `2.11.2` */ + connect(option?: ConnectOption): void + /** [MifareClassic.getMaxTransceiveLength(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareClassic.getMaxTransceiveLength.html) + * + * 获取最大传输长度 + * + * 最低基础库: `2.11.2` */ + getMaxTransceiveLength(option?: GetMaxTransceiveLengthOption): void + /** [MifareClassic.isConnected(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareClassic.isConnected.html) + * + * 检查是否已连接 + * + * 最低基础库: `2.11.2` */ + isConnected(option?: IsConnectedOption): void + /** [MifareClassic.setTimeout(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareClassic.setTimeout.html) + * + * 设置超时时间 + * + * 最低基础库: `2.11.2` */ + setTimeout(option: SetTimeoutOption): void + /** [MifareClassic.transceive(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareClassic.transceive.html) + * + * 发送数据 + * + * 最低基础库: `2.11.2` */ + transceive(option: TransceiveOption): void + } + interface MifareUltralight { + /** [MifareUltralight.close(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareUltralight.close.html) + * + * 断开连接 + * + * 最低基础库: `2.11.2` */ + close(option?: NdefCloseOption): void + /** [MifareUltralight.connect(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareUltralight.connect.html) + * + * 连接 NFC 标签 + * + * 最低基础库: `2.11.2` */ + connect(option?: ConnectOption): void + /** [MifareUltralight.getMaxTransceiveLength(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareUltralight.getMaxTransceiveLength.html) + * + * 获取最大传输长度 + * + * 最低基础库: `2.11.2` */ + getMaxTransceiveLength(option?: GetMaxTransceiveLengthOption): void + /** [MifareUltralight.isConnected(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareUltralight.isConnected.html) + * + * 检查是否已连接 + * + * 最低基础库: `2.11.2` */ + isConnected(option?: IsConnectedOption): void + /** [MifareUltralight.setTimeout(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareUltralight.setTimeout.html) + * + * 设置超时时间 + * + * 最低基础库: `2.11.2` */ + setTimeout(option: SetTimeoutOption): void + /** [MifareUltralight.transceive(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareUltralight.transceive.html) + * + * 发送数据 + * + * 最低基础库: `2.11.2` */ + transceive(option: TransceiveOption): void + } + interface NFCAdapter { + /** [NFCAdapter.offDiscovered(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.offDiscovered.html) + * + * 取消监听 NFC Tag + * + * 最低基础库: `2.11.2` */ + offDiscovered( + /** 的回调函数 */ + callback?: OffDiscoveredCallback + ): void + /** [NFCAdapter.onDiscovered(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.onDiscovered.html) + * + * 监听 NFC Tag + * + * 最低基础库: `2.11.2` */ + onDiscovered( + /** 的回调函数 */ + callback: OnDiscoveredCallback + ): void + /** [NFCAdapter.startDiscovery(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.startDiscovery.html) + * + * + * + * 最低基础库: `2.11.2` */ + startDiscovery(option?: StartDiscoveryOption): void + /** [NFCAdapter.stopDiscovery(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.stopDiscovery.html) + * + * + * + * 最低基础库: `2.11.2` */ + stopDiscovery(option?: StopDiscoveryOption): void + /** [[IsoDep](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/IsoDep.html) NFCAdapter.getIsoDep()](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.getIsoDep.html) + * + * 获取IsoDep实例,实例支持ISO-DEP (ISO 14443-4)标准的读写 + * + * 最低基础库: `2.11.2` */ + getIsoDep(): IsoDep + /** [[MifareClassic](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareClassic.html) NFCAdapter.getMifareClassic()](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.getMifareClassic.html) + * + * 获取MifareClassic实例,实例支持MIFARE Classic标签的读写 + * + * 最低基础库: `2.11.2` */ + getMifareClassic(): MifareClassic + /** [[MifareUltralight](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareUltralight.html) NFCAdapter.getMifareUltralight()](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.getMifareUltralight.html) + * + * 获取MifareUltralight实例,实例支持MIFARE Ultralight标签的读写 + * + * 最低基础库: `2.11.2` */ + getMifareUltralight(): MifareUltralight + /** [[Ndef](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/Ndef.html) NFCAdapter.getNdef()](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.getNdef.html) + * + * 获取Ndef实例,实例支持对NDEF格式的NFC标签上的NDEF数据的读写 + * + * 最低基础库: `2.11.2` */ + getNdef(): Ndef + /** [[NfcA](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcA.html) NFCAdapter.getNfcA()](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.getNfcA.html) + * + * 获取NfcA实例,实例支持NFC-A (ISO 14443-3A)标准的读写 + * + * 最低基础库: `2.11.2` */ + getNfcA(): NfcA + /** [[NfcB](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcB.html) NFCAdapter.getNfcB()](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.getNfcB.html) + * + * 获取NfcB实例,实例支持NFC-B (ISO 14443-3B)标准的读写 + * + * 最低基础库: `2.11.2` */ + getNfcB(): NfcB + /** [[NfcF](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcF.html) NFCAdapter.getNfcF()](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.getNfcF.html) + * + * 获取NfcF实例,实例支持NFC-F (JIS 6319-4)标准的读写 + * + * 最低基础库: `2.11.2` */ + getNfcF(): NfcF + /** [[NfcV](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcV.html) NFCAdapter.getNfcV()](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NFCAdapter.getNfcV.html) + * + * 获取NfcV实例,实例支持NFC-V (ISO 15693)标准的读写 + * + * 最低基础库: `2.11.2` */ + getNfcV(): NfcV + } + interface NFCError { + /** 错误信息 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | 0 | ok | 正常 | + * | 13000 | | 当前设备不支持NFC | + * | 13001 | | 当前设备支持NFC,但系统NFC开关未开启 | + * | 13002 | | 当前设备支持NFC,但不支持HCE | + * | 13003 | | AID列表参数格式错误 | + * | 13004 | | 未设置微信为默认NFC支付应用 | + * | 13005 | | 返回的指令不合法 | + * | 13006 | | 注册AID失败 | */ errMsg: string + /** 错误码 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | 0 | ok | 正常 | + * | 13000 | | 当前设备不支持NFC | + * | 13001 | | 当前设备支持NFC,但系统NFC开关未开启 | + * | 13002 | | 当前设备支持NFC,但不支持HCE | + * | 13003 | | AID列表参数格式错误 | + * | 13004 | | 未设置微信为默认NFC支付应用 | + * | 13005 | | 返回的指令不合法 | + * | 13006 | | 注册AID失败 | */ errCode: number + } + interface Ndef { + /** [Ndef.close(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/Ndef.close.html) + * + * 断开连接 + * + * 最低基础库: `2.11.2` */ + close(option?: NdefCloseOption): void + /** [Ndef.connect(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/Ndef.connect.html) + * + * 连接 NFC 标签 + * + * 最低基础库: `2.11.2` */ + connect(option?: ConnectOption): void + /** [Ndef.isConnected(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/Ndef.isConnected.html) + * + * 检查是否已连接 + * + * 最低基础库: `2.11.2` */ + isConnected(option?: IsConnectedOption): void + /** [Ndef.offNdefMessage(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/Ndef.offNdefMessage.html) + * + * 取消监听 Ndef 消息 + * + * 最低基础库: `2.11.2` */ + offNdefMessage(callback: (...args: any[]) => any): void + /** [Ndef.onNdefMessage(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/Ndef.onNdefMessage.html) + * + * 监听 Ndef 消息 + * + * 最低基础库: `2.11.2` */ + onNdefMessage(callback: (...args: any[]) => any): void + /** [Ndef.setTimeout(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/Ndef.setTimeout.html) + * + * 设置超时时间 + * + * 最低基础库: `2.11.2` */ + setTimeout(option: SetTimeoutOption): void + /** [Ndef.writeNdefMessage(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/Ndef.writeNdefMessage.html) + * + * 重写 Ndef 标签内容 + * + * 最低基础库: `2.11.2` */ + writeNdefMessage(option: WriteNdefMessageOption): void + } + interface NfcA { + /** [NfcA.close(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcA.close.html) + * + * 断开连接 + * + * 最低基础库: `2.11.2` */ + close(option?: NdefCloseOption): void + /** [NfcA.connect(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcA.connect.html) + * + * 连接 NFC 标签 + * + * 最低基础库: `2.11.2` */ + connect(option?: ConnectOption): void + /** [NfcA.getAtqa(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcA.getAtqa.html) + * + * 获取ATQA信息 + * + * 最低基础库: `2.11.2` */ + getAtqa(option?: GetAtqaOption): void + /** [NfcA.getMaxTransceiveLength(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcA.getMaxTransceiveLength.html) + * + * 获取最大传输长度 + * + * 最低基础库: `2.11.2` */ + getMaxTransceiveLength(option?: GetMaxTransceiveLengthOption): void + /** [NfcA.getSak(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcA.getSak.html) + * + * 获取SAK信息 + * + * 最低基础库: `2.11.2` */ + getSak(option?: GetSakOption): void + /** [NfcA.isConnected(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcA.isConnected.html) + * + * 检查是否已连接 + * + * 最低基础库: `2.11.2` */ + isConnected(option?: IsConnectedOption): void + /** [NfcA.setTimeout(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcA.setTimeout.html) + * + * 设置超时时间 + * + * 最低基础库: `2.11.2` */ + setTimeout(option: SetTimeoutOption): void + /** [NfcA.transceive(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcA.transceive.html) + * + * 发送数据 + * + * 最低基础库: `2.11.2` */ + transceive(option: TransceiveOption): void + } + interface NfcB { + /** [NfcB.close(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcB.close.html) + * + * 断开连接 + * + * 最低基础库: `2.11.2` */ + close(option?: NdefCloseOption): void + /** [NfcB.connect(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcB.connect.html) + * + * 连接 NFC 标签 + * + * 最低基础库: `2.11.2` */ + connect(option?: ConnectOption): void + /** [NfcB.getMaxTransceiveLength(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcB.getMaxTransceiveLength.html) + * + * 获取最大传输长度 + * + * 最低基础库: `2.11.2` */ + getMaxTransceiveLength(option?: GetMaxTransceiveLengthOption): void + /** [NfcB.isConnected(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcB.isConnected.html) + * + * 检查是否已连接 + * + * 最低基础库: `2.11.2` */ + isConnected(option?: IsConnectedOption): void + /** [NfcB.setTimeout(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcB.setTimeout.html) + * + * 设置超时时间 + * + * 最低基础库: `2.11.2` */ + setTimeout(option: SetTimeoutOption): void + /** [NfcB.transceive(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcB.transceive.html) + * + * 发送数据 + * + * 最低基础库: `2.11.2` */ + transceive(option: TransceiveOption): void + } + interface NfcF { + /** [NfcF.close(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcF.close.html) + * + * 断开连接 + * + * 最低基础库: `2.11.2` */ + close(option?: NdefCloseOption): void + /** [NfcF.connect(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcF.connect.html) + * + * 连接 NFC 标签 + * + * 最低基础库: `2.11.2` */ + connect(option?: ConnectOption): void + /** [NfcF.getMaxTransceiveLength(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcF.getMaxTransceiveLength.html) + * + * 获取最大传输长度 + * + * 最低基础库: `2.11.2` */ + getMaxTransceiveLength(option?: GetMaxTransceiveLengthOption): void + /** [NfcF.isConnected(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcF.isConnected.html) + * + * 检查是否已连接 + * + * 最低基础库: `2.11.2` */ + isConnected(option?: IsConnectedOption): void + /** [NfcF.setTimeout(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcF.setTimeout.html) + * + * 设置超时时间 + * + * 最低基础库: `2.11.2` */ + setTimeout(option: SetTimeoutOption): void + /** [NfcF.transceive(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcF.transceive.html) + * + * 发送数据 + * + * 最低基础库: `2.11.2` */ + transceive(option: TransceiveOption): void + } + interface NfcV { + /** [NfcV.close(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcV.close.html) + * + * 断开连接 + * + * 最低基础库: `2.11.2` */ + close(option?: NdefCloseOption): void + /** [NfcV.connect(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcV.connect.html) + * + * 连接 NFC 标签 + * + * 最低基础库: `2.11.2` */ + connect(option?: ConnectOption): void + /** [NfcV.getMaxTransceiveLength(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcV.getMaxTransceiveLength.html) + * + * 获取最大传输长度 + * + * 最低基础库: `2.11.2` */ + getMaxTransceiveLength(option?: GetMaxTransceiveLengthOption): void + /** [NfcV.isConnected(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcV.isConnected.html) + * + * 检查是否已连接 + * + * 最低基础库: `2.11.2` */ + isConnected(option?: IsConnectedOption): void + /** [NfcV.setTimeout(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcV.setTimeout.html) + * + * 设置超时时间 + * + * 最低基础库: `2.11.2` */ + setTimeout(option: SetTimeoutOption): void + /** [NfcV.transceive(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/NfcV.transceive.html) + * + * 发送数据 + * + * 最低基础库: `2.11.2` */ + transceive(option: TransceiveOption): void + } + interface Nfcrwerror { + /** 错误信息 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | 13000 | 设备不支持NFC | | + * | 13001 | 系统NFC开关未打开 | | + * | 13010 | 未知错误 | | + * | 13019 | user is not authorized | 用户未授权 | + * | 13011 | invalid parameter | 参数无效 | + * | 13012 | parse NdefMessage failed | 将参数解析为NdefMessage失败 | + * | 13021 | NFC discovery already started | 已经开始NFC扫描 | + * | 13018 | NFC discovery has not started | 尝试在未开始NFC扫描时停止NFC扫描 | + * | 13022 | Tech already connected | 标签已经连接 | + * | 13023 | Tech has not connected | 尝试在未连接标签时断开连接 | + * | 13013 | NFC tag has not been discovered | 未扫描到NFC标签 | + * | 13014 | invalid tech | 无效的标签技术 | + * | 13015 | unavailable tech | 从标签上获取对应技术失败 | + * | 13024 | function not support | 当前标签技术不支持该功能 | + * | 13017 | system internal error | 相关读写操作失败 | + * | 13016 | connect fail | 连接失败 | */ errMsg: string + /** 错误码 + * + * | 错误码 | 错误信息 | 说明 | + * | - | - | - | + * | 13000 | 设备不支持NFC | | + * | 13001 | 系统NFC开关未打开 | | + * | 13010 | 未知错误 | | + * | 13019 | user is not authorized | 用户未授权 | + * | 13011 | invalid parameter | 参数无效 | + * | 13012 | parse NdefMessage failed | 将参数解析为NdefMessage失败 | + * | 13021 | NFC discovery already started | 已经开始NFC扫描 | + * | 13018 | NFC discovery has not started | 尝试在未开始NFC扫描时停止NFC扫描 | + * | 13022 | Tech already connected | 标签已经连接 | + * | 13023 | Tech has not connected | 尝试在未连接标签时断开连接 | + * | 13013 | NFC tag has not been discovered | 未扫描到NFC标签 | + * | 13014 | invalid tech | 无效的标签技术 | + * | 13015 | unavailable tech | 从标签上获取对应技术失败 | + * | 13024 | function not support | 当前标签技术不支持该功能 | + * | 13017 | system internal error | 相关读写操作失败 | + * | 13016 | connect fail | 连接失败 | */ errCode: number + } + interface NodesRef { + /** [[SelectorQuery](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.html) NodesRef.boundingClientRect(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/NodesRef.boundingClientRect.html) +* +* 添加节点的布局位置的查询请求。相对于显示区域,以像素为单位。其功能类似于 DOM 的 `getBoundingClientRect`。返回 `NodesRef` 对应的 `SelectorQuery`。 +* +* **示例代码** +* +* +* ```js +Page({ + getRect () { + wx.createSelectorQuery().select('#the-id').boundingClientRect(function(rect){ + rect.id // 节点的ID + rect.dataset // 节点的dataset + rect.left // 节点的左边界坐标 + rect.right // 节点的右边界坐标 + rect.top // 节点的上边界坐标 + rect.bottom // 节点的下边界坐标 + rect.width // 节点的宽度 + rect.height // 节点的高度 + }).exec() + }, + getAllRects () { + wx.createSelectorQuery().selectAll('.a-class').boundingClientRect(function(rects){ + rects.forEach(function(rect){ + rect.id // 节点的ID + rect.dataset // 节点的dataset + rect.left // 节点的左边界坐标 + rect.right // 节点的右边界坐标 + rect.top // 节点的上边界坐标 + rect.bottom // 节点的下边界坐标 + rect.width // 节点的宽度 + rect.height // 节点的高度 + }) + }).exec() + } +}) +``` */ + boundingClientRect( + /** 回调函数,在执行 `SelectorQuery.exec` 方法后,节点信息会在 `callback` 中返回。 */ + callback?: BoundingClientRectCallback + ): SelectorQuery + /** [[SelectorQuery](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.html) NodesRef.context(function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/NodesRef.context.html) +* +* 添加节点的 Context 对象查询请求。目前支持 [VideoContext](https://developers.weixin.qq.com/miniprogram/dev/api/media/video/VideoContext.html)、[CanvasContext](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html)、[LivePlayerContext](https://developers.weixin.qq.com/miniprogram/dev/api/media/live/LivePlayerContext.html)、[EditorContext](https://developers.weixin.qq.com/miniprogram/dev/api/media/editor/EditorContext.html)和 [MapContext](https://developers.weixin.qq.com/miniprogram/dev/api/media/map/MapContext.html) 的获取。 +* +* **示例代码** +* +* +* ```js +Page({ + getContext () { + wx.createSelectorQuery().select('.the-video-class').context(function(res){ + console.log(res.context) // 节点对应的 Context 对象。如:选中的节点是