moveable 一个可实现前端海报编辑器的 js 库

目录

  • 缘由-胡扯
  • 本文实验环境
  • 通用流程
  • 1.基础移动
    • 1.1 基础代码
      • 1.1.1 data-* 解释
    • 1.2 操作元素创建
    • 1.3 css 修饰
    • 1.4 cdn 引入
    • 1.5 js 实现元素可移动
    • 1.6 图片拖拽
    • 2.缩放
    • 3.旋转
    • 4.裁剪

懒得改文案了,海报编辑器换方案了,如果后面用别的再更。

缘由-胡扯

导火索:睡不着,现在是 25/1/25 快早上了

在这段时间突然有了一个新项目,该项目与前端通过 img 素材编辑一个海报有相同的需求点,又或者说该需求是弱于海报编辑的。

最开始,我是打算直接通过 js 直接实现对应的功能,但是总有一些小bug,刚好公司的前端推给我了 moveable,瞟了一眼后,发现需求完美符合,moveable 可实现前端对单个、多个元素的拖拽、组合、编辑、缩放、旋转、拉扯等操作,甚至于有一种“杀鸡焉用牛刀之感”。

但随着项目的迭代,未来的需求不可得知,但用发展的眼光看待这个项目的话,直接实现一个海报编辑器是最优的选择。

接下来我把我总结的 moveable 编写为教程,包括海报编辑器的制作写在该文之中,计划上下两篇,毕竟一篇使用基础,另一篇就是编辑器的制作。

想必,各位也不想在二次开发的时候看不懂 moveable 的实现逻辑吧,能平滑过渡就平滑过渡吧,大家的脑子都不想承载过多的计算,张飞:俺也一样。

本文实验环境

系统:Windows
前端:html
框架:无
js:其实我不是前端,就当是原生吧,因为笔者并不熟悉标准
编辑器:vs code
参考示例:https://daybrush.com/moveable/storybook/index.html
GitHub:https://github.com/daybrush/moveable
apidoc:https://daybrush.com/moveable/release/latest/doc/

通用流程

首先在此通过 vs 编写一个基础的 html (快捷键感叹号会自动弹出,选择单感叹号即可):
在这里插入图片描述
此时选择之后将会创建一个基础的 html 基础代码(如果没有的就直接复制吧):

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body></body>
</html>

1.基础移动

1.1 基础代码

我们首先实现 moveable 对元素的基础移动。

要实现 moveable 的基础移动需要创建一个 div 为其根容器,此时我们在 body 元素下创建一个 div ,并给与样式修饰 class="root",此处的样式修饰并不是必须项,在此只是为了更好的进行演示,当然,在 storybook 上给与的官方示例也是如此。

此时在基础的 html 元素中的 body 下,创建一个 div 为根节点,代码如下:

<div class="root"></div>

那么此时根节点有了,接下来就需要创建 moveable 的容器。你听的没错,这是一个比较通用的概念,实现某个特殊的元素时,使用一个容器作为存储是一个非常常见的方式。

此时在根节点下,创建一个 div 为容器:

<div class="root"><div class="container" data-croffle-ref="element$0"></div>
</div>

在以上的 html 代码中,样式修饰为 class="container" 则是容器的元素节点,但可能你对这个节点的疑问在 data-croffle-ref="element$0",如果没疑问就更好了,在此给一些不理解这个元素属性的读者做一下解释。

1.1.1 data-* 解释

在以上代码中 data-croffle-ref 为 html5 引入的一种机制,这个机制简单的来说是让开发者在不影响本身元素的语义的情况下,为当前元素增加新的元数据,又或者说开发者自定义了当前元素的属性

一般 data-* 自定义元数据可以与 document.querySelector 搭配,准确的找到对应的元数据,接下来在编写 js 代码时将会解释这一部分。

在这里,读者可以理解为此时定义了一个 moveable 的容器,并且自定义了一个属性 croffle-ref,但由于自定义属性的编写方式为带前缀 data-,即编写为 data-croffle-ref="element$0",其中 element$0 为这个自定义属性的值。

1.2 操作元素创建

当创建完操作元素的容器后,接下来创建用于控制的元素,则创建一个拖动、缩放、选择等操作的元素。

毕竟我们的目标就是添加不同的图片到当前编辑器,并对这些图片进行拖拽、旋转、缩放等操作。

此时在容器下添加一个 div 用于存放对应的操作元素,此时 body 下的html 代码如下:

<div class="root"><div class="container" data-croffle-ref="element$0"><div class="target" data-croffle-ref="targetRef">Target</div></div>
</div>

以上代码中,样式修饰为 class="target" 的 div 元素则为我们的操作元素,并且这个元素由于 moveable 的机制,给与了 data-croffle-ref="targetRef" 的自定义属性,并且这个 div 的内容为 Target 文本。

1.3 css 修饰

其实也不需要修饰,但是没有样式的话可能读者会觉得很奇怪,读者可以将 css 删除查看效果,最后发现还是加上 css 的为好(示例摘抄于 storybook 但做了精简和增加了便于文章讲解的额外内容)。

css 直接复制在 head 上即可,在此处已经给与了 style 标签:

<style>
.root {position: relative;
}.container {position: relative;margin-top: 50px;
}.target {position: absolute;width: 100px;height: 100px;top: 150px;left: 100px;line-height: 100px;text-align: center;background: #ee8;color: #333;font-weight: bold;border: 1px solid #333;box-sizing: border-box;
}
</style>

以上标签中,给与了 root 根元素与容器 root 的定位方式为相对定位,接着给与了操作元素 target 的样式为一个黄色的矩形框。

css 的话我不做多的解释,其实这是布局设计问题,咱们若不是前端就直接看详细的功能实现即可,若是前端页不用说对吧,如果不懂的直接复制即可,毕竟这是个样式,没有设计到特效动效制作。

接下来 cdn 引入后会给出这一部分的代码:

1.4 cdn 引入

以下是 moveable 的 cdn:

<script src="https://cdnjs.cloudflare.com/ajax/libs/moveable/0.53.0/moveable.min.js"integrity="sha512-gFIuV9WCEJeWYkY1ZdJXugypot9ooEtwJf6U8In5JR6z5ZvV1xAvAQe9mQ7IYBXiF9ICXyiCeqgCJzqf64wh7A=="crossorigin="anonymous" referrerpolicy="no-referrer"></script>

如果 cdn 引入有问题,可以在 https://cdnjs.com/libraries/moveable 查看。

此处我也给予 moveable.min.js 的下载地址:TODO

此时的代码(除 js 外应该是这样):

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>csdn 1_bit moveable how to use</title><style>.root {position: relative;}.container {position: relative;margin-top: 50px;}.target {position: absolute;width: 100px;height: 100px;top: 150px;left: 100px;line-height: 100px;text-align: center;background: #ee8;color: #333;font-weight: bold;border: 1px solid #333;box-sizing: border-box;}</style>
</head><body><div class="root"><div class="container" data-croffle-ref="element$0"><div class="target" data-croffle-ref="targetRef">Target</div></div></div>
</body></html>

1.5 js 实现元素可移动

在 moveable 中,若想让一个元素可移动其实很简单,在此我先列出代码:

const element$0 = document.querySelector(`[data-croffle-ref="element$0"]`);
const targetRef = document.querySelector(`[data-croffle-ref="targetRef"]`);const moveable$0 = new Moveable(element$0, {target: targetRef,draggable: true,throttleDrag: 1,edgeDraggable: false,startDragRotate: 0,throttleDragRotate: 0}
);moveable$0.on("drag", e => {e.target.style.transform = e.transform;
});

首先我们看最开始的两行,这两行都是通过 document.querySelector 寻找元素,其中一个元素是[data-croffle-ref="element$0"] 为容器元素,另一个为 [data-croffle-ref="targetRef"] 为操作元素。

容器元素与操作元素的获取这两者是必不可少的,通过确定这俩者从而进行控制。

之前也说过 document.querySelector 与自定义元素搭配即可精确找到元素,在此列出代码后不再赘述了。

在找到元素后,直接 new 一个 Moveable,其中第一个参数传入的是在 html 中定义的 element$0;第二个参数则是操作参数,则你需要如何操作这个元素。

第二个参数为一个字典,在字典中指定了操作的元素 targettargetRef,当前全部 key 键的解释如下:

  • target 操作的目标元素
  • draggable 目标元素是否可拖动
  • throttleDrag 拖动的延时毫秒数,设置为 0 则表示实时更新
  • edgeDraggable 表示目标的边缘是否可拖动
  • startDragRotate 当鼠标旋转多少后才使元素进行转动
  • throttleDragRotate 旋转的延时毫秒数,设置为 0 则表示实时更新

你可能在思考,当前操作只是移动目标元素,并没有进行旋转等操作,为什么会有这些参数。

其实当前示例时摘抄于 storybook 之上,在此列出为方便 查看 storybook 演示的读者作为解释,当前示例并不需要那么多的参数,只需要传入如下字典即可实现拖拽操作:

{target: targetRef,//目标元素draggable: true//是否可拖拽
}

那么最后的代码:

moveable$0.on("drag", e => {e.target.style.transform = e.transform;
});

则表示为 Moveable 实例的 drag 事件添加一个事件监听器,当目标元素被拖动时,会触发这个事件监听器;其中代码为 e.target.style.transform = e.transform; 则表示事件对象 e 包含了拖动操作的相关信息,其中 e.transform 是拖动后的变换样式,将 e.transform 的值赋给目标元素的 style.transform 属性,更新目标元素的位置。

此时当前拖拽的所有代码如下(省略了 style 样式,直接复制在 1.3 中的 css 即可):

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>csdn 1_bit moveable how to use</title><script src="https://cdnjs.cloudflare.com/ajax/libs/moveable/0.53.0/moveable.min.js"integrity="sha512-gFIuV9WCEJeWYkY1ZdJXugypot9ooEtwJf6U8In5JR6z5ZvV1xAvAQe9mQ7IYBXiF9ICXyiCeqgCJzqf64wh7A=="crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head><body><div class="root"><div class="container" data-croffle-ref="element$0"><div class="target" data-croffle-ref="targetRef">Target</div></div></div><script>const element$0 = document.querySelector(`[data-croffle-ref="element$0"]`);const targetRef = document.querySelector(`[data-croffle-ref="targetRef"]`);const moveable$0 = new Moveable(element$0, {target: targetRef,draggable: true});moveable$0.on("drag", e => {e.target.style.transform = e.transform;});</script>
</body></html>

此时效果如下:
请添加图片描述
你可能会想,如果咱们可是使用的是图片,并不是文字,不要担心,图片他来了。

1.6 图片拖拽

此时在 html 中,操作元素是通过一个容器进行包裹的,既然是容器,那么是包裹整个操作元素的,那么此时我们只需要将操作元素下添加一个图片即可。

例如在操作元素下添加一个 img 标签:

<div class="target" data-croffle-ref="targetRef"><img src="1.jpg" alt="" style="width: 100px;height: 100px;">
</div>

此时我给与了这个 img 的样式修饰 style="width: 100px;height: 100px;",此时的样式修饰是为了满足操作元素的大小,否则将会超出(虽然不会影响当前的功能,但美观上接收不了,之后将会用更加“优雅”的方式解决这个问题)。

图片各位自己放到自己的路径下即可,在此不在赘述,此时的结果如下:
请添加图片描述

2.缩放

注意:以下 html 代码与 第 1 小点 相同
使用 moveable 还可以对添加的图片进行缩放,只需要 new 一个Moveable 时传入不同的参数即可:

const moveable$0 = new Moveable(element$0, {target: targetRef,scalable: true,// 启用缩放功能,设置为 true 表示允许对目标元素进行缩放操作keepRatio: false,// 将 keepRatio 变量的值赋给配置对象的 keepRatio 属性,控制缩放时是否保持宽高比renderDirections: renderDirections
});

其中参数定义如下:

  • scalable 启用缩放功能,设置为 true 表示允许对目标元素进行缩放操作
  • keepRatio 控制缩放时是否保持宽高比
  • false为不保持 renderDirections 控制显示缩放的控制点

其中 scalable 与 keepRatio 都比较好理解,只有 renderDirections 较为陌生。

renderDirections 是一个 list,这个list 存储了要显示控制点的内容,例如这个 renderDirections 的代码为 const renderDirections = ["n", "s"]; 则表示在北部和南部有一个控制点,即如下图所示:

在这里插入图片描述
此时具体 js 代码部分如下:

const renderDirections = ["n", "s"];
const element$0 = document.querySelector(`[data-croffle-ref="element$0"]`);
const targetRef = document.querySelector(`[data-croffle-ref="targetRef"]`);
const moveable$0 = new Moveable(element$0, {target: targetRef,scalable: true,// 启用缩放功能,设置为 true 表示允许对目标元素进行缩放操作keepRatio: false,// 控制缩放时是否保持宽高比renderDirections: renderDirections
});moveable$0.on("scale", e => {e.target.style.transform = e.drag.transform;
});

其中代码:

moveable$0.on("scale", e => {e.target.style.transform = e.drag.transform;
});

为表示监听 scale 缩放事件,通过 e.drag.transform 得到最新的样式信息给予到目标元素即可改变以及重新绘制。

此时还可以更改 renderDirections 为 const renderDirections = ["nw","n","ne","w","e","sw","s","se"];,其中 nw 表示东南、ne 表示西南、以此类推运行代码后展现如下:
在这里插入图片描述
其展示效果如下:
请添加图片描述
若将 keepRatio: false,// 控制缩放时是否保持宽高比 改成 true,效果如下:

请添加图片描述

3.旋转

设定目标是否可进行旋转也只是需要 new 一个 moveable 时传入的参数即可:

const moveable$0 = new Moveable(element$0, {target: targetRef,rotatable: true,rotationPosition: rotationPosition
});

参数说明如下:

  • rotatable 控制目标元素是否可旋转 true 为允许
  • rotationPosition 指定旋转控制点的位置

以上代码中 rotationPosition 设置为字符串类型的 val 为 “top” 即可,即:const rotationPosition = "top";

之后为 moveable$0 设置 rotate 事件监听:

moveable$0.on("rotate", e => {e.target.style.transform = e.drag.transform;
});

那么即可设置目标元素的响应。

完整的 js 代码如下:

const rotationPosition = "top";
const element$0 = document.querySelector(`[data-croffle-ref="element$0"]`);
const targetRef = document.querySelector(`[data-croffle-ref="targetRef"]`);
const moveable$0 = new Moveable(element$0, {target: targetRef,rotatable: true,// 控制目标元素是否可旋转rotationPosition: rotationPosition// 指定旋转控制点的位置
});
moveable$0.on("rotate", e => {e.target.style.transform = e.drag.transform;
});

此时的 html 代码并不需要修改,此时页面所展示的内容如下:

在这里插入图片描述
此时的控制点在顶部,若设置 rotationPosition 为 bottom const rotationPosition = "bottom"; 时页面展示效果如下:

在这里插入图片描述
此时的操作效果如下:
请添加图片描述

4.裁剪

在 moveable 中,一般情况下,只需要在 new moveable 时传入不同的参数即可对操作目标开启不同的操作,最后再为其添加对应操作的事件响应,即可完成对开启的操作完成监听。

以下是一个完成裁剪功能的 moveable 代码:

const moveable$0 = new Moveable(element$0, {target: targetRef,draggable: true,clippable: true,clipTargetBounds: false,
});

以下为各个新增参数的解释:

  • draggable 控制目标元素是否可拖动
  • clippable 控制目标元素是否可裁剪
  • clipRelative 控制裁剪区域的定位方式
  • clipTargetBounds 控制裁剪区域是否受目标元素边界限制

此时读者可能发现,以上再 new moveable 时允许目标可拖动以及目标可剪切,那我该怎么样监听两个事件呢?其实很简单,只需要使用 on 监听两个事件即可,即:

moveable$0.on("drag", e => {e.target.style.transform = e.transform;
});
moveable$0.on("clip", e => {e.target.style.clipPath = e.clipStyle;
});

再 drag 时给与 e.transform 到 e.target.style.transform 的 transform 即可,再 clip 时给与 e.clipStyle 到 e.target.style.clipPath 即可。

接下来我们继续回到参数之中,其中 clipTargetBounds 表示在裁剪时会不会超出区域了还可以裁剪,当设置为 true 时不允许过边界(蓝框裁剪),结果如下:

请添加图片描述
如果设置为 false则允许过边界,效果如下(也演示了拖拽效果):
请添加图片描述
此时完整的js 代码如下(html 不需要进行修改):

const element$0 = document.querySelector(`[data-croffle-ref="element$0"]`);
const targetRef = document.querySelector(`[data-croffle-ref="targetRef"]`);const moveable$0 = new Moveable(element$0, {target: targetRef,draggable: true,clippable: true,clipTargetBounds: false,
});moveable$0.on("drag", e => {e.target.style.transform = e.transform;
});moveable$0.on("clip", e => {e.target.style.clipPath = e.clipStyle;
});

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

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

相关文章

Apollo 9.0 速度动态规划决策算法 – path time heuristic optimizer

文章目录 1. 动态规划2. 采样3. 代价函数3.1 障碍物代价3.2 距离终点代价3.3 速度代价3.4 加速度代价3.5 jerk代价 4. 回溯 这一章将来讲解速度决策算法&#xff0c;也就是SPEED_HEURISTIC_OPTIMIZER task里面的内容。Apollo 9.0使用动态规划算法进行速度决策&#xff0c;从类名…

【Day41 LeetCode】单调栈问题

一、单调栈问题 单调栈问题通常是在一维数组中寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置。 1、每日温度 739 这题的目的是对于当天&#xff0c;找到未来温度升高的那一天&#xff0c;也就是当前元素的右边第一个比自己大的元素。所以我们需要维护一个单…

Cherno C++ P55 宏

这篇文章我们讲一下C当中的宏。其实接触过大型项目的朋友可能都被诡异的宏折磨过。 宏是在预处理当中&#xff0c;通过文本替换的方式来实现一些操作&#xff0c;这样可以不用反复的输入代码&#xff0c;帮助我们实现自动化。至于预处理的过程&#xff0c;其实就是文本编辑&am…

web第三次作业

弹窗案例 1.首页代码 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>综合案例</title><st…

深入解析LVS命令参数及DR模式下的ARP抑制原理

深入解析LVS命令参数及DR模式下的ARP抑制原理 一、LVS简介 Linux Virtual Server (LVS) 是基于Linux内核的高性能负载均衡解决方案&#xff0c;支持NAT、DR&#xff08;Direct Routing&#xff09;和TUN&#xff08;IP Tunneling&#xff09;三种模式。其中&#xff0c;ipvsad…

阿里云一键部署DeepSeek-V3、DeepSeek-R1模型

目录 支持的模型列表 模型部署 模型调用 WebUI使用 在线调试 API调用 关于成本 FAQ 点击部署后服务长时间等待 服务部署成功后&#xff0c;调用API返回404 请求太长导致EAS网关超时 部署完成后&#xff0c;如何在EAS的在线调试页面调试 模型部署之后没有“联网搜索…

Win10环境借助DockerDesktop部署大数据时序数据库Apache Druid

Win10环境借助DockerDesktop部署最新版大数据时序数据库Apache Druid32.0.0 前言 大数据分析中&#xff0c;有一种常见的场景&#xff0c;那就是时序数据&#xff0c;简言之&#xff0c;数据一旦产生绝对不会修改&#xff0c;随着时间流逝&#xff0c;每个时间点都会有个新的…

【第13章:自监督学习与少样本学习—13.1 自监督学习最新进展与实现方法】

凌晨三点的实验室,博士生小王盯着屏幕里正在"自娱自乐"的神经网络——这个没有吃过一张标注图片的模型,正在通过旋转、拼图、填色等游戏任务,悄悄掌握着理解世界的秘诀。这种魔法般的修炼方式,正是当今AI领域最炙手可热的技术:自监督学习。 一、打破数据枷锁:自…

数据库报错1045-Access denied for user ‘root‘@‘localhost‘ (using password: YES)解决方式

MySQL 报错 1045 表示用户root从localhost连接时被拒绝访问&#xff0c;通常是因为密码错误、权限问题或配置问题。以下是解决该问题的常见方法&#xff1a; 方法一&#xff1a;检查用户名和密码 • 确认用户名和密码是否正确&#xff1a; 确保输入的用户名和密码完全正确&am…

八大排序——简单选择排序

目录 1.1基本操作&#xff1a; 1.2动态图&#xff1a; 1.3代码&#xff1a; 代码解释 1. main 方法 2. selectSort 方法 示例运行过程 初始数组 每轮排序后的数组 最终排序结果 代码总结 1.1基本操作&#xff1a; 选择排序&#xff08;select sorting&#xff09;也…

与传统光伏相比 城电科技的光伏太阳花有什么优势?

相比于传统光伏&#xff0c;城电科技的光伏太阳花有以下优势&#xff1a; 一、发电效率方面 智能追踪技术&#xff1a;光伏太阳花通过内置的智能追踪系统&#xff0c;采用全球定位跟踪算法&#xff0c;能够实时调整花瓣&#xff08;即光伏板&#xff09;的角度&#xff0c;确…

FPGA的星辰大海

编者按 时下风头正盛的DeepSeek,正值喜好宏大叙事的米国大统领二次上岗就业,OpenAI、软银、甲骨文等宣布投资高达5000亿美元“星际之门”之际,对比尤为强烈。 某种程度上,,是低成本创新理念的直接落地。 包括来自开源社区的诸多赞誉是,并非体现技术有多“超越”,而是…

Elasticsearch:15 年来致力于索引一切,找到重要内容

作者&#xff1a;来自 Elastic Shay Banon 及 Philipp Krenn Elasticsearch 刚刚 15 岁了&#xff01;回顾过去 15 年的索引和搜索&#xff0c;并展望未来 15 年的相关内容。 Elasticsearch 刚刚成立 15 周年。一切始于 2010 年 2 月的一篇公告博客文章&#xff08;带有标志性的…

嵌入式软件、系统、RTOS(高软23)

系列文章目录 4.2嵌入式软件、系统、RTOS 文章目录 系列文章目录前言一、嵌入式软件二、嵌入式系统三、嵌入式系统分类四、真题总结 前言 本节讲明嵌入式相关知识&#xff0c;包括软件、系统。 一、嵌入式软件 二、嵌入式系统 三、嵌入式系统分类 四、真题 总结 就是高软笔记…

数据结构 day02

3. 线性表 3.1. 顺序表 3.1.3. 顺序表编程实现 操作&#xff1a;增删改查 .h 文件 #ifndef __SEQLIST_H__ #define __SEQLIST_H__ #define N 10 typedef struct seqlist {int data[N];int last; //代表数组中最后一个有效元素的下标 } seqlist_t;//1.创建一个空的顺序表 seq…

数据恢复-01-机械硬盘的物理与逻辑结构

磁盘存储原理 磁盘存储数据的原理&#xff1a; 磁盘存储数据的原理是利用磁性材料在磁场作用下的磁化性质&#xff0c;通过在磁盘表面上划分成许多小区域&#xff0c;根据不同的磁化方向来表示0和1的二进制数据&#xff0c;通过读写磁头在磁盘上的移动&#xff0c;可以实现数据…

wordpress get_footer();与wp_footer();的区别的关系

在WordPress中&#xff0c;get_footer() 和 wp_footer() 是两个不同的函数&#xff0c;它们在主题开发中扮演着不同的角色&#xff0c;但都与页面的“页脚”部分有关。以下是它们的区别和关系&#xff1a; 1. get_footer() get_footer() 是一个用于加载页脚模板的函数。它的主…

DeepSeek 通过 API 对接第三方客户端 告别“服务器繁忙”

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 上一期分享了如何在本地部署 DeepSeek R1 模型&#xff0c;但通过命令行运行的本地模型&#xff0c;问答的交互也要使用命令行&#xff0c;体验并不是很好。这期分享几个第三方客户端&#xff0c;涵盖了桌…

跟着李沐老师学习深度学习(十一)

经典的卷积神经网络 在本次笔记中主要介绍一些经典的卷积神经网络模型&#xff0c;主要包含以下&#xff1a; LeNet&#xff1a;最早发布的卷积神经网络之一&#xff0c;目的是识别图像中的手写数字&#xff1b;AlexNet&#xff1a; 是第一个在大规模视觉竞赛中击败传统计算机…

使用JavaScript实现深浅拷贝

1. 拷贝的基本概念和必要性 在 JavaScript 中&#xff0c;数据类型分为基本数据类型&#xff08;如 Number、String、Boolean、Null、Undefined、Symbol&#xff09;和引用数据类型&#xff08;如 Object、Array&#xff09;。基本数据类型存储的是值本身&#xff0c;而引用数…