Mercurial > gemma
changeset 5036:8f421cd3c746 time-sliding
client: Implemented first version of time-sliding
author | Fadi Abbud <fadi.abbud@intevation.de> |
---|---|
date | Thu, 27 Feb 2020 09:18:17 +0100 |
parents | b65898de11ad |
children | 9d288d9b851b |
files | client/src/components/App.vue client/src/components/TimeSlider.vue client/src/components/map/Zoom.vue client/src/components/toolbar/TimeSlider.vue client/src/components/toolbar/Toolbar.vue client/src/store/application.js |
diffstat | 6 files changed, 225 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/client/src/components/App.vue Fri Feb 21 10:08:32 2020 +0100 +++ b/client/src/components/App.vue Thu Feb 27 09:18:17 2020 +0100 @@ -26,6 +26,7 @@ </div> </div> <MapPopup /> + <TimeSlider v-if="isMapVisible" /> </div> <router-view /> <vue-snotify /> @@ -111,6 +112,7 @@ Layers: () => import("./layers/Layers"), Sidebar: () => import("./Sidebar"), Search: () => import("./Search"), + TimeSlider: () => import("./TimeSlider"), Contextbox: () => import("./Contextbox"), Toolbar: () => import("./toolbar/Toolbar"), Popup: () => import("./Popup"),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/components/TimeSlider.vue Thu Feb 27 09:18:17 2020 +0100 @@ -0,0 +1,172 @@ +<template> + <div + id="slider" + :class="[ + 'd-flex box ui-element rounded bg-white mw-100 flex-row', + { expand: showTimeSlider } + ]" + > + <div + id="sliderContainer" + class="d-flex sliderContainer" + style="width: 98%;" + ></div> + <div @click="close" class="d-flex box-control mr-0" style="width: 2%;"> + <font-awesome-icon icon="times"></font-awesome-icon> + </div> + </div> +</template> +<style lang="sass"> +#slider + position: absolute + bottom: 0 + width: 100% + &.expand + max-height: 100%; + max-width: 100%; + margin: 0 +</style> +<script> +/* This is Free Software under GNU Affero General Public License v >= 3.0 + * without warranty, see README.md and license for details. + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * License-Filename: LICENSES/AGPL-3.0.txt + * + * Copyright (C) 2020 by via donau + * – Österreichische Wasserstraßen-Gesellschaft mbH + * Software engineering by Intevation GmbH + * + * Author(s): + * Fadi Abbud <fadiabbud@intevation.de> + */ +import { mapState } from "vuex"; +import * as d3 from "d3"; +export default { + name: "timeslider", + data() { + return { + selectedTime: new Date("2020-01-04"), + newX: null + }; + }, + computed: { + ...mapState("application", ["showTimeSlider"]) + }, + methods: { + close() { + this.$store.commit("application/showTimeSlider", false); + }, + createSlider() { + const element = document.getElementById("sliderContainer"); + const svgWidth = element ? element.clientWidth : 0, + svgHeight = 40, + marginTop = 20, + marginLeft = 0; + + this.newX = this.getScale(); + let svg = d3 + .select(".sliderContainer") + .append("svg") + .attr("width", svgWidth) + .attr("height", svgHeight); + + // zoom event + let zoom = d3 + .zoom() + .scaleExtent([0, Infinity]) + .translateExtent([[0, 0], [svgWidth, svgHeight]]) + .extent([[0, 0], [(svgWidth, svgHeight)]]) + .on("zoom", this.zoomed); + + svg + .append("g") + .attr("class", "axis--x") + .attr("transform", `translate(${marginLeft}, ${marginTop})`) + .call( + d3.axisBottom(this.newX).ticks(12) + //.tickFormat(dFormat) + ); + + // create rectanlge on the slider area to capture mouse events + const eventRect = svg + .append("rect") + .attr("id", "zoom") + .attr("class", "zoom") + .attr("width", svgWidth) + .attr("height", svgHeight) + .attr("fill", "white") + .attr("opacity", 0.2) + .on("mouseover", () => { + svg.select(".zoom").attr("cursor", "move"); + }); + eventRect.call(zoom).on("click", this.onClick); + + const toIsoDate = d => { + return d.toISOString(); + }; + + let drag = d3 + .drag() + .on("start", () => { + d3.select(".line") + .raise() + .classed("active", true); + }) + .on("drag", this.onDrag) + .on("end", () => { + d3.select(".line").classed("active", false); + }); + + // Create cursor to indicate to the selected time + svg + .append("rect") + .attr("class", "line") + .attr("id", "scrubber") + .attr("x", this.newX(d3.isoParse(toIsoDate(this.selectedTime)))) + .attr("y", 0) + .attr("width", 2) + .attr("height", svgHeight) + .attr("stroke", "#17a2b8") + .attr("stroke-width", 2) + .attr("opacity", 0.6) + .on("mouseover", () => { + svg.select(".line").attr("cursor", "e-resize"); + }) + .call(drag); + }, + getScale() { + return d3 + .scaleTime() + .range([0, document.getElementById("sliderContainer").clientWidth || 0]) + .domain([ + d3.isoParse(new Date("2020-01-01")), + d3.isoParse(new Date("2020-03-01")) + ]); + }, + zoomed() { + let scale = this.getScale(); + this.newX = d3.event.transform.rescaleX(scale); + d3.select(".axis--x").call( + d3.axisBottom(this.newX).ticks(12) + //.tickFormat(dFormat) + ); + d3.select(".line").attr("x", this.newX(d3.isoParse(this.selectedTime))); + }, + onClick() { + // Extract the click location + let point = d3.mouse(document.getElementById("zoom")), + p = { x: point[0], y: point[1] }; + d3.select(".line").attr("x", p.x); + this.selectedTime = d3.isoParse(this.newX.invert(p.x + 2)); + }, + onDrag() { + this.selectedTime = d3.isoParse(this.newX.invert(d3.event.x + 2)); + d3.select(".line").attr("x", d3.event.x); + } + }, + mounted() { + setTimeout(this.createSlider, 150); + } +}; +</script>
--- a/client/src/components/map/Zoom.vue Fri Feb 21 10:08:32 2020 +0100 +++ b/client/src/components/map/Zoom.vue Thu Feb 27 09:18:17 2020 +0100 @@ -1,5 +1,5 @@ <template> - <div class="zoom-buttons shadow-xs"> + <div :class="['zoom-buttons shadow-xs', { move: showTimeSlider }]"> <button class="zoom-button border-0 bg-white rounded-left ui-element" @click="zoomOut" @@ -24,7 +24,8 @@ margin-left: -$icon-width margin-bottom: 0 transition: margin-bottom 0.3s - + &.move + bottom: $large-offset * 1.5 .zoom-button min-height: $icon-width min-width: $icon-width @@ -34,6 +35,7 @@ </style> <script> +import { mapState } from "vuex"; /* This is Free Software under GNU Affero General Public License v >= 3.0 * without warranty, see README.md and license for details. * @@ -51,6 +53,7 @@ export default { props: ["map"], computed: { + ...mapState("application", ["showTimeSlider"]), zoomLevel: { get() { return this.map.getView().getZoom();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/components/toolbar/TimeSlider.vue Thu Feb 27 09:18:17 2020 +0100 @@ -0,0 +1,35 @@ +<template> + <div + @click="$store.commit('application/showTimeSlider', !showTimeSlider)" + class="toolbar-button" + v-tooltip.right="label" + > + <font-awesome-icon icon="clock" :class="{ 'text-info': showTimeSlider }" /> + </div> +</template> + +<script> +/* This is Free Software under GNU Affero General Public License v >= 3.0 + * without warranty, see README.md and license for details. + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * License-Filename: LICENSES/AGPL-3.0.txt + * + * Copyright (C) 2020 by via donau + * – Österreichische Wasserstraßen-Gesellschaft mbH + * Software engineering by Intevation GmbH + * + * Author(s): + * Fadi Abbud <fadiabbud@intevation.de> + */ +import { mapState } from "vuex"; + +export default { + computed: { + ...mapState("application", ["showTimeSlider"]), + label() { + return this.$gettext("Time slider"); + } + } +}; +</script>
--- a/client/src/components/toolbar/Toolbar.vue Fri Feb 21 10:08:32 2020 +0100 +++ b/client/src/components/toolbar/Toolbar.vue Thu Feb 27 09:18:17 2020 +0100 @@ -14,6 +14,7 @@ <Linetool /> <Polygontool /> <Pdftool /> + <TimeSlider /> </div> <div @click="$store.commit('application/expandToolbar', !expandToolbar)" @@ -128,7 +129,8 @@ Profiles: () => import("./Profiles"), Gauges: () => import("./Gauges"), Pdftool: () => import("./Pdftool"), - AvailableFairwayDepth: () => import("./AvailableFairwayDepth") + AvailableFairwayDepth: () => import("./AvailableFairwayDepth"), + TimeSlider: () => import("./TimeSlider") }, computed: { ...mapState("application", ["expandToolbar"])
--- a/client/src/store/application.js Fri Feb 21 10:08:32 2020 +0100 +++ b/client/src/store/application.js Thu Feb 27 09:18:17 2020 +0100 @@ -41,10 +41,12 @@ showGauges: false, showFairwayDepth: false, showFairwayDepthLNWL: false, + showTimeSlider: false, contextBoxContent: null, // bottlenecks, imports, staging expandToolbar: true, countries: ["AT", "SK", "HU", "HR", "RS", "BG", "RO"], searchQuery: "", + selectedTime: "2020-01-04", version, tempRoute: "", config: {} @@ -93,6 +95,12 @@ if (state.paneRotate === 5) state.paneRotate = 1; } }, + setselectedTime: (state, time) => { + state.selectedTime = time; + }, + showTimeSlider: (state, show) => { + state.showTimeSlider = show; + }, showSidebar: (state, show) => { state.showSidebar = show; },