mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 21:38:13 +00:00
Optymalizacja aplikcji, małe poprawki
This commit is contained in:
Binary file not shown.
@@ -18,12 +18,6 @@ const routes: Array<RouteConfig> = [
|
|||||||
component: TrainsView,
|
component: TrainsView,
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/timetable',
|
|
||||||
name: 'TimetableView',
|
|
||||||
component: () => import('@/views/TimetableView.vue'),
|
|
||||||
props: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/scenery',
|
path: '/scenery',
|
||||||
name: 'SceneryView',
|
name: 'SceneryView',
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
interface ISceneryInfoData {
|
||||||
|
stationName: string;
|
||||||
|
stationURL: string;
|
||||||
|
stationLines: string;
|
||||||
|
stationProject: string;
|
||||||
|
|
||||||
|
reqLevel: string;
|
||||||
|
supportersOnly: string;
|
||||||
|
signalType: string;
|
||||||
|
controlType: string;
|
||||||
|
SBL: string;
|
||||||
|
twoWayBlock: string;
|
||||||
|
|
||||||
|
routesOneWayCatenary: number;
|
||||||
|
routesOneWayOther: number;
|
||||||
|
routesTwoWayCatenary: number;
|
||||||
|
routesToWayOther: number;
|
||||||
|
|
||||||
|
default: boolean;
|
||||||
|
nonPublic: boolean;
|
||||||
|
unavailable: boolean;
|
||||||
|
hasData: boolean;
|
||||||
|
|
||||||
|
stops: string[];
|
||||||
|
checkpoints: string[];
|
||||||
|
|
||||||
|
currentDispatcher: string;
|
||||||
|
currentDispatcherId: number;
|
||||||
|
currentDispatcherFrom: number;
|
||||||
|
dispatcherHistory: { dispatcherName: string; dispatcherId: number; dispatcherFrom: number; dispatcherTo: number }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ISceneryInfoData;
|
||||||
+81
-27
@@ -14,7 +14,7 @@ enum Status {
|
|||||||
Loaded = 2,
|
Loaded = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TimetableData {
|
interface ITimetableData {
|
||||||
trainNo: number;
|
trainNo: number;
|
||||||
driverName: string;
|
driverName: string;
|
||||||
driverId: number;
|
driverId: number;
|
||||||
@@ -30,8 +30,6 @@ interface TimetableData {
|
|||||||
followingSceneries: string[];
|
followingSceneries: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// const devEnv = true;
|
|
||||||
|
|
||||||
const URLs = {
|
const URLs = {
|
||||||
stations: 'https://api.td2.info.pl:9640/?method=getStationsOnline',
|
stations: 'https://api.td2.info.pl:9640/?method=getStationsOnline',
|
||||||
trains: 'https://api.td2.info.pl:9640/?method=getTrainsOnline',
|
trains: 'https://api.td2.info.pl:9640/?method=getTrainsOnline',
|
||||||
@@ -123,6 +121,7 @@ export default class Store extends VuexModule {
|
|||||||
private stationCount: number = 0;
|
private stationCount: number = 0;
|
||||||
|
|
||||||
private dataConnectionStatus: Status = Status.Loading;
|
private dataConnectionStatus: Status = Status.Loading;
|
||||||
|
private sceneryDataStatus: Status = Status.Loading;
|
||||||
private timetableLoaded: Status = Status.Loading;
|
private timetableLoaded: Status = Status.Loading;
|
||||||
|
|
||||||
private stationList: Station[] = [];
|
private stationList: Station[] = [];
|
||||||
@@ -155,11 +154,31 @@ export default class Store extends VuexModule {
|
|||||||
get getDataStatus() {
|
get getDataStatus() {
|
||||||
return this.dataConnectionStatus;
|
return this.dataConnectionStatus;
|
||||||
}
|
}
|
||||||
|
get getSceneryDataStatus() {
|
||||||
|
return this.sceneryDataStatus;
|
||||||
|
}
|
||||||
|
|
||||||
//ACTIONS
|
//ACTIONS
|
||||||
@Action
|
@Action
|
||||||
async synchronizeData() {
|
async synchronizeData() {
|
||||||
this.context.commit('setJSONData');
|
// axios
|
||||||
|
// .get(URLs.sceneryInfo)
|
||||||
|
// .then(response => {
|
||||||
|
// const data: ISceneryInfoData[] = response.data;
|
||||||
|
|
||||||
|
// this.context.commit('setSceneryData', data);
|
||||||
|
// this.context.commit('setSceneryDataStatus', Status.Loaded);
|
||||||
|
|
||||||
|
// this.context.dispatch('fetchOnlineData');
|
||||||
|
// setInterval(() => this.context.dispatch('fetchOnlineData'), 20000);
|
||||||
|
// })
|
||||||
|
// .catch(err => {
|
||||||
|
// this.context.commit('setSceneryDataStatus', Status.Error);
|
||||||
|
// console.error('Ups! Coś poszło nie tak!', err);
|
||||||
|
// });
|
||||||
|
|
||||||
|
this.context.commit('setSceneryData');
|
||||||
|
this.context.commit('setSceneryDataStatus', Status.Loaded);
|
||||||
|
|
||||||
this.context.dispatch('fetchOnlineData');
|
this.context.dispatch('fetchOnlineData');
|
||||||
setInterval(() => this.context.dispatch('fetchOnlineData'), 20000);
|
setInterval(() => this.context.dispatch('fetchOnlineData'), 20000);
|
||||||
@@ -167,7 +186,7 @@ export default class Store extends VuexModule {
|
|||||||
|
|
||||||
@Action({ commit: 'updateTimetableData' })
|
@Action({ commit: 'updateTimetableData' })
|
||||||
async fetchTimetableData() {
|
async fetchTimetableData() {
|
||||||
return this.trainList.reduce(async (acc: Promise<TimetableData[]>, train) => {
|
return this.trainList.reduce(async (acc: Promise<ITimetableData[]>, train) => {
|
||||||
const timetable = await (await axios.get(timetableURL(train.trainNo))).data.message;
|
const timetable = await (await axios.get(timetableURL(train.trainNo))).data.message;
|
||||||
const trainInfo = timetable.trainInfo;
|
const trainInfo = timetable.trainInfo;
|
||||||
|
|
||||||
@@ -309,7 +328,13 @@ export default class Store extends VuexModule {
|
|||||||
this.dataConnectionStatus = status;
|
this.dataConnectionStatus = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mutation setJSONData() {
|
@Mutation
|
||||||
|
private setSceneryDataStatus(status: Status) {
|
||||||
|
this.sceneryDataStatus = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mutation
|
||||||
|
private setSceneryData() {
|
||||||
/*
|
/*
|
||||||
0: stationName,
|
0: stationName,
|
||||||
1: stationURL,
|
1: stationURL,
|
||||||
@@ -377,6 +402,54 @@ export default class Store extends VuexModule {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Mutation setSceneryData(data: ISceneryInfoData[]) {
|
||||||
|
// this.sceneryData = data;
|
||||||
|
|
||||||
|
// this.stationList = data.map(scenery => ({
|
||||||
|
// stationName: scenery.stationName,
|
||||||
|
// stationURL: scenery.stationURL,
|
||||||
|
// stationLines: scenery.stationLines,
|
||||||
|
// stationProject: scenery.stationProject,
|
||||||
|
// reqLevel: scenery.reqLevel,
|
||||||
|
// supportersOnly: scenery.supportersOnly,
|
||||||
|
// signalType: scenery.signalType,
|
||||||
|
// controlType: scenery.controlType,
|
||||||
|
// SBL: scenery.SBL,
|
||||||
|
// TWB: scenery.twoWayBlock,
|
||||||
|
// routes: {
|
||||||
|
// oneWay: {
|
||||||
|
// catenary: scenery.routesOneWayCatenary,
|
||||||
|
// noCatenary: scenery.routesOneWayOther,
|
||||||
|
// },
|
||||||
|
// twoWay: {
|
||||||
|
// catenary: scenery.routesTwoWayCatenary,
|
||||||
|
// noCatenary: scenery.routesToWayOther,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// checkpoints: scenery.checkpoints.length ? scenery.checkpoints.map(cp => ({ checkpointName: cp, scheduledTrains: [] })) : null,
|
||||||
|
// stops: scenery.stops,
|
||||||
|
|
||||||
|
// default: scenery.default,
|
||||||
|
// nonPublic: scenery.nonPublic,
|
||||||
|
// unavailable: scenery.unavailable,
|
||||||
|
|
||||||
|
// stationHash: '',
|
||||||
|
// maxUsers: 0,
|
||||||
|
// currentUsers: 0,
|
||||||
|
// dispatcherName: '',
|
||||||
|
// dispatcherRate: 0,
|
||||||
|
// dispatcherExp: -1,
|
||||||
|
// dispatcherId: 0,
|
||||||
|
// dispatcherIsSupporter: false,
|
||||||
|
// online: false,
|
||||||
|
// occupiedTo: 'WOLNA',
|
||||||
|
// statusTimestamp: -3,
|
||||||
|
// stationTrains: [],
|
||||||
|
// scheduledTrains: [],
|
||||||
|
// spawns: [],
|
||||||
|
// }));
|
||||||
|
// }
|
||||||
|
|
||||||
@Mutation
|
@Mutation
|
||||||
private updateOnlineStations(updatedStationList: any[]) {
|
private updateOnlineStations(updatedStationList: any[]) {
|
||||||
this.stationList = this.stationList.reduce((acc, station) => {
|
this.stationList = this.stationList.reduce((acc, station) => {
|
||||||
@@ -426,25 +499,6 @@ export default class Store extends VuexModule {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Dodawanie do listy online potencjalnych scenerii niewpisanych do bazy
|
|
||||||
// updatedStationList.forEach(updatedStation => {
|
|
||||||
// const alreadyInList: any = this.stationList.some(station => station.stationName === updatedStation.stationName);
|
|
||||||
|
|
||||||
// console.log(updatedStation, alreadyInList);
|
|
||||||
|
|
||||||
// if (!alreadyInList) {
|
|
||||||
// this.stationList.push({
|
|
||||||
// ...updatedStation,
|
|
||||||
// scheduledTrains: [],
|
|
||||||
// stationTrains: [],
|
|
||||||
// subStations: [],
|
|
||||||
// online: true,
|
|
||||||
// reqLevel: '-1',
|
|
||||||
// nonPublic: true,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
this.stationCount = this.stationList.filter(station => station.online).length;
|
this.stationCount = this.stationList.filter(station => station.online).length;
|
||||||
this.dataConnectionStatus = Status.Loaded;
|
this.dataConnectionStatus = Status.Loaded;
|
||||||
}
|
}
|
||||||
@@ -465,10 +519,10 @@ export default class Store extends VuexModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Mutation
|
@Mutation
|
||||||
private updateTimetableData(timetableList: TimetableData[]) {
|
private updateTimetableData(timetableList: ITimetableData[]) {
|
||||||
this.stationList = this.stationList.map(station => {
|
this.stationList = this.stationList.map(station => {
|
||||||
const stationName = station.stationName.toLowerCase();
|
const stationName = station.stationName.toLowerCase();
|
||||||
const scheduledTrains: Station['scheduledTrains'] = timetableList.reduce((acc: Station['scheduledTrains'], timetableData: TimetableData, index) => {
|
const scheduledTrains: Station['scheduledTrains'] = timetableList.reduce((acc: Station['scheduledTrains'], timetableData: ITimetableData, index) => {
|
||||||
if (!timetableData.followingSceneries.includes(station.stationHash)) return acc;
|
if (!timetableData.followingSceneries.includes(station.stationHash)) return acc;
|
||||||
|
|
||||||
const stopInfoIndex = timetableData.followingStops.findIndex(stop => {
|
const stopInfoIndex = timetableData.followingStops.findIndex(stop => {
|
||||||
|
|||||||
+29
-32
@@ -26,7 +26,7 @@
|
|||||||
<div class="list_wrapper">
|
<div class="list_wrapper">
|
||||||
<!-- <div class="list_loading" v-if="dataLoading">POBIERANIE DANYCH...</div> -->
|
<!-- <div class="list_loading" v-if="dataLoading">POBIERANIE DANYCH...</div> -->
|
||||||
<transition name="list-anim" mode="out-in">
|
<transition name="list-anim" mode="out-in">
|
||||||
<ul class="list_content" v-if="!dataLoading && computedHistoryList.length != 0" :key="inputStationName">
|
<ul class="list_content" v-if="!dataLoading && !historyLoading && computedHistoryList.length != 0" :key="inputStationName">
|
||||||
<li v-if="currentDispatcherFrom != -1" class="current">
|
<li v-if="currentDispatcherFrom != -1" class="current">
|
||||||
<div class="dispatcher-name">
|
<div class="dispatcher-name">
|
||||||
<a :href="`https://td2.info.pl/profile/?u=${currentDispatcherId}`">{{ currentDispatcher}}</a>
|
<a :href="`https://td2.info.pl/profile/?u=${currentDispatcherId}`">{{ currentDispatcher}}</a>
|
||||||
@@ -67,41 +67,30 @@ import { Component, Vue, Watch } from "vue-property-decorator";
|
|||||||
import { Getter } from "vuex-class";
|
import { Getter } from "vuex-class";
|
||||||
|
|
||||||
import Station from "@/scripts/interfaces/Station";
|
import Station from "@/scripts/interfaces/Station";
|
||||||
|
import ISceneryInfoData from "@/scripts/interfaces/ISceneryInfoData";
|
||||||
interface ISceneryHistory {
|
|
||||||
_id: string;
|
|
||||||
stationHash: string;
|
|
||||||
stationName: string;
|
|
||||||
currentDispatcher: string;
|
|
||||||
currentDispatcherId: number;
|
|
||||||
currentDispatcherFrom: number;
|
|
||||||
currentDispatcherTo: number;
|
|
||||||
dispatcherHistory: {
|
|
||||||
dispatcherName: string;
|
|
||||||
dispatcherFrom: number;
|
|
||||||
dispatcherId: number;
|
|
||||||
dispatcherTo: number;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class HistoryView extends Vue {
|
export default class HistoryView extends Vue {
|
||||||
@Getter("getStationList") stationList!: Station[];
|
@Getter("getStationList") stationList!: Station[];
|
||||||
|
|
||||||
sceneryHistoryList: ISceneryHistory[] = [];
|
sceneryHistoryList: ISceneryInfoData[] = [];
|
||||||
|
currentSceneryHistory: ISceneryInfoData["dispatcherHistory"] = [];
|
||||||
|
|
||||||
currentSceneryHistory: ISceneryHistory["dispatcherHistory"] = [];
|
|
||||||
currentDispatcher: string = "";
|
currentDispatcher: string = "";
|
||||||
currentDispatcherId: number = 0;
|
currentDispatcherId: number = 0;
|
||||||
currentDispatcherFrom: number = -1;
|
currentDispatcherFrom: number = -1;
|
||||||
|
|
||||||
inputStationName = "";
|
inputStationName = "";
|
||||||
dataLoading = true;
|
|
||||||
|
dataLoading = true; /* Initial data */
|
||||||
|
historyLoading = false; /* History loaded after input is checked */
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
try {
|
try {
|
||||||
const responseData: ISceneryHistory[] = await (
|
const responseData: ISceneryInfoData[] = await (
|
||||||
await axios.get("https://stacjownik.herokuapp.com/api/getSceneryInfo")
|
await axios.get(
|
||||||
|
"https://stacjownik.herokuapp.com/api/getSceneryInfo?items=-1"
|
||||||
|
)
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
this.sceneryHistoryList = responseData;
|
this.sceneryHistoryList = responseData;
|
||||||
@@ -147,17 +136,25 @@ export default class HistoryView extends Vue {
|
|||||||
.reverse();
|
.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
itemSelected(itemName: string) {
|
async itemSelected(itemName: string) {
|
||||||
const selectedScenery = this.sceneryHistoryList.find(
|
try {
|
||||||
(scenery) => scenery.stationName == itemName
|
this.historyLoading = true;
|
||||||
);
|
|
||||||
|
|
||||||
if (!selectedScenery) return;
|
const selectedScenery: ISceneryInfoData = await (
|
||||||
|
await axios.get(
|
||||||
|
`https://stacjownik.herokuapp.com/api/getSceneryInfo?name=${itemName}&items=10`
|
||||||
|
)
|
||||||
|
).data;
|
||||||
|
|
||||||
this.currentSceneryHistory = selectedScenery.dispatcherHistory;
|
this.currentSceneryHistory = selectedScenery.dispatcherHistory;
|
||||||
this.currentDispatcher = selectedScenery.currentDispatcher;
|
this.currentDispatcher = selectedScenery.currentDispatcher;
|
||||||
this.currentDispatcherId = selectedScenery.currentDispatcherId;
|
this.currentDispatcherId = selectedScenery.currentDispatcherId;
|
||||||
this.currentDispatcherFrom = selectedScenery.currentDispatcherFrom;
|
this.currentDispatcherFrom = selectedScenery.currentDispatcherFrom;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.historyLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -278,7 +275,7 @@ export default class HistoryView extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&_content {
|
&_content {
|
||||||
max-height: 550px;
|
max-height: 600px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
padding: 0.2em 0.5em;
|
padding: 0.2em 0.5em;
|
||||||
|
|||||||
@@ -1,770 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="timetable-view" v-if="stationInfo">
|
|
||||||
<div class="view-content">
|
|
||||||
<div class="timetable-options">
|
|
||||||
<div
|
|
||||||
class="option"
|
|
||||||
v-for="(option, key) in options"
|
|
||||||
:key="key"
|
|
||||||
:class="{ checked: option.state }"
|
|
||||||
@click="setOption(key, !option.state)"
|
|
||||||
>{{ option.name }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="timetable-wrapper">
|
|
||||||
<div class="timetable-title">
|
|
||||||
<b>{{ stationInfo.stationName.toUpperCase() }}</b>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="timetable-header">
|
|
||||||
<span>DO STACJI</span>
|
|
||||||
<span>PRZEZ</span>
|
|
||||||
<span>POCIĄG</span>
|
|
||||||
<span>PLAN. ODJAZD</span>
|
|
||||||
<span>OPÓŹNIENIE</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="timetable-body">
|
|
||||||
<div class="timetable-item" v-for="timetable in computedRows" :key="timetable.trainNo">
|
|
||||||
<div class="row-bar"></div>
|
|
||||||
<div class="timetable-row">
|
|
||||||
<span class="row-destination">
|
|
||||||
<div class="letter-wrapper">
|
|
||||||
<div v-for="(letter, j) in timetable.destinationTable" :key="j" class="letter">
|
|
||||||
<transition name="roll-anim" mode="out-in">
|
|
||||||
<span :key="letter">{{ letter }}</span>
|
|
||||||
</transition>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="row-next" ref="next">
|
|
||||||
<div v-for="(letter, j) in timetable.nearestStopTable" :key="j" class="letter">
|
|
||||||
<transition name="roll-anim" mode="out-in">
|
|
||||||
<span :key="letter">{{ letter }}</span>
|
|
||||||
</transition>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="row-type">
|
|
||||||
<div class="letter-wrapper">
|
|
||||||
<span class="letter" v-for="(letter, j) in timetable.trainCategoryTable" :key="j">
|
|
||||||
<transition name="roll-anim" mode="out-in">
|
|
||||||
<span :key="letter">{{ letter }}</span>
|
|
||||||
</transition>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="letter-wrapper">
|
|
||||||
<span class="letter" v-for="(num, j) in timetable.trainNumberTable" :key="j+3">
|
|
||||||
<transition name="roll-anim" mode="out-in">
|
|
||||||
<span :key="num">{{ num }}</span>
|
|
||||||
</transition>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="row-time">
|
|
||||||
<div class="letter-wrapper">
|
|
||||||
<span class="letter" v-for="(num, h) in timetable.departureHoursTable" :key="h">
|
|
||||||
<transition name="roll-anim" mode="out-in">
|
|
||||||
<span :key="num">{{ num }}</span>
|
|
||||||
</transition>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span>:</span>
|
|
||||||
|
|
||||||
<span
|
|
||||||
class="letter"
|
|
||||||
v-for="(num, i) in timetable.departureMinutesTable"
|
|
||||||
:key="i + 5"
|
|
||||||
>
|
|
||||||
<transition name="roll-anim" mode="out-in">
|
|
||||||
<span :key="num">{{ num }}</span>
|
|
||||||
</transition>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="row-delay">
|
|
||||||
<transition name="roll-anim" mode="out-in">
|
|
||||||
<span
|
|
||||||
:key="timetable.delay"
|
|
||||||
>{{ timetable.delay > 0 ? `${timetable.delay} min` : "" }}</span>
|
|
||||||
</transition>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <div v-if="stationInfo.online">
|
|
||||||
{{ stationInfo.stationName }}
|
|
||||||
</div>
|
|
||||||
<div v-else>Stacja offline!</div>-->
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { Component, Vue } from "vue-property-decorator";
|
|
||||||
import { Getter } from "vuex-class";
|
|
||||||
import { Howl } from "howler";
|
|
||||||
|
|
||||||
import Station from "@/scripts/interfaces/Station";
|
|
||||||
import Timetable from "@/scripts/interfaces/Timetable";
|
|
||||||
|
|
||||||
interface TimetableRow {
|
|
||||||
destinationTable: string[];
|
|
||||||
destinationString: string;
|
|
||||||
|
|
||||||
nearestStopTable: string[];
|
|
||||||
nearestStopString: string;
|
|
||||||
|
|
||||||
departureHoursTable: string[];
|
|
||||||
departureHoursString: string;
|
|
||||||
|
|
||||||
departureMinutesTable: string[];
|
|
||||||
departureMinutesString: string;
|
|
||||||
|
|
||||||
trainNumberTable: string[];
|
|
||||||
trainNumberString: string;
|
|
||||||
|
|
||||||
trainCategoryTable: string[];
|
|
||||||
trainCategoryString: string;
|
|
||||||
|
|
||||||
category: string;
|
|
||||||
number: number;
|
|
||||||
|
|
||||||
delay: number;
|
|
||||||
delayPlate: string;
|
|
||||||
|
|
||||||
departureTimestamp: number;
|
|
||||||
arrivalTimestamp: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sound = new Howl({
|
|
||||||
src: require("@/assets/sound.wav"),
|
|
||||||
loop: true,
|
|
||||||
autoplay: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let soundPlaying = false;
|
|
||||||
|
|
||||||
const filteredNames = [
|
|
||||||
["ALEKSANDRÓW KUJAWSKI", "ALEKSDR KUJ."],
|
|
||||||
["GDAŃSK GŁÓWNY", "GDAŃSK GŁ."],
|
|
||||||
["GDAŃSK POŁUDNIOWY", "GDAŃSK PŁD."],
|
|
||||||
["ARKADIA ZDRÓJ", "ARKADIA ZDR."],
|
|
||||||
["GRABÓW MIASTO", "GRABÓW M."],
|
|
||||||
["ZGIERZ KONTREWERS", "ZGIERZ KONTR."],
|
|
||||||
["BUCZ WILEŃSKI", "BUCZ WIL."],
|
|
||||||
["SZKLANA PORĘBA", "SZKLANA POR."],
|
|
||||||
["TARNOWO GÓRNE", "TARNOWO G."],
|
|
||||||
["BARGOWICE ZACHÓD", "BARGOWICE Z."],
|
|
||||||
];
|
|
||||||
|
|
||||||
const letterSet: string[] = Array.from(
|
|
||||||
" -.,/AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWXYZŹŻ"
|
|
||||||
);
|
|
||||||
|
|
||||||
const numberSet: string[] = Array.from(" 0123456789");
|
|
||||||
|
|
||||||
const delaySet: string[] = [
|
|
||||||
"5 min",
|
|
||||||
"10 min",
|
|
||||||
"15 min",
|
|
||||||
"20 min",
|
|
||||||
"25 min",
|
|
||||||
"30 min",
|
|
||||||
"35 min",
|
|
||||||
"40 min",
|
|
||||||
"45 min",
|
|
||||||
"50 min",
|
|
||||||
"55 min",
|
|
||||||
"60 min",
|
|
||||||
">60 min",
|
|
||||||
];
|
|
||||||
|
|
||||||
let globalID = 0;
|
|
||||||
let letterSeekArray: {
|
|
||||||
currentRowIndex: number;
|
|
||||||
letterIndex: number;
|
|
||||||
currentChar: string;
|
|
||||||
finalChar: string;
|
|
||||||
arrayName: string;
|
|
||||||
numeric: boolean;
|
|
||||||
id: number;
|
|
||||||
}[] = [];
|
|
||||||
|
|
||||||
let plateSeekArray: {
|
|
||||||
currentRowIndex: number;
|
|
||||||
currentPlate: string;
|
|
||||||
wantedPlate: string;
|
|
||||||
}[] = [];
|
|
||||||
|
|
||||||
let nextRefreshTime = 0;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
export default class TimetableView extends Vue {
|
|
||||||
@Getter("getStationList") stationList!: Station[];
|
|
||||||
|
|
||||||
maxRows = 5;
|
|
||||||
|
|
||||||
options = {
|
|
||||||
excludeCargo: { name: "Tylko pasażerskie", state: false },
|
|
||||||
playSounds: { name: "Dźwięki", state: false },
|
|
||||||
};
|
|
||||||
|
|
||||||
timetableRows: TimetableRow[] = new Array(this.maxRows)
|
|
||||||
.fill(0)
|
|
||||||
.map((row) => ({
|
|
||||||
origin: new Array(13).fill(" "),
|
|
||||||
|
|
||||||
destinationTable: new Array(13).fill(" "),
|
|
||||||
destinationString: "",
|
|
||||||
|
|
||||||
nearestStopTable: new Array(13).fill(" "),
|
|
||||||
nearestStopString: "",
|
|
||||||
|
|
||||||
departureHoursTable: new Array(2).fill(" "),
|
|
||||||
departureHoursString: "",
|
|
||||||
|
|
||||||
departureMinutesTable: new Array(2).fill(" "),
|
|
||||||
departureMinutesString: "",
|
|
||||||
|
|
||||||
trainNumberTable: new Array(6).fill(" "),
|
|
||||||
trainNumberString: "",
|
|
||||||
|
|
||||||
trainCategoryTable: new Array(3).fill(" "),
|
|
||||||
trainCategoryString: "",
|
|
||||||
|
|
||||||
category: "",
|
|
||||||
number: 0,
|
|
||||||
|
|
||||||
delay: 0,
|
|
||||||
delayPlate: "",
|
|
||||||
|
|
||||||
departureTimestamp: 0,
|
|
||||||
arrivalTimestamp: 0,
|
|
||||||
}));
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
window.requestAnimationFrame(this.findNextLetters);
|
|
||||||
}
|
|
||||||
|
|
||||||
deactivated() {
|
|
||||||
this.resetTimetable();
|
|
||||||
letterSeekArray.length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Options */
|
|
||||||
setOption(optionKey: string, optionValue: any) {
|
|
||||||
this.$set(this.options[optionKey], "state", optionValue);
|
|
||||||
|
|
||||||
// this.options[optionKey].state = optionValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===== */
|
|
||||||
|
|
||||||
resetTimetable(indexFrom: number = 0, forceClear: boolean = false) {
|
|
||||||
if (indexFrom > this.maxRows - 1) return;
|
|
||||||
|
|
||||||
console.log("reset");
|
|
||||||
|
|
||||||
for (let i = indexFrom; i < this.timetableRows.length; i++) {
|
|
||||||
const currentRow = this.timetableRows[i];
|
|
||||||
|
|
||||||
for (let propName in currentRow) {
|
|
||||||
let propValue = currentRow[propName];
|
|
||||||
|
|
||||||
switch (typeof propValue) {
|
|
||||||
case "object":
|
|
||||||
propValue = propValue.map((v) => " ");
|
|
||||||
this.addToSeek(i, propName, propValue, false);
|
|
||||||
this.$set(this.timetableRows[i], propName, propValue);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "string":
|
|
||||||
propValue = "";
|
|
||||||
this.$set(this.timetableRows[i], propName, propValue);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "number":
|
|
||||||
propValue = 0;
|
|
||||||
this.$set(this.timetableRows[i], propName, propValue);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addToSeek(
|
|
||||||
currentRowIndex: number,
|
|
||||||
arrayName: string,
|
|
||||||
next: string | string[],
|
|
||||||
numeric: boolean
|
|
||||||
) {
|
|
||||||
// globalID++;
|
|
||||||
|
|
||||||
this.timetableRows[currentRowIndex][arrayName].forEach((letter, i) => {
|
|
||||||
letterSeekArray.push({
|
|
||||||
currentRowIndex,
|
|
||||||
letterIndex: i,
|
|
||||||
currentChar: letter,
|
|
||||||
finalChar: next[i] ? next[i] : numeric ? "0" : " ",
|
|
||||||
arrayName,
|
|
||||||
numeric,
|
|
||||||
id: ++globalID,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get stationInfo(): Station | null {
|
|
||||||
if (!this.$route.query.station) return null;
|
|
||||||
|
|
||||||
const station = this.stationList.find(
|
|
||||||
(station) => station.stationHash === this.$route.query.station
|
|
||||||
);
|
|
||||||
|
|
||||||
return station || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateRows(info: Station | null = this.stationInfo) {
|
|
||||||
if (!info) return;
|
|
||||||
if (info.scheduledTrains.length == 0) return;
|
|
||||||
|
|
||||||
console.log("update");
|
|
||||||
|
|
||||||
const scheduledTrains = info.scheduledTrains
|
|
||||||
.filter((train) => {
|
|
||||||
if (train.stopInfo.departureTimestamp == 0) return false;
|
|
||||||
|
|
||||||
if (this.options.excludeCargo.state) {
|
|
||||||
if (train.trainNo.toString().length === 5) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (!this.options.excludeCargo.state) return true;
|
|
||||||
|
|
||||||
// const trainNumberLength = train.trainNo.toString().length;
|
|
||||||
|
|
||||||
// if (trainNumberLength === 4 || trainNumberLength === 5) return true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.sort((a, b) => {
|
|
||||||
if (a.stopInfo.departureTimestamp >= b.stopInfo.departureTimestamp)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
let currentRowIndex = 0;
|
|
||||||
for (let train of scheduledTrains) {
|
|
||||||
if (train.stopInfo.confirmed) continue;
|
|
||||||
if (currentRowIndex > this.maxRows - 1) break;
|
|
||||||
|
|
||||||
const currentRow = this.timetableRows[currentRowIndex];
|
|
||||||
|
|
||||||
const departureHours = new Array(2)
|
|
||||||
.fill("0")
|
|
||||||
.map((num, i) => train.stopInfo.departureTimeString[i]);
|
|
||||||
|
|
||||||
const departureMinutes = new Array(2)
|
|
||||||
.fill("0")
|
|
||||||
.map((num, i) => train.stopInfo.departureTimeString[i + 3]);
|
|
||||||
|
|
||||||
const trainNumberString = train.trainNo.toString();
|
|
||||||
|
|
||||||
let destination = train.terminatesAt.toUpperCase();
|
|
||||||
let nearestStop = train.nearestStop.toUpperCase();
|
|
||||||
|
|
||||||
for (let name of filteredNames) {
|
|
||||||
if (name[0] === destination) destination = name[1];
|
|
||||||
|
|
||||||
if (name[0] === nearestStop) nearestStop = name[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRow.destinationString !== destination) {
|
|
||||||
console.log(destination);
|
|
||||||
|
|
||||||
this.addToSeek(currentRowIndex, "destinationTable", destination, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRow.nearestStopString !== nearestStop) {
|
|
||||||
this.addToSeek(currentRowIndex, "nearestStopTable", nearestStop, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRow.departureHoursString != departureHours.toString()) {
|
|
||||||
this.addToSeek(
|
|
||||||
currentRowIndex,
|
|
||||||
"departureHoursTable",
|
|
||||||
departureHours,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRow.departureMinutesString != departureMinutes.toString()) {
|
|
||||||
this.addToSeek(
|
|
||||||
currentRowIndex,
|
|
||||||
"departureMinutesTable",
|
|
||||||
departureMinutes,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRow.trainNumberString != train.trainNo.toString()) {
|
|
||||||
this.addToSeek(
|
|
||||||
currentRowIndex,
|
|
||||||
"trainNumberTable",
|
|
||||||
trainNumberString,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRow.trainCategoryString != train.category) {
|
|
||||||
this.addToSeek(
|
|
||||||
currentRowIndex,
|
|
||||||
"trainCategoryTable",
|
|
||||||
train.category,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRow.delayPlate != currentRow.delay.toString()) {
|
|
||||||
plateSeekArray.push({
|
|
||||||
currentRowIndex,
|
|
||||||
currentPlate: currentRow.delayPlate,
|
|
||||||
wantedPlate: currentRow.delay > 0 ? currentRow.delay.toString() : "",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRow.destinationString = destination;
|
|
||||||
currentRow.nearestStopString = nearestStop;
|
|
||||||
|
|
||||||
currentRow.category = train.category;
|
|
||||||
currentRow.number = train.trainNo;
|
|
||||||
|
|
||||||
currentRow.departureHoursString = departureHours.toString();
|
|
||||||
currentRow.departureMinutesString = departureMinutes.toString();
|
|
||||||
|
|
||||||
currentRow.trainNumberString = trainNumberString;
|
|
||||||
currentRow.trainCategoryString = train.category;
|
|
||||||
|
|
||||||
currentRow.arrivalTimestamp = train.stopInfo.arrivalTimestamp;
|
|
||||||
currentRow.departureTimestamp = train.stopInfo.departureTimestamp;
|
|
||||||
|
|
||||||
currentRow.delay =
|
|
||||||
train.stopInfo.arrivalDelay >= 0 ? train.stopInfo.arrivalDelay : 0;
|
|
||||||
|
|
||||||
currentRowIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.resetTimetable(currentRowIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
get computedRows(): TimetableRow[] {
|
|
||||||
this.updateRows();
|
|
||||||
|
|
||||||
return this.timetableRows;
|
|
||||||
}
|
|
||||||
|
|
||||||
seekMainLetters() {
|
|
||||||
if (letterSeekArray.length == 0) return;
|
|
||||||
|
|
||||||
for (let i = 0; i < letterSeekArray.length; i++) {
|
|
||||||
const wanted = letterSeekArray[i];
|
|
||||||
|
|
||||||
const currentSet = wanted.numeric ? numberSet : letterSet;
|
|
||||||
|
|
||||||
if (wanted.currentChar === wanted.finalChar) {
|
|
||||||
letterSeekArray = letterSeekArray.filter(
|
|
||||||
(letter) => letter.id !== wanted.id
|
|
||||||
);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentIndex = currentSet.findIndex(
|
|
||||||
(char) => char === wanted.currentChar
|
|
||||||
);
|
|
||||||
const nextIndex =
|
|
||||||
currentIndex == currentSet.length - 1 ? 0 : currentIndex + 1;
|
|
||||||
const nextChar = currentSet[nextIndex];
|
|
||||||
|
|
||||||
this.$set(
|
|
||||||
this.timetableRows[wanted.currentRowIndex][wanted.arrayName],
|
|
||||||
wanted.letterIndex,
|
|
||||||
currentSet[nextIndex]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (nextChar === wanted.finalChar) {
|
|
||||||
letterSeekArray = letterSeekArray.filter(
|
|
||||||
(letter) => letter.id !== wanted.id
|
|
||||||
);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
letterSeekArray[i].currentChar = nextChar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
seekDelayPlate() {
|
|
||||||
if (plateSeekArray.length == 0) return;
|
|
||||||
|
|
||||||
for (let i = 0; i < plateSeekArray.length; i++) {
|
|
||||||
const plate = plateSeekArray[i];
|
|
||||||
|
|
||||||
if (plate.currentPlate === plate.wantedPlate) {
|
|
||||||
plateSeekArray = plateSeekArray.filter(
|
|
||||||
(p) => plate.currentRowIndex === p.currentRowIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentIndex = delaySet.findIndex((p) => p === plate.currentPlate);
|
|
||||||
|
|
||||||
const nextIndex =
|
|
||||||
currentIndex == delaySet.length - 1 ? 0 : currentIndex + 1;
|
|
||||||
|
|
||||||
const nextPlate = delaySet[nextIndex];
|
|
||||||
|
|
||||||
this.timetableRows[plate.currentRowIndex].delayPlate =
|
|
||||||
delaySet[nextIndex];
|
|
||||||
|
|
||||||
if (nextPlate === plate.wantedPlate) {
|
|
||||||
plateSeekArray = plateSeekArray.filter(
|
|
||||||
(p) => plate.currentRowIndex === p.currentRowIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
findNextLetters(time) {
|
|
||||||
if (time > nextRefreshTime) {
|
|
||||||
this.seekMainLetters();
|
|
||||||
// this.seekDelayPlate();
|
|
||||||
// this.seekSideLetters(time);
|
|
||||||
|
|
||||||
nextRefreshTime = time + 140;
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// letterSeekArray.length < 10 ? letterSeekArray : letterSeekArray.length
|
|
||||||
// );
|
|
||||||
|
|
||||||
if (this.options.playSounds.state) {
|
|
||||||
if (letterSeekArray.length > 0 && !soundPlaying) {
|
|
||||||
sound.play();
|
|
||||||
soundPlaying = true;
|
|
||||||
} else if (letterSeekArray.length == 0) {
|
|
||||||
setTimeout(() => {
|
|
||||||
sound.stop();
|
|
||||||
soundPlaying = false;
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sound.stop();
|
|
||||||
soundPlaying = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(this.findNextLetters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
$bg: #111;
|
|
||||||
|
|
||||||
.timetable-view {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
font-size: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timetable-wrapper {
|
|
||||||
width: 1500px;
|
|
||||||
min-width: 1500px;
|
|
||||||
|
|
||||||
padding: 0.5rem;
|
|
||||||
|
|
||||||
background: $bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timetable-options {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
font-size: 0.4em;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.option {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
|
|
||||||
margin: 0.3em;
|
|
||||||
padding: 0.35em 0.7em;
|
|
||||||
border-radius: 0.4em;
|
|
||||||
|
|
||||||
transition: all 0.2s ease-in;
|
|
||||||
|
|
||||||
&:not(.checked) {
|
|
||||||
background-color: #585858;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.checked {
|
|
||||||
background-color: #05b702;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
box-shadow: 0 0 6px 1px #05b702;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
position: absolute;
|
|
||||||
content: "";
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
border-radius: inherit;
|
|
||||||
transition: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.timetable {
|
|
||||||
&-title {
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
padding: 0.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-header,
|
|
||||||
&-row {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 4fr 4fr 3fr 1fr 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-header {
|
|
||||||
span {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.35em;
|
|
||||||
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-row {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
font-size: 0.4em;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.letter {
|
|
||||||
border: 1px solid gray;
|
|
||||||
width: 25px;
|
|
||||||
height: 30px;
|
|
||||||
|
|
||||||
margin: 0 2px;
|
|
||||||
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.letter-wrapper {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
&-bar {
|
|
||||||
width: 100%;
|
|
||||||
height: 3px;
|
|
||||||
background: white;
|
|
||||||
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-type {
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
div {
|
|
||||||
margin: 0 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
// span {
|
|
||||||
// display: inline-block;
|
|
||||||
// width: 60%;
|
|
||||||
// min-height: 40px;
|
|
||||||
// line-height: 40px;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
&-delay {
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: inline-block;
|
|
||||||
width: 70%;
|
|
||||||
min-height: 40px;
|
|
||||||
line-height: 40px;
|
|
||||||
|
|
||||||
background: #444;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.type {
|
|
||||||
&-category {
|
|
||||||
background: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.roll-anim-enter-active,
|
|
||||||
.roll-anim-leave-active {
|
|
||||||
transition: transform 70ms;
|
|
||||||
will-change: transform;
|
|
||||||
|
|
||||||
// opacity: 0;
|
|
||||||
}
|
|
||||||
.roll-anim-enter,
|
|
||||||
.roll-anim-leave-to {
|
|
||||||
transform: rotate3d(1, 0, 0, -90deg);
|
|
||||||
// opacity: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
Reference in New Issue
Block a user