如何分析AWR--zz

本文转自 http://www.os2ora.com

如何分析AWR (0)

Kaya 发表于 os2ora.com

Automatic Workload Repository是10g引入的一个重要组件。在里面存贮着近期一段时间内,默认是7天,数据库活动状态的详细信息。

AWR报告是对AWR视图进行查询而得到的一份自动生成的报告。可以通过下面的脚本手工得到一份AWR报告。

exec dbms_workload_repository.create_snapshot;
... running the specified workload
exec dbms_workload_repository.create_snapshot;
@?/rdbms/admin/awrrpt

通过AWR和AWR报告,DBA可以容易地获知最近数据库的活动状态,数据库的各种性能指标的变化趋势曲线,最近数据库可能存在的异常,分析数据库可能存在的性能瓶颈从而对数据库进行优化。

AWR报告所有的数据来源于AWR视图,即以DBA_HIST_开头的所有系统表,Database Reference有对所有这些系统表的描述,这应该是Oracle官方对AWR报告的官方注释了。

而对于如何有效地去分析AWR报告,这可能更需要DBA经验的日积月累。

AWR的前身是Statspack,Statspack在10g和11g中也有提供,同时和AWR一起做了同步更新,而且Statspack是公开源代码的,因此,关于Statspack的资料,还有Statspack的源代码,都是理解AWR的一个有用的辅助。

本系列文章准备着重对AWR中的一些要点进行剖析,欢迎一起讨论AWR相关的问题。

如何分析AWR (1)

<!-- Post Body Copy -->

Kaya 发表于 os2ora.com

如果关注数据库的性能,那么当拿到一份AWR报告的时候,最想知道的第一件事情可能就是系统资源的利用情况了,而首当其冲的,就是CPU。

而细分起来,CPU可能指的是

  1. OS级的User%, Sys%, Idle%
  2. DB所占OS CPU资源的Busy%
  3. DB CPU又可以分为前台所消耗的CPU和后台所消耗的CPU

如果数据库的版本是11g,那么很幸运的,这些信息在AWR报告中一目了然:

image

OS级的%User为75.4,%Sys为2.8,%Idle为21.2,所以%Busy应该是78.8。

DB占了OS CPU资源的69.1,%Busy CPU则可以通过上面的数据得到:
%Busy CPU = %Total CPU/(%Busy) * 100 = 69.1/78.8 * 100 = 87.69,和报告的87.7相吻合。

如果是10g呢,则需要手工对Report里的一些数据进行计算了。

Host CPU的结果来源于DBA_HIST_OSSTAT,AWR 报告里已经帮忙整出了这段时间内的绝对数据(这里的时间单位是centi second,也就是1/100秒)

image

这里,

%User = USER_TIME/(BUSY_TIME+IDLE_TIME)*100 = 146355/(152946+41230)*100 = 75.37

%Sys  = SYS_TIME/(BUSY_TIME+IDLE_TIME)*100

%Idle = IDLE_TIME/(BUSY_TIME+IDLE_TIME)*100

值得注意的,这里已经隐含着这个AWR报告所捕捉的两个snapshot之间的时间长短了。有下面的公式

BUSY_TIME + IDLE_TIME = ELAPSED_TIME * CPU_COUNT

正确的理解这个公式可以对系统CPU资源的使用及其度量的方式有更深一步的理解。

因此ELAPSED_TIME = (152946+41230)/8/100 =  242.72 seconds

当然,这正确无误。

至于DB对CPU的利用情况,这就涉及到10g新引入的一个关于时间统计的视图了, v$sys_time_model,简单而言,Oracle采用了一个统一的时间模型对一些重要的时间指标进行了记录,具体而言,这些指标包括:

1) background elapsed time
    2) background cpu time
          3) RMAN cpu time (backup/restore)
1) DB time
    2) DB CPU
    2) connection management call elapsed time
    2) sequence load elapsed time
    2) sql execute elapsed time
    2) parse time elapsed
          3) hard parse elapsed time
                4) hard parse (sharing criteria) elapsed time
                    5) hard parse (bind mismatch) elapsed time
          3) failed parse elapsed time
                4) failed parse (out of shared memory) elapsed time
    2) PL/SQL execution elapsed time
    2) inbound PL/SQL rpc elapsed time
    2) PL/SQL compilation elapsed time
    2) Java execution elapsed time
    2) repeated bind elapsed time

我们这里关注的只有和CPU相关的两个: background cpu time 和 DB CPU。

这两个值在AWR里面也有记录:

image

Total DB CPU = DB CPU + background cpu time = 1305.89 + 35.91 = 1341.8 seconds

再除以总的 BUSY_TIME + IDLE_TIME

% Total CPU = 1341.8/1941.76 = 69.1%,这刚好与上面Report的值相吻合。

其实,在Load Profile部分,我们也可以看出DB对系统CPU的资源利用情况。

image

用DB CPU per Second除以CPU Count就可以得到DB在前台所消耗的CPU%了。

这里 5.3/8 = 66.25 %

比69.1%稍小,说明DB在后台也消耗了大约3%的CPU,这是不是一个最简单的方法了呢?

<template> <view> <view class="map" id="mapDiv" style="width: 100%;height: 67vh;z-index: 0;position: relative;" :prop="dataOption" :change:prop="Trenderjs.initTMap" :change:renderTrigger="Trenderjs.handleMessage" :renderTrigger="triggerCounter"> </view> </view> </template> <script> export default { data() { return { triggerCounter: 0, isProcessing: false, message: null }; }, props: { dataOption: { type: String, default: '' }, }, methods: { renderjsData() { if (this.isProcessing) return; this.isProcessing = true; this.triggerCounter++; }, someMethodInVue(newVal) { this.$emit('childEvent', newVal); this.message = newVal; this.isProcessing = false; } } }; </script> <script module="Trenderjs" lang="renderjs"> var control; export default { data() { return { tk: '27657afc3318f8f2166fceba83427734', options: { lng: '104.397894', lat: '31.126855', }, Tmap: null, gisDataObj: {}, gisData: {}, geocode: null, gisPointLngLat: {}, // 定位优化相关变量 positionHistory: [], // 位置历史记录 maxHistorySize: 10, // 最大历史记录数 realTimeMarker: null, // 实时定位标记 realTimeLabel: null, // 实时定位标签 // 动画相关变量 animationTarget: null, // 动画目标位置 animationStep: 0, // 动画步数 animationTimer: null, // 动画计时器 animationDuration: 500, // 动画时长(ms) hasFitViewOnFirstLocation: false, // 方向相关变量 currentHeading: 0, // 当前设备方向(度数) compassWatchId: null, // 指南针监听ID isCompassAvailable: false, // 是否支持指南针 lastHeadingUpdate: 0, // 上次方向更新时间 headingFilterFactor: 0.1, // 方向滤波系数 // 修改 baseIcon 为更明显的箭头图标 baseIcon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiIgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIj48Y2lyY2xlIGN4PSIxNiIgY3k9IjE2IiByPSIxMCIgZmlsbD0iIzNEODVGRiIgZmlsbC1vcGFjaXR5PSIwLjgiLz48cGF0aCBkPSJNMTYgMkwyOCAyOEg0eiIgZmlsbD0iIzNEODVGRiIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjIiLz48L3N2Zz4=', iconCache: {}, // 缓存不同角度的图标 lastRenderedHeading: null // 上次渲染的角度 } }, mounted() { if (!window.T) { const script = document.createElement('script') script.src = 'http://api.tianditu.gov.cn/api?v=4.0&tk=' + this.tk document.head.appendChild(script) script.onload = this.initChartsRender.bind() } else { this.initMap(); } this.initCompass(); }, methods: { // 初始化地图 initMap() { const { lng, lat } = this.options; this.Tmap = new T.Map('mapDiv'); this.Tmap.centerAndZoom(new T.LngLat(lng, lat), 15); this.Tmap.setStyle("indigo"); // this.Tmap.disableDoubleClickZoom(); // this.Tmap.disableScrollWheelZoom(); // this.Tmap.disableInertia(); // this.Tmap.disableKeyboard(); this.geocode = new T.Geocoder(); // control = new T.Control.Zoom(); // this.Tmap.addControl(control); // control.setPosition(T_ANCHOR_BOTTOM_RIGHT); }, // 若没有加载地图方法 initChartsRender() { this.initMap(); }, // 初始化地图数据 initTMap(newValue, oldValue) { let that = this; // 清除旧的定位监听 that._watchId && plus.geolocation.clearWatch(that._watchId); // 启动新的定位监听 that._watchId = plus.geolocation.watchPosition( position => that._handlePosition(position), err => console.log('err', err), { provider: 'system', enableHighAccuracy: true, maximumAge: 0, timeout: 10000 } ); // 处理传入的地图数据 if (newValue) { setTimeout(function() { try { that.gisDataObj = JSON.parse(newValue); if (that.gisDataObj.hasOwnProperty('data') && that.gisDataObj.data) { that.gisData = JSON.parse(that.gisDataObj.data); if (that.gisData.hasOwnProperty('data') && that.gisData.data) { let gisPoint = JSON.parse(that.gisData.data); that.gisPointLngLat = gisPoint; // 设置中心点 let centerLng, centerLat; if (that.gisData.type == 'marker') { centerLng = gisPoint.lng; centerLat = gisPoint.lat; } else if (that.gisData.type == 'polyline' || that.gisData.type == 'polygon') { centerLng = gisPoint[0].lng; centerLat = gisPoint[0].lat; } else { centerLng = '104.397894'; centerLat = '31.126855'; } that.Tmap.centerAndZoom(new T.LngLat(centerLng, centerLat), 15); // 绘制地图元素 that._drawMapElements(); } } } catch (e) { console.error('数据解析错误:', e); } }, 1000); } }, // 处理定位点 - 核心优化 _handlePosition(position) { const coords = position.coords; // console.log("position: " + JSON.stringify(position)) const now = Date.now(); const newPoint = { lng: coords.longitude, lat: coords.latitude, accuracy: coords.accuracy, timestamp: now }; // 添加到历史记录 this.positionHistory.push(newPoint); if (this.positionHistory.length > this.maxHistorySize) { this.positionHistory.shift(); } // 使用卡尔曼滤波平滑位置 const smoothedPoint = this._kalmanFilter(newPoint); // 启动平滑动画 this._startAnimation(smoothedPoint); // 只在第一次定位时适配视野 if (!this.hasFitViewOnFirstLocation) { this._fitAllPoints(); this.hasFitViewOnFirstLocation = true; } }, // 卡尔曼滤波算法 _kalmanFilter(newPoint) { // 初始化状态 if (!this.kalmanState) { this.kalmanState = { x: newPoint.lng, y: newPoint.lat, vx: 0, // x方向速度 vy: 0, // y方向速度 P: [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ], // 协方差矩阵 lastTime: newPoint.timestamp }; return newPoint; } // 时间差(秒) const dt = (newPoint.timestamp - this.kalmanState.lastTime) / 1000; this.kalmanState.lastTime = newPoint.timestamp; // 状态转移矩阵 const F = [ [1, 0, dt, 0], [0, 1, 0, dt], [0, 0, 1, 0], [0, 0, 0, 1] ]; // 过程噪声协方差矩阵 const Q = [ [Math.pow(dt, 4) / 4, 0, Math.pow(dt, 3) / 2, 0], [0, Math.pow(dt, 4) / 4, 0, Math.pow(dt, 3) / 2], [Math.pow(dt, 3) / 2, 0, Math.pow(dt, 2), 0], [0, Math.pow(dt, 3) / 2, 0, Math.pow(dt, 2)] ].map(row => row.map(val => val * 0.1)); // 噪声系数 // 预测状态 const x_pred = [ this.kalmanState.x + this.kalmanState.vx * dt, this.kalmanState.y + this.kalmanState.vy * dt, this.kalmanState.vx, this.kalmanState.vy ]; // 预测协方差 let P_pred = this._matrixMultiply(F, this.kalmanState.P); P_pred = this._matrixMultiply(P_pred, this._matrixTranspose(F)); P_pred = this._matrixAdd(P_pred, Q); // 测量矩阵 const H = [ [1, 0, 0, 0], [0, 1, 0, 0] ]; // 测量噪声协方差 const R = [ [Math.pow(newPoint.accuracy / 1000000, 2), 0], [0, Math.pow(newPoint.accuracy / 1000000, 2)] ]; // 卡尔曼增益 const S = this._matrixAdd( this._matrixMultiply(H, this._matrixMultiply(P_pred, this._matrixTranspose(H))), R ); const K = this._matrixMultiply( this._matrixMultiply(P_pred, this._matrixTranspose(H)), this._matrixInverse2x2(S) ); // 更新状态 const z = [newPoint.lng, newPoint.lat]; const y = [ z[0] - (H[0][0] * x_pred[0] + H[0][1] * x_pred[1] + H[0][2] * x_pred[2] + H[0][3] * x_pred[3]), z[1] - (H[1][0] * x_pred[0] + H[1][1] * x_pred[1] + H[1][2] * x_pred[2] + H[1][3] * x_pred[3]) ]; const x_upd = [ x_pred[0] + K[0][0] * y[0] + K[0][1] * y[1], x_pred[1] + K[1][0] * y[0] + K[1][1] * y[1], x_pred[2] + K[2][0] * y[0] + K[2][1] * y[1], x_pred[3] + K[3][0] * y[0] + K[3][1] * y[1] ]; // 更新协方差 const I = [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ]; const KH = this._matrixMultiply(K, H); const I_KH = this._matrixSubtract(I, KH); const P_upd = this._matrixMultiply(I_KH, P_pred); // 更新状态 this.kalmanState = { x: x_upd[0], y: x_upd[1], vx: x_upd[2], vy: x_upd[3], P: P_upd, lastTime: newPoint.timestamp }; return { lng: x_upd[0], lat: x_upd[1], accuracy: newPoint.accuracy }; }, // 矩阵乘法 _matrixMultiply(A, B) { const result = []; for (let i = 0; i < A.length; i++) { result[i] = []; for (let j = 0; j < B[0].length; j++) { let sum = 0; for (let k = 0; k < A[0].length; k++) { sum += A[i][k] * B[k][j]; } result[i][j] = sum; } } return result; }, // 矩阵转置 _matrixTranspose(A) { return A[0].map((_, colIndex) => A.map(row => row[colIndex])); }, // 矩阵加法 _matrixAdd(A, B) { return A.map((row, i) => row.map((val, j) => val + B[i][j])); }, // 矩阵减法 _matrixSubtract(A, B) { return A.map((row, i) => row.map((val, j) => val - B[i][j])); }, // 2x2矩阵求逆 _matrixInverse2x2(A) { const det = A[0][0] * A[1][1] - A[0][1] * A[1][0]; return [ [A[1][1] / det, -A[0][1] / det], [-A[1][0] / det, A[0][0] / det] ]; }, // 开始平滑动画 _startAnimation(targetPoint) { // 取消之前的动画 if (this.animationTimer) { clearInterval(this.animationTimer); this.animationTimer = null; } // 设置目标位置 this.animationTarget = new T.LngLat(targetPoint.lng, targetPoint.lat); // 如果没有当前位置,直接设置 if (!this.currentPosition) { this.currentPosition = this.animationTarget; this._updateRealTimeMarker(targetPoint); return; } // 计算起点和终点 const startLng = this.currentPosition.getLng(); const startLat = this.currentPosition.getLat(); const endLng = this.animationTarget.getLng(); const endLat = this.animationTarget.getLat(); // 动画参数 const steps = Math.max(5, Math.min(20, this.animationDuration / 50)); const stepSize = 1 / steps; let progress = 0; // 启动动画 this.animationTimer = setInterval(() => { progress += stepSize; if (progress >= 1) { clearInterval(this.animationTimer); this.animationTimer = null; this.currentPosition = this.animationTarget; this._updateRealTimeMarker(targetPoint); // 定位点动画结束后,适配视野 // this._fitAllPoints(); return; } // 使用缓动函数使动画更平滑 const easedProgress = this._easeInOutCubic(progress); // 计算中间点 const currentLng = startLng + (endLng - startLng) * easedProgress; const currentLat = startLat + (endLat - startLat) * easedProgress; // 更新位置 this.currentPosition = new T.LngLat(currentLng, currentLat); this._updateRealTimeMarker({ lng: currentLng, lat: currentLat, accuracy: targetPoint.accuracy }); }, this.animationDuration / steps); }, // 缓动函数(三次缓动) _easeInOutCubic(t) { return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; }, // 更新实时定位标记 // _updateRealTimeMarker(point) { // const mapPoint = new T.LngLat(point.lng, point.lat); // if (!this.realTimeMarker) { // // 创建标记 // this.realTimeMarker = new T.Marker(mapPoint); // this.Tmap.addOverLay(this.realTimeMarker); // // 创建文本标签 // this.realTimeLabel = new T.Label({ // // text: `实时定位:${point.lng.toFixed(6)},${point.lat.toFixed(6)}`, // text: '实时定位', // position: mapPoint, // offset: new T.Point(-40, 10) // }); // this.Tmap.addOverLay(this.realTimeLabel); // } else { // // 更新位置 // this.realTimeMarker.setLngLat(mapPoint); // this.realTimeLabel.setLngLat(mapPoint); // // this.realTimeLabel.setLabel(`实时定位:${point.lng.toFixed(6)},${point.lat.toFixed(6)}`); // } // // 平滑移动地图中心 // // this.Tmap.panTo(mapPoint); // }, _fitAllPoints() { let points = []; // 点/线/面 if (this.gisData && this.gisPointLngLat) { if (this.gisData.type === 'marker') { points.push(new T.LngLat(this.gisPointLngLat.lng, this.gisPointLngLat.lat)); } else if (this.gisData.type === 'polyline' || this.gisData.type === 'polygon') { points = points.concat( this.gisPointLngLat.map(p => new T.LngLat(p.lng, p.lat)) ); } } // 当前定位点 if (this.currentPosition) { points.push(this.currentPosition); } // 过滤无效点 points = points.filter(p => p && typeof p.getLng === 'function' && typeof p.getLat === 'function'); if (points.length > 0) { this.Tmap.setViewport(points); } }, // 绘制地图元素(折线、多边形等) _drawMapElements() { if (!this.gisData || !this.gisPointLngLat) return; switch (this.gisData.type) { case 'marker': this._drawMarker(); break; case 'polyline': this._drawPolyline(); break; case 'polygon': this._drawPolygon(); break; } // 新增:绘制完后适配视野 this._fitAllPoints(); }, // 绘制标记点 _drawMarker() { const point = new T.LngLat(this.gisPointLngLat.lng, this.gisPointLngLat.lat); const marker = new T.Marker(point); this.Tmap.addOverLay(marker); const label = new T.Label({ text: this.gisData.name, position: point, offset: new T.Point(-40, 10) }); this.Tmap.addOverLay(label); }, // 绘制折线 _drawPolyline() { const points = this.gisPointLngLat.map(p => new T.LngLat(p.lng, p.lat) ); const polyline = new T.Polyline(points, { color: this.gisDataObj.color || '#FF0000', weight: 6, opacity: 0.7 }); this.Tmap.addOverLay(polyline); // 添加终点标签 const endPoint = points[points.length - 1]; const label = new T.Label({ text: this.gisData.name, position: endPoint, offset: new T.Point(-40, 10) }); this.Tmap.addOverLay(label); }, // 绘制多边形 _drawPolygon() { const points = this.gisPointLngLat.map(p => new T.LngLat(p.lng, p.lat) ); const polygon = new T.Polygon(points, { color: this.gisDataObj.color || '#1E90FF', weight: 3, fillColor: this.gisDataObj.color || '#1E90FF', fillOpacity: 0.3 }); this.Tmap.addOverLay(polygon); // 添加中心点标签 const center = this._calculateCenter(points); const label = new T.Label({ text: this.gisData.name, position: center, offset: new T.Point(-40, 10) }); this.Tmap.addOverLay(label); }, // 计算多边形中心点 _calculateCenter(points) { const sum = points.reduce((acc, point) => { acc.lng += point.getLng(); acc.lat += point.getLat(); return acc; }, { lng: 0, lat: 0 }); return new T.LngLat( sum.lng / points.length, sum.lat / points.length ); }, // RenderJS事件处理器 handleMessage(newVal, oldVal) { if (newVal !== oldVal && this.gisPointLngLat) { const geolocation = new T.Geolocation(); geolocation.getCurrentPosition(e => { const distance = this.getDistance( e.lnglat.lat, e.lnglat.lng, this.gisPointLngLat.lat, this.gisPointLngLat.lng ); this.$ownerInstance.callMethod('someMethodInVue', { lng: e.lnglat.lng, lat: e.lnglat.lat, distance: distance * 1000 }); }); } }, // 计算两点间距离(Haversine公式) getDistance(lat1, lng1, lat2, lng2) { const radLat1 = (lat1 * Math.PI) / 180.0; const radLat2 = (lat2 * Math.PI) / 180.0; const a = radLat1 - radLat2; const b = (lng1 * Math.PI) / 180.0 - (lng2 * Math.PI) / 180.0; const s = 2 * Math.asin( Math.sqrt( Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2) ) ); return s * 6378.137; // 返回公里数 }, // 初始化指南针 initCompass() { if (plus.orientation && plus.orientation.watchOrientation) { this.isCompassAvailable = true; this.compassWatchId = plus.orientation.watchOrientation( (orientation) => this._handleCompass(orientation), (err) => console.error('指南针错误:', err) ); } else { console.warn('设备不支持指南针功能'); this.isCompassAvailable = false; } }, // 处理指南针数据 _handleCompass(orientation) { const now = Date.now(); // 限制更新频率(100ms/次) if (now - this.lastHeadingUpdate < 100) return; this.lastHeadingUpdate = now; let heading = orientation.magneticHeading; // 平滑处理 if (this.currentHeading === undefined) { this.currentHeading = heading; } else { let diff = heading - this.currentHeading; if (diff > 180) diff -= 360; else if (diff < -180) diff += 360; this.currentHeading += diff * this.headingFilterFactor; if (this.currentHeading >= 360) this.currentHeading -= 360; else if (this.currentHeading < 0) this.currentHeading += 360; } // 更新标记方向 this._updateMarkerDirection(); }, // 更新标记方向 _updateMarkerDirection() { if (!this.realTimeMarker || this.currentHeading === undefined) return; // 检查是否需要更新 if (this.lastRenderedHeading === this.currentHeading) return; // 尝试从缓存获取 if (this.iconCache[this.currentHeading]) { this._setMarkerIcon(this.iconCache[this.currentHeading]); this.lastRenderedHeading = this.currentHeading; return; } // 动态生成旋转图标 this._generateRotatedIcon(this.currentHeading) .then(rotatedIcon => { this.iconCache[this.currentHeading] = rotatedIcon; this._setMarkerIcon(rotatedIcon); this.lastRenderedHeading = this.currentHeading; }); }, // 生成旋转后的图标 _generateRotatedIcon(heading) { return new Promise((resolve) => { const size = 30; const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); const img = new Image(); img.src = this.baseIcon; img.onload = () => { // 清除画布 ctx.clearRect(0, 0, size, size); // 移动到中心点 ctx.translate(size / 2, size / 2); // 旋转画布 ctx.rotate((heading * Math.PI) / 180); // 绘制图像(注意调整位置) ctx.drawImage(img, -size / 2, -size / 2, size, size); // 恢复画布状态 ctx.setTransform(1, 0, 0, 1, 0, 0); // 获取Base64数据 resolve(canvas.toDataURL('image/png')); }; }); }, // 设置标记图标 _setMarkerIcon(iconUrl) { if (!this.realTimeMarker) return; this.realTimeMarker.setIcon(new T.Icon({ iconUrl: iconUrl, iconSize: new T.Point(30, 30), className: 'direction-marker' })); }, // 在_updateRealTimeMarker中添加方向支持 _updateRealTimeMarker(point) { const mapPoint = new T.LngLat(point.lng, point.lat); if (!this.realTimeMarker) { // 使用基础图标创建标记 this.realTimeMarker = new T.Marker(mapPoint, { icon: new T.Icon({ iconUrl: this.baseIcon, iconSize: new T.Point(30, 30), className: 'direction-marker' }) }); this.Tmap.addOverLay(this.realTimeMarker); // 创建文本标签 this.realTimeLabel = new T.Label({ text: '实时定位', position: mapPoint, offset: new T.Point(-40, 10) }); this.Tmap.addOverLay(this.realTimeLabel); } else { // 更新位置 this.realTimeMarker.setLngLat(mapPoint); this.realTimeLabel.setLngLat(mapPoint); } // 如果支持指南针,更新方向 if (this.isCompassAvailable) { this._updateMarkerDirection(); } }, }, // 在组件销毁时清理 beforeDestroy() { // 清除位置监听 this._watchId && plus.geolocation.clearWatch(this._watchId); // 清除指南针监听 if (this.compassWatchId && plus.orientation.clearWatch) { plus.orientation.clearWatch(this.compassWatchId); } this.iconCache = {}; } }; </script> 两个问题 1.icon我想变得更明显一点,和打车软件自身的icon一样,有指向有圆球,指向是可以转动的 2.现在的转动一点都不丝滑,我想转动和市面上的软件一样,比如高德,百度,美团等
07-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值