<template>
  <div
    class="risk-desc">
    <div class="risk-desc-wrap">
      <span
        @click="tooltipShow"
        ref="iconMark"
        class="risk-desc-iconSize">
        <slot name="icon">
          <img src="//store.sdbao.com/product/detail/riskDescMark.png">
        </slot>
      </span>
        <div
          ref="icon"
          v-show="visible"
          :style="{
            ...iconStyle,
            borderColor: `transparent transparent ${backgroundColor}`
          }"
          class="risk-desc-icon"
          :class="!isAuto ? `risk-desc-icon-${type}` : ''">
        </div>
        <div
          ref="tooltip"
          v-show="visible"
          :style="{
            ...dialogDescStyle,
            backgroundColor
          }"
          class="risk-desc-container"
          :class="!isAuto ? `risk-desc-container-${type}` : ''">
          <slot name="container"></slot>
        </div>
    </div>
  </div>
</template>
<script>
import create from '../utils/create-basic';

const HtmlFontSize = document.documentElement.style.fontSize || 37.5 // 获取当前HTML的字体大小
const getNumber = parseFloat(HtmlFontSize) // 获取浮点数
const cardinalNumber = 750 / (getNumber * 10) // 算出图纸和真机之间的系数
const defaultCoefficient = +parseFloat(20 / cardinalNumber).toFixed(2) // 左右边距
const dialogMargin = +parseFloat(26 / cardinalNumber).toFixed(2) * 1 // 上下边距
const documentW = document.documentElement.clientWidth // 获取浏览器的宽
const documentH = document.documentElement.clientHeight // 获取浏览器的高

export default create({
  name: 'tooltip',
  props: {
    visible: {
      type: Boolean,
      default: false
    },
    type: {
      type: String,
      default: 'auto'
    },
    backgroundColor: {
      type: String,
      default: '#fff'
    }
  },
  data () {
    return {
      isdiff: false,
      dialogDescStyle: {},
      iconStyle: {},
      tooltipLocation: {}, // 弹框数据
      tooltipIcon: {}
    }
  },
  computed: {
    isAuto () {
      const type = ['top', 'right', 'bottom', 'left']
      return !type.includes(this.type)
    }
  },
  watch: {
    // 当弹框隐藏的时候初始化方便下次计算
    visible (visible) {
      if (!visible) {
        this.initDom()
      }
    }
  },
  mounted() {
    this.listenerDescMark = this.throttle((e) => {
      // 如果是false 就不计算了
      if (!this.visible) return
      // 判断当前点击
      const isParentClassName = this.getParentName(e.target, 'risk-desc')
      if (!isParentClassName) {
        this.$emit('update:visible', false)
      }
    }, 100, {}, this)
    this.tooltipShow = this.throttle(this.tooltipShow, 100, {}, this)
    document.addEventListener('click', this.listenerDescMark)
  },
  methods: {
    init () {
      if (!this.isAuto) return
      const dom = this.$refs.tooltip // 获取弹框dom
      const iconDom = this.$refs.icon // 获取小三角的dom
      this.tooltipLocation = dom.getBoundingClientRect()
      this.tooltipIcon = iconDom.getBoundingClientRect() // 获取小三角距离左上角个个参数
      this.iconMarkWidth = this.$refs.iconMark.offsetWidth // 获取问号的宽
      this.iconMarkHeight = this.$refs.iconMark.offsetHeight // 获取问号的高
      this.bottomFlag = (documentH - dialogMargin) < (this.tooltipLocation.top + this.tooltipLocation.height * 2) // 底部判断如果弹框超出当前底部视口
      this.riskDescX = -defaultCoefficient // 弹框初始化 translateX的距离
      this.riskDescY = this.tooltipLocation.height + dialogMargin // 弹框初始化 translateY的距离
      this.iconX = (this.iconMarkWidth / 2) - (this.tooltipIcon.width / 2) // ?icon的宽度一半 减去 方框一半的宽度
      this.iconY = dialogMargin // 方框距离问号的距离
      this.rotate = 'rotate(0deg)'
      this.auto()
    },
    autoLeftRight () {
      if ((this.tooltipLocation.right + defaultCoefficient) > documentW) { // 弹框右边超出
        this.riskDescX = documentW - this.tooltipLocation.right - defaultCoefficient // 浏览器宽度 减去 弹框右边距离浏览器左上角的距离 就是超出的距离 再减去 右边距离 就是弹框偏移量
      } else if (this.tooltipLocation.left <= defaultCoefficient) {
        this.riskDescX = -this.tooltipLocation.left + defaultCoefficient // 左边超出的距离 加上左边距 就是弹框偏移量
      }
    },
    auto () {
      this.autoLeftRight()
      if (this.bottomFlag) { // 是否超出当前窗口底部
        this.riskDescY = -this.iconMarkHeight - dialogMargin // 问号高度 减去当前上边距 就是弹框偏移量
        this.iconY = -this.iconMarkHeight - dialogMargin + this.tooltipIcon.height
        this.rotate = 'rotate(180deg)'
      }
      this.setPositon()
    },
    setPositon () {
      this.dialogDescStyle = {
        transform: `translate3D(${this.riskDescX}px, ${this.riskDescY}px, 0)`
      }
      this.iconStyle = {
        transform: `translate3D(${this.iconX}px, ${this.iconY}px, 0) ${this.rotate}`
      }
    },
    /**
     * underscore 节流函数，返回函数连续调用时，func 执行频率限定为 次 / wait
     *
     * @param  {function}   func      回调函数
     * @param  {number}     wait      表示时间窗口的间隔
     * @param  {object}     options   如果想忽略开始函数的的调用，传入{leading: false}。
     *                                如果想忽略结尾函数的调用，传入{trailing: false}
     *                                两者不能共存，否则函数不能执行
     * @return {function}             返回客户调用函数
     */
    throttle(func, wait, options, ctx) {
      let context, args, result
      let timeout = null
      // 之前的时间戳
      let previous = 0
      // 如果 options 没传则设为空对象
      if (!options) options = {}
      // 定时器回调函数
      const later = function() {
        // 如果设置了 leading，就将 previous 设为 0
        // 用于下面函数的第一个 if 判断
        previous = options.leading === false ? 0 : +new Date()
        // 置空一是为了防止内存泄漏，二是为了下面的定时器判断
        timeout = null
        result = func.apply(context, args)
        if (!timeout) context = args = null
      }
      return function() {
        // 获得当前时间戳
        const now = +new Date()
        // 首次进入前者肯定为 true
        // 如果需要第一次不执行函数
        // 就将上次时间戳设为当前的
        // 这样在接下来计算 remaining 的值时会大于0
        if (!previous && options.leading === false) previous = now
        // 计算剩余时间
        const remaining = wait - (now - previous)
        context = ctx
        args = arguments
        // 如果当前调用已经大于上次调用时间 + wait
        // 或者用户手动调了时间
        // 如果设置了 trailing，只会进入这个条件
        // 如果没有设置 leading，那么第一次会进入这个条件
        // 还有一点，你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
        // 其实还是会进入的，因为定时器的延时
        // 并不是准确的时间，很可能你设置了2秒
        // 但是他需要2.2秒才触发，这时候就会进入这个条件
        if (remaining <= 0 || remaining > wait) {
          // 如果存在定时器就清理掉否则会调用二次回调
          if (timeout) {
            clearTimeout(timeout)
            timeout = null
          }
          previous = now
          result = func.apply(context, args)
          if (!timeout) context = args = null
        } else if (!timeout && options.trailing !== false) {
          // 判断是否设置了定时器和 trailing
          // 没有的话就开启一个定时器
          // 并且不能不能同时设置 leading 和 trailing
          timeout = setTimeout(later, remaining)
        }
        return result
      }
    },
    initDom() {
      this.dialogDescStyle = {
        transform: ''
      }
      this.iconStyle = {
        transform: ''
      }
    },
    // 弹框显示
    tooltipShow() {
      this.$emit('change')
      this.$nextTick(() => {
        if (!this.visible) return
        this.init()
      })
    },
    /**
     * 判断当前元素祖父元素是否有某个class
     * @param {obj} 子元素
     * @param {name} 祖父元素className
     */
    getParentName (obj, name) {
      let parentNodes = obj
      let flag = false
      while (parentNodes) {
        if (parentNodes.className && parentNodes.className.split(' ').includes(name)) {
          flag = true
          parentNodes = null
        } else {
          parentNodes = parentNodes.parentNode
        }
      }
      return flag
    }
  },
  destroyed () {
    document.removeEventListener('click', this.listenerDescMark)
  }
})
</script>
