1 import { controllerEvents
, controllers
} from './Controller.js';
2 import { setURL
, utcToLocal
} from '../util.js';
3 import { SimulationSlider
} from './simulationSlider.js';
4 import { simVars
} from '../simVars.js';
7 * A Component that builds the animation controller for the simulation. Creates a UI component that
8 * includes a play / pause / prev / next buttons to iterate through the simulation. Also includes a
9 * slider bar with a head that indicates relative position in animation that can be dragged to a
10 * specific location. Bar itself can also be clicked to seek to a specific position.
12 export class SimulationController
extends HTMLElement
{
16 <div class='slider-container'>
17 <div id='slider-header'>
18 <div id='slider-play-bar'>
19 <button class='slider-button' id='slider-slow-down'>
20 <svg class='svgIcon slider-icon'>
21 <use href="#fast_rewind_black_24dp"></use>
24 <button class='slider-button' id='slider-prev'>
25 <svg class='svgIcon slider-icon'>
26 <use href="#arrow_left-24px"></use>
29 <button class='slider-button' id='slider-play-pause'>
30 <svg id='play-button' class='svgIcon slider-icon'>
31 <use href="#play_arrow-24px"></use>
33 <svg id='pause-button' class='svgIcon slider-icon hidden'>
34 <use href="#pause-24px"></use>
37 <button class='slider-button' id='slider-next'>
38 <svg class='svgIcon slider-icon'>
39 <use href="#arrow_right-24px"></use>
42 <button class='slider-button' id='slider-fast-forward'>
43 <svg class='svgIcon slider-icon'>
44 <use href="#fast_forward_black_24dp"></use>
48 <div id='slider-timestamp'>
49 <span id='timestamp'></span>
58 this.normalRate
= 330;
59 this.frameRate
= this.normalRate
;
60 this.simulationSlider
;
63 /** Called when component is attached to DOM. Sets up functionality for buttons and slider. */
65 const container
= this.querySelector('.slider-container');
66 const slider
= new SimulationSlider();
67 container
.appendChild(slider
);
68 this.simulationSlider
= slider
;
70 if (document
.body
.clientWidth
< 769) {
71 const timeStamp
= this.querySelector('#slider-timestamp');
72 const playButtons
= this.querySelector('#slider-play-bar');
73 timeStamp
.parentNode
.insertBefore(timeStamp
, playButtons
);
75 L
.DomEvent
.disableScrollPropagation(container
);
76 L
.DomEvent
.disableClickPropagation(container
);
78 controllers
.currentDomain
.subscribe(() => {
80 }, controllerEvents
.ALL
);
81 controllers
.currentTimestamp
.subscribe(() => {
85 document
.addEventListener('keydown', (e
) => {
86 e
= e
|| window
.event
;
87 if (e
.key
== 'ArrowRight') {
90 if (e
.key
== 'ArrowLeft') {
95 this.querySelector('#slider-play-pause').onpointerdown
= () => {
98 this.querySelector('#slider-prev').onpointerdown
= () => {
102 this.querySelector('#slider-next').onpointerdown
= () => {
106 const speedUp
= this.querySelector('#slider-fast-forward');
107 const slowDown
= this.querySelector('#slider-slow-down');
108 speedUp
.onpointerdown
= () => {
109 this.toggleRate(this.fastRate
, speedUp
, slowDown
);
111 slowDown
.onpointerdown
= () => {
112 this.toggleRate(this.slowRate
, slowDown
, speedUp
);
116 toggleRate(rate
, togglePrimary
, toggleSecondary
) {
117 var unPressedColor
= '#d6d6d6';
118 togglePrimary
.style
.background
= unPressedColor
;
119 toggleSecondary
.style
.background
= unPressedColor
;
121 if (this.frameRate
== rate
) {
122 this.frameRate
= this.normalRate
;
124 this.frameRate
= rate
;
125 togglePrimary
.style
.background
= '#e5e5e5';
134 const sliderContainer
= this.querySelector('.slider-container');
135 sliderContainer
.style
.display
= (simVars
.sortedTimestamps
.length
< 2) ? 'none' : 'block';
139 /** Called to update the UI when the currentFrame has been updated. */
141 var currentTimestamp
= controllers
.currentTimestamp
.getValue();
142 this.querySelector('#timestamp').innerText
= utcToLocal(currentTimestamp
);
145 /** Called when play/pause button clicked. Starts animation, disables prev / next buttons
146 * changes play icon to pause icon. */
148 const prevButton
= this.querySelector('#slider-prev');
149 const nextButton
= this.querySelector('#slider-next');
150 const playButton
= this.querySelector('#play-button');
151 const pauseButton
= this.querySelector('#pause-button');
153 this.playing
= !this.playing
;
155 playButton
.classList
.remove('hidden');
156 pauseButton
.classList
.add('hidden');
158 prevButton
.disabled
= false;
159 nextButton
.disabled
= false;
160 prevButton
.classList
.remove('disabled-button');
161 nextButton
.classList
.remove('disabled-button');
164 playButton
.classList
.add('hidden');
165 pauseButton
.classList
.remove('hidden');
167 prevButton
.disabled
= true;
168 nextButton
.disabled
= true;
169 prevButton
.classList
.add('disabled-button');
170 nextButton
.classList
.add('disabled-button');
175 /** Iterates to next frame while still playing */
178 var endDate
= controllers
.endDate
.getValue();
179 var nextTimestamp
= this.nextFrame();
180 if (nextTimestamp
== endDate
) {
181 window
.setTimeout(() => this.play(), 2*this.frameRate
);
183 window
.setTimeout(() => this.play(), this.frameRate
);
188 /** Moves one frame to the right. */
190 var nextTimestamp
= this.simulationSlider
.nextTimestamp();
192 controllers
.currentTimestamp
.setValue(nextTimestamp
);
193 return nextTimestamp
;
196 /** Moves one frame to the left. */
198 var prevTimestamp
= this.simulationSlider
.prevTimestamp();
200 controllers
.currentTimestamp
.setValue(prevTimestamp
);
201 return prevTimestamp
;
205 window
.customElements
.define('simulation-controller', SimulationController
);