当前位置: 首页 > news >正文

QWidget应用封装为qt插件,供其他qt应用调用

在之前的文章中,有介绍通过QProcess的方式启动QWidget应用,然后将其窗口嵌入到其他的qt应用中,作为子窗口使用.这篇文章主要介绍qt插件的方式将QWidget应用的窗口封装为插件,然后作为其他Qt应用中的子窗口使用. 

插件优点:

与主程序为同一个进程,免去了进程间繁琐的通信方式,在不同进程间通信时,需要有一套协议来保证数据的及时性及稳定性.比较繁琐,而插件的方式,通过插件接口文件来进行通信,在接口文件中定义API,

接口文件(定义为纯虚类)(主程序通过接口文件来访问该插件,插件通过继承该接口,重写所有纯虚函数)

#pragma once#include <QtPlugin>QT_BEGIN_NAMESPACE
class QString;
class QWidget;
QT_END_NAMESPACE//定义视觉检测接口
class InterfaceVisionMeasure
{
public:virtual ~InterfaceVisionMeasure() = default;/// <summary>/// 创建视觉窗口对象指针/// </summary>/// <param name="iLanguage">0为英文,1为中文</param>/// <param name="parent">父对象</param>/// <returns>窗口指针</returns>virtual void* CreatWindow(int iLanguage, QWidget* parent = nullptr) = 0;/// <summary>/// 获取对应序号轴编码器坐标/// </summary>/// <param name="GetAxisEncVal">回调对象</param>virtual void RegisterGetAxisEncVal(const std::function<void(int, double&)>& GetAxisEncVal) = 0;/// <summary>/// 获取对应序号轴规划位置坐标/// </summary>/// <param name="GetAxisPrfVal">回调对象</param>virtual void RegisterGetAxisPrfVal(const std::function<void(int, double&)>& GetAxisPrfVal) = 0;/// <summary>/// 获取内部变量的值/// </summary>/// <param name="GetVariableInVal">回调对象</param>virtual void RegisterGetVariableInVal(const std::function<void(int, double&)>& GetVariableInVal) = 0;/// <summary>/// 设置内部变量的值/// </summary>/// <param name="SetVariableInVal">回调对象</param>virtual void RegisterSetVariableInVal(const std::function<void(int, const double&)>& SetVariableInVal) = 0;/// <summary>/// 设置当前测量文件路径(测量文件)/// </summary>/// <param name="dir">测量文件路径</param>virtual void SetCurrentMeaureFile(const QString& dir) = 0;/// <summary>/// 开启测量/// </summary>/// <param name="indexMain">工序号</param>/// <param name="IndexSub">工序号中的子序号</param>virtual void StartMeasureProcess(int indexMain, int IndexSub) = 0;/// <summary>/// 测量结果返回到主程序/// </summary>/// <param name="MeasureResult">回调对象</param>virtual void RegisterMeasureResult(const std::function<void(const int&, const QStringList&, const QVector<double>&)>& MeasureResult) = 0;/// <summary>/// 创建测量文件(编辑文件)/// </summary>/// <param name="dir">创建路径</param>/// <returns>0为正常,非0为失败</returns>virtual int NewMeasureFile(const QString& dir)=0;/// <summary>/// 打开测量文件(编辑文件)/// </summary>/// <param name="dir">打开路径</param>/// <returns>0为正常,非0为失败</returns>virtual int OpenMeasureFile(const QString& dir) = 0;/// <summary>/// 另存为测量文件(编辑文件)/// </summary>/// <param name="srcDir">源文件路径</param>/// <param name="desDir">目标文件路径</param>/// <returns>0为正常,非0为失败</returns>virtual int SaveAsMeasureFile(const QString& srcDir, const QString& desDir) = 0;/// <summary>/// 设置当前显示页/// </summary>/// <param name="indexMain">对应工序页, -10为找中心,-11为对焦</param>/// <param name="indexSub">对应工序页中的第几页</param>/// <returns>0为成功, </returns>virtual int SetShowMeasurePage(int indexMain, int indexSub)=0;/// <summary>/// 获取当前测量工程工序列表(测量文件)/// </summary>/// <param name="listType">工序列表</param>/// <returns>0为成功</returns>virtual int GetCurMeasureProgram(QVector<int>& listType)=0;
};QT_BEGIN_NAMESPACE
#define Interface_VisionMeasure_IID "Demina.Nc.VisionMeasureInterface/1.0"
Q_DECLARE_INTERFACE(InterfaceVisionMeasure, Interface_VisionMeasure_IID)
QT_END_NAMESPACE

重写接口类:   h文件

#pragma once#include <QObject>
#include "InterfacesVisionMeasure.h"class qcDllVisionMeasure : public QObject, public InterfaceVisionMeasure
{Q_OBJECTQ_INTERFACES(InterfaceVisionMeasure)Q_PLUGIN_METADATA(IID Interface_VisionMeasure_IID FILE "DllVisionMeasure.json")
public:qcDllVisionMeasure(QObject* parent = nullptr);~qcDllVisionMeasure() override;/// <summary>/// 创建视觉窗口对象指针/// </summary>/// <param name="parent">父对象</param>/// <returns>窗口指针</returns>void* CreatWindow(int iLanguage, QWidget* parent = nullptr) override;/// <summary>/// 获取对应序号轴编码器坐标/// </summary>/// <param name="GetAxisEncVal">回调对象</param>void RegisterGetAxisEncVal(const std::function<void(int, double&)>& GetAxisEncVal) override;/// <summary>/// 获取对应序号轴规划位置坐标/// </summary>/// <param name="GetAxisPrfVal">回调对象</param>void RegisterGetAxisPrfVal(const std::function<void(int, double&)>& GetAxisPrfVal) override;/// <summary>/// 获取内部变量的值/// </summary>/// <param name="GetVariableInVal">回调对象</param>void RegisterGetVariableInVal(const std::function<void(int, double&)>& GetVariableInVal) override;//设置内部变量的值void RegisterSetVariableInVal(const std::function<void(int, const double&)>& SetVariableInVal) override;/// <summary>/// 设置当前测量文件路径/// </summary>/// <param name="dir">测量文件路径</param>void SetCurrentMeaureFile(const QString& dir) override;/// <summary>/// 开启测量/// </summary>/// <param name="indexMain">工序号</param>/// <param name="IndexSub">工序号中的子序号</param>void StartMeasureProcess(int indexMain, int IndexSub) override;/// <summary>/// 测量结果返回到主程序/// </summary>/// <param name="MeasureResult">回调对象</param>void RegisterMeasureResult(const std::function<void(const int&, const QStringList&, const QVector<double>&)>& MeasureResult) override;/// <summary>
/// 创建测量文件
/// </summary>
/// <param name="dir">创建路径</param>
/// <returns>0为正常,非0为失败</returns>int NewMeasureFile(const QString& dir) override;/// <summary>/// 打开测量文件/// </summary>/// <param name="dir">打开路径</param>/// <returns>0为正常,非0为失败</returns>int OpenMeasureFile(const QString& dir) override;/// <summary>/// 另存为测量文件/// </summary>/// <param name="srcDir">源文件路径</param>/// <param name="desDir">目标文件路径</param>/// <returns>0为正常,非0为失败</returns>int SaveAsMeasureFile(const QString& srcDir, const QString& desDir) override;/// <summary>/// 设置当前显示页/// </summary>/// <param name="indexMain">对应工序页, -10为找中心,-11为对焦</param>/// <param name="indexSub">对应工序页中的第几页</param>/// <returns>0为成功, </returns>int SetShowMeasurePage(int indexMain, int indexSub) override;/// <summary>/// 获取当前测量工程工序列表/// </summary>/// <param name="listType">工序列表</param>/// <returns>0为成功</returns>int GetCurMeasureProgram(QVector<int>& listType) override;
private://视觉界面对象指针void* m_pMainWnd{};
};

在实现cpp中,封装QWidget窗口,创建其实例

#include "stdafx.h"
#include "qcDllVisionMeasure.h"
#include "qwaMeasureToolsVM.h"qcDllVisionMeasure::qcDllVisionMeasure(QObject *parent)
:QObject(parent)
{}qcDllVisionMeasure::~qcDllVisionMeasure()
{}void* qcDllVisionMeasure::CreatWindow(int iLanguage, QWidget* parent)
{qwaMeasureToolsVM* widget = new qwaMeasureToolsVM(iLanguage,true,parent);m_pMainWnd =(void*) widget;return m_pMainWnd;
}//采用回调的方式实现,插件通过回调函数来获取,设置参数
//获取对应序号轴编码器坐标
void qcDllVisionMeasure::RegisterGetAxisEncVal(const std::function<void(int, double&)>& GetAxisEncVal)
{if (m_pMainWnd){((qwaMeasureToolsVM*)m_pMainWnd)->RegisterGetAxisEncVal(GetAxisEncVal);}
}//获取对应序号轴规划位置坐标
void qcDllVisionMeasure::RegisterGetAxisPrfVal(const std::function<void(int, double&)>& GetAxisPrfVal)
{}//获取内部变量的值
void qcDllVisionMeasure::RegisterGetVariableInVal(const std::function<void(int, double&)>& GetVariableInVal)
{if (m_pMainWnd){((qwaMeasureToolsVM*)m_pMainWnd)->RegisterGetVariableInVal(GetVariableInVal);}
}//设置内部变量的值
void qcDllVisionMeasure::RegisterSetVariableInVal(const std::function<void(int, const double&)>& SetVariableInVal) 
{if (m_pMainWnd){((qwaMeasureToolsVM*)m_pMainWnd)->RegisterSetVariableInVal(SetVariableInVal);}
}/// <summary>/// 设置当前测量文件路径/// </summary>/// <param name="dir">测量文件路径</param>
void qcDllVisionMeasure::SetCurrentMeaureFile(const QString& dir)
{if (m_pMainWnd){((qwaMeasureToolsVM*)m_pMainWnd)->SetCurrentMeaureFileByMainApp(dir);}
}/// <summary>
/// 开启测量
/// </summary>
/// <param name="indexMain">工序号</param>
/// <param name="IndexSub">工序号中的子序号</param>
void qcDllVisionMeasure::StartMeasureProcess(int indexMain, int IndexSub)
{if (m_pMainWnd){((qwaMeasureToolsVM*)m_pMainWnd)->StartMeasureProcessByMainApp(indexMain, IndexSub);}
}/// <summary>
/// 测量结果返回到主程序
/// </summary>
/// <param name="MeasureResult">回调对象</param>
void qcDllVisionMeasure::RegisterMeasureResult(const std::function<void(const int&, const QStringList&, const QVector<double>&)>& MeasureResult)
{if (m_pMainWnd){((qwaMeasureToolsVM*)m_pMainWnd)->RegisterMeasureResult(MeasureResult);}
}/// <summary>
/// 创建测量文件
/// </summary>
/// <param name="dir">创建路径</param>
/// <returns>0为正常,非0为失败</returns>
int qcDllVisionMeasure::NewMeasureFile(const QString& dir)
{if (m_pMainWnd){return ((qwaMeasureToolsVM*)m_pMainWnd)->NewMeasureFile(dir);}return -1;
}/// <summary>
/// 打开测量文件
/// </summary>
/// <param name="dir">打开路径</param>
/// <returns>0为正常,非0为失败</returns>
int qcDllVisionMeasure::OpenMeasureFile(const QString& dir) 
{if (m_pMainWnd){return ((qwaMeasureToolsVM*)m_pMainWnd)->OpenMeasureFile(dir);}return -1;
}/// <summary>
/// 另存为测量文件
/// </summary>
/// <param name="srcDir">源文件路径</param>
/// <param name="desDir">目标文件路径</param>
/// <returns>0为正常,非0为失败</returns>
int qcDllVisionMeasure::SaveAsMeasureFile(const QString& srcDir, const QString& desDir) 
{ if (m_pMainWnd){return ((qwaMeasureToolsVM*)m_pMainWnd)->SaveAsMeasureFile(srcDir, desDir);}return -1;
}/// <summary>
/// 设置当前显示页
/// </summary>
/// <param name="indexMain">对应工序页, -10为找中心,-11为对焦</param>
/// <param name="indexSub">对应工序页中的第几页</param>
/// <returns>0为成功, </returns>
int qcDllVisionMeasure::SetShowMeasurePage(int indexMain, int indexSub) 
{ if (m_pMainWnd){return ((qwaMeasureToolsVM*)m_pMainWnd)->SetShowMeasurePage(indexMain, indexSub);}return -1;
}/// <summary>
/// 获取当前测量工程工序列表
/// </summary>
/// <param name="listType">工序列表</param>
/// <returns>0为成功</returns>
int qcDllVisionMeasure::GetCurMeasureProgram(QVector<int>& listType) 
{ if (m_pMainWnd){return ((qwaMeasureToolsVM*)m_pMainWnd)->GetCurMeasureProgram(listType);}return -1;
}

 json接口描述json文件,放在头文件同目录

{"Type": ["BT150D"],"Name": "VisionMeasure","Description": "this is vision measure tools.","Version": "1.0.0","Vendor": "demina"
}

设置工程生成为从exe改为dll

将qt插件功能启动

至此重新封装QWdiget结束, 等待生成的dll,将其复制到主程序的exe文件路径下即可.

在主程序中要做的工作,即通过接口文件 声明一个接口实例指针.

bool NcCoreQt::InitPluginVisionMeasure()
{QString dir = qApp->applicationDirPath();dir += "/VisionMeasure.dll";//视觉插件的dllm_pPluginLoaderVM = new QPluginLoader(dir, this);QObject* plugin = m_pPluginLoaderVM->instance();//qDebug() << pluginLoader.metaData();if (plugin) {m_pInterfaceVM = qobject_cast<InterfaceVisionMeasure*>(plugin);if (m_pInterfaceVM){ qInfo() << tr("Load vision measure interface successful");return true;}}else{qDebug() << m_pPluginLoaderVM->errorString();}return false;
}

相关文章:

QWidget应用封装为qt插件,供其他qt应用调用

在之前的文章中,有介绍通过QProcess的方式启动QWidget应用,然后将其窗口嵌入到其他的qt应用中,作为子窗口使用.这篇文章主要介绍qt插件的方式将QWidget应用的窗口封装为插件,然后作为其他Qt应用中的子窗口使用. 插件优点: 与主程序为同一个进程,免去了进程间繁琐的通信方式,…...

UE(虚幻)学习(四) 第一个C++类来控制小球移动来理解蓝图和脚本如何工作

UE5视频看了不少&#xff0c;但基本都是蓝图如何搞&#xff0c;或者改一下属性&#xff0c;理解UE系统现有组件使用的。一直对C脚本和蓝图之间的关系不是很理解&#xff0c;看到一个视频讲的很好&#xff0c;我也做笔记记录一下。 我的环境是UE5.3.2. 创建UE空项目 我们创建…...

使用FreeNAS软件部署ISCSI的SAN架构存储(IP-SAN)练习题

一&#xff0c;实验用到工具分别为&#xff1a; VMware虚拟机&#xff0c;安装教程&#xff1a;VMware Workstation Pro 17 安装图文教程 FreeNAS系统&#xff0c;安装教程&#xff1a;FreeNAS-11.2-U4.1安装教程2024&#xff08;图文教程&#xff09; 二&#xff0c;新建虚…...

Sql Sqserver 相关知识总结

Sql Sqserver 相关知识总结 文章目录 Sql Sqserver 相关知识总结前言优化语句查询&#xff08;select&#xff09;条件过滤&#xff08;Where&#xff09;分组处理&#xff08;GROUP BY&#xff09;模糊查询&#xff08;like&#xff09;包含&#xff08;in&#xff09;合集&am…...

面试题整理17----K8s中request和limit资源限制是如何实现的

面试题整理17----K8s中request和limit资源限制是如何实现的 1. 资源请求&#xff08;Resource Requests&#xff09;2. 资源限制&#xff08;Resource Limits&#xff09;3. 总结 在Kubernetes&#xff08;K8s&#xff09;中&#xff0c;Pod的资源限制&#xff08;Resource Lim…...

Spring Boot @Conditional注解

在Spring Boot中&#xff0c;Conditional 注解用于条件性地注册bean。这意味着它可以根据某些条件来决定是否应该创建一个特定的bean。这个注解可以放在配置类或方法上&#xff0c;并且它会根据提供的一组条件来判断是否应该实例化对应的组件。 要使用 Conditional注解时&#…...

jpeg文件学习

相关最全的一篇文章链接&#xff1a;https://www.cnblogs.com/wtysos11/p/14089482.html YUV基础知识 Y表示亮度分量&#xff1a;如果只显示Y的话&#xff0c;图像看起来会是一张黑白照。 U&#xff08;Cb&#xff09;表示色度分量&#xff1a;是照片蓝色部分去掉亮度&#x…...

c++基于过程

前言&#xff1a; 笔记基于C黑马程序员网课视频&#xff1a;黑马程序员匠心之作|C教程从0到1入门编程,学习编程不再难_哔哩哔哩_bilibili 在此发布笔记&#xff0c;只是为方便学习&#xff0c;不做其他用途&#xff0c;原作者为黑马程序员。 1. C基础 1.1 用Visual Studio写C程…...

FOC软件 STM32CubeMX 使用

1、安装-及相关软件版本 展示版本注意事项:keil MDK和STM32CubeMX版本至少要大于等于图中版本。 2、 Motor Profiler 5.2.0使用方法...

leetcode hot 100 全排列

46. 全排列 已解答 中等 相关标签 相关企业 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 class Solution(object): def permute(self, nums): """ :type nums: List[int] :rtype: List[List[int…...

使用qrcode.vue生成当前网页的二维码(H5)

使用npm&#xff1a; npm install qrcode.vue 使用yarn&#xff1a; yarn add qrcode.vue package.json&#xff1a; 实现&#xff1a; <template><div class"code"><qrcode-vue :value"currentUrl" :size"size" render-as&…...

0055. shell命令--useradd

目录 55. shell命令--useradd 功能说明 语法格式 选项说明 选项 退出值 相关文件 /etc/passwd /etc/shadow /etc/group /etc/gshadow /etc/skel/ /etc/login.defs /etc/default/useradd 实践操作 注意事项 55. shell命令--useradd 功能说明 useradd 命令是 Lin…...

blender中合并的模型,在threejs中显示多个mesh;blender多材质烘培成一个材质

描述&#xff1a;在blender中合并的模型导出为glb&#xff0c;在threejs中导入仍显示多个mesh&#xff0c;并不是统一的整体&#xff0c;导致需要整体高亮或者使用DragControls等不能统一控制。 原因&#xff1a;模型有多个材质&#xff0c;在blender中合并的时候&#xff0c;…...

vue 本地自测iframe通讯

使用 postMessage API 来实现跨窗口&#xff08;跨域&#xff09;的消息传递。postMessage 允许你安全地发送消息到其他窗口&#xff0c;包括嵌套的 iframe&#xff0c;而不需要担心同源策略的问题。 发送消息&#xff08;父应用&#xff09; 1. 父应用&#xff1a;发送消息给…...

C++:单例模式

创建自己的对象&#xff0c;同时确保对象的唯一性。 单例类只能有一个实例☞静态成员static☞静态成员 必须类外初始化 单例类必须自己创建自己的唯一实例 单例类必须给所有其他对象提供这一实例 静态成员类内部可以访问 构造函数私有化☞构造函数私有外部不能创建&#x…...

SOME/IP 协议详解——信息格式

文章目录 1. 头部格式1.1 消息 ID&#xff08;Message ID&#xff09;1.2 长度&#xff08;Length&#xff09;1.3 请求 ID&#xff08;Request ID&#xff09;1.4 协议版本&#xff08;Protocol Version&#xff09;&#xff1a;1.5 接口版本&#xff08;Interface Version&am…...

C# GDI+数码管数字控件

调用方法 int zhi 15;private void button1_Click(object sender, EventArgs e){if (zhi > 19){zhi 0;}lcdDisplayControl1.DisplayText zhi.ToString();} 运行效果 控件代码 using System; using System.Collections.Generic; using System.Drawing.Drawing2D; using …...

在交叉编译中,常见的ELF(elf)到底是什么意思?

ELF 是 Executable and Linkable Format 的缩写&#xff0c;中文翻译为“可执行与可链接格式”。它是一种通用的文件格式&#xff0c;主要用于存储可执行文件、目标文件&#xff08;编译后的中间文件&#xff09;、动态库&#xff08;.so 文件&#xff09;以及内存转储文件&…...

Unity开发AR之Vuforia-MultiTarget笔记

前言 在增强现实(AR)技术蓬勃发展的今天,越来越多的开发者开始探索如何将AR应用于各种场景中。Vuforia作为一个领先的AR开发平台,为开发者提供了强大的工具和功能,使得创建AR体验变得更加简单和直观。本文将为您介绍Vuforia的基本概念、特点,以及如何配置和使用MultiTar…...

深入解析 Oracle 的聚合函数 ROLLUP

目录 深入解析 Oracle 的聚合函数 ROLLUP一、ROLLUP 函数概述二、ROLLUP 函数语法三、ROLLUP 实例详解&#xff08;一&#xff09;基础分组聚合&#xff08;二&#xff09;引入 ROLLUP 函数&#xff08;三&#xff09;ROLLUP 与 NULL 值&#xff08;四&#xff09;多列复杂分组…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...