diff --git a/package-lock.json b/package-lock.json index 6b6a5ec..fe5aeac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,19 @@ { - "name": "cesium-plot", - "version": "0.0.1", + "name": "cesium-plot-js", + "version": "0.0.4", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "cesium-plot", - "version": "0.0.1", + "name": "cesium-plot-js", + "version": "0.0.4", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lodash.merge": "^4.6.2" + }, "devDependencies": { + "@types/lodash.clonedeep": "^4.5.9", + "@types/lodash.merge": "^4.6.9", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "cesium": "1.99.0", @@ -570,6 +576,30 @@ "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", + "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==", + "dev": true + }, + "node_modules/@types/lodash.clonedeep": { + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz", + "integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/lodash.merge": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz", + "integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/node": { "version": "20.11.19", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", @@ -1569,11 +1599,15 @@ "node": ">=10" } }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/long": { "version": "5.2.3", @@ -2532,6 +2566,30 @@ "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, + "@types/lodash": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", + "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==", + "dev": true + }, + "@types/lodash.clonedeep": { + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz", + "integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/lodash.merge": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz", + "integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, "@types/node": { "version": "20.11.19", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", @@ -3317,11 +3375,15 @@ "p-locate": "^5.0.0" } }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, "lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "long": { "version": "5.2.3", diff --git a/package.json b/package.json index e74e2a4..abf2bad 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,17 @@ "preview": "vite preview" }, "devDependencies": { + "@types/lodash.clonedeep": "^4.5.9", + "@types/lodash.merge": "^4.6.9", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "cesium": "1.99.0", "eslint": "^8.45.0", "typescript": "^5.0.2", - "vite": "^4.4.5", - "cesium": "1.99.0" + "vite": "^4.4.5" + }, + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lodash.merge": "^4.6.2" } } diff --git a/src/arrow/double-arrow.ts b/src/arrow/double-arrow.ts index e5d6d85..dc1cd4c 100644 --- a/src/arrow/double-arrow.ts +++ b/src/arrow/double-arrow.ts @@ -42,7 +42,7 @@ export default class DoubleArrow extends Base { } else if (this.points.length === 2) { this.setGeometryPoints(this.points); this.drawPolygon(); - } else { + } else if (this.points.length === 4) { this.finishDrawing(); } } diff --git a/src/base.ts b/src/base.ts index b2f6dbb..484104d 100644 --- a/src/base.ts +++ b/src/base.ts @@ -3,6 +3,10 @@ import * as CesiumTypeOnly from 'cesium'; import { State, GeometryStyle, PolygonStyle, LineStyle, EventType, EventListener } from './interface'; import EventDispatcher from './events'; +import cloneDeep from 'lodash.clonedeep'; +import merge from 'lodash.merge'; + + export default class Base { cesium: typeof CesiumTypeOnly; @@ -22,6 +26,8 @@ export default class Base { dragEventHandler: CesiumTypeOnly.ScreenSpaceEventHandler; entityId: string = ''; points: CesiumTypeOnly.Cartesian3[] = []; + isHidden: boolean = false; + styleCache: GeometryStyle | undefined; constructor(cesium: CesiumTypeOnly, viewer: CesiumTypeOnly.Viewer, style?: GeometryStyle) { this.cesium = cesium; @@ -53,6 +59,8 @@ export default class Base { style, ); } + //Cache the initial settings to avoid modification of properties due to reference type assignment. + this.styleCache = cloneDeep(this.style); } /** @@ -159,13 +167,17 @@ export default class Base { } finishDrawing() { + // Some polygons draw a separate line between the first two points before drawing the complete shape; + // this line should be removed after drawing is complete. + this.type === 'polygon' && this.lineEntity && this.viewer.entities.remove(this.lineEntity); + this.removeMoveListener(); + // Editable upon initial drawing completion. this.setState('edit'); this.addControlPoints(); this.draggable(); const entity = this.polygonEntity || this.lineEntity; this.entityId = entity.id; - // this.entityId = `CesiumPlot-${entity.id}`; /** * "I've noticed that CallbackProperty can lead to significant performance issues. * After drawing multiple shapes, the map becomes noticeably laggy. Using methods @@ -478,11 +490,170 @@ export default class Base { } } + showAnimation(duration: number = 1000, delay: number = 0, callback: (() => void) | undefined) { + if (this.state != 'static' || this.isHidden === false) { + //If not in a static state or already displayed, do not process. + return; + } + this.isHidden = false; + if (this.type === 'polygon') { + let alpha = 0.3; + const material = this.styleCache.material; + if (material.image) { + // With Texture + alpha = material.color.getValue().alpha; + } else { + alpha = material.alpha; + } + + this.animateOpacity(this.polygonEntity, alpha, duration, delay, callback, this.state); + const outlineAlpha = this.styleCache?.outlineMaterial?.alpha; + this.animateOpacity( + this.outlineEntity, + outlineAlpha || 1.0, + duration, + delay, + undefined, + this.state, + ); + } else if (this.type === 'line') { + const material = this.styleCache.material; + let alpha = 1.0; + if (material.image) { + // With Texture + alpha = material.color.alpha; + } else if (material.dashLength) { + // Dashed Line + const color = material.color.getValue(); + alpha = color.alpha; + } else { + // Solid Color + alpha = this.styleCache?.material?.alpha; + } + this.animateOpacity(this.lineEntity, alpha, duration, delay, callback, this.state); + } + if (duration != 0) { + this.setState('animating'); + } + } + + hideAnimation(duration: number = 1000, delay: number = 0, callback: (() => void) | undefined) { + if (this.state != 'static' || this.isHidden === true) { + return; + } + if (this.type === 'polygon') { + this.animateOpacity(this.polygonEntity, 0.0, duration, delay, callback, this.state); + this.animateOpacity(this.outlineEntity, 0.0, duration, delay, undefined, this.state); + } else if (this.type === 'line') { + this.animateOpacity(this.lineEntity, 0.0, duration, delay, callback, this.state); + } + // if (this.state == 'edit') { + // this.controlPoints.forEach(p => { + // this.animateOpacity(p, 0.0, duration, delay, undefined, this.state); + // }); + // } + if (duration != 0) { + this.setState('animating'); + } + } + + animateOpacity( + entity: CesiumTypeOnly.Entity, + targetAlpha: number, + duration: number, + delay: number, + callback?: () => void, + state?: State, + ): void { + setTimeout(() => { + const graphics = entity.polygon || entity.polyline || entity.billboard; + let startAlpha: number; + let material = graphics.material; + if (material) { + if (material.image && material.color.alpha !== undefined) { + // Texture material, setting the alpha channel in the color of the custom ImageFlowMaterialProperty. + startAlpha = material.color.alpha; + } else { + startAlpha = material.color.getValue().alpha; + } + } else { + // billbord + const color = graphics.color.getValue(); + startAlpha = color.alpha; + } + + let startTime = 0; + + const animate = (currentTime: number) => { + if (!startTime) { + startTime = currentTime; + } + const elapsedTime = currentTime - startTime; + + if (elapsedTime < duration) { + const deltalpha = (elapsedTime / duration) * (targetAlpha - startAlpha); + const newAlpha = startAlpha + deltalpha; + + if (material) { + if (material.image && material.color.alpha !== undefined) { + // Texture Material + material.color.alpha = newAlpha; + } else { + // Solid Color + const newColor = material.color.getValue().withAlpha(newAlpha); + material.color.setValue(newColor); + } + } else { + // billbord + const color = graphics.color.getValue(); + const newColor = color.withAlpha(newAlpha); + graphics.color.setValue(newColor); + } + + requestAnimationFrame(animate); + } else { + // Animation Ended + callback && callback(); + const restoredState = state ? state : 'static'; + + if (targetAlpha === 0) { + this.isHidden = true; + } + + // if (duration == 0) { + this.setState('drawing'); + if (material) { + if (material.image && material.color.alpha !== undefined) { + // Texture Material + material.color.alpha = targetAlpha; + } else { + // Solid Color + const newColor = material.color.getValue().withAlpha(targetAlpha); + material.color.setValue(newColor); + } + } else { + // billbord + const color = graphics.color.getValue(); + const newColor = color.withAlpha(targetAlpha); + graphics.color.setValue(newColor); + } + requestAnimationFrame(() => { + this.setState(restoredState); + }); + // } else { + // this.setState(restoredState); + // } + } + }; + + requestAnimationFrame(animate); + }, delay); + } + remove() { if (this.type === 'polygon') { this.viewer.entities.remove(this.polygonEntity); this.viewer.entities.remove(this.outlineEntity); - this.lineEntity && this.viewer.entities.remove(this.lineEntity); this.polygonEntity = null; this.outlineEntity = null; this.lineEntity = null;