From 0e736ff49ba087e8979bd4e2d41b6fd851842b65 Mon Sep 17 00:00:00 2001 From: Kanahiro Date: Sun, 29 Dec 2024 16:52:04 +0900 Subject: [PATCH] feat: allow to control from outside control --- README.md | 19 +++++--- example/raster.html | 11 ++++- example/raster.ts | 26 +++++++++++ package.json | 1 - pnpm-lock.yaml | 13 +----- src/icons.ts | 10 ++-- src/index.ts | 111 +++++++++++++++++++++++++++++++++----------- 7 files changed, 139 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index dfbf8c5..aeea21a 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ ## examples -- RasterTiles: https://mug-jp.github.io/maplibre-gl-temporal-control/raster.html -- VectorTiles: https://mug-jp.github.io/maplibre-gl-temporal-control/vector.html +- RasterTiles: +- VectorTiles: ## usage @@ -49,11 +49,18 @@ const temporalControl = new TemporalControl(temporalFrames, { performance: true // set when rendering is too slow, but frames which are not current are shown mostly transparent }); map.addControl(temporalControl); + +// you can control programatically +temporalControl.prev() +temporalControl.next() +temporalControl.play() +temporalControl.pause() +temporalControl.setLoopEnabled(true) ``` ### Tips -- In frames, You must set layer-objects corresponding to in map. -- Layers set in frames must be added in map -- when `performance: true`, not-current frames are shown as opacity=0.000000000000000000001 - - this option may not be neccesary for ordinary usecases +- In frames, You must set layer-objects corresponding to in map. +- Layers set in frames must be added in map +- when `performance: true`, not-current frames are shown as opacity=0.000000000000000000001 + - this option may not be neccesary for ordinary usecases diff --git a/example/raster.html b/example/raster.html index 803afb5..a39a74c 100644 --- a/example/raster.html +++ b/example/raster.html @@ -11,7 +11,7 @@
+
+ + + + + +
diff --git a/example/raster.ts b/example/raster.ts index d0ad081..4793fb2 100644 --- a/example/raster.ts +++ b/example/raster.ts @@ -125,4 +125,30 @@ nowcast.getTimeData().then((timedata) => { interval: 100, }); map.addControl(temporalControl); + + // control from outside + const btnPrev = document.getElementById('btn-prev')!; + btnPrev.addEventListener('click', () => { + temporalControl.prev(); + }); + + const btnNext = document.getElementById('btn-next')!; + btnNext.addEventListener('click', () => { + temporalControl.next(); + }); + + const btnPlay = document.getElementById('btn-play')!; + btnPlay.addEventListener('click', () => { + temporalControl.play(); + }); + + const btnPause = document.getElementById('btn-pause')!; + btnPause.addEventListener('click', () => { + temporalControl.pause(); + }); + + const btnLoop = document.getElementById('btn-loop')!; + btnLoop.addEventListener('click', () => { + temporalControl.setLoopEnabled(!temporalControl.isLoopEnabled()); + }); }); diff --git a/package.json b/package.json index 02fff96..0ca4123 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ }, "homepage": "https://github.com/mug-jp/maplibre-gl-temporal-controller#readme", "devDependencies": { - "@types/node": "^14.18.63", "jma-utils": "^0.1.0", "maplibre-gl": "5.0.0-pre.10", "typescript": "^4.9.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1e16791..631fa6e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,6 @@ importers: .: devDependencies: - '@types/node': - specifier: ^14.18.63 - version: 14.18.63 jma-utils: specifier: ^0.1.0 version: 0.1.0 @@ -22,7 +19,7 @@ importers: version: 4.9.5 vite: specifier: ^6.0.6 - version: 6.0.6(@types/node@14.18.63)(terser@5.37.0) + version: 6.0.6(terser@5.37.0) packages: @@ -335,9 +332,6 @@ packages: '@types/mapbox__vector-tile@1.3.4': resolution: {integrity: sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==} - '@types/node@14.18.63': - resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} - '@types/pbf@3.0.5': resolution: {integrity: sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==} @@ -746,8 +740,6 @@ snapshots: '@types/mapbox__point-geometry': 0.1.4 '@types/pbf': 3.0.5 - '@types/node@14.18.63': {} - '@types/pbf@3.0.5': {} '@types/supercluster@7.1.3': @@ -946,13 +938,12 @@ snapshots: typescript@4.9.5: {} - vite@6.0.6(@types/node@14.18.63)(terser@5.37.0): + vite@6.0.6(terser@5.37.0): dependencies: esbuild: 0.24.2 postcss: 8.4.49 rollup: 4.29.1 optionalDependencies: - '@types/node': 14.18.63 fsevents: 2.3.3 terser: 5.37.0 diff --git a/src/icons.ts b/src/icons.ts index 9508a41..110306d 100644 --- a/src/icons.ts +++ b/src/icons.ts @@ -1,11 +1,11 @@ // https://materialdesignicons.com/ -export const pause = +export const pauseSvg = 'pause'; -export const play = +export const playSvg = 'play'; -export const reload = +export const reloadSvg = 'reload'; -export const skipBackward = +export const skipBackwardSvg = 'skip-backward'; -export const skipForward = +export const skipForwardSvg = 'skip-forward'; diff --git a/src/index.ts b/src/index.ts index ccbdd86..539aca5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,13 @@ import type { ControlPosition, } from 'maplibre-gl'; -import { play, pause, reload, skipBackward, skipForward } from './icons'; +import { + playSvg, + pauseSvg, + reloadSvg, + skipBackwardSvg, + skipForwardSvg, +} from './icons'; const ACTIVE_BUTTON_COLOR = 'rgb(204, 204, 204)'; @@ -23,11 +29,13 @@ const makeImg = (svg: string): HTMLImageElement => { return img; }; +let timerId: number | undefined; + const makeContainer = ({ length, interval, onSliderValueChange, -}: ContainerOptions): [HTMLDivElement, HTMLDivElement, HTMLInputElement] => { +}: ContainerOptions) => { // outest div const container = document.createElement('div'); container.classList.add('maplibregl-ctrl'); @@ -63,18 +71,21 @@ const makeContainer = ({ buttonsDiv.style.margin = '4px 0 0 0'; // loop button + const setLoopEnabled = (enabled: boolean) => { + loopButton.style.backgroundColor = enabled ? ACTIVE_BUTTON_COLOR : ''; + }; + const isLoopEnabled = () => + loopButton.style.backgroundColor === ACTIVE_BUTTON_COLOR; const loopButton = document.createElement('button'); - loopButton.appendChild(makeImg(reload)); + loopButton.appendChild(makeImg(reloadSvg)); loopButton.style.border = '0'; loopButton.style.borderRadius = '0'; loopButton.style.marginRight = '16px'; loopButton.style.height = '24px'; loopButton.style.borderRadius = '4px'; - loopButton.onclick = () => { - loopButton.style.backgroundColor = - loopButton.style.backgroundColor === '' ? ACTIVE_BUTTON_COLOR : ''; - }; + loopButton.onclick = () => setLoopEnabled(!isLoopEnabled()); buttonsDiv.appendChild(loopButton); + const decrement = () => { slider.value = String(Math.max(0, Number(slider.value) - 1)); onSliderValueChange(); @@ -97,42 +108,48 @@ const makeContainer = ({ // prev button const prevButton = document.createElement('button'); - prevButton.appendChild(makeImg(skipBackward)); + prevButton.appendChild(makeImg(skipBackwardSvg)); prevButton.onclick = decrement; prevButton.style.border = '0'; prevButton.style.height = '24px'; prevButton.style.borderRadius = '4px'; // pause button + const pause = () => { + if (timerId === undefined) return; + clearInterval(timerId); + timerId = undefined; + pauseButton.onclick = null; + playButton.style.backgroundColor = ''; + }; const pauseButton = document.createElement('button'); - pauseButton.appendChild(makeImg(pause)); + pauseButton.appendChild(makeImg(pauseSvg)); pauseButton.style.border = '0'; pauseButton.style.height = '24px'; pauseButton.style.borderRadius = '4px'; + pauseButton.onclick = pause; // play button - const playButton = document.createElement('button'); - playButton.appendChild(makeImg(play)); - playButton.style.border = '0'; - playButton.style.height = '24px'; - playButton.style.borderRadius = '4px'; - playButton.onclick = () => { - if (playButton.style.backgroundColor === ACTIVE_BUTTON_COLOR) return; - + const isPlaying = () => + playButton.style.backgroundColor === ACTIVE_BUTTON_COLOR; + const play = () => { + if (isPlaying()) return; playButton.style.backgroundColor = ACTIVE_BUTTON_COLOR; - const timerId = setInterval(() => { + timerId = setInterval(() => { increment(); }, interval); - pauseButton.onclick = () => { - clearInterval(timerId); - pauseButton.onclick = null; - playButton.style.backgroundColor = ''; - }; }; + const playButton = document.createElement('button'); + playButton.appendChild(makeImg(playSvg)); + playButton.style.border = '0'; + playButton.style.height = '24px'; + playButton.style.borderRadius = '4px'; + playButton.onclick = play; + // next button const nextButton = document.createElement('button'); - nextButton.appendChild(makeImg(skipForward)); + nextButton.appendChild(makeImg(skipForwardSvg)); nextButton.style.border = '0'; nextButton.style.height = '24px'; nextButton.style.borderRadius = '4px'; @@ -145,7 +162,18 @@ const makeContainer = ({ container.appendChild(buttonsDiv); - return [container, titleDiv, slider]; + return { + container, + titleDiv, + slider, + increment, + decrement, + isPlaying, + play, + pause, + isLoopEnabled, + setLoopEnabled, + }; }; type Position = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; @@ -170,6 +198,14 @@ export default class TemporalControl implements IControl { private temporalSlider!: HTMLInputElement; private temporalFrames: TemporalFrame[]; + next: () => boolean; + prev: () => boolean; + play: () => void; + pause: () => void; + isPlaying: () => boolean; + isLoopEnabled: () => boolean; + setLoopEnabled: (enabled: boolean) => void; + constructor(temporalFrames: TemporalFrame[], options: Options = {}) { this.temporalFrames = temporalFrames; this.options = options; @@ -180,8 +216,29 @@ export default class TemporalControl implements IControl { onSliderValueChange: () => this.refresh(), }; - [this.container, this.containerTitle, this.temporalSlider] = - makeContainer(containerOptions); + const { + container, + titleDiv, + slider, + increment, + decrement, + play, + pause, + isPlaying, + isLoopEnabled, + setLoopEnabled, + } = makeContainer(containerOptions); + + this.container = container; + this.containerTitle = titleDiv; + this.temporalSlider = slider; + this.next = increment; + this.prev = decrement; + this.play = play; + this.pause = pause; + this.isPlaying = isPlaying; + this.isLoopEnabled = isLoopEnabled; + this.setLoopEnabled = setLoopEnabled; } onAdd(map: Map) {