vue词云根据值显示不同大小的文字

vue yekong

vue数据大屏项目开发中,需要实现词云效果,这里使用的是插件js-tag-cloud,但是这款插件不能根据数值显示不同大小的文字,所以只能进行二次修改了。

vue词云根据值显示不同大小的文字

修改后的代码

class JsTagCloud {
  /* 构造函数 */
  constructor(container = document.body, texts, options) {
    const self = this
    if (!container || container.nodeType !== 1) {
      return new Error('Incorrect element type')
    }

    // 处理参数
    self.$container = container
    self.texts = texts || []
    self.config = { ...JsTagCloud._defaultConfig, ...(options || {}) }
    self.colors = self.config.colors || ['#47A1FF', '#59CB74', '#FBD54A', '#FFB952', '#FC6980', '#9F8CF1', '#6367EC', '#ADDF84', '#6CD3FF', '#ED8CCE', '#659AEC', '#A2E5FF', '#84E0BE', '#4DCCCB', '#5982F6', '#E37474', '#F79452', '#DA65CC', '#9861E5', '#3FDDC7']
    // 计算配置
    self.radius = self.config.radius // 滚动半径
    self.depth = 2 * self.radius // 滚动深度
    self.size = 1.5 * self.radius // 随鼠标滚动变速作用区域
    self.maxSpeed = JsTagCloud._getMaxSpeed(self.config.maxSpeed) // 滚动最大速度倍数
    self.initSpeed = JsTagCloud._getInitSpeed(self.config.initSpeed) // 滚动初速度
    self.direction = self.config.direction // 初始滚动方向
    self.keep = self.config.keep // 鼠标移出后是否保持之前滚动

    // 创建元素
    self._createElment()
    // 初始化
    self._init()
    // 设置元素及实例
    JsTagCloud.list.push({ el: self.$el, container, instance: self })
  }

  /* 静态属性方法 */
  // 所有 JsTagCloud 的个数
  static list = []

  // 默认配置
  static _defaultConfig = {
    radius: 100, // 滚动半径, 单位px
    maxSpeed: 'normal', // 滚动最大速度, 取值: slow, normal(默认), fast
    initSpeed: 'normal', // 滚动初速度, 取值: slow, normal(默认), fast
    direction: 135, // 初始滚动方向, 取值角度(顺时针deg): 0 对应 top, 90 对应 left, 135 对应 right-bottom(默认)...
    keep: false // 鼠标移出组件后是否继续随鼠标滚动, 取值: false, true(默认) 对应 减速至初速度滚动, 随鼠标滚动
  }

  // 速度对应的数值
  static _getMaxSpeed = name => ({ slow: 5, normal: 10, fast: 20 }[name] || 10)

  static _getInitSpeed = name => ({ slow: 20, normal: 40, fast: 80 }[name] || 50)

  // 事件监听
  static _on(el, ev, handler, cap) {
    if (el.addEventListener) {
      el.addEventListener(ev, handler, cap)
    } else if (el.attachEvent) {
      el.attachEvent(`on${ev}`, handler)
    } else {
      el[`on${ev}`] = handler
    }
  }

  /* 实例属性方法 */
  // 创建元素
  _createElment() {
    const self = this

    // 创建容器元素
    const $el = document.createElement('div')
    $el.className = 'JsTagCloud'
    $el.style.position = 'relative'
    $el.style.width = `${2 * self.radius}px`
    $el.style.height = `${2 * self.radius}px`

    // 创建文本元素
    self.items = []
    self.texts.forEach((text, index) => {
      const item = self._createTextItem(text, index)
      $el.appendChild(item.el)
      self.items.push(item)
    })
    self.$container.appendChild($el)
    self.$el = $el
  }

  // 创建单个文本项
  _createTextItem(textObj, index = 0) {
    const self = this;
    const itemEl = document.createElement('span');
    itemEl.className = 'js-tag-cloud-item';
    itemEl.style.position = 'absolute';
    itemEl.style.top = '50%';
    itemEl.style.left = '50%';
    itemEl.style.zIndex = index + 1;
    itemEl.style.filter = 'alpha(opacity=0)';
    itemEl.style.opacity = 0;
    itemEl.style.color = self.colors[Math.floor(Math.random() * self.colors.length)];
    itemEl.style.willChange = 'transform, opacity, filter';

    // 设置文字大小
    const fontSize = Math.max(10, textObj.value) + 'px'; // 设置文字大小,最小10px
    itemEl.style.fontSize = fontSize;

    // 确保文字不换行
    itemEl.style.whiteSpace = 'nowrap';

    // 显示 name
    itemEl.innerText = textObj.name;

    // 鼠标悬停显示 name 和 value
    itemEl.title = `${textObj.name}: ${textObj.value}`;

    const transformOrigin = '50% 50%';
    itemEl.style.WebkitTransformOrigin = transformOrigin;
    itemEl.style.MozTransformOrigin = transformOrigin;
    itemEl.style.OTransformOrigin = transformOrigin;
    itemEl.style.transformOrigin = transformOrigin;

    const transform = 'translateX(-50%) translateY(-50%) scale(1)';
    itemEl.style.WebkitTransform = transform;
    itemEl.style.MozTransform = transform;
    itemEl.style.OTransform = transform;
    itemEl.style.transform = transform;

    const transition = 'all .1s';
    itemEl.style.WebkitTransition = transition;
    itemEl.style.MozTransition = transition;
    itemEl.style.OTransition = transition;
    itemEl.style.transition = transition;

    return {
      el: itemEl,
      text: textObj.name,
      value: textObj.value,
      ...self._computePosition(index) // 分布在合适的位置
    };
  }

  // 计算位置
  _computePosition(index, random = false) {
    const self = this
    const textsLength = self.texts.length
    // random 为 true, 则表示生成随机的合适位置, 位置将 index 无关
    if (random) index = Math.floor(Math.random() * (textsLength + 1))
    const phi = Math.acos(-1 + (2 * index + 1) / textsLength)
    const theta = Math.sqrt((textsLength + 1) * Math.PI) * phi
    return {
      x: (self.size * Math.cos(theta) * Math.sin(phi)) / 2,
      y: (self.size * Math.sin(theta) * Math.sin(phi)) / 2,
      z: (self.size * Math.cos(phi)) / 2
    }
  }

  // 初始化
  _init() {
    const self = this

    self.active = false // 是否为鼠标激活态

    self.mouseX0 = self.initSpeed * Math.sin(self.direction * (Math.PI / 180)) // 鼠标与滚动圆心x轴初始距离
    self.mouseY0 = -self.initSpeed * Math.cos(self.direction * (Math.PI / 180)) // 鼠标与滚动圆心y轴初始距离

    self.mouseX = self.mouseX0 // 鼠标与滚动圆心x轴距离
    self.mouseY = self.mouseY0 // 鼠标与滚动圆心y轴距离

    // 鼠标移入
    JsTagCloud._on(self.$el, 'mouseover', () => {
      self.active = true
    })
    // 鼠标移出
    JsTagCloud._on(self.$el, 'mouseout', () => {
      self.active = false
    })
    // 鼠标在内移动
    JsTagCloud._on(self.keep ? window : self.$el, 'mousemove', ev => {
      ev = ev || window.event
      const rect = self.$el.getBoundingClientRect()
      self.mouseX = (ev.clientX - (rect.left + rect.width / 2)) / 5
      self.mouseY = (ev.clientY - (rect.top + rect.height / 2)) / 5
    })

    // 定时更新状态
    self._next() // 初始更新状态
    self.interval = setInterval(() => {
      self._next.bind(self)()
    }, 100)
  }

  // 运算下一个状态
  _next() {
    const self = this

    // keep 为 false 时, 鼠标移出组件后暂停滚动
    if (!self.keep && !self.active) {
      self.mouseX = Math.abs(self.mouseX - self.mouseX0) < 1 ? self.mouseX0 : (self.mouseX + self.mouseX0) / 2 // 重置鼠标与滚动圆心x轴距离
      self.mouseY = Math.abs(self.mouseY - self.mouseY0) < 1 ? self.mouseY0 : (self.mouseY + self.mouseY0) / 2 // 重置鼠标与滚动圆心y轴距离
    }

    const a = -(Math.min(Math.max(-self.mouseY, -self.size), self.size) / self.radius) * self.maxSpeed
    const b = (Math.min(Math.max(-self.mouseX, -self.size), self.size) / self.radius) * self.maxSpeed

    if (Math.abs(a) <= 0.01 && Math.abs(b) <= 0.01) return // 停止

    // 计算偏移量
    const l = Math.PI / 180
    const sc = [Math.sin(a * l), Math.cos(a * l), Math.sin(b * l), Math.cos(b * l)]

    self.items.forEach(item => {
      const rx1 = item.x
      const ry1 = item.y * sc[1] + item.z * -sc[0]
      const rz1 = item.y * sc[0] + item.z * sc[1]

      const rx2 = rx1 * sc[3] + rz1 * sc[2]
      const ry2 = ry1
      const rz2 = rz1 * sc[3] - rx1 * sc[2]

      const per = (2 * self.depth) / (2 * self.depth + rz2) // todo

      item.x = rx2
      item.y = ry2
      item.z = rz2
      item.scale = per.toFixed(3)
      let alpha = per * per - 0.25
      alpha = (alpha > 1 ? 1 : alpha).toFixed(3)

      const itemEl = item.el
      const left = (item.x - itemEl.offsetWidth / 2).toFixed(2)
      const top = (item.y - itemEl.offsetHeight / 2).toFixed(2)
      const transform = `translateX(``{left}px) translateY(``{top}px) scale(${item.scale})`
      itemEl.style.WebkitTransform = transform
      itemEl.style.MozTransform = transform
      itemEl.style.OTransform = transform
      itemEl.style.transform = transform
      itemEl.style.filter = `alpha(opacity=${100 * alpha})`
      itemEl.style.opacity = alpha
    })
  }

  /* 暴露的实例属性与方法 */
  // 更新
  update(texts) {
    const self = this
    // 处理参数
    self.texts = texts || []
    // 根据 texts 判断并处理 items
    let addWord = []
    let curTexts = []
    self.items.forEach(item => {
      let atInd = texts.indexOf(item.text)
      if (atInd === -1) {
        item.status = 'remove'
      }
      curTexts.push(item.text)
    })
    texts.forEach(text => {
      let atInd = curTexts.indexOf(text)
      if (atInd === -1) {
        addWord.push(text)
      }
    })
    addWord.forEach(text => {
      // add some tags
      let index = self.items.length + 1
      let item = self._createTextItem(text, index)
      Object.assign(item, self._computePosition(index, true)) // 随机位置
      self.$el.appendChild(item.el)
      self.items.push(item)
    })
    for (let i = 0; i < self.items.length; i++) {
      let item = self.items[i]
      if (item.status === 'remove') {
        self.items.splice(i, 1)
        self.$el.removeChild(item.el)
        i--
      }
    }
  }

  // 摧毁
  destroy() {
    const self = this
    self.interval = null
    // 在 JsTagCloud.list 中清除
    const index = JsTagCloud.list.findIndex(e => e.el === self.$el)
    if (index !== -1) JsTagCloud.list.splice(index, 1)
    // 清理元素
    if (self.$container && self.$el) {
      self.$container.removeChild(self.$el)
    }
  }
}

export default (els, texts, options) => {
  if (typeof els === 'string') els = document.querySelectorAll(els)
  if (!els.forEach) els = [els]
  const instances = []
  els.forEach(el => {
    if (el) {
      instances.push(new JsTagCloud(el, texts, options))
    }
  })
  return instances.length <= 1 ? instances[0] : instances
}

使用实例

<template>
  <div class="tagCloud" ref="tagCloud"></div>
</template>

<script>
import TagCloud from './js/index.js'

export default {
  name: "tagCloud",
  data() {
    return {
      list: [
        { name: '智慧政务', value: 20 },
        { name: '数据监控', value: 15 },
        { name: '政务服务', value: 25 },
        { name: '智慧城市', value: 30 },
        { name: '政务大数据', value: 18 },
        { name: '智能分析', value: 22 },
        { name: '实时监控', value: 28 },
        { name: '政务云平台', value: 26 },
        { name: '智慧社区', value: 14 },
        { name: '数据可视化', value: 19 },
        { name: '政务效能', value: 17 },
        { name: '智慧决策', value: 24 },
        { name: '政务协同', value: 21 },
        { name: '智能预警', value: 23 },
        { name: '政务公开', value: 16 },
        { name: '智慧管理', value: 27 },
        { name: '数据共享', value: 13 },
        { name: '智慧政务大厅', value: 29 },
        { name: '政务信息化', value: 12 },
        { name: '智能运维', value: 11 }
      ]
    }
  },
  mounted() {
    // 获取 div 的高度
    const tagCloudElement = this.$refs.tagCloud;
    const height = tagCloudElement.clientHeight;

    const options = {
      radius: height / 2, // 使用 div 高度的一半作为半径
      maxSpeed: 'normal',
      initSpeed: 'normal',
      direction: -(height / 2),
      keep: false,
    }

    TagCloud(tagCloudElement, this.list, options);
  }
}
</script>

<style lang="scss">
.tagCloud {
  position: relative;
  width: 100%;
  height: 100%; // 确保父容器有明确的高度
  display: flex;
  justify-content: center;
  align-items: center;
}
</style>
喜欢