view client/src/components/TimeSlider.vue @ 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
children 9d288d9b851b
line wrap: on
line source

<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>