mirror of
https://github.com/ethan-zf/cesium-plot-js.git
synced 2025-06-23 19:17:29 +00:00
To incorporate double-arrow animation.
This commit is contained in:
parent
c23c9aa654
commit
b234fca7dd
@ -47,6 +47,7 @@ const editStartHandler = () => {
|
||||
const editEndHandler = (geometryPoints: any) => {
|
||||
console.error('editEnd', geometryPoints);
|
||||
};
|
||||
|
||||
const buttonGroup = document.getElementById('button-group') as HTMLElement;
|
||||
buttonGroup.onclick = (evt) => {
|
||||
const targetElement = evt.target as HTMLElement;
|
||||
@ -160,6 +161,12 @@ buttonGroup.onclick = (evt) => {
|
||||
geometry.off('editEnd', editEndHandler);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'startGrowthAnimation':
|
||||
if (geometry) {
|
||||
geometry.startGrowthAnimation();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -68,6 +68,7 @@
|
||||
<button id="remove">删除</button>
|
||||
<button id="addEvent">绑定事件</button>
|
||||
<button id="removeEvent">解绑事件</button>
|
||||
<button id="startGrowthAnimation">生长动画</button>
|
||||
</div>
|
||||
<script>
|
||||
window.CESIUM_BASE_URL = 'node_modules/cesium/Build/Cesium';
|
||||
|
@ -15,6 +15,12 @@ export default class DoubleArrow extends Base {
|
||||
neckHeightFactor: number;
|
||||
connPoint: Position;
|
||||
tempPoint4: Position;
|
||||
minPointsForShape: number;
|
||||
llBodyPnts: Position[] = [];
|
||||
rrBodyPnts: Position[] = [];
|
||||
curveControlPointLeft: Cartesian3;
|
||||
curveControlPointRight: Cartesian3;
|
||||
isClockWise: boolean;
|
||||
|
||||
constructor(cesium: any, viewer: any, style?: PolygonStyle) {
|
||||
super(cesium, viewer, style);
|
||||
@ -25,6 +31,7 @@ export default class DoubleArrow extends Base {
|
||||
this.neckWidthFactor = 0.15;
|
||||
this.connPoint = [0, 0];
|
||||
this.tempPoint4 = [0, 0];
|
||||
this.minPointsForShape = 4;
|
||||
this.setState('drawing');
|
||||
}
|
||||
|
||||
@ -42,8 +49,30 @@ export default class DoubleArrow extends Base {
|
||||
} else if (this.points.length === 2) {
|
||||
this.setGeometryPoints(this.points);
|
||||
this.drawPolygon();
|
||||
} else if (this.points.length === 4) {
|
||||
} else if (this.points.length === 3) {
|
||||
this.lineEntity && this.viewer.entities.remove(this.lineEntity);
|
||||
} else {
|
||||
this.finishDrawing();
|
||||
this.curveControlPointLeft = this.cesium.Cartesian3.fromDegrees(this.llBodyPnts[2][0], this.llBodyPnts[2][1]);
|
||||
this.curveControlPointRight = this.cesium.Cartesian3.fromDegrees(this.rrBodyPnts[1][0], this.rrBodyPnts[1][1]);
|
||||
|
||||
// 辅助查看插值控制点位置
|
||||
// this.CesiumViewer.entities.add({
|
||||
// position: this.curveControlPointLeft,
|
||||
// point: {
|
||||
// pixelSize: 10,
|
||||
// heightReference: this.cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
// color: this.cesium.Color.RED,
|
||||
// },
|
||||
// });
|
||||
// this.CesiumViewer.entities.add({
|
||||
// position: this.curveControlPointRight,
|
||||
// point: {
|
||||
// pixelSize: 10,
|
||||
// heightReference: this.cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
// color: this.cesium.Color.RED,
|
||||
// },
|
||||
// });
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -55,7 +84,7 @@ export default class DoubleArrow extends Base {
|
||||
if (tempPoints.length === 2) {
|
||||
this.addFirstLineOfTheArrow();
|
||||
} else if (tempPoints.length > 2) {
|
||||
const geometryPoints = this.createPolygon(tempPoints);
|
||||
const geometryPoints = this.createGraphic(tempPoints);
|
||||
this.setGeometryPoints(geometryPoints);
|
||||
this.drawPolygon();
|
||||
}
|
||||
@ -66,7 +95,7 @@ export default class DoubleArrow extends Base {
|
||||
*/
|
||||
updateDraggingPoint(cartesian: Cartesian3, index: number) {
|
||||
this.points[index] = cartesian;
|
||||
const geometryPoints = this.createPolygon(this.points);
|
||||
const geometryPoints = this.createGraphic(this.points);
|
||||
this.setGeometryPoints(geometryPoints);
|
||||
this.drawPolygon();
|
||||
}
|
||||
@ -74,7 +103,7 @@ export default class DoubleArrow extends Base {
|
||||
/**
|
||||
* Generate geometric shapes based on key points.
|
||||
*/
|
||||
createPolygon(positions: Cartesian3[]) {
|
||||
createGraphic(positions: Cartesian3[]) {
|
||||
const lnglatPoints = positions.map((pnt) => {
|
||||
return this.cartesianToLnglat(pnt);
|
||||
});
|
||||
@ -92,7 +121,8 @@ export default class DoubleArrow extends Base {
|
||||
}
|
||||
let leftArrowPnts: Position[];
|
||||
let rightArrowPnts;
|
||||
if (Utils.isClockWise(pnt1, pnt2, pnt3)) {
|
||||
this.isClockWise = Utils.isClockWise(pnt1, pnt2, pnt3);
|
||||
if (this.isClockWise) {
|
||||
leftArrowPnts = this.getArrowPoints(pnt1, this.connPoint, this.tempPoint4, false);
|
||||
rightArrowPnts = this.getArrowPoints(this.connPoint, pnt2, pnt3, true);
|
||||
} else {
|
||||
@ -104,9 +134,11 @@ export default class DoubleArrow extends Base {
|
||||
const llBodyPnts = leftArrowPnts.slice(0, t);
|
||||
const lArrowPnts = leftArrowPnts.slice(t, t + 5);
|
||||
let lrBodyPnts = leftArrowPnts.slice(t + 5, m);
|
||||
this.llBodyPnts = llBodyPnts;
|
||||
let rlBodyPnts = rightArrowPnts.slice(0, t);
|
||||
const rArrowPnts = rightArrowPnts.slice(t, t + 5);
|
||||
const rrBodyPnts = rightArrowPnts.slice(t + 5, m);
|
||||
this.rrBodyPnts = rrBodyPnts;
|
||||
rlBodyPnts = Utils.getBezierPoints(rlBodyPnts);
|
||||
const bodyPnts = Utils.getBezierPoints(rrBodyPnts.concat(llBodyPnts.slice(1)));
|
||||
lrBodyPnts = Utils.getBezierPoints(lrBodyPnts);
|
||||
@ -220,4 +252,16 @@ export default class DoubleArrow extends Base {
|
||||
getPoints() {
|
||||
return this.points;
|
||||
}
|
||||
|
||||
getBezierControlPointforGrowthAnimation() {
|
||||
return this.isClockWise
|
||||
? {
|
||||
left: this.curveControlPointLeft,
|
||||
right: this.curveControlPointRight,
|
||||
}
|
||||
: {
|
||||
right: this.curveControlPointLeft,
|
||||
left: this.curveControlPointRight,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
175
src/base.ts
175
src/base.ts
@ -1,5 +1,4 @@
|
||||
// @ts-ignore
|
||||
// import * as CesiumTypeOnly from 'cesium';
|
||||
import * as CesiumTypeOnly from 'cesium';
|
||||
import {
|
||||
State,
|
||||
@ -9,10 +8,12 @@ import {
|
||||
EventType,
|
||||
EventListener,
|
||||
VisibleAnimationOpts,
|
||||
GrowthAnimationOpts,
|
||||
} from './interface';
|
||||
import EventDispatcher from './events';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import merge from 'lodash.merge';
|
||||
// import merge from 'lodash.merge';
|
||||
import * as Utils from './utils';
|
||||
|
||||
export default class Base {
|
||||
cesium: typeof CesiumTypeOnly;
|
||||
@ -34,6 +35,7 @@ export default class Base {
|
||||
points: CesiumTypeOnly.Cartesian3[] = [];
|
||||
isHidden: boolean = false;
|
||||
styleCache: GeometryStyle | undefined;
|
||||
minPointsForShape: number = 0;
|
||||
|
||||
constructor(cesium: CesiumTypeOnly, viewer: CesiumTypeOnly.Viewer, style?: GeometryStyle) {
|
||||
this.cesium = cesium;
|
||||
@ -642,6 +644,170 @@ export default class Base {
|
||||
}, delay);
|
||||
}
|
||||
|
||||
startGrowthAnimation(opts: GrowthAnimationOpts) {
|
||||
const { duration = 3000, delay = 0, callback } = opts || {};
|
||||
if (this.state !== 'static') {
|
||||
return;
|
||||
}
|
||||
if (!this.minPointsForShape) {
|
||||
console.warn('Growth animation is not supported for this type of shape');
|
||||
return;
|
||||
}
|
||||
this.setState('animating');
|
||||
if (this.minPointsForShape === 4) {
|
||||
// For double arrows, special handling is required.
|
||||
this.doubleArrowGrowthAnimation(duration, delay, callback);
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.hideWithAnimation(0, 0, undefined);
|
||||
const points = this.getPoints();
|
||||
|
||||
let segmentDuration = 0;
|
||||
if (this.minPointsForShape === 2) {
|
||||
segmentDuration = duration / (points.length - 1);
|
||||
} else {
|
||||
segmentDuration = duration / (points.length - 2);
|
||||
}
|
||||
|
||||
let startTime = Date.now();
|
||||
let movingPointIndex = 0;
|
||||
this.viewer.clock.shouldAnimate = true;
|
||||
|
||||
const frameListener = (clock) => {
|
||||
const currentTime = Date.now();
|
||||
const elapsedTime = currentTime - startTime;
|
||||
if (elapsedTime >= duration) {
|
||||
// Animation ends
|
||||
callback && callback();
|
||||
startTime = 0;
|
||||
this.viewer.clock.shouldAnimate = false;
|
||||
this.viewer.clock.onTick.removeEventListener(frameListener);
|
||||
this.setState('static');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentSegment = Math.floor(elapsedTime / segmentDuration);
|
||||
let startPoint;
|
||||
|
||||
if (this.minPointsForShape === 2) {
|
||||
movingPointIndex = currentSegment + 1;
|
||||
} else {
|
||||
movingPointIndex = currentSegment + 2;
|
||||
}
|
||||
startPoint = points[movingPointIndex - 1];
|
||||
if (currentSegment == 0 && this.minPointsForShape === 3) {
|
||||
// The face-arrow determined by three points, with the animation starting from the midpoint of the line connecting the first two points.
|
||||
startPoint = this.cesium.Cartesian3.midpoint(points[0], points[1], new this.cesium.Cartesian3());
|
||||
}
|
||||
let endPoint = points[movingPointIndex];
|
||||
// To dynamically add points between the startPoint and endPoint, consistent with the initial drawing logic,
|
||||
// update the point at index movingPointIndex in the points array with the newPosition,
|
||||
// generate the arrow, and execute the animation.
|
||||
const t = (elapsedTime - currentSegment * segmentDuration) / segmentDuration;
|
||||
const newPosition = this.cesium.Cartesian3.lerp(startPoint, endPoint, t, new this.cesium.Cartesian3());
|
||||
const tempPoints = points.slice(0, movingPointIndex + 1);
|
||||
tempPoints[tempPoints.length - 1] = newPosition;
|
||||
const geometryPoints = this.createGraphic(tempPoints);
|
||||
this.setGeometryPoints(geometryPoints);
|
||||
this.showAnimation(0, 0, undefined);
|
||||
};
|
||||
this.viewer.clock.onTick.addEventListener(frameListener);
|
||||
}, delay);
|
||||
}
|
||||
|
||||
private doubleArrowGrowthAnimation(duration: number = 3000, delay: number = 0, callback?: Function) {
|
||||
setTimeout(() => {
|
||||
this.viewer.entities.remove(this.polygonEntity);
|
||||
this.viewer.entities.remove(this.outlineEntity);
|
||||
this.polygonEntity = null;
|
||||
this.outlineEntity = null;
|
||||
const points = this.getPoints();
|
||||
let startTime = Date.now();
|
||||
this.viewer.clock.shouldAnimate = true;
|
||||
|
||||
const frameListener = (clock) => {
|
||||
const currentTime = Date.now();
|
||||
const elapsedTime = currentTime - startTime;
|
||||
if (elapsedTime >= duration) {
|
||||
// Animation ends
|
||||
callback && callback();
|
||||
startTime = 0;
|
||||
this.viewer.clock.shouldAnimate = false;
|
||||
this.viewer.clock.onTick.removeEventListener(frameListener);
|
||||
this.setState('static');
|
||||
return;
|
||||
}
|
||||
|
||||
// Utils.isClockWise(pnt1, pnt2, pnt3)
|
||||
const midPoint = this.cesium.Cartesian3.midpoint(points[0], points[1], new this.cesium.Cartesian3());
|
||||
|
||||
const startPointLeft = this.cesium.Cartesian3.midpoint(points[0], midPoint, new this.cesium.Cartesian3());
|
||||
|
||||
const startPointRight = this.cesium.Cartesian3.midpoint(midPoint, points[1], new this.cesium.Cartesian3());
|
||||
let endPointLeft = points[3];
|
||||
let endPointRight = points[2];
|
||||
const t = elapsedTime / duration;
|
||||
const controlPoint = this.getBezierControlPointforGrowthAnimation();
|
||||
let curveControlPointsLeft = [startPointLeft, controlPoint.left, endPointLeft];
|
||||
let curveControlPointsRight = [startPointRight, controlPoint.right, endPointRight];
|
||||
const newPositionLeft = this.getNewPosition(curveControlPointsLeft, t);
|
||||
const newPositionRight = this.getNewPosition(curveControlPointsRight, t);
|
||||
|
||||
// Assist in viewing exercise routes
|
||||
// this.viewer.entities.add({
|
||||
// position: newPositionLeft,
|
||||
// point: {
|
||||
// pixelSize: 4,
|
||||
// heightReference: this.cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
// color: this.cesium.Color.RED,
|
||||
// },
|
||||
// });
|
||||
// this.viewer.entities.add({
|
||||
// position: newPositionRight,
|
||||
// point: {
|
||||
// pixelSize: 4,
|
||||
// heightReference: this.cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
// color: this.cesium.Color.RED,
|
||||
// },
|
||||
// });
|
||||
const tempPoints = [...points];
|
||||
tempPoints[2] = newPositionRight;
|
||||
tempPoints[3] = newPositionLeft;
|
||||
const geometryPoints = this.createGraphic(tempPoints);
|
||||
this.setGeometryPoints(geometryPoints);
|
||||
this.drawPolygon();
|
||||
};
|
||||
this.viewer.clock.onTick.addEventListener(frameListener);
|
||||
}, delay);
|
||||
}
|
||||
|
||||
private getNewPosition(curveControlPoints, t) {
|
||||
curveControlPoints = curveControlPoints.map((item) => {
|
||||
return this.cartesianToLnglat(item);
|
||||
});
|
||||
let curvePoints = Utils.getCurvePoints(0.3, curveControlPoints);
|
||||
curvePoints = curvePoints.map((p) => {
|
||||
return this.cesium.Cartesian3.fromDegrees(p[0], p[1]);
|
||||
});
|
||||
|
||||
let newPosition = this.interpolateAlongCurve(curvePoints, t);
|
||||
return newPosition;
|
||||
}
|
||||
|
||||
private interpolateAlongCurve(curvePoints, t) {
|
||||
const numPoints = curvePoints.length - 1;
|
||||
const index = Math.floor(t * numPoints);
|
||||
const tSegment = t * numPoints - index;
|
||||
const startPoint = curvePoints[index];
|
||||
const endPoint = curvePoints[index + 1];
|
||||
const x = startPoint.x + (endPoint.x - startPoint.x) * tSegment;
|
||||
const y = startPoint.y + (endPoint.y - startPoint.y) * tSegment;
|
||||
const z = startPoint.z + (endPoint.z - startPoint.z) * tSegment;
|
||||
|
||||
return new this.cesium.Cartesian3(x, y, z);
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (this.type === 'polygon') {
|
||||
this.viewer.entities.remove(this.polygonEntity);
|
||||
@ -692,4 +858,9 @@ export default class Base {
|
||||
return 'polygon';
|
||||
//Abstract method that must be implemented by subclasses.
|
||||
}
|
||||
|
||||
createGraphic(points: CesiumTypeOnly.Cartesian3[]): CesiumTypeOnly.Cartesian3[] {
|
||||
//Abstract method that must be implemented by subclasses.
|
||||
return [new this.cesium.Cartesian3()];
|
||||
}
|
||||
}
|
||||
|
@ -12,14 +12,20 @@ export type LineStyle = {
|
||||
lineWidth?: number;
|
||||
};
|
||||
|
||||
export type State = 'drawing' | 'edit' | 'static';
|
||||
export type State = 'drawing' | 'edit' | 'static' | 'animating';
|
||||
export type GeometryStyle = PolygonStyle | LineStyle;
|
||||
|
||||
export type EventType = 'drawStart' | 'drawUpdate' | 'drawEnd' | 'editEnd' | 'editStart';
|
||||
export type EventListener = (eventData?: any) => void;
|
||||
|
||||
export type VisibleOpts = {
|
||||
export type VisibleAnimationOpts = {
|
||||
duration?: number;
|
||||
delay?: number;
|
||||
callback?: (() => void)
|
||||
}
|
||||
callback?: () => void;
|
||||
};
|
||||
|
||||
export type GrowthAnimationOpts = {
|
||||
duration: number;
|
||||
delay: number;
|
||||
callback: Function;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user