动易网站 修改栏目名字/网站搜索排名优化怎么做
Canvas鼠标滚轮缩放以及画布拖动
本文会带大家认识Canvas中常用的坐标变换方法 translate 和 scale,并结合这两个方法,实现鼠标滚轮缩放以及画布拖动功能。
Canvas的坐标变换
Canvas
绘图的缩放以及画布拖动主要通过 CanvasRenderingContext2D 提供的 translate
和 scale
两个方法实现的,先来认识下这两个方法。
translate 方法
语法:
translate(x, y)
translate
的用法记住一句话:
translate 方法重新映射画布上的(0, 0)位置。
说白了就是把画布的原点移动到了 translate
方法指定的坐标,之后所有图形的绘制都会以该坐标进行参照。
举个例子:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 600;
canvas.height = 400;ctx.fillStyle = 'red';
ctx.fillRect(50, 50, 50, 50);ctx.translate(50, 50);ctx.fillStyle = 'green';
ctx.fillRect(50, 50, 50, 50);
开始的时候,Canvas
容器原点和绘图原点重合,绘制一个背景色为红色,原点坐标(50, 50),长宽各为 50 的矩形,接着调用 translate
方法将绘图原点沿水平和纵向各偏移50,再绘制一个背景色是绿色,原点坐标(50, 50),长宽各为 50 的矩形,示意图如下,其中灰色的背景为 Canvas
区域。
需要注意的是,如果此时继续调用 translate
方法进行偏移操作,后续的偏移会基于原来偏移的基础上进行的。
ctx.fillStyle = 'red';
ctx.fillRect(50, 50, 50, 50);// 第一次坐标系偏移
ctx.translate(50, 50);ctx.fillStyle = 'green';
ctx.fillRect(50, 50, 50, 50);// 第二次坐标系偏移
ctx.translate(50, 50);ctx.fillStyle = 'blue';
ctx.fillRect(50, 50, 50, 50);
因此,如果涉及到多次调用 translate
方法进行坐标变换,很容易将坐标系搞混乱,所以,一般在translate
之前会调用 save
方法先保存下绘图的状态,再调用 translate
后,绘制完图形后,调用 restore
方法恢复之前的上下文,对坐标系进行还原,这样不容易搞乱坐标系。
save方法通过将当前状态压入堆栈来保存画布的整个状态。
保存到堆栈上的图形状态包括:
- 当前转换矩阵。
- 当前裁剪区域。
- 当前的破折号列表。
- 包含的属性:strokeStyle、ill Style、lobalAlpha、linewidth、lineCap、lineJoin、miterLimit、lineDashOffset、shadowOffsetX、shadowOffsetY、shadowBlur、shadowColor、global alCompositeOperation、Font、extAlign、extBaseline、Direction、ImageSmoothingEnabled。
restore 方法通过弹出绘制状态堆栈中的顶部条目来恢复最近保存的画布状态。
ctx.fillStyle = 'red';
ctx.fillRect(50, 50, 50, 50);// 保存绘图上下文
ctx.save()ctx.translate(50, 50);
ctx.fillStyle = 'green';
ctx.fillRect(50, 50, 50, 50);// 绘制完成后恢复上下文
ctx.restore()ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 50, 50);
scale 方法
语法:
scale(x, y)
缩放 (scale) 就是将一个图形围绕中心点,然后将宽和高分别乘以一定的因子(sx,sy)
默认情况下,画布上的一个单位正好是一个像素。缩放变换会修改此行为。例如,如果比例因子为0.5,则单位大小为0.5像素;因此,形状的绘制大小为正常大小的一半。类似地,比例因子为2会增加单位大小,使一个单位变为两个像素;从而以正常大小的两倍绘制形状。
举个例子:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');ctx.scale(0.5,2);
ctx.fillStyle="blue";
ctx.fillRect(50,50,100,50);
调用 scale(0.5,2)
将画布水平方向缩小一倍,垂直方向放大一倍,绘制一个坐标原点 (50, 50),宽度 100,高度 50 的矩形。经过缩放变换后,距离原点的实际像素是横轴 25像素,纵轴 100 像素,宽度 50 像素,高度 100 像素。
实现鼠标拖动画布
效果
创建Sence类
Sence类:
class Scene {constructor(id, options = {width: 600,height: 400}) {this.canvas = document.querySelector('#' + id)this.width = options.width;this.height = options.height;this.canvas.width = options.width;this.canvas.height = options.height;this.ctx = this.canvas.getContext('2d');}draw() {this.ctx.fillStyle = 'red';this.ctx.fillRect(50, 50, 50, 50);this.ctx.fillStyle = 'green';this.ctx.fillRect(150, 150, 50, 50);}clear() {this.canvas.width = this.width;}paint() {this.clear();this.draw();}
}let scene = new Scene('canvas');scene.draw();
在 Sence
类的构造函数中初始化 Canvas
,得到 CanvasRenderingContext2D
对象,并设置 Canvas
的宽高属性,draw
方法里面绘制了两个矩形。
在进行下面的工作之前,我们先来了解下 Canvas
的事件机制。
通过 addEventListener
方法可以给 Canvas
绑定一个事件。
this.canvas.addEventListener('mousedown', (event) => {console.log(event.x)
});
事件的回调函数参数的 event
对象中可以获取鼠标点击 Canvas
时的坐标信息,event
对象中经常会用到的坐标有两个,一个是 event.x
和 event.y
,另一个是 event.offsetX
和 event.offsetY
,其中,event.x
和 event.y
获取的是鼠标点击时相对于屏幕的坐标,而 event.offsetX
和 event.offsetY
是相对于 Canvas
容器的坐标。
通过下面这张图可以清晰的看出两个坐标的区别,明白这一点对于我们后续的坐标变换非常重要。
在构造函数中添加对 Canvas
的 mousedown
事件监听,记录点击鼠标时相对屏幕的位置 x
和 y
。
class Scene {x = 0; // 记录鼠标点击Canvas时的横坐标y = 0; // 记录鼠标点击Canvas时的纵坐标constructor(id, options = {width: 600,height: 400}) {this.canvas.addEventListener('mousedown', this.onMousedown);}onMousedown(e) {if (e.button === 0) {// 点击了鼠标左键this.x = x;this.y = y;}}
}
画布拖动的整体思路就是利用前面介绍的 Canvas
的 translate
方法。画布的整体偏移量记录在 offset.x
和 offset.y
,鼠标触发 mousedown
事件时,记录当前鼠标点击的位置相对于屏幕的坐标 x
, 和 y
,并且开始监听鼠标的 mousemove
和 mouseup
事件。鼠标触发 mousemove
事件时计算每次移动时整体累加的偏移量:
onMousemove(e) {this.offset.x = this.curOffset.x + (e.x - this.x);this.offset.y = this.curOffset.y + (e.y - this.y);this.paint();
}
其中 curOffset.x
和 curOffset.y
记录的是鼠标触发 mouseup
时保存的当前的偏移量,便于计算累加的偏移量。每次触发完鼠标 mousemove
事件后,重新进行图形绘制。
onMouseup() {this.curOffset.x = this.offset.x;this.curOffset.y = this.offset.y;window.removeEventListener('mousemove', this.onMousemove);window.removeEventListener('mouseup', this.onMouseup);
}
Sence 类完整代码如下:
class Scene {offset = { x: 0, y: 0 }; // 拖动偏移curOffset = { x: 0, y: 0 }; // 记录上一次的偏移量x = 0; // 记录鼠标点击Canvas时的横坐标y = 0; // 记录鼠标点击Canvas时的纵坐标constructor(id, options = {width: 600,height: 400}) {this.canvas = document.querySelector('#' + id);this.width = options.width;this.height = options.height;this.canvas.width = options.width;this.canvas.height = options.height;this.ctx = this.canvas.getContext('2d');this.onMousedown = this.onMousedown.bind(this);this.onMousemove = this.onMousemove.bind(this);this.onMouseup = this.onMouseup.bind(this);this.canvas.addEventListener('mousedown', this.onMousedown);}onMousedown(e) {if (e.button === 0) {// 鼠标左键this.x = e.x;this.y = e.ywindow.addEventListener('mousemove', this.onMousemove);window.addEventListener('mouseup', this.onMouseup);}}onMousemove(e) {this.offset.x = this.curOffset.x + (e.x - this.x);this.offset.y = this.curOffset.y + (e.y - this.y);this.paint();}onMouseup() {this.curOffset.x = this.offset.x;this.curOffset.y = this.offset.y;window.removeEventListener('mousemove', this.onMousemove);window.removeEventListener('mouseup', this.onMouseup);}draw() {this.ctx.fillStyle = 'red';this.ctx.fillRect(50, 50, 50, 50);this.ctx.fillStyle = 'green';this.ctx.fillRect(150, 150, 50, 50);}clear() {this.canvas.width = this.width;}paint() {this.clear();this.ctx.translate(this.offset.x, this.offset.y);this.draw();}
}
上述代码中有几点需要注意:
- 事件函数中的this指向问题
细心的同学可能注意到,在 Sence 类的构造函数里有这样几行代码:
constructor(id, options = {width: 600,height: 400}) {this.onMousedown = this.onMousedown.bind(this);this.onMousemove = this.onMousemove.bind(this);this.onMouseup = this.onMouseup.bind(this);}
为什么要使用 bind
函数给事件函数重新绑定this对象呢?
主要的原因在于一个事件有监听就会有移除。假设我们想要销毁 mousemove
事件怎么办呢?
可以调用 removeEventListener
方法进行事件监听的移除,比如上述代码会在 onMouseup
中移除对 mousemove
事件的监听:
onMouseup() {this.curOffset.x = this.offset.x;this.curOffset.y = this.offset.y;window.removeEventListener('mousemove', this.onMousemove);
}
如果不在构造函数中使用 bind
方法重新绑定 this
指向,此时的 this
指向的就是window
,因为 this
指向的是调用 onMouseup
的对象,而 onMouseup
方法是被 window
上的 mouseup
事件调用的,但是实际上我们想要的this指向应该 Sence 实例
。为了避免上述问题的出现,最好的解决办法就是在 Sence
类的构造函数中重新绑定 this
指向。
- 画布的清空问题
每次鼠标移动的时候会改变 Canvas
的 CanvasRenderingContext2D
偏移量,并重新进行图形的绘制,重新绘制的过程就是先将画布清空,然后设置画布的偏移量(调用 translate 方法),接着绘制图形。其中清空画布这里选择了重新设置Canvas的宽度,而不是调用 clearRect
方法,主要是因为clearRect
方法只在 Canvas
的渲染上下文没有进行过平移、缩放、旋转等变换时有效,如果 Canvas
的渲染上下文已经经过了变换,那么在使用 clearRect
清空画布前,需要先重置变换,否则 clearRect
将无法有效地清除整块画布。
实现鼠标滚轮缩放
效果
实现原理
鼠标滚轮的放大需要结合上面介绍的 Canvas
的 translate
和 scale
两个方法进行组合变换。
计算放大系数
监听鼠标滚轮的 mousewheel
事件,在事件的回调函数中通过 event.wheelDelta
值的变化来实时计算当前的缩放值,其中 event.wheelDelta > 0
表示放大,反之表示缩小,放大和缩小都有对应的阈值,超过阈值就禁止继续放大和缩小。
改造 Sence
类,添加 onMousewheel
事件:
onMousewheel(e) {if (e.wheelDelta > 0) {// 放大this.scale = parseFloat((this.scaleStep + this.scale).toFixed(2)); // 解决小数点运算丢失精度的问题if (this.scale > this.maxScale) {this.scale = this.maxScale;return;}} else {// 缩小this.scale = parseFloat((this.scale - this.scaleStep).toFixed(2)); // 解决小数点运算丢失精度的问题if (this.scale < this.minScale) {this.scale = this.minScale;return;}}this.preScale = this.scale;
}
其中,this.scale / this.preScale
计算出来的值就是放大系数,暂且记做 n
。
在计算放大系数的时候,需要注意两个浮点型数值在计算不能直接相加,否则会出现丢失精度的问题。
缩放原理
在缩放的时候,会调用 scale(n, n)
方法,将坐标系放大 n
倍。假设鼠标滚轮停在 A 点进行放大操作,放大之后得到坐标 A’ 点。
可以看到,放大之后,A(x1, y1)
坐标变换到了 A'(x1, y1)
,A => A'
放大了 n
倍,因此得到 x1 = x * n
,y1 = y1 * n
。
这个时候就会存在一个问题,我们在 A
点进行放大,放大后得到的 A'
的位置应该是不变的,所以需要在放大之后需要调整 A’
点的位置到 A
点。
这里我们采用的策略是在放大前先偏移一段距离,然后进行放大之后就可以保持 A
点和 A‘
点的重合。
鼠标停留在 A
点对蓝色矩形进行放大,放大系数为 n
,蓝色矩形的起点左上角和坐标原点重合,宽度和高度分别是 x
和 y
,因此,A点的坐标为 (x, y)
。
前面我们说过,对 A
点进行放大后得到的 A’
点应该和A点重合,这样就需要先把整个坐标系沿着x轴和y轴分别向左和向上偏移 offsetX
和 offsetY
,偏移后得到的 A'
点坐标记作 (x1, x2)
,因为 A
点是经过放大 n
倍后得到的 A'
点,所以得到以下距离关系:
x1 = x * n;
y1 = y * n
进一步就可以得到横纵坐标的偏移量 offsetX
和 offsetY
的绝对值:
offsetX = x*n-x;
offsetY =x*n - y;
因此,这需要将坐标系经过 translate(-offsetX, -offsetY)
之后,再 scale(n, n)
,就能确保 A
点 和 A‘
点重合了。
明白了缩放的基本原理,下面就继续码代码吧😜。
onMousewheel(e) {e.preventDefault();this.mousePosition.x = e.offsetX; // 记录当前鼠标点击的横坐标this.mousePosition.y = e.offsetY; // 记录当前鼠标点击的纵坐标if (e.wheelDelta > 0) {// 放大this.scale = parseFloat((this.scaleStep + this.scale).toFixed(2)); // 解决小数点运算丢失精度的问题if (this.scale > this.maxScale) {this.scale = this.maxScale;return;}} else {// 缩小this.scale = parseFloat((this.scale - this.scaleStep).toFixed(2)); // 解决小数点运算丢失精度的问题if (this.scale < this.minScale) {this.scale = this.minScale;return;}}this.offset.x = this.mousePosition.x - ((this.mousePosition.x - this.offset.x) * this.scale) / this.preScale;this.offset.y = this.mousePosition.y - ((this.mousePosition.y - this.offset.y) * this.scale) / this.preScale;this.paint(this.ctx);this.preScale = this.scale;this.curOffset.x = this.offset.x;this.curOffset.y = this.offset.y;
}paint() {this.clear();this.ctx.translate(this.offset.x, this.offset.y);this.ctx.scale(this.scale, this.scale);this.draw();
}
总结
本文从基础原理到代码实现,完整给大家讲解了 Canvas
画布绘制中经常会遇到的画布拖动和鼠标滚轮缩放功能,希望对大家有帮助,更多精彩文章欢迎大家关注我的vx公众号:前端架构师笔记。本文完整代码地址:https://github.com/astonishqft/scanvas-translate-and-scale
相关文章:

Canvas鼠标滚轮缩放以及画布拖动(图文并茂版)
Canvas鼠标滚轮缩放以及画布拖动 本文会带大家认识Canvas中常用的坐标变换方法 translate 和 scale,并结合这两个方法,实现鼠标滚轮缩放以及画布拖动功能。 Canvas的坐标变换 Canvas 绘图的缩放以及画布拖动主要通过 CanvasRenderingContext2D 提供的 …...

[ECCV 2020] FGVC via progressive multi-granularity training of jigsaw patches
Contents IntroductionProgressive Multi-Granularity (PMG) training frameworkExperimentsReferencesIntroduction 不同于显式地寻找特征显著区域并抽取其特征,作者充分利用了 CNN 不同 stage 输出的特征图的语义粒度信息,并使用 Jigsaw Puzzle Generator 进行数据增强来帮…...

Python推导式
列表(list)推导式 [remove for source in xx_list]或者[remove for source in xx_list if condition] 实例: names[Bob,Mark,Mausk,Johndan,Wendy] new_names[name.upper() for name in names if len(name)<5] print(new_names)即迭代列…...

Java列表List的定查改增删操作
Java列表List的定查改增删操作定义查找遍历元素与下标互查修改增加删除java.util中提供了三种常用的集合类,列表List、集合Map和字典Set。这些集合类相较于数组有更多功能,并且都可以通过Iterator(迭代器)来访问。 在这篇博客中&…...

day03java语言特性 JDK、JRE、JVM
1、Java语言的特性 1.1、简单性在Java语言当中真正操作内存的是:JVM(Java虚拟机)所有的java程序都是运行在Java虚拟机当中的。而Java虚拟机执行过程中再去操作内存。对于C或者C来说程序员都是可以直接通过指针操作内存的。C或者C更灵活&…...

HydroD 实用教程(二)有限元模型
目 录一、前言二、模型种类三、单元类型四、FEM文件五、参考文献一、前言 SESAM (Super Element Structure Analysis Module)是由挪威船级社(DNV-GL)开发的一款有限元分析(FEA)系统,它以 GeniE、…...

Java中的Set集合
Set不能存储重复元素,元素无序(指的是不按照添加的顺序,List集合是按照添加顺序存储的)hashSet注:源码底层是hashMap实现的,因为hashMap是双列的,其中键是不能重复的,而hashSet是单列…...

【RabbitMQ五】——RabbitMQ路由模式(Routing)
RabbitMQ路由模式前言RabbitMQ模式的基本概念为什么要使用Rabbitmq 路由模式RabbitMQ路由模式组成元素路由模式完整代码Pom文件引入RabbtiMQ依赖RabbitMQ工具类生产者消费者1消费者2运行结果截图前言 通过本篇博客能够简单使用RabbitMQ的路由模式。 本篇博客主要是博主通过官网…...

【C语言】宏定义 结构体 枚举变量的用法
目录 一、数据类型 二、C语言宏定义 三、C语言typedef重命名 四、 #define与typedef的区别 五、结构体 六、枚举变量 补充学习一点STM32的必备基础知识 一、数据类型 二、C语言宏定义 关键字:#define 用途:用一个字符串代替一个数字,…...

锁升级之Synchronized
Synchronized JVM系统锁一个对象里如果有多个synchronized方法,同一时刻,只要有一个线程去调用其中的一个synchronized方法,其他线程只能等待!锁的是当前对象,对象被锁定后,其他线程都不能访问当前对象的其…...

基于nodejs+vue疫情网课管理系统
疫情网课也都将通过计算机进行整体智能化操作,对于疫情网课管理系统所牵扯的管理及数据保存都是非常多的,例如管理员:首页、个人中心、学生管理、教师管理、班级管理、课程分类管理、课程表管理、课程信息管理、作业信息管理、请假信息管理、上课签到管理、论坛交流…...

Zabbix 构建监控告警平台(三)
Zabbix User parametersZabbix Trigger1.Zabbix User parameters 1.1即自定义KEY 注意:mysql安装在被监测主机 [rootlocalhost ~]# yum -y install mariadb-server mariadb [rootlocalhost ~]# systemctl start mariadb [rootlocalhost ~]# mysqladmin -uroot statu…...

Linux系统之dool命令行工具的基本使用
Linux系统之dool命令行工具的基本使用一、dool命令行工具介绍二、本地系统环境检查1.检查系统版本2.检查系统内核版本三、下载dool软件包1.创建下载目录2.下载dool四、安装dool1.安装python32.安装dool五、dool的命令帮助六、dool的基本使用1.直接使用dool监控系统2.监控cpu和网…...

LeetCode-2335-装满杯子需要的最短总时长
1、堆 我们可以维护一个堆,首先我们将数组中不为0的数全部加入堆中,而后进行循环。当堆不为空时,我们将堆顶元素出堆并减一,而后观察是否还能继续出堆,若能则出堆,否则跳过,最后我们将处理后的…...

npm ERR! code ELIFECYCLE解决方案,npm犯错!myweb@1.0.0构建脚本失败。
1.问题npm ERR! code ELIFECYCLEnpm ERR! errno 1npm ERR! myweb1.0.0 build: webpack --config config/webpack.config.jsnpm ERR! Exit status 1npm ERR!npm ERR! Failed at the myweb1.0.0 build script.npm犯错!代码ELIFECYCLEnpm犯错!errno 1npm犯错!myweb1.0.0 build: we…...

最小二乘支持向量机”在学习偏微分方程 (PDE) 解方面的应用(Matlab代码实现)
目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨💻4 Matlab代码 💥1 概述 本代码说明了“最小二乘支持向量机”在学习偏微分方程 (PDE) 解方面的应用。提供了一个示例,…...

ISYSTEM调试实践8-winIDEA Analyzer功能1
前面几篇介绍了ISYSTEM的基本调试界面和功能,相比我之前用过的IDE,除了几种断点方式和脚本功能以外,应该都是比较简单,稍微操作一下就可以直接上手,后续我将介绍winIDEA的Analyzer 功能。 1 Analyzer简介 iSYSTEM An…...

每日学术速递2.11
CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.IR、cs.MM 1.A Comprehensive Survey on Multimodal Recommender Systems: Taxonomy, Evaluation, and Future Directions 标题:关于多模态推荐系统的综合调查:分…...

宝塔搭建实战php开源likeadmin通用管理admin端vue3源码(二)
大家好啊,我是测评君,欢迎来到web测评。 上一期给大家分享了server端的部署方式,今天来给大家分享admin端在本地搭建,与打包发布到宝塔的方法。感兴趣的朋友可以自行下载学习。 技术架构 vscode node16 vue3 elementPlus vit…...

网络基础-虚拟化工具-网桥
系列文章目录 本系列文章主要是回顾和学习工作中常用的网络基础命令,在此记录以便于回顾。 该篇文章主要是讲解虚拟化的工具网桥相关的概念和常用命令 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录系…...

剑指 Offer 14- II. 剪绳子 II
剑指 Offer 14- II. 剪绳子 II 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少&a…...

English Learning - Day55 作业打卡 2023.2.9 周四
English Learning - Day55 作业打卡 2023.2.9 周四引言1. Jim 在看电视的时候他的老婆正在做饭。2. 他刚睡着电话就响了。3. 我正在想事情,这时忽然有人从后面抓我胳膊。4. 我们总是边吃火锅边唱歌。5. 他一听说出了事故,马上就来了现场。6. He entered …...

pixhawk2.4.8-地面站配置-APM固件
文章目录一、硬件准备二、软件准备1 已实飞测试2 MP地面站 任意版本下载:3 APM固件 任意版本下载:三、飞控校准1 刷固件2 机架选择3 加速度计校准4 指南针校准5 遥控器校准6 飞行模式7 紧急断电&无头模式8 基础参数设置9 电流计校准10 电调校准11 起…...

golang 通道类型
文章目录一、什么是通道类型二、通道产生的原因三、声明channel四、创建channel五、channel相关操作1、发送值2、接收值3、关闭通道3.1 注意3.2 特点四、通道类型1、无缓冲通道2、有缓冲通道五、单向通道一、什么是通道类型 Go 语言中的通道(channel)是一…...

并发、并行、吞吐量、延迟、响应时间 含义理解
并发、并行、吞吐量、延迟、响应时间 知识点了解 1. 响应时间(RT) 理解:响应时间是指系统对请求作出响应的时间。例如一个正在运行的服务,服务内程序接受到参数请求开始,到程序计算完,并将结果返回出去结束,这段时间…...

HTTP 和 HTTPS 的区别
文章目录前言一、HTTP 与 HTTPS 的基本概念HTTPHTTPS二、HTTP 和 HTTPS协议的区别前言 浏览网站时,我们会发现网址有两种格式,一种以http://开头,一种https://开头。好像这两种格式差别不大,只多了一个s,实际上他们有…...

微搭低代码从入门到精通07-基础布局组件
低码开发不同于传统开发,传统开发我们通常需要编写前端代码和后端代码。前端代码由HTML、CSS和JavaScript组成,后端代码我们通常要用后端语言比如Java来编写接口。 低码开发的特点是可视化开发,在编辑器中通过组件的拖拽来完成页面的编制。如…...

Docker镜像的创建
Docker镜像Docker镜像Docker 镜像是一个特殊的文件系统提供容器运行时所需的程序、库、资源、配置等文件包含一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)镜像不包含任何动态数据,其内容在构建之后也不会被改变。Docker镜像的…...

电子技术——MOS差分输入对
电子技术——MOS差分输入对 差分输入系统因其极高的共模抑制能力,差分输入几乎是是构建所有通用模拟IC的基本前级输入,也是现代信号传输理论的基础。本节我们讲解MOS差分输入对。 MOS差分输入对 下图展示了MOS差分输入对的基本原理图: 一个…...

树莓派 - 小记
文章目录关于树莓派Raspberry Pi OSGPIOScratch 编程Minecraft相关硬件关于树莓派 树莓派:Raspberry Pi,由美国树莓派基金会开发,是一款专门用于计算机教育的极简计算机。 第一代发布于 2012年。 特点:精致小巧,价格低…...