react中使用react-konva实现画板框选内容

news2024/11/17 3:42:56

文章目录

  • 一、前言
    • 1.1、`API`文档
    • 1.2、`Github`仓库
  • 二、图形
    • 2.1、拖拽`draggable`
    • 2.2、图片`Image`
    • 2.3、变形`Transformer`
  • 三、实现
    • 3.1、依赖
    • 3.2、源码
      • 3.2.1、`KonvaContainer`组件
      • 3.2.2、`use-key-press`文件
    • 3.3、效果图
  • 四、最后

一、前言

本文用到的react-konva是基于react封装的图形绘制。Konva 是一个HTML5 Canvas JavaScript 框架,它通过对 2d context 的扩展实现了在桌面端和移动端的可交互性。Konva 提供了高性能的动画,补间,节点嵌套,布局,滤镜,缓存,事件绑定(桌面/移动端)等等功能。你可以使用 Konva 在舞台上绘制图形,给图形添加事件,移动、缩放和旋转图形并且支持高性能的动画即使包含数千个图形。

1.1、API文档

英文文档点击【前往】,中文文档点击【前往】

1.2、Github仓库

点击【前往】访问Github仓库,在线示例地址,点击【前往】

二、图形

在线制图最基础的应用是拖拽元素,比如,在画布上拖拽一张图片或某种形状,对该图片进行缩放或旋转操作。

画布就是<Stage>,每个图层为<Layer>

2.1、拖拽draggable

konva 中内置了很多形状的元素,比如圆形、矩形等,以下示例为星型,这里先用<Star>试一下:

import Konva from 'konva'
import { Circle, Rect, Stage, Layer, Text, Star } from 'react-konva'

const Shape = () => {
    const [star, setStar] = useState({
        x: 300,
        y: 300,
        rotation: 20,
        isDragging: false,
    })

    const handleDragStart = () => {
        setStar({
            ...star,
            isDragging: true,
        })
    }

    const handleDragEnd = (e: any) => {
        setStar({
            ...star,
            x: e.target.x(),
            y: e.target.y(),
            isDragging: false,
        })
    }

    return (
        <Stage width={1000} height={600}>
            <Layer>
                <Star
                    key="starid"
                    id="starid"
                    x={star.x}
                    y={star.y}
                    numPoints={5}
                    innerRadius={20}
                    outerRadius={40}
                    fill="#89b717"
                    opacity={0.8}
                    draggable
                    rotation={star.rotation}
                    shadowColor="black"
                    shadowBlur={10}
                    shadowOpacity={0.6}
                    shadowOffsetX={star.isDragging ? 10 : 5}
                    shadowOffsetY={star.isDragging ? 10 : 5}
                    scaleX={star.isDragging ? 1.2 : 1}
                    scaleY={star.isDragging ? 1.2 : 1}
                    onDragStart={handleDragStart}
                    onDragEnd={handleDragEnd}
                />
            </Layer>
        </Stage>
    )
}

其中,可以给 Star 配置一些基础的属性,如:xy 指该元素在画布上的坐标位置,rotaition 指元素的旋转角度;fill 指元素的填充颜色,scaleXscaleY 指元素在 xy 轴上的放大比例等等。

在拖拽的时候,我们要给该元素添加一些拖拽事件,如上:添加 handleDragStart 更改isDragging属性,使其在拖动时产生形变;添加 onDragEnd 事件,更改isDraggingxy 属性,来改变拖动位置,关闭拖动形变特效等。

观察上面的代码发现某些属性和react-dnd类似,但在使用 drag 事件的时候,发现比 react-dnd 方便很多,可能因为底层是 canvas 的原因吧!

2.2、图片Image

有两种方式可以导入图片,一个是用 react-hooks,一个是调用 react 生命周期函数,这里为了图省事,用 hooks

先安装 konva 的官方库use-imageuse-image提供好了跨域属性anonymous,封装一下图片组件:

import { Image } from 'react-konva'
import useImage from 'use-image'

const KonvaImage = ({ url = '' }) => {
    const [image] = useImage(url, 'anonymous')
    return <Image image={image} />
}

export default KonvaImage

如果仍显示跨域问题不能生成图片,需要在服务器端添加跨域头或者做一层转发了。

2.3、变形Transformer

元素变形,需要引用 konvaTransformer组件,该组件可以使元素的缩放、旋转。如下代码,在选中某元素后,会展示 Transformer 组件,在该组件上存在boundBoxFunc属性,当用户触发元素的变形行为时,该函数会被调用,返回一个包含形变后元素的信息(下面代码中为 newBox)。

import React, { useState, useEffect, useRef } from 'react'
import { Image, Transformer } from 'react-konva'
import Konva from 'konva'
import useImage from 'use-image'

const KonvaImage = ({ url = '', isSelected = false }) => {
    const [image] = useImage(url)
    const imgRef = useRef()
    const trRef = useRef()

    useEffect(() => {
        if (isSelected) {
            trRef.current.nodes([imgRef.current])
            trRef.current.getLayer().batchDraw()
        }
    }, [isSelected])
    return (
        <>
            <Image image={image} draggable ref={imgRef} />
            {isSelected && (
                <Transformer
                    ref={trRef}
                    boundBoxFunc={(oldBox, newBox) => {
                        // limit resize
                        if (newBox.width < 5 || newBox.height < 5) {
                            return oldBox
                        }
                        const { width, height } = newBox
                        // console.log('width', width);
                        // console.log('height', height);
                        return newBox
                    }}
                />
            )}
        </>
    )
}

export default KonvaImage

三、实现

3.1、依赖

安装如下所需依赖:

npm install react-konva konva use-image --save

3.2、源码

3.2.1、KonvaContainer组件

KonvaContainer图片框选区域组件源码如下所示:

/**
 * @Description: KonvaContainer图片框选区域组件
 * @props url 需要框选的图片的URL地址
 * @props width 宽度
 * @props height 高度
 * @props defaultValue 默认框选起来区域的数据
 * @onChange 回调方法,通知父组件框选的内容信息
 * @author 小马甲丫
 * @date 2023-12-05 03:22:27
*/
import React from 'react';
import useImage from 'use-image';
import { Stage, Layer, Rect, Image, Transformer } from 'react-konva';
import useKeyPress from '@/hooks/use-key-press';

/**
 * 框选的图片
 * @param url
 * @constructor
 */
const BackgroundImage = ({ url }) => {
  const [image] = useImage(url);
  return <Image image={image} />;
};

/**
 * 背景白板
 * @param width
 * @param height
 * @constructor
 */
const BackgroundWhite = ({ width, height }) => {
  return (
    <Rect
      x={0}
      y={0}
      width={width}
      height={height}
      fill="#fff"
      id="rectangleBg"
      name="rectangleBg"
    />
  );
};

/**
 * 框选出来的框
 * @param canvas
 * @param shapeProps
 * @param onSelect
 * @param onChange
 * @constructor
 */
const Rectangle = ({ canvas, shapeProps, onSelect, onChange }) => {
  const shapeRef = React.useRef();

  return (
    <Rect
      onClick={() => onSelect(shapeRef)}
      onTap={() => onSelect(shapeRef)}
      ref={shapeRef}
      {...shapeProps}
      name="rectangle"
      draggable
      onMouseOver={() => {
        document.body.style.cursor = 'move';
      }}
      onMouseOut={() => {
        document.body.style.cursor = 'default';
      }}
      onDragEnd={(e) => {
        onChange({
          ...shapeProps,
          x: e.target.x(),
          y: e.target.y(),
        });
      }}
      dragBoundFunc={(pos) => {
        const shapeWidth = shapeRef.current.attrs.width;
        const shapeHeight = shapeRef.current.attrs.height;
        let x = pos.x;
        if (x <= 0) {
          x = 0;
        } else if (x + shapeWidth >= canvas.width) {
          x = canvas.width - shapeWidth;
        }
        let y = pos.y;
        if (y < 0) {
          y = 0;
        } else if (y + shapeHeight > canvas.height) {
          y = canvas.height - shapeHeight;
        }
        return {
          x,
          y,
        };
      }}
      onTransformEnd={() => {
        // transformer is changing scale of the node
        // and NOT its width or height
        // but in the store we have only width and height
        // to match the data better we will reset scale on transform end
        const node = shapeRef.current;
        const scaleX = node.scaleX();
        const scaleY = node.scaleY();

        // we will reset it back
        node.scaleX(1);
        node.scaleY(1);
        onChange({
          ...shapeProps,
          x: node.x(),
          y: node.y(),
          // set minimal value
          width: Math.max(5, node.width() * scaleX),
          height: Math.max(node.height() * scaleY),
        });
      }}
    />
  );
};

/**
 * 主容器
 * @param props
 * @constructor
 */
const KonvaContainer = (props) => {
  const [imageObject, setImageObject] = React.useState({
    width: props.width,
    height: props.height,
    url: props.url,
  });
  const [rectanglesField, setRectanglesField] = React.useState([]);
  const [selectedId, selectShape] = React.useState(null);
  const trRef = React.useRef();
  const layerRef = React.useRef();
  const Konva = window.Konva;

  const hideTransformer = () => {
    trRef.current.nodes([]);
  };

  /**
   * 初始化框选框
   * @param list
   */
  const initRectangles = (list) => {
    const rects = list.map((item, index) => ({
      ...item,
      id: `rect_${index}`,
      fill: 'rgb(160, 76,4, 0.3)',
    }));
    setRectanglesField(rects);
  };

  /**
   * 监听prop值变换
   */
  React.useEffect(() => {
    const {
      url = '',
      width = 0,
      height = 0,
      defaultValue = [],
    } = props || {};
    setImageObject({
      width,
      height,
      url,
    });
    hideTransformer();
    // 图片地址不一致说明变更图片,需要重置选框
    if (url !== imageObject.url) {
      setRectanglesField([]);
      selectShape(null);
    }
    initRectangles(defaultValue);
  }, [props.url, props.width, props.height, props.defaultValue]);

  /**
   * 更新框选框数据
   * @param rects
   */
  const updateRectangles = (rects) => {
    setRectanglesField(rects);
    props.onChange(rects);
  };

  /**
   * 添加框选框
   */
  const addRec = () => {
    const data = rectanglesField;
    const rects = data.slice();
    const id = `rect_${rects.length}`;
    rects[rects.length] = {
      id,
      ...getSelectionObj(),
    };
    updateRectangles(rects);
    selectShape(id);
  };

  /**
   * 删除框选框
   */
  const delRec = () => {
    const data = rectanglesField;
    const rects = data.slice().filter((rect) => rect.id !== selectedId);
    updateRectangles(rects);
    hideTransformer();
    document.body.style.cursor = 'default';
    selectShape(null);
  };

  const selectionRectRef = React.useRef();
  const selection = React.useRef({
    visible: false,
    x1: 0,
    y1: 0,
    x2: 0,
    y2: 0,
  });

  /**
   * 高亮框选框
   * @param id
   */
  const activeTransformer = (id) => {
    const activeRect =
      layerRef.current.find('.rectangle').find((elementNode) => elementNode.attrs.id === id) ||
      selectionRectRef.current;
    trRef.current.nodes([activeRect]);
  };

  /**
   * useKeyPress监听键盘按键删除键del和返回键backspace
   * 8  返回键
   * 46 删除键
   */
  useKeyPress([8, 46], (e) => {
    // disable click event
    Konva.listenClickTap = false;
    if (e.target.style[0] === 'cursor') delRec();
  });

  /**
   * 获取选中的框选框的信息
   */
  const getSelectionObj = () => {
    return {
      x: Math.min(selection.current.x1, selection.current.x2),
      y: Math.min(selection.current.y1, selection.current.y2),
      width: Math.abs(selection.current.x1 - selection.current.x2),
      height: Math.abs(selection.current.y1 - selection.current.y2),
      fill: 'rgb(160, 76,4, 0.3)',
    };
  };

  /**
   * 更新框选框
   */
  const updateSelectionRect = () => {
    const node = selectionRectRef.current;
    node.setAttrs({
      ...getSelectionObj(),
      visible: selection.current.visible,
    });
    node.getLayer().batchDraw();
  };

  /**
   * 开始绘制框选框
   * @param e
   */
  const onMouseDown = (e) => {
    const isTransformer = e.target.findAncestor('Transformer');
    if (isTransformer) {
      return;
    }
    hideTransformer();

    const pos = e.target.getStage().getPointerPosition();
    selection.current.visible = true;
    selection.current.x1 = pos.x;
    selection.current.y1 = pos.y;
    selection.current.x2 = pos.x;
    selection.current.y2 = pos.y;
    updateSelectionRect();
  };

  /**
   * 绘制框选框中
   * @param e
   */
  const onMouseMove = (e) => {
    if (!selection.current.visible) {
      return;
    }
    const pos = e.target.getStage().getPointerPosition();
    selection.current.x2 = pos.x;
    selection.current.y2 = pos.y;
    updateSelectionRect();
  };

  /**
   * 结束绘制框选框
   * @param e
   */
  const onMouseUp = (e) => {
    // 点击Rect框时,会返回该Rect的id
    // 画框时鼠标在Rect上松开,会返回该Rect的id
    const dragId = e.target.getId();
    if (!selection.current.visible) {
      return;
    }

    // 是否鼠标拖动,并且偏移量大于10时才算拖动。拖动Rect没有偏移量,画框才有偏移量
    const { current: { x1 = 0, x2 = 0, y1 = 0, y2 = 0 } = {} } = selection || {};
    const isMove = (x1 !== x2 && Math.abs(x1 - x2) > 10) || (y1 !== y2 && Math.abs(y1 - y2) > 10);
    // 点击后有拖动就添加Rect框,并且偏移量大于10时才算拖动
    if (isMove) {
      addRec();
    }
    // 设置可调节大小节点
    if (!!dragId && !isMove) {
      // 点击已有的Rect框才设置,并且拖动小于10,也就是没有拖动
      activeTransformer(dragId);
    } else if (isMove) {
      // 拖动大于10,生成新的Rect框
      activeTransformer();
    }

    selection.current.visible = false;
    // disable click event
    Konva.listenClickTap = false;
    updateSelectionRect();
  };

  return (
    <Stage
      width={imageObject.width}
      height={imageObject.height}
      onMouseDown={onMouseDown}
      onMouseUp={onMouseUp}
      onMouseMove={onMouseMove}
    >
      <Layer ref={layerRef}>
        <BackgroundWhite {...imageObject} />
        <BackgroundImage {...imageObject} />
        {
          rectanglesField.map((rect, i) => {
            return (
              <Rectangle
                key={i}
                getKey={i}
                canvas={imageObject}
                shapeProps={rect}
                isSelected={rect.id === selectedId}
                getLength={rectanglesField.length}
                onSelect={() => {
                  selectShape(rect.id);
                }}
                onChange={(newAttrs) => {
                  const rects = rectanglesField.slice();
                  rects[i] = newAttrs;
                  updateRectangles(rects);
                }}
              />
            );
          })
        }
        <Transformer
          ref={trRef}
          rotationSnaps={[0, 90, 180, 270]}
          keepRatio={false}
          anchorSize={4}
          anchorStroke='#a04c04'
          anchorFill="#fff"
          borderStroke='#a04c04'
          borderDash={[1, 1]}
          enabledAnchors={['top-left', 'top-right', 'bottom-left', 'bottom-right']}
          boundBoxFunc={(oldBox, newBox) => {
            // limit resize
            // newBox.rotation !== 0进入return oldBox,就可实现不让旋转
            if (newBox.width < 20 || newBox.height < 20) {
              return oldBox;
            }
            return newBox;
          }}
        />
        <Rect ref={selectionRectRef} />
      </Layer>
    </Stage>
  );
};

export default KonvaContainer;

3.2.2、use-key-press文件

用到了下面这个hook文件use-key-press

import { useCallback, useEffect, MutableRefObject } from 'react';

type keyType = KeyboardEvent['keyCode'] | KeyboardEvent['key'];
type keyFilter = keyType | keyType[];
type EventHandler = (event: KeyboardEvent) => void;
type keyEvent = 'keydown' | 'keyup';
type BasicElement = HTMLElement | Element | Document | Window;
type TargetElement = BasicElement | MutableRefObject<null | undefined>;
type EventOptions = {
  events?: keyEvent[];
  target?: TargetElement;
};

const modifierKey: any = {
  ctrl: (event: KeyboardEvent) => event.ctrlKey,
  shift: (event: KeyboardEvent) => event.shiftKey,
  alt: (event: KeyboardEvent) => event.altKey,
  meta: (event: KeyboardEvent) => event.metaKey,
};

const defaultEvents: keyEvent[] = ['keydown'];

/**
 * 判断对象类型
 * @param obj 参数对象
 * @returns String
 */
function isType<T>(obj: T): string {
  return Object.prototype.toString
    .call(obj)
    .replace(/^\[object (.+)\]$/, '$1')
    .toLowerCase();
}

/**
 * 获取当前元素
 * @param target TargetElement
 * @param defaultElement 默认绑定的元素
 */
function getTargetElement(target?: TargetElement, defaultElement?: BasicElement) {
  if (!target) {
    return defaultElement;
  }

  if ('current' in target) {
    return target.current;
  }

  return target;
}

/**
 * 按键是否激活
 * @param event 键盘事件
 * @param keyFilter 当前键
 */
const keyActivated = (event: KeyboardEvent, keyFilter: any) => {
  const type = isType(keyFilter);
  const { keyCode } = event;

  if (type === 'number') {
    return keyCode === keyFilter;
  }

  const keyCodeArr = keyFilter.split('.');
  // 符合条件的长度
  let genLen = 0;
  // 组合键
  keyCodeArr.forEach((key) => {
    const genModifier = modifierKey[key];

    if ((genModifier && genModifier) || keyCode === key) {
      genLen++;
    }
  });

  return genLen === keyCodeArr.length;
};

/**
 * 键盘按下预处理方法
 * @param event 键盘事件
 * @param keyFilter 键码集
 */
const genKeyFormate = (event: KeyboardEvent, keyFilter: any) => {
  const type = isType(keyFilter);

  if (type === 'string' || type === 'number') {
    return keyActivated(event, keyFilter);
  }

  // 多个键
  if (type === 'array') {
    return keyFilter.some((item: keyFilter) => keyActivated(event, item));
  }

  return false;
};

/**
 * 监听键盘按下/松开
 * @param keyCode
 * @param eventHandler
 * @param options
 */
const useKeyPress = (
  keyCode: keyFilter,
  eventHandler?: EventHandler,
  options: EventOptions = {},
) => {
  const { target, events = defaultEvents } = options;

  const callbackHandler = useCallback(
    (event) => {
      if (genKeyFormate(event, keyCode)) {
        typeof eventHandler === 'function' && eventHandler(event);
      }
    },
    [keyCode],
  );

  useEffect(() => {
    const el = getTargetElement(target, window)!;

    events.forEach((eventName) => {
      el.addEventListener(eventName, callbackHandler);
    });

    return () => {
      events.forEach((eventName) => {
        el.removeEventListener(eventName, callbackHandler);
      });
    };
  }, [keyCode, events, callbackHandler]);
};

export default useKeyPress;

3.3、效果图

页面效果如下所示:

在这里插入图片描述

四、最后

本人每篇文章都是一字一句码出来,希望大佬们多提提意见。顺手来个三连击,点赞👍收藏💖关注✨。创作不易,给我打打气,加加油☕

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1299107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【rabbitMQ】rabbitMQ控制台模拟收发消息

目录 1.新建队列 2.交换机绑定队列 3.查看消息是否到达队列 总结&#xff1a; 1.新建队列 2.交换机绑定队列 点击amq.fonout 3.查看消息是否到达队列 总结&#xff1a; 生产者&#xff08;publisher&#xff09;发送消息&#xff0c;先到达交换机&#xff0c;再到队列&…

深度学习之全面了解预训练模型

在本专栏中&#xff0c;我们将讨论预训练模型。有很多模型可供选择&#xff0c;因此也有很多考虑事项。 这次的专栏与以往稍有不同。我要回答的问题全部源于 MathWorks 社区论坛&#xff08;ww2.mathworks.cn/matlabcentral/&#xff09;的问题。我会首先总结 MATLAB Answers …

计算机视觉-05-目标检测:LeNet的PyTorch复现(MNIST手写数据集篇)(包含数据和代码)

文章目录 0. 数据下载1. 背景描述2. 预测目的3. 数据总览4. 数据预处理4.1 下载并加载数据&#xff0c;并做出一定的预先处理4.2 搭建 LeNet-5 神经网络结构&#xff0c;并定义前向传播的过程4.3 将定义好的网络结构搭载到 GPU/CPU&#xff0c;并定义优化器4.4 定义训练过程4.5…

机器学习算法(9)——集成技术(Bagging——随机森林分类器和回归)

一、说明 在这篇文章&#xff0c;我将向您解释集成技术和著名的集成技术之一&#xff0c;它属于装袋技术&#xff0c;称为随机森林分类器和回归。 集成技术是机器学习技术&#xff0c;它结合多个基本模块和模型来创建最佳预测模型。为了更好地理解这个定义&#xff0c;我们需要…

C语言进阶之路之结构体、枚举关卡篇

目录 一、学习目标 二、组合数据类型-结构体 结构体基本概念 结构体的声明&#xff1a; 小怪实战 结构体初始化 指定成员初始化的好处&#xff1a; 结构体成员引用 结构体指针与数组 关卡BOOS 三、结构体的尺寸 CPU字长 地址对齐 结构体的M值 可移植性 四、联合体…

ssm的健身房预约系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; ssm的健身房预约系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring Spring…

极智一周 | AI 算力国产化、通义开源、Gemini、鸿蒙、蔚来 And so on

欢迎关注我的公众号 [极智视界]&#xff0c;获取我的更多技术分享 大家好&#xff0c;我是极智视界&#xff0c;带来本周的 [极智一周]&#xff0c;关键词&#xff1a;AI 算力国产化、通义开源、Gemini、鸿蒙、蔚来 And so on。 邀您加入我的知识星球「极智视界」&#xff0c;…

c-语言->数据在内存的存储

系列文章目录 文章目录 系列文章目录前言 前言 目的&#xff1a;学习整数在内存的储存&#xff0c;什么是大小端&#xff0c;浮点数的储存。 1. 整数在内存中的存储 在讲解操作符的时候&#xff0c;我们就讲过了下⾯的内容&#xff1a; 整数的2进制表⽰⽅法有三种&#xff0…

带有 RaspiCam 的 Raspberry Pi 监控和延时摄影摄像机

一、说明 一段时间以来&#xff0c;我一直想构建一个运动激活且具有延时功能的树莓派相机&#xff0c;但从未真正找到我喜欢的案例。我在thingiverse上找到了这个适合树莓派和相机的好案例。它是为特定的鱼眼相机设计的&#xff0c;但从模型来看&#xff0c;我拥有的廉价中国鱼…

STM32F1之CAN报文传输

目录 报文传输 1. 帧类型 1.1 数据帧 1.1.1 帧起始 1.1.2 仲裁场 1.1.3 控制场 1.1.4 数据场 1.1.5 CRC 场 1.1.6 应答场 1.1.7 帧结尾 1.2 远程帧 1.3 错误帧 1.4 过载帧 1.5 帧间空间&#xff08;INTERFRAME SPACING&#xff09; 2. 发送器/接收器的…

【动态规划】【广度优先】LeetCode2258:逃离火灾

作者推荐 本文涉及的基础知识点 二分查找算法合集 动态规划 二分查找 题目 给你一个下标从 0 开始大小为 m x n 的二维整数数组 grid &#xff0c;它表示一个网格图。每个格子为下面 3 个值之一&#xff1a; 0 表示草地。 1 表示着火的格子。 2 表示一座墙&#xff0c;你跟…

【C语言】内联函数

一、内联函数 在C语言中&#xff0c;内联函数&#xff08;Inline function&#xff09;是一种代码优化技术&#xff0c;它的目的是减少函数调用的开销。内联函数通知编译器在每个函数调用的位置插入函数的实际代码&#xff0c;而不是进行传统的函数调用。这避免了调用函数时的…

球上进攻^^

欢迎来到程序小院 球上进攻 玩法&#xff1a;点击鼠标走动躲避滚动的球球&#xff0c;球球碰到即为游戏结束&#xff0c;看看你能坚持多久&#xff0c;快去玩吧^^。开始游戏https://www.ormcc.com/play/gameStart/214 html <div id"game" class"game" …

【基于Flask、MySQL和Echarts的热门游戏数据可视化平台设计与实现】

基于Flask、MySQL和Echarts的热门游戏数据可视化平台设计与实现 前言数据获取与清洗数据集数据获取数据清洗 数据分析与可视化数据分析功能可视化功能 创新点结语 前言 随着游戏产业的蓬勃发展&#xff0c;了解游戏销售数据对于游戏从业者和游戏爱好者都至关重要。为了更好地分…

(六)五种最新算法(SWO、COA、LSO、GRO、LO)求解无人机路径规划MATLAB

一、五种算法&#xff08;SWO、COA、LSO、GRO、LO&#xff09;简介 1、蜘蛛蜂优化算法SWO 蜘蛛蜂优化算法&#xff08;Spider wasp optimizer&#xff0c;SWO&#xff09;由Mohamed Abdel-Basset等人于2023年提出&#xff0c;该算法模型雌性蜘蛛蜂的狩猎、筑巢和交配行为&…

DataFrame的使用

查看数据类型及属性 # 查看df类型 type(df) # 查看df的shape属性&#xff0c;可以获取DataFrame的行数&#xff0c;列数 df.shape # 查看df的columns属性&#xff0c;获取DataFrame中的列名 df.columns # 查看df的dtypes属性&#xff0c;获取每一列的数据类型 df.dtypes df.i…

模型能力赋能搜索——零样本分类(Zero-Shot Classification)在搜索意图识别上的探索

什么是Zero-Shot Classification https://huggingface.co/tasks/zero-shot-classification hugging face上的零样本分类模型 facebook/bart-large-mnli https://huggingface.co/facebook/bart-large-mnli 当然这是一个英文模型&#xff0c;我们要去用一些多语言的模型。 可以在…

Android 样式小结

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、使用3.1 创建并应用样式3.2 创建并…

Azure Machine Learning - 使用 Azure OpenAI 服务生成图像

在浏览器/Python中使用 Azure OpenAI 生成图像&#xff0c;图像生成 API 根据文本提示创建图像。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0c;复旦机器人智能实验室成员&#x…

点击el-tree小三角后去除点击后的高亮背景样式,el-tree样式修改

<div class"videoTree" v-loading"loadingTree" element-loading-text"加载中..." element-loading-spinner"el-icon-loading" element-loading-background"rgba(0, 0, 0, 0.8)" > <el-tree :default-expand-all&q…