D3.js+svg 绘制时间条

2022-12-12  本文已影响0人  冰落寞成

1、效果

时间条.png

2、vue 引入d3

npm install d3 --save-dev
import * as d3 from 'd3'
Vue.prototype.$d3 = d3 

3、使用line 绘制时间线

<g stroke="#909399" fill="white" stroke-width="1" class="xAxis">
        <line
          :x1="margin.left"
          :y1="margin.top * 1.5"
          :x2="width + margin.left"
          :y2="margin.top * 1.5"
          class="line"
        />
        <template v-for="(item, index) in xAxisDatas">
          <g class="xAxis-point" :key="item">
            <line
              :x1="margin.left + (width / (xAxisDatas.length - 1)) * index"
              :y1="margin.top * 1.5"
              :x2="margin.left + (width / (xAxisDatas.length - 1)) * index"
              :y2="margin.top"
            />
            <text
              v-if="
                item % ((xAxisDatas.length - 1) / svgConfig.splitLine.num) === 0
              "
              stroke-width="0.1"
              :x="margin.left + (width / (xAxisDatas.length - 1)) * index"
              :y="margin.top - svgConfig.xAxisTextOffsetX"
              text-anchor="middle"
              fill="#303133"
            >
              {{ item }}
            </text>
          </g>
        </template>
      </g>

4、使用rect 绘制时间条背景,时间条,左右滑块

5、使用linearGradient 绘制渐变背景

<defs>
        <linearGradient id="BtnBg" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" stop-color="#fff" />
          <stop offset="50%" stop-color="#409EFF" />
          <stop offset="100%" stop-color="#fff" />
        </linearGradient>
      </defs>

6、使用filter 渲染光晕效果

<defs>
        <filter id="BarBgFilter" x="0" y="0" width="200%" height="200%">
          <feOffset result="offOut" in="SourceGraphic" dx="0" dy="0" />
          <feGaussianBlur result="floodFill" in="offOut" stdDeviation="10" />
          <feBlend in="SourceGraphic" in2="floodFill" mode="multiply" />
        </filter>
      </defs>

7、全部代码

<template>
  <div class="time-bar-container">
    <svg :class="`T${svgId} time-bar-content`" viewBox="0 0 600 50">
      <!-- <path class="domain" stroke="#fff" d="M20.5,6V30H580.5V6" y="50"></path> -->
      <g stroke="#909399" fill="white" stroke-width="1" class="xAxis">
        <line
          :x1="margin.left"
          :y1="margin.top * 1.5"
          :x2="width + margin.left"
          :y2="margin.top * 1.5"
          class="line"
        />
        <template v-for="(item, index) in xAxisDatas">
          <g class="xAxis-point" :key="item">
            <line
              :x1="margin.left + (width / (xAxisDatas.length - 1)) * index"
              :y1="margin.top * 1.5"
              :x2="margin.left + (width / (xAxisDatas.length - 1)) * index"
              :y2="margin.top"
            />
            <text
              v-if="
                item % ((xAxisDatas.length - 1) / svgConfig.splitLine.num) === 0
              "
              stroke-width="0.1"
              :x="margin.left + (width / (xAxisDatas.length - 1)) * index"
              :y="margin.top - svgConfig.xAxisTextOffsetX"
              text-anchor="middle"
              fill="#303133"
            >
              {{ item }}
            </text>
          </g>
        </template>
      </g>

      <defs>
        <linearGradient id="BtnBg" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" stop-color="#fff" />
          <stop offset="50%" stop-color="#409EFF" />
          <stop offset="100%" stop-color="#fff" />
        </linearGradient>
      </defs>
      <defs>
        <filter id="BarBgFilter" x="0" y="0" width="200%" height="200%">
          <feOffset result="offOut" in="SourceGraphic" dx="0" dy="0" />
          <feGaussianBlur result="floodFill" in="offOut" stdDeviation="10" />
          <feBlend in="SourceGraphic" in2="floodFill" mode="multiply" />
        </filter>
      </defs>
      <defs>
        <linearGradient id="BarBg" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" stop-color="#79bbff" />
          <stop offset="30%" stop-color="#409EFF" />
          <stop offset="50%" stop-color="#409EFF" />
          <stop offset="80%" stop-color="#409EFF" />
          <stop offset="100%" stop-color="#79bbff" />
        </linearGradient>
      </defs>
      <g class="time-bar-main">
        <rect
          :x="margin.left"
          :y="margin.top * 1.5"
          :width="width"
          :height="svgConfig.barHeight"
          class="time-bar-bg"
          @mousedown="handleMouseDown"
        ></rect>
        <g
          class="time-bar-container"
          stroke="#fff"
          fill="url(#BarBg)"
          stroke-width="2"
        ></g>
        <g
          class="start-btn-container btn-container"
          stroke="#409EFF"
          fill="url(#BtnBg)"
          stroke-width="1"
        ></g>
        <g
          class="end-btn-container btn-container"
          stroke="#409EFF"
          fill="url(#BtnBg)"
          stroke-width="1"
        ></g>
        <g
          class="tip-container"
          stroke="#409EFF"
          fill="url(#BtnBg)"
          stroke-width="1"
          v-show="showTip"
        >
          <use class="tip-main" x="0" y="0" xlink:href="#tipIcon" fill="#fff" />
          <Tip :tipText="tipText" :tipWidth="svgConfig.barBtnConfig.tipWidth" />
        </g>
      </g>
      <!-- <DeleteTimeBar :size="30" /> -->
    </svg>
  </div>
</template>
<script>
import DeleteTimeBar from './DeleteTimeBar.vue'
import Tip from './MarkTip.vue'
export default {
  components: { Tip },
  data () {
    return {
      showTip: false,
      svgId: new Date().getTime(),
      margin: {
        top: 20,
        right: 20,
        bottom: 20,
        left: 20
      },
      offsetWidth: 600,
      offsetHeight: 50,
      width: 600,
      height: 50,

      svg: null,
      xAxisDatas: [],
      spacing: 0, // 间距
      secondSpan: 0, // 1个间距之间对应1秒的跨度
      svgConfig: {
        xAxisTextOffsetX: 3, // 坐标轴偏移量
        splitLine: {
          // 分割线设置对象
          num: 12
        },
        barHeight: 15, // 时间条设置
        barBtnConfig: {
          //  滑块设置
          width: 8,
          tipWidth: 30
        }
      },
      currentStartBtnG: null, // 当前开始按钮对象
      currentEndBtnG: null, // 当前结束按钮对象
      currentBar: null, // 当前时间条对象
      currentStartBtnOffsetX: null, // 当前开始按钮偏移量
      currentEndBtnOffsetX: null, // 当前结束按钮偏移量
      currentTimeBarWidth: 0, // 当前时间条宽度
      timeBarBg: null, // 背景条对象
      tipMark: null, // 提示框对象,
      currentTimeObjec: {
        // 当前时间对象
        hour: 0,
        minute: 0,
        second: 0
      },
      tipText: '00:00',
      startTime: null,
      endTime: null,
      showTimeHour: false
    }
  },
  created () {
    this.svgId = this.initUUID()
  },
  mounted () {
    this.initSvg()
    this.initWidth()
  },

  methods: {
    /**
     * 生成UUID
     */
    initUUID () {
      function S4 () {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
      }
      return S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4()
    },
    /**
     * 生成所选时间
     */
    handleTimeRange (type) {
      if (type === 'start') {
        this.currentStartBtnG.attr('class', 'start-btn-g opt-btn-g') // 更改鼠标样式为禁止样式
        this.currentStartBtnG.on('mousemove', null, false)
      } else if (type === 'end') {
        this.currentEndBtnG.on('mousemove', null, false)
      } else {
        this.currentStartBtnG.attr('class', 'start-btn-g opt-btn-g') // 更改鼠标样式为禁止样式
        this.currentEndBtnG.attr('class', 'end-btn-g opt-btn-g') // 更改鼠标样式为禁止样式
        this.currentStartBtnG.on('mousemove', null, false)
        this.currentEndBtnG.on('mousemove', null, false)
      }
      this.showTip = false

      // console.log('width', this.currentTimeBarWidth)
      if (this.currentTimeBarWidth === 0) {
        // 时间条是0 的时候,删除所创建的组件
        this.currentStartBtnG.remove()
        this.currentEndBtnG.remove()
        this.currentBar.remove()
        this.currentStartBtnG = null
        this.currentEndBtnG = null
        this.currentBar = null
        this.startTime = null
        this.endTime = null
      }
      this.$emit('getTime', {
        startTime: this.startTime,
        endTime: this.endTime
      })
    },
    /**
     * 初始化时间
     */
    initTime (obj) {
      this.startTime = obj?.startTime || null
      this.endTime = obj?.endTime || null

      if (this.startTime && this.endTime) {
        this.$nextTick(() => {
          this.dataBackfill()
        })
      } else {
        if (this.currentStartBtnG && this.currentEndBtnG && this.currentBar) {
          this.currentStartBtnG.remove()
          this.currentEndBtnG.remove()
          this.currentBar.remove()
          this.currentStartBtnG = null
          this.currentEndBtnG = null
        }
      }
    },
    /**
     * 清空时间
     */
    clearTimes () {
      if (this.startTime && this.endTime) {
        this.currentStartBtnG.remove()
        this.currentEndBtnG.remove()
        this.currentBar.remove()
        this.currentStartBtnG = null
        this.currentEndBtnG = null
        this.currentBar = null
        this.startTime = null
        this.endTime = null
      }

      this.$emit('getTime', {
        startTime: this.startTime,
        endTime: this.endTime
      })
    },
    /**
     * 数据回填
     */
    dataBackfill () {
      this.initOptBtn()
      this.initTimeBar() //  初始化时间条
      this.currentStartBtnG.attr('class', 'start-btn-g opt-btn-g') // 更改鼠标样式为禁止样式
      this.currentEndBtnG.attr('class', 'end-btn-g opt-btn-g') // 更改鼠标样式为禁止样式
      this.currentStartBtnG.on('mousemove', null, false)
      this.currentEndBtnG.on('mousemove', null, false)
    },
    /**
     * 鼠标在时间背景条上摁下
     */
    handleMouseDown (e) {
      if (!this.currentStartBtnG && !this.currentEndBtnG) {
        this.initOptBtn(e)
        this.initTimeBar(e) //  初始化时间条
      }
    },
    /**
     * 初始化时间条
     */
    initTimeBar (e) {
      this.currentBar = this.svg
        .select('.time-bar-container')
        .append('g')
        .attr('class', 'time-bar-g')
        .append('rect')
        .attr('class', 'time-bar-strip')
        .attr('x', this.currentStartBtnOffsetX)
        .attr('y', this.margin.top * 1.5)
        .attr('width', this.currentTimeBarWidth)
        .attr('height', this.svgConfig.barHeight)
    },
    /**
     * 设置时间条的宽度
     */
    handleSetTimeBarWidth (type = 'end') {
      this.currentTimeBarWidth =
        this.currentEndBtnOffsetX - this.currentStartBtnOffsetX
      if (type === 'start') {
        this.currentBar
          .attr('x', () => {
            return this.currentStartBtnOffsetX
          })
          .attr('width', () => {
            return this.currentTimeBarWidth
          })
      }
      if (type === 'end') {
        this.currentBar.attr('width', () => {
          return this.currentTimeBarWidth
        })
      }
    },
    /**
     * 设置时间映射关系
     */
    handleTimeMapping (offsetX = 0, type = 'end') {
      let x = offsetX
      // console.log('offsetX', offsetX, offsetX + this.svgConfig.barBtnConfig.width / 2)
      if ((offsetX + this.svgConfig.barBtnConfig.width / 2 >= this.margin.left) && offsetX < (this.width + this.margin.left)) {
        x = offsetX + this.svgConfig.barBtnConfig.width / 2 - this.margin.left
      } else if (offsetX <= this.margin.left) {
        x = 0
      } else if (offsetX >= this.width) {
        x = this.width
      }
      this.currentTimeObjec.hour = Math.floor(x / this.spacing)
      let remainder = x - this.currentTimeObjec.hour * this.spacing
      // console.log('dd', remainder)
      let remainderSecond = remainder / this.secondSpan //  剩余秒数
      if (remainder === 0 && this.currentTimeObjec.hour !== 0) {
        this.currentTimeObjec.hour = Math.floor(x / this.spacing) - 1
        remainder = x - this.currentTimeObjec.hour * this.spacing
        this.currentTimeObjec.minute =
          parseInt(remainder / this.secondSpan / 60) - 1 // 59分钟处理

        this.currentTimeObjec.second =
          parseInt(
            remainder / this.secondSpan - this.currentTimeObjec.minute * 60
          ) - 1 // 59秒处理
      } else {
        this.currentTimeObjec.minute = parseInt(remainderSecond / 60) // 分钟处理
        this.currentTimeObjec.second = parseInt(
          remainderSecond - this.currentTimeObjec.minute * 60
        )
      }
      if (this.showTimeHour) {
        this.tipText = `${this.currentTimeObjec.hour < 10 ? `0${this.currentTimeObjec.hour}` : this.currentTimeObjec.hour}
       :${this.currentTimeObjec.minute < 10 ? `0${this.currentTimeObjec.minute}` : this.currentTimeObjec.minute}
      :${this.currentTimeObjec.second < 10 ? `0${this.currentTimeObjec.second}` : this.currentTimeObjec.second}`
      } else {
        this.tipText = `${
        this.currentTimeObjec.minute < 10
          ? `0${this.currentTimeObjec.minute}`
          : this.currentTimeObjec.minute
      }
      :${
        this.currentTimeObjec.second < 10
          ? `0${this.currentTimeObjec.second}`
          : this.currentTimeObjec.second
      }`
      }
      let time = `${
        this.currentTimeObjec.hour < 10
          ? `0${this.currentTimeObjec.hour}`
          : this.currentTimeObjec.hour
      }:${
        this.currentTimeObjec.minute < 10
          ? `0${this.currentTimeObjec.minute}`
          : this.currentTimeObjec.minute
      }:${
        this.currentTimeObjec.second < 10
          ? `0${this.currentTimeObjec.second}`
          : this.currentTimeObjec.second
      }`
      if (type === 'start') {
        this.startTime = time || null
      }
      if (type === 'end') {
        this.endTime = time || null
      }
      // this.$emit('getTime', { startTime: this.startTime, endTime: this.endTime })
    },
    /**
     * 设置tip 位置 和文字提示
     */
    handleSetTip ({ offX = 0, showBool = false, type = 'end' } = {}) {
      this.showTip = showBool
      this.handleTimeMapping(offX, type)
      this.tipMark.attr('x', () => {
        let reaOffX =
          (this.svgConfig.barBtnConfig.tipWidth -
            this.svgConfig.barBtnConfig.width) /
          2
        return offX - reaOffX
      })
    },
    /**
     * 创建滑块btn
     */
    initOptBtn (e) {
      /**
       * 创建开始btn 按钮
       */

      if (this.startTime && this.endTime) {
        // console.log('dddddddddddddddddddddd')
        let startTimeArr = this.startTime.split(':')
        let startTimeHour = parseInt(startTimeArr[0])
        let startTimeMinute = parseInt(startTimeArr[1])
        let startTimeSecond = parseInt(startTimeArr[1])
        let endTimeArr = this.endTime.split(':')
        let endTimeHour = parseInt(endTimeArr[0])
        let endTimeMinute = parseInt(endTimeArr[1])
        let endTimeSecond = parseInt(endTimeArr[1])
        this.currentStartBtnOffsetX =
          startTimeHour * this.spacing +
          (startTimeMinute * 60 + startTimeSecond) * this.secondSpan +
          this.margin.left - this.svgConfig.barBtnConfig.width / 2
        this.currentEndBtnOffsetX =
          endTimeHour * this.spacing +
          (endTimeMinute * 60 + endTimeSecond) * this.secondSpan +
          this.margin.left -
          this.svgConfig.barBtnConfig.width / 2
        this.currentTimeBarWidth =
          this.currentEndBtnOffsetX - this.currentStartBtnOffsetX //  时间条设置
      } else {
        // console.log('___________________')
        let optOffsetX = e.offsetX - this.svgConfig.barBtnConfig.width / 2 // 滑块居中
        if (optOffsetX < this.margin.left) {
          // 最左侧判断
          optOffsetX = this.margin.left - this.svgConfig.barBtnConfig.width / 2
        }
        this.currentStartBtnOffsetX = optOffsetX
        this.currentEndBtnOffsetX = optOffsetX
      }

      // 开始滑块按钮
      this.currentStartBtnG = this.handleOptBtnCreate({
        optBtnContainerClassName: '.start-btn-container',
        optBtnGClassName: 'start-btn-g',
        optBtnClassName: 'start-btn',
        offsetX: this.currentStartBtnOffsetX,
        tipShowBool: true
      })
      // 结束滑块按钮
      this.currentEndBtnG = this.handleOptBtnCreate({
        optBtnContainerClassName: '.end-btn-container',
        optBtnGClassName: 'end-btn-g',
        optBtnClassName: 'end-btn',
        offsetX: this.currentEndBtnOffsetX,
        tipShowBool: true
      })
      this.handleOptBtnBindEvent() //  绑定事件
      this.handleTimeMapping(this.currentStartBtnOffsetX, 'start')
      this.handleTimeMapping(this.currentEndBtnOffsetX, 'end')
    },
    /**
     * @param{类名} optBtnContainerClassName 开始、结束滑块父级节点选择器(class,id,元素)
     * @param{类名} optBtnGClassName 开始、结束滑块节点组选择器要命名的class 名称
     * @param{类名} optBtnClassName 开始、结束滑块节点选择器要命名的class 名称
     * @param{int} offsetX 相对x 的位置
     * @param{int} tipShowBool 提示框显隐设置,true-> 显示,false 隐藏
     * 给开始、结束滑块绑定事件
     */
    handleOptBtnCreate ({
      optBtnContainerClassName = '.end-btn-container',
      optBtnGClassName = 'end-btn-g',
      optBtnClassName = 'end-btn',
      offsetX = this.currentEndBtnOffsetX,
      tipShowBool = true
    }) {
      let optBtnG = null
      // console.log('this.svg', this.svg)
      optBtnG = this.svg
        .select(optBtnContainerClassName)
        .append('g')
        .attr('class', `${optBtnGClassName} move opt-btn-g`)
        .on('mouseover', (e) => {
          // this.currentStartBtnG.attr('class', 'start-btn-g move opt-btn-g') // 更改鼠标样式为可移动样式
          // this.currentEndtBtnG.attr('class', 'end-btn-g move opt-btn-g') // 更改鼠标样式为可移动样式
          if (optBtnClassName === 'end-btn') {
            this.handleSetTip({
              offX: this.currentEndBtnOffsetX,
              showBool: tipShowBool,
              type: 'end'
            })
          }
          if (optBtnClassName === 'start-btn') {
            this.handleSetTip({
              offX: this.currentStartBtnOffsetX,
              showBool: tipShowBool,
              type: 'start'
            })
          }
        })
        .on('mouseleave', () => {
          this.handleTimeRange()
        })
      /**
       * 创建结束滑块,并绑定事件
       */
      optBtnG
        .append('rect')
        .attr('class', `${optBtnClassName} opt-btn`)
        .attr('x', offsetX) // 为了让指针居中
        .attr('y', this.margin.top * 1.5)
        .attr('rx', 3)
        .attr('width', this.svgConfig.barBtnConfig.width)
        .attr('height', this.svgConfig.barHeight)
      return optBtnG
    },
    /**
     * 开始、结束滑块按钮绑定鼠标事件
     */
    handleOptBtnBindEvent () {
      //  开始滑块按钮事件绑定
      // this.currentStartBtnG.on('mousemove', this.handleStartBtnMouseMove, false)
      this.currentStartBtnG.on(
        'mousedown',
        () => {
          this.currentStartBtnG.attr('class', 'start-btn-g move opt-btn-g') // 更改鼠标样式为可移动样式
          // console.log('this.currentStartBtnG')
          this.currentStartBtnG.on(
            'mousemove',
            this.handleStartBtnMouseMove,
            false
          )
        },
        false
      )
      this.currentStartBtnG.on(
        'mouseup',
        () => {
          this.currentEndBtnG
            .select('.end-btn')
            .style('pointer-events', 'auto') // 事件禁止穿透
          // console.log('start-mouseup')
          // this.$emit('getTime', {
          //   startTime: this.startTime,
          //   endTime: this.endTime
          // })
          // this.currentStartBtnG.on('mousemove', null, false)
          this.handleTimeRange('start')
        },
        false
      )

      //  结束滑块按钮事件绑定
      this.currentEndBtnG.on('mousemove', this.handleEndBtnMouseMove, false)
      this.currentEndBtnG.on(
        'mousedown',
        () => {
          this.currentEndBtnG.attr('class', 'end-btn-g move opt-btn-g') // 更改鼠标样式为可移动样式
          this.currentEndBtnG.on(
            'mousemove',
            this.handleEndBtnMouseMove,
            false
          )
        },
        false
      )
      this.currentEndBtnG.on(
        'mouseup',
        () => {
          this.handleTimeRange('end')
        },
        false
      )
    },
    /** 开始按钮移动事件 */
    handleStartBtnMouseMove (e) {
      this.currentEndBtnG.select('.end-btn').style('pointer-events', 'none') // 事件可以穿透
      let barOffsetX = e.offsetX - this.svgConfig.barBtnConfig.width / 2 // 实时滑块的定位
      let barWidth = this.currentEndBtnOffsetX - barOffsetX // 实时时间条宽度
      if (barWidth >= 0 && e.offsetX >= this.margin.left) {
        barOffsetX = e.offsetX - this.svgConfig.barBtnConfig.width / 2
      } else if (barWidth < 0) {
        barOffsetX = this.currentEndBtnOffsetX
      } else if (e.offsetX < this.margin.left) {
        barOffsetX = this.margin.left - this.svgConfig.barBtnConfig.width / 2
      }
      // if (barWidth >= 0 && barOffsetX >= this.margin.left) {
      //   // console.log('zhe')
      //   barOffsetX = e.offsetX - this.svgConfig.barBtnConfig.width / 2
      // } else if (barWidth < 0) {
      //   // console.log('临街')
      //   barOffsetX = this.currentEndBtnOffsetX
      // } else if (barOffsetX < this.margin.left) {
      //   barOffsetX = this.margin.left
      // }
      this.currentStartBtnOffsetX = barOffsetX //  判断后的结束滑块定位
      this.currentStartBtnG.select('.start-btn').attr('x', () => {
        return this.currentStartBtnOffsetX
      })
      this.handleSetTip({
        offX: this.currentStartBtnOffsetX,
        showBool: true,
        type: 'start'
      }) // 移动tip 位置
      this.handleSetTimeBarWidth('start')
    },
    /** 结束按钮移动事件 */
    handleEndBtnMouseMove (e) {
      let barOffsetX = e.offsetX - this.svgConfig.barBtnConfig.width / 2 // 实时滑块的定位
      let barWidth = barOffsetX - this.currentStartBtnOffsetX // 实时时间条宽度
      if (
        barWidth >= 0 &&
        (e.offsetX - this.margin.left) < this.width
      ) {
        barOffsetX = e.offsetX - this.svgConfig.barBtnConfig.width / 2
      } else if (barWidth < 0) {
        barOffsetX = this.currentStartBtnOffsetX
      } else if (
        (e.offsetX - this.margin.left) >= this.width
      ) {
        barOffsetX =
          this.width + this.margin.left - this.svgConfig.barBtnConfig.width / 2
      }
      // if (
      //   barWidth >= 0 &&
      //   barOffsetX + this.svgConfig.barBtnConfig.width <=
      //     this.width + this.margin.left
      // ) {
      //   barOffsetX = e.offsetX - this.svgConfig.barBtnConfig.width / 2
      // } else if (barWidth < 0) {
      //   barOffsetX = this.currentStartBtnOffsetX
      // } else if (
      //   barOffsetX + this.svgConfig.barBtnConfig.width >
      //   this.width + this.margin.left
      // ) {
      //   barOffsetX =
      //     this.width + this.margin.left - this.svgConfig.barBtnConfig.width
      // }
      this.currentEndBtnOffsetX = barOffsetX //  判断后的结束滑块定位
      this.currentEndBtnG.select('.end-btn').attr('x', () => {
        return this.currentEndBtnOffsetX
      })
      this.handleSetTip({
        offX: this.currentEndBtnOffsetX,
        showBool: true,
        type: 'end'
      }) // 移动tip 位置
      this.handleSetTimeBarWidth()
    },
    /**
     * 初始化宽度
     */
    initWidth () {
      this.width = this.offsetWidth - this.margin.left - this.margin.right
      this.height = this.offsetHeight - this.margin.top - this.margin.bottom
      this.spacing = this.width / (this.xAxisDatas.length - 1)
      this.secondSpan = this.spacing / (60 * 60) // (1个小时之间的秒跨度)
      // console.log('this.width', this.width)
      // console.log('this.spacing', this.spacing, this.secondSpan)
    },
    /**
     * 创建svg
     */
    initSvg () {
      this.svg = this.$d3.select(`.T${this.svgId}`)
      this.timeBarBg = this.svg.select('.time-bar-bg')
      this.tipMark = this.svg.select('.tip-container').select('.tip-main')
      this.xAxisDatas = []
      for (let i = 0; i < 25; i++) {
        this.xAxisDatas.push(i)
      }
    }
  }
}
</script>
<style lang="scss" scoped>
.time-bar-container {
  .time-bar-content {
    width: 600px;
    height: 50px;
    .xAxis-point {
      text {
        font-size: 12px;
        font-weight: normal;
        user-select: none;
      }
    }
    .time-bar-main {
      .time-bar-bg {
        fill: #fff;
        stroke: #909399;
        cursor: pointer;
      }
      .btn-container {
        ::v-deep .opt-btn-g {
          position: relative;
          cursor: not-allowed;

          &.move {
            cursor: move;
          }
          // .end-btn{
          //   pointer-events: none;
          // }
          // .opt-btn{
          //   pointer-events: none;
          // }
          .btn-tip {
            background: red;
            color: #fff;
            fill: red;
            text-anchor: middle;
          }
        }
      }
    }
  }
}
</style>

8、tip 代码markTip.vue

<template>
  <svg class="icon">
    <defs>
      <linearGradient id="TipBg" x1="0%" y1="0%" x2="0%" y2="100%">
        <stop offset="0%"  stop-color="#409EFF"/>
        <stop offset="50%" stop-color="#409EFF"/>
        <stop offset="100%" stop-color="#fff"/>
      </linearGradient>
  </defs>
    <symbol id="tipIcon" viewBox="0 0 1024 1024" :width="tipWidth" :height="tipWidth" fill="#000" fill-opacity="0.7">
      <mask id="tipMask"  maskContentUnits="userSpaceOnUse" >
        <text x="512" y="512" fill="#fff" font-size="300px" text-anchor="middle" >{{tipText}}</text>
      </mask>
      <!-- tip 图标 -->
      <path id="tip"
      d="M170.666667 85.333333 853.333333 85.333333C900.266667 85.333333 938.666667 123.733333 938.666667 170.666667L938.666667 682.666667C938.666667 729.6 900.266667 768 853.333333 768L682.666667 768 512 938.666667 341.333333 768 170.666667 768C123.733333 768 85.333333 729.6 85.333333 682.666667L85.333333 170.666667C85.333333 123.733333 123.733333 85.333333 170.666667 85.333333Z"></path>
      <use mask="url(#tipMask)" xlink:href="#tip" fill="#fff"  />
      </symbol>
  </svg>
</template>
<script>
export default {
  name: 'markTip',
  props: {
    tipText: {
      type: String,
      default: () => ''
    },
    tipWidth: {
      type: Number,
      default: () => 50
    }
  }
}
</script>

上一篇下一篇

猜你喜欢

热点阅读