我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何利用vue3.x绘制流程图

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何利用vue3.x绘制流程图

这篇文章主要介绍“如何利用vue3.x绘制流程图”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“如何利用vue3.x绘制流程图”文章能帮助大家解决问题。

如何利用vue3.x绘制流程图

下面是效果图:

如何利用vue3.x绘制流程图

整体结构布局:

如何利用vue3.x绘制流程图

需要实现的功能列表:

  • 节点与连接线的可配置

  • 节点的拖拽与渲染及连接线的绘制

  • 节点与连接线的选择

  • 节点的样式调整

  • 节点移动时的吸附

  • 撤销和恢复

节点与连接线的可配置

  • 节点配置信息

[  {    'id': '', // 每次渲染会生成一个新的id    'name': 'start', // 节点名称,也就是类型    'label': '开始', // 左侧列表节点的名称    'displayName': '开始', // 渲染节点的显示名称(可修改)    'className': 'icon-circle start', // 节点在渲染时候的class,可用于自定义节点的样式    'attr': { // 节点的属性      'x': 0, // 节点相对于画布的 x 位置      'y': 0, // 节点相对于画布的 y 位置      'w': 70, // 节点的初始宽度      'h': 70  // 节点的初始高度    },    'next': [], // 节点出度的线    'props': [] // 节点可配置的业务属性  },  // ...]
  • 连接线配置信息

// next[  {    // 连接线的id    'id': 'ee1c5fa3-f822-40f1-98a1-f76db6a2362b',    // 连接线的结束节点id    'targetComponentId': 'fa7fbbfa-fc43-4ac8-8911-451d0098d0cb',    // 连接线在起始节点的方向    'directionStart': 'right',    // 连接线在结束节点的方向    'directionEnd': 'left',    // 线的类型(直线、折线、曲线)    'lineType': 'straight',    // 显示在连接线中点的标识信息    'extra': '',    // 连接线在起始节点的id    'componentId': 'fde2a040-3795-4443-a57b-af412d06c023'  },  // ...]
  • 节点的属性配置结构

// props[  {    // 表单的字段    name: 'displayName',    // 表单的标签    label: '显示名称',    // 字段的值    value: '旅客运输',    // 编辑的类型    type: 'input',    // 属性的必填字段    required: true,    // 表单组件的其它属性    props: {        placeholder: 'xxx'    }  },  // ...]

对于下拉选择的数据,如果下拉的数据非常多,那么配置保存的数据量也会很大,所以可以把所有的下拉数据统一管理,在获取左侧的配置节点的信息时,将所有的下拉数据提取出来,以 props 的 name 值为 key 保存起来,在用的时候用 props.name 来取对应的下拉数据。

另外还需要配置连接线的属性,相对于节点的属性,每一个节点的属性都有可能不一样,但是连接线在没有节点的时候是没有的,所以我们要先准备好连接线的属性,在连接线生成的时候,在加到连接线的属性里去。当然我们可以把连接线的属性设置为一样的,也可以根据节点的不同来设置不同连接线的属性。

最后使用的方式:

<template>  <workflow    ref="workflowRef"    @component-change="getActiveComponent"    @line-change="getActiveLine"    main-height="calc(100vh - 160px)">  </workflow></template><script setup>import { ref } from 'vue'import Workflow from '@/components/workflow'import { commonRequest } from '@/utils/common'import { ElMessage, ElMessageBox } from 'element-plus'import { useRoute } from 'vue-router'const route = useRoute()const processId = route.query.processId // || 'testca08c433c34046e4bb2a8d3ce3ebc'const processType = route.query.processType// 切换的当前节点const getActiveComponent = (component: Record<string, any>) => {  console.log('active component', component)}// 切换的当前连接线const getActiveLine = (line: Record<string, any>) => {  console.log('active line', line)}const workflowRef = ref<InstanceType<typeof Workflow>>()// 获取配置的节点列表const getConfig = () => {  commonRequest(`/workflow/getWorkflowConfig?processType=${processType}`).then((res: Record<string, any>) => {    // 需要把所有的属性根据name转换成 key - value 形式    const props: Record<string, any> = {}    transferOptions(res.result.nodes, props)    // 设置左侧配置的节点数据    workflowRef.value?.setConfig(res.result)    getData(props)  })}// 获取之前已经配置好的数据const getData = (props: Record<string, any>) => {  commonRequest(`/workflow/getWfProcess/${processId}`).then((res: Record<string, any>) => {    // 调整属性,这里是为了当配置列表的节点或者属性有更新,从而更新已配置的节点的属性    adjustProps(props, res.result.processJson)    // 设置已配置好的数据,并渲染    workflowRef.value?.setData(res.result.processJson, res.result.type || 'add')  })}const init = () => {  if (!processId) {    ElMessageBox.alert('当前没有流程id')    return  }  getConfig()}init()const transferOptions = (nodes: Record<string, any>[], props: Record<string, any>) => {  nodes?.forEach((node: Record<string, any>) => {    props[node.name] = node.props  })}const adjustProps = (props: Record<string, any>, nodes: Record<string, any>[]) => {  nodes.forEach((node: Record<string, any>) => {    const oldProp: Record<string, any>[] = node.props    const res = transferKV(oldProp)    node.props = JSON.parse(JSON.stringify(props[node.name]))    node.props.forEach((prop: Record<string, any>) => {      prop.value = res[prop.name]    })  })}const transferKV = (props: Record<string, any>[]) => {  const res: Record<string, any> = {}  props.forEach((prop: Record<string, any>) => {    res[prop.name] = prop.value  })  return res}</script>

节点的拖拽与渲染及连接线的绘制

关于节点的拖拽就不多说了,就是 drag 相关的用法,主要是渲染区域的节点和连接线的设计。

这里的渲染区域的思路是:以 canvas 元素作为画布背景,节点是以 div 的方式渲染拖拽进去的节点,拖拽的位置将是以 canvas 的相对位置来移动,大概的结构如下:

<template>    <!-- 渲染区域的祖先元素 -->    <div>        <!-- canvas 画布,绝对于父级元素定位, inset: 0; -->        <canvas></canvas>        <!-- 节点列表渲染的父级元素,绝对于父级元素定位, inset: 0; -->        <div>            <!-- 节点1,绝对于父级元素定位 -->            <div></div>            <!-- 节点2,绝对于父级元素定位 -->            <div></div>            <!-- 节点3,绝对于父级元素定位 -->            <div></div>            <!-- 节点4,绝对于父级元素定位 -->            <div></div>        </div>    </div></template>

而连接线的绘制是根据 next 字段的信息,查找到 targetComponentId 组件的位置,然后在canvas上做两点间的 线条绘制。

链接的类型分为3种: 直线,折线,曲线

  • 直线

直线的绘制最为简单,取两个点连接就行。

// 绘制直线const drawStraightLine = (  ctx: CanvasRenderingContext2D,   points: [number, number][],   highlight?: boolean) => {  ctx.beginPath()  ctx.moveTo(points[0][0], points[0][1])  ctx.lineTo(points[1][0], points[1][1])  // 是否是当前选中的连接线,当前连接线高亮  shadowLine(ctx, highlight)  ctx.stroke()  ctx.restore()  ctx.closePath()}

如何利用vue3.x绘制流程图

  • 折线

折线的方式比较复杂,因为折线需要尽可能的不要把连接线和节点重合,所以它要判断每一种连接线的场景,还有两个节点的宽度和高度也需要考虑计算。如下:

如何利用vue3.x绘制流程图

起始节点有四个方向,目标节点也有四个方向,还有目标节点相对于起始节点有四个象限,所以严格来说,总共有 4 * 4 * 4 = 64 种场景。这些场景中的折线点也不一样,最多的有 4 次, 最少的折 0 次,单求出这 64 种坐标点就用了 700 行代码。

如何利用vue3.x绘制流程图

最后的绘制方法与直线一样:

// 绘制折线const drawBrokenLine = ({ ctx, points }: WF.DrawLineType, highlight?: boolean) => {  ctx.beginPath()  ctx.moveTo(points[0][0], points[0][1])  for (let i = 1; i < points.length; i++) {    ctx.lineTo(points[i][0], points[i][1])  }  shadowLine(ctx, highlight)  ctx.stroke()  ctx.restore()  ctx.closePath()}
  • 曲线

曲线相对于折线来说,思路会简单很多,不需要考虑折线这么多场景。

如何利用vue3.x绘制流程图

这里的折线是用三阶的贝塞尔曲线来绘制的,固定的取四个点,两个起止点,两个控制点,其中两个起止点是固定的,我们只需要求出两个控制点的坐标即可。这里代码不多,可以直接贴出来:

import WF from '../type'const coeff = 0.5export default function calcBezierPoints({ startDire, startx, starty, destDire, destx, desty }: WF.CalcBezierType,  points: [number, number][]) {  const p = Math.max(Math.abs(destx - startx), Math.abs(desty - starty)) * coeff  switch (startDire) {    case 'down':      points.push([startx, starty + p])      break    case 'up':      points.push([startx, starty - p])      break    case 'left':      points.push([startx - p, starty])      break    case 'right':      points.push([startx + p, starty])      break    // no default  }  switch (destDire) {    case 'down':      points.push([destx, desty + p])      break    case 'up':      points.push([destx, desty - p])      break    case 'left':      points.push([destx - p, desty])      break    case 'right':      points.push([destx + p, desty])      break    // no default  }}

简单一点来说,第一个控制点是根据起始点来算的,第二个控制点是跟根据结束点来算的。算的方式是根据当前点相对于节点的方向,继续往前算一段距离,而这段距离是根据起止两个点的最大相对距离的一半(可能有点绕...)。

绘制方法:

// 绘制贝塞尔曲线const drawBezier = ({ ctx, points }: WF.DrawLineType, highlight?: boolean) => {  ctx.beginPath()  ctx.moveTo(points[0][0], points[0][1])  ctx.bezierCurveTo(    points[1][0], points[1][1], points[2][0], points[2][1], points[3][0], points[3][1]  )  shadowLine(ctx, highlight)  ctx.stroke()  ctx.restore()  ctx.globalCompositeOperation = 'source-over'    //目标图像上显示源图像}

节点与连接线的选择

节点是用 div 来渲染的,所以节点的选择可以忽略,然后就是连接点的选择,首先第一点是鼠标在移动的时候都要判断鼠标的当前位置下面是否有连接线,所以这里就有 3 种判断方法,呃... 严格来说是两种,因为折线是多条直线,所以是按直线的判断方法来。

// 判断当前鼠标位置是否有线export const isAboveLine = (offsetX: number, offsetY: number, points: WF.LineInfo[]) => {  for (let i = points.length - 1; i >= 0; --i) {    const innerPonints = points[i].points    let pre: [number, number], cur: [number, number]    // 非曲线判断方法    if (points[i].type !== 'bezier') {      for (let j = 1; j < innerPonints.length; j++) {        pre = innerPonints[j - 1]        cur = innerPonints[j]        if (getDistance([offsetX, offsetY], pre, cur) < 20) {          return points[i]        }      }    } else {      // 先用 x 求出对应的 t,用 t 求相应位置的 y,再比较得出的 y 与 offsetY 之间的差值      const tsx = getBezierT(innerPonints[0][0], innerPonints[1][0], innerPonints[2][0], innerPonints[3][0], offsetX)      for (let x = 0; x < 3; x++) {        if (tsx[x] <= 1 && tsx[x] >= 0) {          const ny = getThreeBezierPoint(tsx[x], innerPonints[0], innerPonints[1], innerPonints[2], innerPonints[3])          if (Math.abs(ny[1] - offsetY) < 8) {            return points[i]          }        }      }      // 如果上述没有结果,则用 y 求出对应的 t,再用 t 求出对应的 x,与 offsetX 进行匹配      const tsy = getBezierT(innerPonints[0][1], innerPonints[1][1], innerPonints[2][1], innerPonints[3][1], offsetY)      for (let y = 0; y < 3; y++) {        if (tsy[y] <= 1 && tsy[y] >= 0) {          const nx = getThreeBezierPoint(tsy[y], innerPonints[0], innerPonints[1], innerPonints[2], innerPonints[3])          if (Math.abs(nx[0] - offsetX) < 8) {            return points[i]          }        }      }    }  }  return false}

直线的判断方法是点到线段的距离:

function getDistance(pt: [number, number], p: [number, number], q: [number, number]) {  const pqx = q[0] - p[0]  const pqy = q[1] - p[1]  let dx = pt[0] - p[0]  let dy = pt[1] - p[1]  const d = pqx * pqx + pqy * pqy   // qp线段长度的平方  let t = pqx * dx + pqy * dy     // p pt向量 点积 pq 向量(p相当于A点,q相当于B点,pt相当于P点)  if (d > 0) {  // 除数不能为0; 如果为零 t应该也为零。下面计算结果仍然成立。                       t /= d      // 此时t 相当于 上述推导中的 r。  }  if (t < 0) {  // 当t(r)< 0时,最短距离即为 pt点 和 p点(A点和P点)之间的距离。    t = 0  } else if (t > 1) { // 当t(r)> 1时,最短距离即为 pt点 和 q点(B点和P点)之间的距离。    t = 1  }  // t = 0,计算 pt点 和 p点的距离; t = 1, 计算 pt点 和 q点 的距离; 否则计算 pt点 和 投影点 的距离。  dx = p[0] + t * pqx - pt[0]  dy = p[1] + t * pqy - pt[1]  return dx * dx + dy * dy}

关于曲线的判断方法比较复杂,这里就不多介绍, 想了解的可以去看这篇:如何判断一个坐标点是否在三阶贝塞尔曲线附近

连接线还有一个功能就是双击连接线后可以编辑这条连接线的备注信息。这个备注信息的位置是在当前连接线的中心点位置。所以我们需要求出中心点,这个相对简单。

// 获取一条直线的中点坐标const getStraightLineCenterPoint = ([[x1, y1], [x2, y2]]: [number, number][]): [number, number] => {  return [(x1 + x2) / 2, (y1 + y2) / 2]}// 获取一条折线的中点坐标const getBrokenCenterPoint = (points: [number, number][]): [number, number] => {  const lineDistancehalf = getLineDistance(points) >> 1  let distanceSum = 0, pre = 0, tp: [number, number][] = [], distance = 0  for (let i = 1; i < points.length; i++) {    pre = getTwoPointDistance(points[i - 1], points[i])    if (distanceSum + pre > lineDistancehalf) {      tp = [points[i - 1], points[i]]      distance = lineDistancehalf - distanceSum      break    }    distanceSum += pre  }  if (!tp.length) {    return [0, 0]  }  let x = tp[0][0], y = tp[0][1]  if (tp[0][0] === tp[1][0]) {    if (tp[0][1] > tp[1][1]) {      y -= distance    } else {      y += distance    }  } else {    if (tp[0][0] > tp[1][0]) {      x -= distance    } else {      x += distance    }  }  return [x, y]}

曲线的中心点位置,可以直接拿三阶贝塞尔曲线公式求出

// 获取三阶贝塞尔曲线的中点坐标const getBezierCenterPoint = (points: [number, number][]) => {  return getThreeBezierPoint(    0.5, points[0], points[1], points[2], points[3]  )}export const getThreeBezierPoint = (  t: number,  p1: [number, number],  cp1: [number, number],  cp2: [number, number],  p2: [number, number]): [number, number] => {  const [x1, y1] = p1  const [x2, y2] = p2  const [cx1, cy1] = cp1  const [cx2, cy2] = cp2  const x =    x1 * (1 - t) * (1 - t) * (1 - t) +    3 * cx1 * t * (1 - t) * (1 - t) +    3 * cx2 * t * t * (1 - t) +    x2 * t * t * t  const y =    y1 * (1 - t) * (1 - t) * (1 - t) +    3 * cy1 * t * (1 - t) * (1 - t) +    3 * cy2 * t * t * (1 - t) +    y2 * t * t * t  return [x | 0, y | 0]}

在算出每一条的中心点位置后,在目标位置添加备注信息即可:

如何利用vue3.x绘制流程图

节点的样式调整

节点的样式调整主要是位置及大小,而这些属性就是节点里面的 attr,在相应的事件下根据鼠标移动的方向及位置,来调整节点的样式。

如何利用vue3.x绘制流程图

还有批量操作也是同样,不过批量操作是要先计算出哪些节点的范围。

// 获取范围选中内的组件export const getSelectedComponent = (componentList: WF.ComponentType[], areaPosi: WF.Attr) => {  let selectedArea: WF.Attr | null = null  let minx = Infinity, miny = Infinity, maxx = -Infinity, maxy = -Infinity  const selectedComponents = componentList.filter((component: WF.ComponentType) => {    const res = areaPosi.x <= component.attr.x &&      areaPosi.y <= component.attr.y &&      areaPosi.x + areaPosi.w >= component.attr.x + component.attr.w &&      areaPosi.y + areaPosi.h >= component.attr.y + component.attr.h    if (res) {      minx = Math.min(minx, component.attr.x)      miny = Math.min(miny, component.attr.y)      maxx = Math.max(maxx, component.attr.x + component.attr.w)      maxy = Math.max(maxy, component.attr.y + component.attr.h)    }    return res  })  if (selectedComponents.length) {    selectedArea = {      x: minx,      y: miny,      w: maxx - minx,      h: maxy - miny    }    return {      selectedArea, selectedComponents    }  }  return null}

如何利用vue3.x绘制流程图

这个有个小功能没有做,就是在批量调整大小的时候,节点间的相对距离应该是不动的,这里忽略了。

节点移动时的吸附

这里的吸附功能其实是做了一个简单版的,就是 x 和 y 轴都只有一条校准线,且校准的优先级是从左至右,从上至下。

如何利用vue3.x绘制流程图

这里吸附的标准是节点的 6 个点:X 轴的左中右,Y 轴的上中下,当前节点在移动的时候,会用当前节点的 6 个点,一一去与其它节点的 6 个点做比较,在误差正负 2px 的情况,自动更新为0,即自定对齐。

因为移动当前节点时候,其它的节点是不动的,所以这里是做了一步预处理,即在鼠标按下去的时候,把其它的节点的 6 个点都线算出来,用 Set 结构保存,在移动的过程的比较中,计算量会相对较少。

// 计算其它节点的所有点位置export const clearupPostions = (componentList: WF.ComponentType[], currId: string) => {  // x 坐标集合  const coordx = new Set<number>()  // y 坐标集合  const coordy = new Set<number>()  componentList.forEach((component: WF.ComponentType) => {    if (component.id === currId) {      return    }    const { x, y, w, h } = component.attr    coordx.add(x)    coordx.add(x + (w >> 1))    coordx.add(x + w)    coordy.add(y)    coordy.add(y + (h >> 1))    coordy.add(y + h)  })  return [coordx, coordy]}

判读是否有可吸附的点

// 可吸附范围const ADSORBRANGE = 2// 查询是否有可吸附坐标const hasAdsorbable = (  coords: Set<number>[], x: number, y: number, w: number, h: number) => {  // x, y, w, h, w/2, h/2  const coord: (number | null)[] = [null, null, null, null, null, null]  // 查询 x 坐标  for (let i = 0; i <= ADSORBRANGE; i++) {    if (coords[0].has(x + i)) {      coord[0] = i      break    }    if (coords[0].has(x - i)) {      coord[0] = -i      break    }  }  // 查询 y 坐标  for (let i = 0; i <= ADSORBRANGE; i++) {    if (coords[1].has(y + i)) {      coord[1] = i      break    }    if (coords[1].has(y - i)) {      coord[1] = -i      break    }  }  // 查询 x + w 坐标  for (let i = 0; i <= ADSORBRANGE; i++) {    if (coords[0].has(x + w + i)) {      coord[2] = i      break    }    if (coords[0].has(x + w - i)) {      coord[2] = -i      break    }  }  // 查询 y + h 坐标  for (let i = 0; i <= ADSORBRANGE; i++) {    if (coords[1].has(y + h + i)) {      coord[3] = i      break    }    if (coords[1].has(y + h - i)) {      coord[3] = -i      break    }  }  // 查询 x + w/2 坐标  for (let i = 0; i <= ADSORBRANGE; i++) {    if (coords[0].has(x + (w >> 1) + i)) {      coord[4] = i      break    }    if (coords[0].has(x + (w >> 1) - i)) {      coord[4] = -i      break    }  }  // 查询 y + h/2 坐标  for (let i = 0; i <= ADSORBRANGE; i++) {    if (coords[1].has(y + (h >> 1) + i)) {      coord[5] = i      break    }    if (coords[1].has(y + (h >> 1) - i)) {      coord[5] = -i      break    }  }  return coord}

最后更新状态。

// 获取修正后的 x, y,还有吸附线的状态export const getAdsordXY = (  coords: Set<number>[], x: number, y: number, w: number, h: number) => {  const vals = hasAdsorbable(    coords, x, y, w, h  )  let linex = null  let liney = null  if (vals[0] !== null) { // x    x += vals[0]    linex = x  } else if (vals[2] !== null) { // x + w    x += vals[2]    linex = x + w  } else if (vals[4] !== null) { // x + w/2    x += vals[4]    linex = x + (w >> 1)  }  if (vals[1] !== null) { // y    y += vals[1]    liney = y  } else if (vals[3] !== null) { // y + h    y += vals[3]    liney = y + h  } else if (vals[5] !== null) { // y + h/2    y += vals[5]    liney = y + (h >> 1)  }  return {    x, y, linex, liney  }}

撤销和恢复

撤销和恢复的功能是比较简单的,其实就是用栈来保存每一次需要保存的配置结构,就是要考虑哪些操作是可以撤销和恢复的,就是像节点移动,节点的新增和删除,连接线的连接,连接线的备注新增和编辑等等,在相关的操作下面入栈即可。

// 撤销和恢复操作const cacheComponentList = ref<WF.ComponentType[][]>([])const currentComponentIndex = ref(-1)// 撤销const undo = () => {  componentRenderList.value = JSON.parse(JSON.stringify(cacheComponentList.value[--currentComponentIndex.value]))  // 更新视图  updateCanvas(true)  cancelSelected()}// 恢复const redo = () => {  componentRenderList.value = JSON.parse(JSON.stringify(cacheComponentList.value[++currentComponentIndex.value]))  // 更新视图  updateCanvas(true)  cancelSelected()}// 缓存入栈const chacheStack = () => {  if (cacheComponentList.value.length - 1 > currentComponentIndex.value) {    cacheComponentList.value.length = currentComponentIndex.value + 1  }  cacheComponentList.value.push(JSON.parse(JSON.stringify(componentRenderList.value)))  currentComponentIndex.value++}

如何利用vue3.x绘制流程图

关于“如何利用vue3.x绘制流程图”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何利用vue3.x绘制流程图

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何利用vue3.x绘制流程图

这篇文章主要介绍“如何利用vue3.x绘制流程图”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“如何利用vue3.x绘制流程图”文章能帮助大家解决问题。下面是效果图:整体结构布局:需要实现的功能列表:
2023-07-02

如何利用Python pyecharts绘制饼图

这篇文章主要为大家展示了“如何利用Python pyecharts绘制饼图”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何利用Python pyecharts绘制饼图”这篇文章吧。一、pyec
2023-06-22

如何利用Matlab绘制有趣图像

这篇文章主要介绍了如何利用Matlab绘制有趣图像,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1.随机樱花树function sakura% @author:slandar
2023-06-29

如何利用pyecharts绘制地理散点图

小编给大家分享一下如何利用pyecharts绘制地理散点图,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!1.背景利用上海市7000+办公楼项目,包括项目名称,地理位置,每天的租金,建筑面积和项目所在的商圈,现在要让这些项目
2023-06-29

怎么在Android中利用view绘制流程

怎么在Android中利用view绘制流程?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。绘制流程measure 流程测量出 View 的宽高尺寸。layout 流程确定 V
2023-06-15

如何利用Python matplotlib绘制风能玫瑰图

这篇文章主要介绍如何利用Python matplotlib绘制风能玫瑰图,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!概述在之前的风资源分析文章中,有提到过用widrose包来进行玫瑰图的绘制,目前的可视化绘图包有很
2023-06-14

QT如何利用QPainter绘制三维饼状图

这篇“QT如何利用QPainter绘制三维饼状图”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“QT如何利用QPainter绘
2023-07-02

Python如何绘制交通流折线图

这篇文章主要介绍了Python如何绘制交通流折线图的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Python如何绘制交通流折线图文章都会有所收获,下面我们一起来看看吧。一、数据集下载这里绘制PEMS04中的交通
2023-07-02

如何利用R语言绘制时间序列图

这篇文章主要讲解了“如何利用R语言绘制时间序列图”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何利用R语言绘制时间序列图”吧!数据GDP.csv文件,存储1879~2019年河南省GDP数
2023-06-08

如何利用python库matplotlib绘制不同的图表

这篇文章主要介绍如何利用python库matplotlib绘制不同的图表,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1、绘制简单曲线图思路:通过3个坐标点,绘制曲线import matplotlib.pyplot
2023-06-29

如何利用ECharts和Python接口绘制箱线图

如何利用ECharts和Python接口绘制箱线图,需要具体代码示例引言:箱线图(Box plot)是统计学中常用的一种可视化方法,用于显示实数型数据的分布情况,通过绘制数据的五数概括(最小值、下四分位数、中位数、上四分位数和最大值)以及异
如何利用ECharts和Python接口绘制箱线图
2023-12-17

如何利用OpenLayer绘制扇形

今天小编给大家分享一下如何利用OpenLayer绘制扇形的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。创建openlayer
2023-06-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录