|
|
|
|
/**
|
|
|
|
|
* @fileoverview 视角轨迹动画
|
|
|
|
|
* 主入口类是<a href="example/index.html">TrackAnimation</a>,
|
|
|
|
|
* 基于Baidu Map API GL 1.0。
|
|
|
|
|
*
|
|
|
|
|
* @author Baidu Map Api Group
|
|
|
|
|
* @version 1.0
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @namespace BMapGL的所有library类均放在BMapGLLib命名空间下
|
|
|
|
|
*/
|
|
|
|
|
var BMapGLLib = window.BMapGLLib = BMapGLLib || {};
|
|
|
|
|
|
|
|
|
|
(function () {
|
|
|
|
|
var DELTA_ZOOM = 1;
|
|
|
|
|
var DEFAULT_TILT = 55;
|
|
|
|
|
var DEFAULT_HEADING = 0;
|
|
|
|
|
var DEFAULT_DURATION = 10000;
|
|
|
|
|
var DEFAULT_DELAY = 0;
|
|
|
|
|
var DEFAULT_OVERALLVIEW = true;
|
|
|
|
|
var PLAY = 1;
|
|
|
|
|
var CANCEL = 2;
|
|
|
|
|
var PAUSE = 3;
|
|
|
|
|
var start = 0;
|
|
|
|
|
var TrackAnimation =
|
|
|
|
|
/**
|
|
|
|
|
* 构造函数
|
|
|
|
|
*
|
|
|
|
|
* @param {BMapGL.Map} map 地图实例
|
|
|
|
|
* @param {Polyline} polyline 折线实例
|
|
|
|
|
* @param {TrackAnimationOptions} opts 配置
|
|
|
|
|
* {
|
|
|
|
|
* zoom
|
|
|
|
|
* tilt
|
|
|
|
|
* duration
|
|
|
|
|
* delay
|
|
|
|
|
* overallView
|
|
|
|
|
* }
|
|
|
|
|
*/
|
|
|
|
|
BMapGLLib.TrackAnimation = function (map, polyline, opts) {
|
|
|
|
|
this._map = map;
|
|
|
|
|
this._polyline = polyline;
|
|
|
|
|
this._totalPath = polyline.getPath();
|
|
|
|
|
this._overallView = map.getViewport(polyline.getPath());
|
|
|
|
|
this._status = CANCEL;
|
|
|
|
|
this._opts = {
|
|
|
|
|
zoom: this._getZoom(),
|
|
|
|
|
tilt: DEFAULT_TILT,
|
|
|
|
|
heading: DEFAULT_HEADING,
|
|
|
|
|
duration: DEFAULT_DURATION,
|
|
|
|
|
delay: DEFAULT_DELAY,
|
|
|
|
|
overallView: DEFAULT_OVERALLVIEW
|
|
|
|
|
};
|
|
|
|
|
this._initOpts(opts);
|
|
|
|
|
this._expandPath = this._addPath(polyline.getPath());
|
|
|
|
|
// window.requestAnimationFrame(this._step);
|
|
|
|
|
// 暂停时累计经历的时间
|
|
|
|
|
this._pauseTime = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取轨迹播放合适的缩放级别
|
|
|
|
|
* @return {number} zoom
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype._getZoom = function () {
|
|
|
|
|
return Math.min(this._overallView.zoom + DELTA_ZOOM, this._map.getMaxZoom());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据当前配置更新动画参数
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype._updateAniParams = function () {
|
|
|
|
|
this._updatePathAni();
|
|
|
|
|
this._updateViewAni();
|
|
|
|
|
this._polyline.setPath(this._expandPath.slice(0, 1));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 轨迹动画
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype._updatePathAni = function () {
|
|
|
|
|
this._expandPath = this._addPath(this._totalPath);
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 视角动画
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype._updateViewAni = function () {
|
|
|
|
|
this._overallView = this._map.getViewport(this._totalPath);
|
|
|
|
|
var length = this._totalPath.length;
|
|
|
|
|
var keyFrames = [];
|
|
|
|
|
var duration = this._opts.overallView ? this._opts.duration + 2000 : this._opts.duration;
|
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
|
var item = this._totalPath[i];
|
|
|
|
|
var percent = this._pathPercents[i] * (this._opts.duration / duration)
|
|
|
|
|
keyFrames.push({
|
|
|
|
|
center: new BMapGL.Point(item.lng, item.lat),
|
|
|
|
|
zoom: this._opts.zoom,
|
|
|
|
|
tilt: i === 0 ? 0 : this._opts.tilt,
|
|
|
|
|
heading: i === 0 ? 0 : this._opts.heading,
|
|
|
|
|
percentage: percent
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (this._opts.overallView) {
|
|
|
|
|
keyFrames.push({
|
|
|
|
|
center: new BMapGL.Point(this._overallView.center.lng, this._overallView.center.lat),
|
|
|
|
|
zoom: this._overallView.zoom - DELTA_ZOOM,
|
|
|
|
|
tilt: 0,
|
|
|
|
|
heading: 0,
|
|
|
|
|
percentage: 1
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var opts = {
|
|
|
|
|
duration: duration,
|
|
|
|
|
delay: 0,
|
|
|
|
|
interation: 1
|
|
|
|
|
};
|
|
|
|
|
this._viewAni = new BMapGL.ViewAnimation(keyFrames, opts);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 扩充Path
|
|
|
|
|
* @param {Array} path 原始路径
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype._addPath = function (path) {
|
|
|
|
|
var TOTAL_NUM = this._opts.duration / 10;
|
|
|
|
|
var length = path.length;
|
|
|
|
|
var totalDistance = 0;
|
|
|
|
|
var distances = [];
|
|
|
|
|
var expandPath = [];
|
|
|
|
|
for (var i = 1; i < length; i++) {
|
|
|
|
|
var distance = this._map.getDistance(path[i - 1], path[i]);
|
|
|
|
|
distances.push(distance);
|
|
|
|
|
totalDistance += distance;
|
|
|
|
|
}
|
|
|
|
|
var percents = [0];
|
|
|
|
|
for (var i = 1; i < length; i++) {
|
|
|
|
|
var percent = (distances[i - 1] / totalDistance).toFixed(2);
|
|
|
|
|
// percents.push(percent);
|
|
|
|
|
percents[i] = percents[i - 1] + parseFloat(percent, 10);
|
|
|
|
|
expandPath = expandPath.concat(this._getPath(path[i - 1], path[i], percent * TOTAL_NUM));
|
|
|
|
|
}
|
|
|
|
|
this._pathPercents = percents;
|
|
|
|
|
return expandPath;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取差值Path
|
|
|
|
|
* @param {Object} start 起始点
|
|
|
|
|
* @param {Object} end 终止点
|
|
|
|
|
* @param {number} num 差值点数量
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype._getPath = function (start, end, num) {
|
|
|
|
|
var result = [];
|
|
|
|
|
if (num === 0) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
for (var i = 0; i <= num; i++) {
|
|
|
|
|
var point = new BMapGL.Point(
|
|
|
|
|
(end.lng - start.lng) / num * i + start.lng,
|
|
|
|
|
(end.lat - start.lat) / num * i + start.lat
|
|
|
|
|
);
|
|
|
|
|
result.push(point);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 初始化配置参数
|
|
|
|
|
* @param {Object} opts 配置
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype._initOpts = function (opts) {
|
|
|
|
|
for (var p in opts) {
|
|
|
|
|
if (opts.hasOwnProperty(p)) {
|
|
|
|
|
this._opts[p] = opts[p];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 启动动画
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.start = function () {
|
|
|
|
|
var me = this;
|
|
|
|
|
setTimeout(function () {
|
|
|
|
|
me._updateAniParams();
|
|
|
|
|
me._map.removeOverlay(me._polyline);
|
|
|
|
|
me._map.addOverlay(me._polyline);
|
|
|
|
|
me._status = PLAY;
|
|
|
|
|
me._step(performance.now());
|
|
|
|
|
me._map.startViewAnimation(me._viewAni);
|
|
|
|
|
}, this._opts.delay);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 终止动画
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.cancel = function () {
|
|
|
|
|
this._clearRAF();
|
|
|
|
|
this._status = CANCEL;
|
|
|
|
|
start = 0;
|
|
|
|
|
this._pauseTime = 0;
|
|
|
|
|
this._map.cancelViewAnimation(this._viewAni);
|
|
|
|
|
this._map.removeOverlay(this._polyline);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 暂停动画
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.pause = function () {
|
|
|
|
|
if (this._status === PLAY) {
|
|
|
|
|
this._clearRAF();
|
|
|
|
|
this._map.pauseViewAnimation(this._viewAni);
|
|
|
|
|
this._status = PAUSE;
|
|
|
|
|
this._isPausing = performance.now();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 继续动画
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.continue = function () {
|
|
|
|
|
if (this._status === PAUSE) {
|
|
|
|
|
this._pauseTime += performance.now() - this._isPausing;
|
|
|
|
|
this._isPausing = undefined;
|
|
|
|
|
this._status = PLAY;
|
|
|
|
|
this._step(performance.now());
|
|
|
|
|
this._map.continueViewAnimation(this._viewAni);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* rAF动画函数
|
|
|
|
|
* @param {number} timestamp 时间戳
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype._step = function (timestamp) {
|
|
|
|
|
if (this._status === CANCEL) {
|
|
|
|
|
start = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!start) {
|
|
|
|
|
start = timestamp;
|
|
|
|
|
}
|
|
|
|
|
timestamp = timestamp - this._pauseTime;
|
|
|
|
|
|
|
|
|
|
var percent = (timestamp - start) / this._opts.duration;
|
|
|
|
|
var end = Math.round(this._expandPath.length * percent);
|
|
|
|
|
this._polyline.setPath(this._expandPath.slice(0, end));
|
|
|
|
|
if (timestamp < start + this._opts.duration) {
|
|
|
|
|
this._timer = window.requestAnimationFrame(this._step.bind(this));
|
|
|
|
|
} else {
|
|
|
|
|
start = 0;
|
|
|
|
|
this._status = CANCEL;
|
|
|
|
|
this._pauseTime = 0;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 清除rAF动画函数
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype._clearRAF = function () {
|
|
|
|
|
if (this._timer) {
|
|
|
|
|
window.cancelAnimationFrame(this._timer);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 设置缩放级别
|
|
|
|
|
* @param {number} zoom 缩放级别
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.setZoom = function (zoom) {
|
|
|
|
|
this._opts.zoom = zoom;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取缩放级别
|
|
|
|
|
* @return {number} zoom
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.getZoom = function (zoom) {
|
|
|
|
|
return this._opts.zoom;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 设置角度
|
|
|
|
|
* @param {number} tilt 角度
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.setTilt = function (tilt) {
|
|
|
|
|
this._opts.tilt = tilt;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取角度
|
|
|
|
|
* @return {number} tilt
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.getTilt = function (tilt) {
|
|
|
|
|
return this._opts.tilt;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 设置延迟
|
|
|
|
|
* @param {number} delay 延迟
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.setDelay = function (delay) {
|
|
|
|
|
this._opts.delay = delay;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取延迟
|
|
|
|
|
* @return {number} delay
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.getDelay = function (delay) {
|
|
|
|
|
return this._opts.delay;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 设置持续事件
|
|
|
|
|
* @param {number} duration 持续事件
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.setDuration = function (duration) {
|
|
|
|
|
this._opts.duration = duration;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取持续事件
|
|
|
|
|
* @return {number} duration
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.getDuration = function (duration) {
|
|
|
|
|
return this._opts.duration;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 开启总览
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.enableOverallView = function () {
|
|
|
|
|
this._opts.overallView = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 关闭总览
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.disableOverallView = function () {
|
|
|
|
|
this._opts.overallView = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 更新折线
|
|
|
|
|
* @param {Object} polyline 更新
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.setPolyline = function (polyline) {
|
|
|
|
|
this._polyline = polyline;
|
|
|
|
|
this._totalPath = polyline.getPath();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取折线
|
|
|
|
|
* @return {Object} polyline
|
|
|
|
|
*/
|
|
|
|
|
TrackAnimation.prototype.getPolyline = function () {
|
|
|
|
|
return this._polyline;
|
|
|
|
|
};
|
|
|
|
|
})();
|