首页 > 其他分享 >前端使用 Konva 实现可视化设计器(22)- 绘制图形(矩形、直线、折线)

前端使用 Konva 实现可视化设计器(22)- 绘制图形(矩形、直线、折线)

时间:2024-09-10 18:15:44浏览次数:1  
标签:const 22 graph Konva shape points 折线 anchor

本章分享一下如何使用 Konva 绘制基础图形:矩形、直线、折线,希望大家继续关注和支持哈!

请大家动动小手,给我一个免费的 Star 吧~

大家如果发现了 Bug,欢迎来提 Issue 哟~

github源码

gitee源码

示例地址

矩形

先上效果!

image
image

实现方式基本和《前端使用 Konva 实现可视化设计器(21)- 绘制图形(椭圆)》是一致的,主要区别矩形的大小和椭圆形的大小设置方式不一样,特别是矩形无需设置 offset。其它就不再赘述了哈。

直线、折线

先上效果!

image
image

简单描述一下上面的交互:

首先,绘制一条直线,淡出画一条直线还是比较简单的,根据记录鼠标按下的位置和鼠标释放的位置,就很容易得到 Konva.Line 的 points 应该设定的值了。

然后,沿用绘制 椭圆形、矩形 的思路,它只有特定的 2 个“调整点”,分别代表 起点 和 终点。

// src/Render/graphs/Line.ts

// 略

/**
 * 直线、折线
 */
export class Line extends BaseGraph {
  // 略

  constructor(render: Types.Render, dropPoint: Konva.Vector2d) {
    super(render, dropPoint, {
      type: Types.GraphType.Line,
      // 定义了 2 个 调整点
      anchors: [{ adjustType: 'start' }, { adjustType: 'end' }].map((o) => ({
        adjustType: o.adjustType // 调整点 类型定义
      })),
      linkAnchors: [
        { x: 0, y: 0, alias: 'start' },
        { x: 0, y: 0, alias: 'end' }
      ] as Types.AssetInfoPoint[]
    })

    // 新建 直线、折线
    this.line = new Konva.Line({
      name: 'graph',
      x: 0,
      y: 0,
      stroke: 'black',
      strokeWidth: 1,
      hitStrokeWidth: render.toStageValue(5)
    })

    // 给予 1 像素,防止导出图片 toDataURL 失败
    this.group.size({
      width: 1,
      height: 1
    })

    // 加入
    this.group.add(this.line)
    // 鼠标按下位置 作为起点
    this.group.position(this.dropPoint)
  }

  // 实现:拖动进行时
  override drawMove(point: Konva.Vector2d): void {
    // 鼠标拖动偏移量
    const offsetX = point.x - this.dropPoint.x,
      offsetY = point.y - this.dropPoint.y

    // 起点、终点
    const linkPoints = [
      [this.line.x(), this.line.y()],
      [this.line.x() + offsetX, this.line.y() + offsetY]
    ]

    // 直线、折线 路径
    this.line.points(_.flatten(linkPoints))

    // 更新 图形 的 调整点 的 锚点位置
    Line.updateAnchorShadows(this.group, this.anchorShadows, this.line)

    // 更新 图形 的 连接点 的 锚点位置
    Line.updateLinkAnchorShadows(this.group, this.linkAnchorShadows, this.line)

    // 重绘
    this.render.redraw([Draws.GraphDraw.name, Draws.LinkDraw.name, Draws.PreviewDraw.name])
  }

  // 实现:拖动结束
  override drawEnd(): void {
    if (this.line.width() <= 1 && this.line.height() <= 1) {
      // 加入只点击,无拖动

      // 默认大小
      const width = Line.size,
        height = width

      // 起点、终点
      const linkPoints = [
        [this.line.x(), this.line.y()],
        [this.line.x() + width, this.line.y() + height]
      ]

      // 直线、折线 位置大小
      this.line.points(_.flatten(linkPoints))
    }

    // 更新 调整点(拐点)
    Line.updateAnchor(this.render, this.group)

    // 更新 图形 的 调整点 的 锚点位置
    Line.updateAnchorShadows(this.group, this.anchorShadows, this.line)

    // 更新 图形 的 连接点 的 锚点位置
    Line.updateLinkAnchorShadows(this.group, this.linkAnchorShadows, this.line)

    // 对齐线清除
    this.render.attractTool.alignLinesClear()

    // 更新历史
    this.render.updateHistory()

    // 重绘
    this.render.redraw([Draws.GraphDraw.name, Draws.LinkDraw.name, Draws.PreviewDraw.name])
  }

  // 略
}

调整点,可以改变 直线、折线 的 起点、终点。

// 略

/**
 * 直线、折线
 */
export class Line extends BaseGraph {
  // 实现:更新 图形 的 调整点 的 锚点位置
  static override updateAnchorShadows(
    graph: Konva.Group,
    anchorShadows: Konva.Circle[],
    shape?: Konva.Line
  ): void {
    if (shape) {
      const points = shape.points()
      //
      for (const shadow of anchorShadows) {
        switch (shadow.attrs.adjustType) {
          case 'start':
            shadow.position({
              x: points[0],
              y: points[1]
            })
            break
          case 'end':
            shadow.position({
              x: points[points.length - 2],
              y: points[points.length - 1]
            })
            break
        }
      }
    }
  }
  
  // 略

  // 实现:生成 调整点
  static override createAnchorShapes(
    render: Types.Render,
    graph: Konva.Group,
    anchorAndShadows: {
      anchor: Types.GraphAnchor
      anchorShadow: Konva.Circle
      shape?: Konva.Shape
    }[],
    adjustAnchor?: Types.GraphAnchor
  ): {
    anchorAndShadows: {
      anchor: Types.GraphAnchor
      anchorShadow: Konva.Circle
      shape?: Konva.Shape | undefined
    }[]
  } {
    // stage 状态
    const stageState = render.getStageState()

    const graphShape = graph.findOne('.graph') as Konva.Line

    if (graphShape) {
      const points = graphShape.points()

      for (const anchorAndShadow of anchorAndShadows) {
        let rotate = 0
        const { anchor, anchorShadow } = anchorAndShadow

        const x = render.toStageValue(anchorShadow.getAbsolutePosition().x - stageState.x),
          y = render.toStageValue(anchorShadow.getAbsolutePosition().y - stageState.y)

        if (anchor.adjustType === 'manual') {
          // 略
        } else {
          if (anchor.adjustType === 'start') {
            rotate = Line.calculateAngle(points[2] - points[0], points[3] - points[1])
          } else if (anchor.adjustType === 'end') {
            rotate = Line.calculateAngle(
              points[points.length - 2] - points[points.length - 4],
              points[points.length - 1] - points[points.length - 3]
            )
          }

          const cos = Math.cos((rotate * Math.PI) / 180)
          const sin = Math.sin((rotate * Math.PI) / 180)

          const offset = render.toStageValue(render.pointSize + 5)

          const offsetX = offset * sin
          const offsetY = offset * cos

          const anchorShape = new Konva.Circle({
            name: 'anchor',
            anchor: anchor,
            //
            fill:
              adjustAnchor?.adjustType === anchor.adjustType && adjustAnchor?.groupId === graph.id()
                ? 'rgba(0,0,255,0.8)'
                : 'rgba(0,0,255,0.2)',
            radius: render.toStageValue(3),
            strokeWidth: 0,
            // 位置
            x: x,
            y: y,
            offsetX:
              anchor.adjustType === 'start' ? offsetX : anchor.adjustType === 'end' ? -offsetX : 0,
            offsetY:
              anchor.adjustType === 'start' ? offsetY : anchor.adjustType === 'end' ? -offsetY : 0,
            // 旋转角度
            rotation: graph.getAbsoluteRotation()
          })

          anchorShape.on('mouseenter', () => {
            anchorShape.fill('rgba(0,0,255,0.8)')
            document.body.style.cursor = 'move'
          })
          anchorShape.on('mouseleave', () => {
            anchorShape.fill(
              anchorShape.attrs.adjusting ? 'rgba(0,0,255,0.8)' : 'rgba(0,0,255,0.2)'
            )
            document.body.style.cursor = anchorShape.attrs.adjusting ? 'move' : 'default'
          })

          anchorAndShadow.shape = anchorShape
        }
      }
    }

    return { anchorAndShadows }
  }

  // 略

  // 实现:调整 图形
  static override adjust(
    render: Types.Render,
    graph: Konva.Group,
    graphSnap: Konva.Group,
    adjustShape: Konva.Shape,
    anchorAndShadows: {
      anchor: Types.GraphAnchor
      anchorShadow: Konva.Circle
      shape?: Konva.Shape | undefined
    }[],
    startPoint: Konva.Vector2d,
    endPoint: Konva.Vector2d
  ) {
    // 目标 直线、折线
    const line = graph.findOne('.graph') as Konva.Line
    // 镜像
    const lineSnap = graphSnap.findOne('.graph') as Konva.Line

    // 调整点 锚点
    const anchors = (graph.find('.anchor') ?? []) as Konva.Circle[]
    // 镜像
    const anchorsSnap = (graphSnap.find('.anchor') ?? []) as Konva.Circle[]

    // 连接点 锚点
    const linkAnchors = (graph.find('.link-anchor') ?? []) as Konva.Circle[]

    if (line && lineSnap) {
      // stage 状态
      const stageState = render.getStageState()

      {
        const [graphRotation, adjustType, ex, ey] = [
          Math.round(graph.rotation()),
          adjustShape.attrs.anchor?.adjustType,
          endPoint.x,
          endPoint.y
        ]

        const { x: cx, y: cy, width: cw, height: ch } = graphSnap.getClientRect()

        const { x, y } = graph.position()

        const [centerX, centerY] = [cx + cw / 2, cy + ch / 2]

        const { x: sx, y: sy } = Line.rotatePoint(ex, ey, centerX, centerY, -graphRotation)
        const { x: rx, y: ry } = Line.rotatePoint(x, y, centerX, centerY, -graphRotation)

        const points = line.points()
        const manualPoints = (line.attrs.manualPoints ?? []) as Types.LineManualPoint[]

        if (adjustType === 'manual') {
          // 略
        } else {
          const anchor = anchors.find((o) => o.attrs.adjustType === adjustType)
          const anchorShadow = anchorsSnap.find((o) => o.attrs.adjustType === adjustType)

          if (anchor && anchorShadow) {
            {
              const linkPoints = [
                [points[0], points[1]],
                ...manualPoints.sort((a, b) => a.index - b.index).map((o) => [o.x, o.y]),
                [points[points.length - 2], points[points.length - 1]]
              ]

              switch (adjustType) {
                case 'start':
                  {
                    linkPoints[0] = [sx - rx, sy - ry]
                    line.points(_.flatten(linkPoints))
                  }
                  break
                case 'end':
                  {
                    linkPoints[linkPoints.length - 1] = [sx - rx, sy - ry]
                    line.points(_.flatten(linkPoints))
                  }
                  break
              }
            }
          }
        }
      }

      // 更新 调整点(拐点)
      Line.updateAnchor(render, graph)

      // 更新 调整点 的 锚点 位置
      Line.updateAnchorShadows(graph, anchors, line)

      // 更新 图形 的 连接点 的 锚点位置
      Line.updateLinkAnchorShadows(graph, linkAnchors, line)

      // 更新 调整点 位置
      for (const anchor of anchors) {
        for (const { shape } of anchorAndShadows) {
          if (shape) {
            if (shape.attrs.anchor?.adjustType === anchor.attrs.adjustType) {
              const anchorShadow = graph
                .find(`.anchor`)
                .find((o) => o.attrs.adjustType === anchor.attrs.adjustType)

              if (anchorShadow) {
                shape.position({
                  x: render.toStageValue(anchorShadow.getAbsolutePosition().x - stageState.x),
                  y: render.toStageValue(anchorShadow.getAbsolutePosition().y - stageState.y)
                })
                shape.rotation(graph.getAbsoluteRotation())
              }
            }
          }
        }
      }

      // 重绘
      render.redraw([Draws.GraphDraw.name, Draws.LinkDraw.name, Draws.PreviewDraw.name])
    }
  }

  // 略
}

折线

相比绘制 椭圆形、矩形 比较不一样的地方在于,椭圆形、矩形 的“调整点”是固定的,而绘制 折线 不一样,没调整一个新的拐点,就会新增 2 个新调整点,整体交互与 手动连接线 类似。

image

// src/Render/draws/GraphDraw.ts

// 略

export interface GraphDrawState {
  // 略

  /**
   * 调整中 调整点
   */
  adjustAnchor?: Types.GraphAnchor

  /**
   * 鼠标按下 调整点 位置
   */
  startPointCurrent: Konva.Vector2d

  /**
   * 图形 group
   */
  graphCurrent?: Konva.Group

  /**
   * 图形 group 镜像,用于计算位置、大小的偏移
   */
  graphCurrentSnap?: Konva.Group
}

// 略

export class GraphDraw extends Types.BaseDraw implements Types.Draw {
  // 略

  state: GraphDrawState = {
    adjusting: false,
    adjustGroupId: '',
    startPointCurrent: { x: 0, y: 0 }
  }

  // 略

  override draw() {
    this.clear()
    // 所有图形
    const graphs = this.render.layer
      .find('.asset')
      .filter((o) => o.attrs.assetType === Types.AssetType.Graph) as Konva.Group[]

    for (const graph of graphs) {
      // 非选中状态才显示 调整点
      if (!graph.attrs.selected) {
        // 略

        for (const anchorAndShadow of anchorAndShadows) {
          const { shape } = anchorAndShadow

          if (shape) {
            // 鼠标按下
            shape.on('mousedown', () => {
              const pos = this.getStagePoint()
              if (pos) {
                this.state.adjusting = true
                this.state.adjustAnchor = shape.attrs.anchor
                this.state.adjustGroupId = graph.id()

                this.state.startPointCurrent = pos

                this.state.graphCurrent = graph
                this.state.graphCurrentSnap = graph.clone()

                shape.setAttr('adjusting', true)

                if (this.state.adjustAnchor) {
                  switch (shape.attrs.anchor?.type) {
                    case Types.GraphType.Line:
                      // 使用 直线、折线 静态处理方法
                      Graphs.Line.adjustStart(this.render, graph, this.state.adjustAnchor, pos)
                      break
                  }
                }
              }
            })

            // 略

            // 调整结束
            this.render.stage.on('mouseup', () => {
              // 略
              
              this.state.adjusting = false
              this.state.adjustAnchor = undefined
              this.state.adjustGroupId = ''

              // 恢复显示所有 调整点
              for (const { shape } of anchorAndShadows) {
                if (shape) {
                  shape.opacity(1)
                  shape.setAttr('adjusting', false)
                  if (shape.attrs.anchor?.type === Types.GraphType.Line) {
                    if (shape.attrs.anchor.adjusted) {
                      shape.fill('rgba(0,0,0,0.4)')
                    } else {
                      shape.fill('rgba(0,0,255,0.2)')
                    }
                  } else {
                    shape.stroke('rgba(0,0,255,0.2)')
                  }
                }

                // 略
              }

              // 略
            })

            // 略
          }
        }
      }
    }
  }
}

上面除了需要更多的状态记录 调整 信息,还需要定义 Line 特有的 adjustStart 方法:

// src/Render/graphs/Line.ts

// 略

/**
 * 直线、折线
 */
export class Line extends BaseGraph {
  // 略

  /**
   * 调整之前
   */
  static adjustStart(
    render: Types.Render,
    graph: Konva.Group,
    adjustAnchor: Types.GraphAnchor & { manualIndex?: number; adjusted?: boolean },
    endPoint: Konva.Vector2d
  ) {
    const { x: gx, y: gy } = graph.position()

    const shape = graph.findOne('.graph') as Konva.Line

    if (shape && typeof adjustAnchor.manualIndex === 'number') {
      const manualPoints = (shape.attrs.manualPoints ?? []) as Types.LineManualPoint[]
      if (adjustAnchor.adjusted) {
        //
      } else {
        manualPoints.push({
          x: endPoint.x - gx,
          y: endPoint.y - gy,
          index: adjustAnchor.manualIndex
        })
        shape.setAttr('manualPoints', manualPoints)
      }

      // 更新 调整点(拐点)
      Line.updateAnchor(render, graph)
    }
  }
}

// 略

动态的调整点,会记录在 line 的 attrs 中 manualPoints,每次首次调整一处 拐点,就会新增一个 新 拐点,主要应用在:

// 略

/**
 * 直线、折线
 */
export class Line extends BaseGraph {
  // 略

  // 实现:调整 图形
  static override adjust(
    render: Types.Render,
    graph: Konva.Group,
    graphSnap: Konva.Group,
    adjustShape: Konva.Shape,
    anchorAndShadows: {
      anchor: Types.GraphAnchor
      anchorShadow: Konva.Circle
      shape?: Konva.Shape | undefined
    }[],
    startPoint: Konva.Vector2d,
    endPoint: Konva.Vector2d
  ) {
    // 目标 直线、折线
    const line = graph.findOne('.graph') as Konva.Line
    // 镜像
    const lineSnap = graphSnap.findOne('.graph') as Konva.Line

    // 调整点 锚点
    const anchors = (graph.find('.anchor') ?? []) as Konva.Circle[]
    // 镜像
    const anchorsSnap = (graphSnap.find('.anchor') ?? []) as Konva.Circle[]

    // 连接点 锚点
    const linkAnchors = (graph.find('.link-anchor') ?? []) as Konva.Circle[]

    if (line && lineSnap) {
      // stage 状态
      const stageState = render.getStageState()

      {
        const [graphRotation, adjustType, ex, ey] = [
          Math.round(graph.rotation()),
          adjustShape.attrs.anchor?.adjustType,
          endPoint.x,
          endPoint.y
        ]

        const { x: cx, y: cy, width: cw, height: ch } = graphSnap.getClientRect()

        const { x, y } = graph.position()

        const [centerX, centerY] = [cx + cw / 2, cy + ch / 2]

        const { x: sx, y: sy } = Line.rotatePoint(ex, ey, centerX, centerY, -graphRotation)
        const { x: rx, y: ry } = Line.rotatePoint(x, y, centerX, centerY, -graphRotation)

        const points = line.points()
        const manualPoints = (line.attrs.manualPoints ?? []) as Types.LineManualPoint[]

        if (adjustType === 'manual') {
          if (adjustShape.attrs.anchor?.manualIndex !== void 0) {
            const index = adjustShape.attrs.anchor?.adjusted
              ? adjustShape.attrs.anchor?.manualIndex
              : adjustShape.attrs.anchor?.manualIndex + 1

            const manualPointIndex = manualPoints.findIndex((o) => o.index === index)

            if (manualPointIndex > -1) {
              manualPoints[manualPointIndex].x = sx - rx
              manualPoints[manualPointIndex].y = sy - ry
            }

            const linkPoints = [
              [points[0], points[1]],
              ...manualPoints.sort((a, b) => a.index - b.index).map((o) => [o.x, o.y]),
              [points[points.length - 2], points[points.length - 1]]
            ]

            line.setAttr('manualPoints', manualPoints)

            line.points(_.flatten(linkPoints))

            //
            const adjustAnchorShadow = anchors.find(
              (o) => o.attrs.adjustType === 'manual' && o.attrs.manualIndex === index
            )
            if (adjustAnchorShadow) {
              adjustAnchorShadow.position({
                x: sx - rx,
                y: sy - ry
              })
            }
          }
        } else {
          // 略
        }
      }

      // 略
    }
  }

  // 略

  /**
   * 更新 调整点(拐点)
   * @param render
   * @param graph
   */
  static updateAnchor(render: Types.Render, graph: Konva.Group) {
    const anchors = graph.attrs.anchors ?? []
    const anchorShadows = graph.find('.anchor') ?? []

    const shape = graph.findOne('.graph') as Konva.Line

    if (shape) {
      // 已拐
      let manualPoints = (shape.attrs.manualPoints ?? []) as Types.LineManualPoint[]
      const points = shape.points()

      // 调整点 + 拐点
      const linkPoints = [
        [points[0], points[1]],
        ...manualPoints.sort((a, b) => a.index - b.index).map((o) => [o.x, o.y]),
        [points[points.length - 2], points[points.length - 1]]
      ]

      // 清空 调整点(拐点),保留 start end
      anchors.splice(2)
      const shadows = anchorShadows.splice(2)
      for (const shadow of shadows) {
        shadow.remove()
        shadow.destroy()
      }

      manualPoints = []

      for (let i = linkPoints.length - 1; i > 0; i--) {
        linkPoints.splice(i, 0, [])
      }

      // 调整点(拐点)
      for (let i = 1; i < linkPoints.length - 1; i++) {
        const anchor = {
          type: graph.attrs.graphType,
          adjustType: 'manual',
          //
          name: 'anchor',
          groupId: graph.id(),
          //
          manualIndex: i,
          adjusted: false
        }

        if (linkPoints[i].length === 0) {
          anchor.adjusted = false

          // 新增
          const prev = linkPoints[i - 1]
          const next = linkPoints[i + 1]

          const circle = new Konva.Circle({
            adjustType: anchor.adjustType,
            anchorType: anchor.type,
            name: anchor.name,
            manualIndex: anchor.manualIndex,
            radius: 0,
            // radius: render.toStageValue(2),
            // fill: 'red',
            //
            x: (prev[0] + next[0]) / 2,
            y: (prev[1] + next[1]) / 2,
            anchor
          })

          graph.add(circle)
        } else {
          anchor.adjusted = true

          // 已拐
          const circle = new Konva.Circle({
            adjustType: anchor.adjustType,
            anchorType: anchor.type,
            name: anchor.name,
            manualIndex: anchor.manualIndex,
            adjusted: true,
            radius: 0,
            // radius: render.toStageValue(2),
            // fill: 'red',
            //
            x: linkPoints[i][0],
            y: linkPoints[i][1],
            anchor
          })

          graph.add(circle)

          manualPoints.push({
            x: linkPoints[i][0],
            y: linkPoints[i][1],
            index: anchor.manualIndex
          })
        }

        anchors.push(anchor)
      }

      shape.setAttr('manualPoints', manualPoints)

      graph.setAttr('anchors', anchors)
    }
  }

  // 略
}

上面简单的说,就是处理 manualPoints 的算法,负责控制新增拐点,然后把“点”们插入到 起点、终点 之间,最后处理成 Konva.Line 的 points 的值。

顺带一说。区分 起点、终点 和 拐点 是通过 attrs 中的 adjustType 字段;区分 拐点 是否已经操作过 是通过 attrs 中的 adjusted 字段;拐点是存在明确的顺序的,会记录在 attrs 的 manualIndex 字段中。

个人觉得,目前,绘制图形的 代码结构 和 变量命名 容易产生歧义,后面尽量抽出时间重构一下,大家支持支持

标签:const,22,graph,Konva,shape,points,折线,anchor
From: https://www.cnblogs.com/xachary/p/18406894

相关文章

  • C++环境搭建(Visual Studio 2022软件安装)
    安装环境:Windows11家庭中文版VisualStudio2022下载地址:        https://pan.baidu.com/s/15U8AEIwThxp-fAZFJqnCgQ    提取码:0000 安装步骤:        1.下载后选择安装包进行解压。    2.以管理员身份运行安装程序。(企业版功能最全,这......
  • ME软件下载安装 2022:软件附带安装教程及全版本安装使用指南
    #ME软件下载安装2022:软件附带安装教程及全版本安装使用指南引言ME软件是一款功能强大的多媒体编辑工具,广泛应用于视频剪辑、音频处理、图像编辑等领域。随着技术的不断进步,ME软件也在不断更新迭代,为用户提供更加高效、便捷的编辑体验。本文将详细介绍ME软件的下载、安装及全版本......
  • visual studio 2022 ,一打开项目就提示各种 内部错误,功能不可用
    原文链接:https://blog.csdn.net/weixin_45589116/article/details/133956055解决方法:  红色框中部分去掉 ......
  • 【JPCS独立出版】第四届电子通信与计算机科学技术国际学术会议(ECCST 2024,9月20-22)
    2024年第四届电子通信与计算机科学技术国际学术会议将于2024年9月20-22日在中国上海举行。会议旨在为从电子与通信、网络、人工智能与计算机技术研究的专家学者、工程技术人员、技术研发人员提供一个共享科研成果和前沿技术,了解学术发展趋势,拓宽研究思路,加强学术研究和探......
  • 华为-2022-测试面试题
    文章目录一、源数组a,将a中所有元素乘以2之后组成一个新数组,则这个新数组就叫双倍数组,给你一个数组a,判断它是不是双倍数组,如果是则输出源数组,不是则输出空数组。二、如果想把一个文件移动到另一个文件夹里面去,用什么命令三、自我介绍四、你觉得功能测试,按照你的理解,工作内......
  • 计算机毕业设计必看必学!!12222 springboo儿童疫苗预约系统 ,原创定制程序, java、PHP
    摘  要随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。儿童疫苗预约管理,主要的模块包括查看首页、站点管理(轮播图、公告栏)用户管理(管理员、系统用户)内容管理(资讯列表、分类列表)更多管理(预约订单、疫苗信息、......
  • COMP2230/COMP6230 Algorithms
    TheUniversityofNewcastle,AustraliaSchoolofInformationandPhysicalSciencesCOMP2230/COMP6230AlgorithmsAssignment1Marks100Weight15%IndividualSubmissionviaCanvas1LearningOutcomesThisassignmentwillrequirestudentsto:Applyspecific......
  • 数组与贪心算法——179、56、57、228(2简2中)
    179.最大数(简单)给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。解法一、自定义比较器大的排前面然后进行一个比较jpg一开始想的其实是字典序,但是测试里就败了。例如3,3......
  • Windows环境下 VS2022 编译 OGG 源码
    OGGOGG音频编码格式,全称为OggVorbis,是一种开源且无专利限制的音频压缩格式。它被设计用来提供高质量的音频存储和传输,同时保持较小的文件大小。OGGVorbis支持多声道音频,并且可以处理可变比特率,这意味着它可以根据音频内容的复杂性动态调整压缩率,以优化音质和文件大小。......
  • 【倒计时10天...IEEE出版】第五届大数据、人工智能与软件工程国际研讨会(ICBASE 2024,9
    第五届大数据、人工智能与软件工程国际研讨会(ICBASE2024)将于2024年09月20-22日在中国温州隆重举行。会议主要围绕大数据、人工智能与软件工程等研究领域展开讨论。会议旨在为从事大数据、人工智能与软件工程研究的专家学者、工程技术人员、技术研发人员提供一个共享科研......