小程序canvas2d实现横版全屏和竖版逐字的签名组件(字帖式米字格签名组件)
文章标题
- 01 功能说明
- 02 效果预览
- 2.1 横版
- 2.2 竖版
- 03 使用方式
- 04 横向签名组件源码
- 4.1 html 代码
- 4.2 业务 Js
- 4.3 样式 Css
- 05 竖向签名组件源码
- 5.1 布局 Html
- 5.2 业务 Js
- 5.3 样式 Css
01 功能说明
技术栈:uniapp、vue、canvas 2d
需求:
- 实现横版的全名字帖式米字格签名组件,竖版逐字的字帖式米字格签名组件;
- 支持配置文字描述、画笔颜色、画笔大小等;
- 提供 submit 事件,当点击提交按钮时触发,回调参数是canvas转化为图片的地址;
02 效果预览
2.1 横版


2.2 竖版


03 使用方式
// 使用横向签名------------------------
<template><HorizontalSign signText="赵钱孙" @submit="handleSubmit" />
</template><script>
import HorizontalSign from '@/components/HorizontalSign.vue';export default {components: { HorizontalSign },methods: {handleSubmit(imagePath) {console.log('--image--', imagePath);},},
}
<script> // 使用竖向签名------------------------
<template><VerticalSign signText="赵钱孙" @submit="handleSubmit" />
</template><script>
import VerticalSign from '@/components/VerticalSign.vue';export default {components: { VerticalSign },methods: {handleSubmit(imagePath) {console.log('--image--', imagePath);},},
}
<script>
04 横向签名组件源码
4.1 html 代码
<template><view class="wrapping"><!-- header 定位后以左上角顺时针旋转 90° --><view class="header-title flex col-center"><!-- <text> 签名:</text> --><!-- 预览图片(图片本身是正向的,但由于父元素旋转了90°所以正好能横向观看) --><!-- <image :src="previewImage" mode="aspectFit" class="small-preview" /> --><text class="desc-text">{{ description }}</text></view><!-- 实际保持直立正向 canvas 容器 --><view class="canvas-wrapper"><!-- 只展示限制数量文字的米字格,超过配置数量文字则不展示 --><view class="char-group flex-col flex-center" v-if="signText && signText.length <= riceGridLimit"><view class="char-box" v-for="(item, index) in signText" :key="index">{{ item }}</view></view><canvasid="signatureCanvas"type="2d"class="signature-canvas"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd"@touchcancel="handleTouchEnd"disable-scroll></canvas></view><!-- footer 定位后以右下角顺时针旋转 90° --><view class="footer-btn flex"><view class="action-btn" @click="resetCanvas">重签</view><view class="action-btn submit-btn" @click="handleSubmit">{{ submitText }}</view></view><!--用于绘制并生成旋转为正向签名图片的 canvas 容器--><canvas id="previewCanvas" type="2d" class="preview-canvas"></canvas></view>
</template>
4.2 业务 Js
<script>
export default {props: {description: {type: String,default: '请使用正楷字体,逐字签写', // 文字描述},submitText: {type: String,default: '提交', // 提交按钮文字},dotSize: {type: Number,default: 4, // 签名笔大小},penColor: {type: String,default: '#000000', // 签名笔颜色},signText: {type: String,default: '', // 签名文字},riceGridLimit: {type: Number,default: 3, // 米字格展示字数最大限制},},data() {return {mainCtx: null,mainCanvas: null,isDrawing: false,touchPoints: [],signIsMove: false,previewImage: '',canvasRatio: 1,};},mounted() {this.canvasRatio = uni.getWindowInfo().pixelRatio ?? 1;this.initCanvas();},methods: {initCanvas() {const domItem = uni.createSelectorQuery().in(this).select('#signatureCanvas');domItem.fields({ node: true, size: true }).exec((res) => {// Canvas 对象this.mainCanvas = res[0]?.node;// 渲染上下文this.mainCtx = this.mainCanvas.getContext('2d');// Canvas 画布的实际绘制宽高const width = res[0].width;const height = res[0].height;// 初始化画布大小this.mainCanvas.width = width * this.canvasRatio;this.mainCanvas.height = height * this.canvasRatio;this.mainCtx.scale(this.canvasRatio, this.canvasRatio);this.setPen();});},setPen() {this.mainCtx.strokeStyle = this.penColor;this.mainCtx.lineWidth = this.dotSize;this.mainCtx.lineCap = 'round';this.mainCtx.lineJoin = 'round';},handleTouchStart(e) {const point = {x: e.changedTouches[0].x,y: e.changedTouches[0].y,};this.touchPoints.push(point);this.isDrawing = true;},handleTouchMove(e) {if (!this.isDrawing) return;const point = {x: e.touches[0].x,y: e.touches[0].y,};this.touchPoints.push(point);const len = this.touchPoints.length;if (len >= 2) {const prevPoint = this.touchPoints[len - 2];const currentPoint = this.touchPoints[len - 1];this.mainCtx.beginPath();this.mainCtx.moveTo(prevPoint.x, prevPoint.y);this.mainCtx.lineTo(currentPoint.x, currentPoint.y);this.mainCtx.stroke();this.signIsMove = true;}},handleTouchEnd() {this.isDrawing = false;this.touchPoints = [];},resetCanvas() {if (!this.signIsMove) {return;}this.mainCtx.clearRect(0, 0, 1000, 1000);this.setPen();this.touchPoints = [];this.previewImage = '';this.signIsMove = false;},async handleSubmit() {if (!this.signIsMove) {uni.showToast({ title: '请先完成签名', icon: 'none' });return;}try {const _this = this;uni.canvasToTempFilePath({canvas: this.mainCanvas,quality: 1,fileType: 'png',success: (res) => {let path = res.tempFilePath;_this.handlePreviewImage(path);},fail: (res) => {uni.showToast({ title: '提交失败,请重新尝试', icon: 'none' });},});} catch (err) {uni.showToast({ title: '签名失败,请重试', icon: 'none' });} finally {uni.hideLoading();}},handlePreviewImage(imagePath) {const _this = this;const previewDom = uni.createSelectorQuery().in(_this).select('#previewCanvas');previewDom.fields({ node: true, size: true }).exec((res) => {// Canvas 对象const canvas = res[0]?.node;// 渲染上下文const previewCtx = canvas.getContext('2d');const image = canvas.createImage();image.src = imagePath;image.onload = () => {let { width, height } = image;// 获取图片的宽高初始画布,canvas交换宽高canvas.width = height;canvas.height = width;// 设置白色背景previewCtx.fillStyle = '#FFFFFF';previewCtx.fillRect(0, 0, height, width);// 图片逆时针旋转90度,且换为弧度previewCtx.rotate((-90 * Math.PI) / 180);// 旋转后调整绘制的位置下移一个宽度的距离previewCtx.drawImage(image, -width, 0);};// 最终导出setTimeout(() => {uni.canvasToTempFilePath({canvas,fileType: 'png', // 指定文件类型quality: 1, // 最高质量success: (res) => {_this.previewImage = res.tempFilePath;uni.previewImage({ urls: [res.tempFilePath], current: 0 });_this.$emit('submit', res.tempFilePath);},fail: (err) => {uni.showToast({ title: '合成失败,请重试', icon: 'none' });},},_this);}, 300); // 增加最终导出前的延迟});},},
};
</script>
4.3 样式 Css
<style scoped>
.wrapping {position: relative;padding: 20rpx;margin: 20rpx;background-color: #fff;box-sizing: border-box;
}.header-title {position: absolute;right: 20rpx;top: 20rpx;height: 50rpx;z-index: 1000;transform-origin: top left;transform: translateX(100%) rotate(90deg);font-size: 32rpx;color: #333;
}.desc-text {color: #969799;
}.small-preview {width: 100rpx;height: 50rpx;border-bottom: 1px solid #333;
}.canvas-wrapper {position: relative;margin: auto;width: 60%;height: 80vh;background: #f7f8fa;
}.char-group {position: absolute;top: 0px;left: 0px;width: 100%;height: 100%;pointer-events: none;user-select: none;z-index: 1;gap: 20rpx;
}.char-box {padding: 36rpx;width: 30vw;height: 30vw;transform: rotate(90deg);font-size: 30vw;line-height: 30vw;text-align: center;color: #eeeeee;/* 使用虚线边框框住字体 *//* border: 1px dashed #ccc; *//* 使用米字格照片当背景图 */background: url('https://img1.baidu.com/it/u=2622499137,3527900847&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500') no-repeat;background-size: 100%;text-shadow: 1px 1px black, -1px -1px black, 1px -1px black, -1px 1px black;
}.signature-canvas {position: relative;width: 100%;height: 100%;z-index: 2;
}.footer-btn {position: absolute;left: 20rpx;bottom: 20rpx;transform-origin: bottom right;transform: translateX(-100%) rotate(90deg);z-index: 1000;gap: 32rpx;
}.action-btn {text-align: center;width: 200rpx;height: 96rpx;border-radius: 100rpx;font-size: 32rpx;line-height: 96rpx;color: #3874f6;border: 2rpx solid #3874f6;background: #fff;
}.submit-btn {color: #fff;border: 2rpx solid #3874f6;background: #3874f6;
}.preview-canvas {visibility: hidden;position: fixed;/* 将画布移出展示区域 */top: 100vh;left: 100vw;opacity: 0;z-index: 0;
}
</style>
05 竖向签名组件源码
5.1 布局 Html
<template><view class="signature-container"><view class="desc-text">{{ description }}</view><view class="signature-area"><view class="canvas-wrapper"><!-- 逐字展示文字 --><view class="char-box" v-if="signText && currentCharIndex < signText.length">{{ signText[currentCharIndex] }}</view><canvasid="signatureCanvas"class="signature-canvas"type="2d"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd"@touchcancel="handleTouchEnd"disable-scroll></canvas></view><view class="action-box"><view class="action-btn" v-if="currentCharIndex > 0" @click="prevChar">上一字</view><view class="action-btn" @click="resetCanvas">清空画板</view><view class="action-btn" v-if="currentCharIndex < signText.length" @click="nextChar">{{ currentCharIndex < signText.length - 1 ? '下一字' : '确认' }}</view></view></view><view class="preview-title">逐字预览</view><view class="preview-content"><image v-for="(img, index) in previewImages" :key="index" :src="img" mode="aspectFit" class="preview-char" /></view><view class="action-box"><view class="action-btn submit-btn" @click="resetAllRecord">全部重签</view><view class="action-btn submit-btn" @click="handleSubmit">{{ submitText }}</view></view><!--用于拼接合并为完整签名图片的 canvas 容器--><canvas id="previewCanvas" type="2d" class="preview-canvas"></canvas></view>
</template>
5.2 业务 Js
<script>
export default {props: {description: {type: String,default: '请使用正楷字体,逐字签写', // 文字描述},submitText: {type: String,default: '提交', // 提交按钮文字},dotSize: {type: Number,default: 4, // 签名笔大小},penColor: {type: String,default: '#000000', // 签名笔颜色},signText: {type: String,default: '', // 签名文字},},data() {return {mainCtx: null,mainCanvas: null,isDrawing: false,touchPoints: [],allTouchPoints: [],signIsMove: false,currentCharIndex: 0,canvasRatio: 1,previewImages: [],};},mounted() {this.canvasRatio = uni.getWindowInfo().pixelRatio ?? 1;this.initCanvas();},methods: {initCanvas() {const domItem = uni.createSelectorQuery().in(this).select('#signatureCanvas');domItem.fields({ node: true, size: true }).exec((res) => {// Canvas 对象this.mainCanvas = res[0]?.node;// 渲染上下文this.mainCtx = this.mainCanvas.getContext('2d');// Canvas 画布的实际绘制宽高const width = res[0].width;const height = res[0].height;// 初始化画布大小this.mainCanvas.width = width * this.canvasRatio;this.mainCanvas.height = height * this.canvasRatio;this.mainCtx.scale(this.canvasRatio, this.canvasRatio);this.setPen();});},setPen() {this.mainCtx.strokeStyle = this.penColor;this.mainCtx.lineWidth = this.dotSize;this.mainCtx.lineCap = 'round';this.mainCtx.lineJoin = 'round';},handleTouchStart(e) {const point = {x: e.changedTouches[0].x,y: e.changedTouches[0].y,};this.touchPoints.push(point);this.allTouchPoints.push(point);this.isDrawing = true;},handleTouchMove(e) {if (!this.isDrawing) return;const point = {x: e.touches[0].x,y: e.touches[0].y,};this.touchPoints.push(point);this.allTouchPoints.push(point);const len = this.touchPoints.length;if (len >= 2) {const prevPoint = this.touchPoints[len - 2];const currentPoint = this.touchPoints[len - 1];this.mainCtx.beginPath();this.mainCtx.moveTo(prevPoint.x, prevPoint.y);this.mainCtx.lineTo(currentPoint.x, currentPoint.y);this.mainCtx.stroke();this.signIsMove = true;}},handleTouchEnd() {this.isDrawing = false;this.touchPoints = [];},getRectangle(points) {// 计算每个字符的实际大小let minX = Number.POSITIVE_INFINITY;let minY = Number.POSITIVE_INFINITY;let maxX = Number.NEGATIVE_INFINITY;let maxY = Number.NEGATIVE_INFINITY;for (let point of points) {minX = Math.min(minX, point.x);minY = Math.min(minY, point.y);maxX = Math.max(maxX, point.x);maxY = Math.max(maxY, point.y);}return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };},prevChar() {if (this.previewImages.length > 0) {this.previewImages.pop();this.currentCharIndex--;this.resetCanvas();}},nextChar() {if (!this.signIsMove) {uni.showToast({ title: '请先完成签名', icon: 'none' });return;}try {const { x, y, width, height } = this.getRectangle(this.allTouchPoints);const offset = 10;const _this = this;uni.canvasToTempFilePath({canvas: this.mainCanvas,x: x - offset,y: y - offset,width: width + offset * 2,height: height + offset * 2,success: (res) => {_this.previewImages.push(res.tempFilePath);_this.currentCharIndex++;_this.resetCanvas();},fail: () => {uni.showToast({ title: '提交失败,请重新尝试', icon: 'none' });},},_this);} catch (err) {uni.showToast({ title: '保存失败,请重试', icon: 'none' });}},resetCanvas() {this.mainCtx.clearRect(0, 0, 1000, 1000);this.setPen();this.touchPoints = [];this.allTouchPoints = [];this.signIsMove = false;},resetAllRecord() {this.previewImages = [];this.currentCharIndex = 0;this.resetCanvas();},async handleSubmit() {if (this.previewImages.length <= 0) {uni.showToast({ title: '请至少签写一个字', icon: 'none' });return;}try {this.handlePreviewImage();} catch (err) {uni.showToast({ title: '合成失败,请重试', icon: 'none' });}},handlePreviewImage() {const _this = this;const previewDom = uni.createSelectorQuery().in(_this).select('#previewCanvas');previewDom.fields({ node: true, size: true }).exec((res) => {// Canvas 对象const canvas = res[0]?.node;// 渲染上下文const previewCtx = canvas.getContext('2d');// 计算总宽度和单个字的尺寸const charWidth = 300 / this.previewImages.length;const charHeight = 300 / this.previewImages.length;const totalWidth = charWidth * this.previewImages.length;// 设置白色背景previewCtx.fillStyle = '#FFFFFF';previewCtx.fillRect(0, 0, totalWidth, charHeight);// 按顺序绘制每个图片for (let i = 0; i < this.previewImages.length; i++) {const image = canvas.createImage();image.src = this.previewImages[i];image.onload = () => {const x = i * charWidth;// 绘制当前图片previewCtx.drawImage(image, x, 0, charWidth, charHeight);};}// 最终导出setTimeout(() => {uni.canvasToTempFilePath({canvas,x: 0,y: 0,width: totalWidth,height: charHeight,fileType: 'png', // 指定文件类型quality: 1, // 最高质量success: (res) => {uni.previewImage({ urls: [res.tempFilePath], current: 0 });_this.$emit('submit', res.tempFilePath);},fail: (err) => {uni.showToast({ title: '合成失败,请重试', icon: 'none' });},},_this);}, 300); // 增加最终导出前的延迟});},},
};
</script>
5.3 样式 Css
<style scoped>
.signature-container {padding: 0 20rpx 40rpx 20rpx;background-color: #f5f5f5;box-sizing: border-box;
}.signature-area {padding: 50rpx;background-color: #fff;box-sizing: border-box;
}.desc-text {padding: 20rpx 0;font-size: 32rpx;color: #333;text-align: center;box-sizing: border-box;
}.canvas-wrapper {position: relative;width: 100%;/* 保持宽高比 */aspect-ratio: 1;/* height: 600rpx; */background: #fff;/* 使用虚线边框框住字体 *//* border: 1px dashed #ccc; *//* 使用米字格照片当背景图 */background: url('https://img1.baidu.com/it/u=2622499137,3527900847&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500') no-repeat;background-size: 100%;
}.char-box {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);font-size: 400rpx;text-shadow: 1px 1px black, -1px -1px black, 1px -1px black, -1px 1px black;color: #eeeeee;pointer-events: none;user-select: none;z-index: 1;
}.signature-canvas {position: relative;width: 100%;height: 100%;z-index: 2;
}.action-box {display: flex;margin-top: 32rpx;gap: 20rpx;
}.action-btn {flex: 1;text-align: center;padding: 16rpx 30rpx;font-size: 28rpx;color: #3874f6;border: 2rpx solid #3874f6;border-radius: 80rpx;box-sizing: border-box;
}.submit-btn {background: #3874f6;color: #fff;
}.preview-title {margin-top: 32rpx;width: 100%;text-align: center;font-size: 28rpx;color: #666;
}.preview-content {display: flex;flex-wrap: wrap;margin-top: 20rpx;background-color: #fff;padding: 20rpx 20rpx 0 20rpx;min-height: 190rpx;box-sizing: border-box;
}.preview-char {width: 150rpx;height: 150rpx;margin-right: 19rpx;margin-bottom: 20rpx;
}.preview-canvas {position: fixed;left: -2000px;width: 300px;height: 300px;
}
</style>
相关文章:
小程序canvas2d实现横版全屏和竖版逐字的签名组件(字帖式米字格签名组件)
文章标题 01 功能说明02 效果预览2.1 横版2.2 竖版 03 使用方式04 横向签名组件源码4.1 html 代码4.2 业务 Js4.3 样式 Css 05 竖向签名组件源码5.1 布局 Html5.2 业务 Js5.3 样式 Css 01 功能说明 技术栈:uniapp、vue、canvas 2d 需求: 实现横版的全…...
MoE演变过程
MoE演变过程 1 MoE1.1 BasicMoE1.2 SparseMoE1.2.1 实现 1.3 Shared Expert SparseMoE 1 MoE 参考:https://huggingface.co/blog/zh/moe 1.1 BasicMoE 用router给出各专家的权重,然后让输入过每一个专家,然后做加权求和。 1.2 SparseMoE …...
【Leetcode 热题 100】1287. 有序数组中出现次数超过25%的元素
问题背景 给你一个非递减的 有序 整数数组,已知这个数组中恰好有一个整数,它的出现次数超过数组元素总数的 25 % 25\% 25%。 请你找到并返回这个整数。 数据约束 1 ≤ a r r . l e n g t h ≤ 1 0 4 1 \le arr.length \le 10 ^ 4 1≤arr.length≤104 0…...
ruby 的安装
在51cto搜索的资料 ruby on rails的安装 http://developer.51cto.com/art/200906/129669.htm http://developer.51cto.com/art/200912/169391.htm http://developer.51cto.com/art/200908/147276.htm 史上最完整的ruby,rails环境架设配置(Apachefast…...
【java】List<String> fruits = new ArrayList<>(); 这一句是什么
1. 代码分解 java Copy List<String> fruits new ArrayList<>(); List<String>: List 是 Java 中的一个接口,表示一个有序的集合(可以重复元素)。 <String> 是泛型,表示这个列表中的元素…...
通俗诠释 DeepSeek-V3 模型的 “671B” ,“37B”与 “128K”,用生活比喻帮你理解模型的秘密!
欢迎来到涛涛聊AI。 在DeepSeek-V3模型的参数描述中,你可能会看到类似“671B 37B 128K”这样的标记。这些字母和数字的组合看起来像密码,但其实它们揭示了模型的“大脑容量”和“工作方式”。我们用日常生活的比喻来解释: 一、数字含义&…...
【鸿蒙Next】优秀鸿蒙博客集锦
鸿蒙基础开发:多文件压缩上传及断点续传_鸿蒙 断点续传-CSDN博客...
【实战项目】BP神经网络识别人脸朝向----MATLAB实现
(꒪ꇴ꒪ ),Hello我是祐言QAQ我的博客主页:C/C语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍快上🚘,一起学习,让我们成为一个强大的攻城狮࿰…...
C++线程安全函数
在 C 中,线程安全的函数是指在多线程环境下可以安全调用,不会导致数据竞争或其他并发问题的函数。C 标准库提供了许多线程安全的函数,同时也要求开发者在使用自定义函数时确保线程安全。以下是一些常见的线程安全函数和实现线程安全的方法&am…...
Java中的分布式(概念说明)
1. 分布式的基本概念 1.1 什么是分布式系统? 分布式系统(Distributed System):由多台服务器(或节点)协同工作,对外提供一个整体服务。不同节点之间通过网络通信来协同处理请求或共享数据&…...
【1.8w字深入解析】从依赖地狱到依赖天堂:pnpm 如何革新前端包管理?
目录 前言npm 的诞生与发展嵌套依赖模型存在的问题npm3架构与yarnYarn 的诞生与局限Yarn 的诞生背景Yarn 仍然存在的问题 何为幽灵依赖依赖结构的不确定性pnpm王牌登场 -- 网状平铺结构安装包速度快依赖管理软链接 和 硬链接 机制 幽灵依赖产生的根本原因包管理工具的依赖解析机…...
【ISO 14229-1:2023 UDS诊断(ECU复位0x11服务)测试用例CAPL代码全解析⑤】
ISO 14229-1:2023 UDS诊断【ECU复位0x11服务】_TestCase05 作者:车端域控测试工程师 更新日期:2025年02月17日 关键词:UDS诊断协议、ECU复位服务、0x11服务、ISO 14229-1:2023 TC11-005测试用例 用例ID测试场景验证要点参考条款预期结果TC…...
137,【4】 buuctf web [SCTF2019]Flag Shop
进入靶场 都点击看看 发现点击work会增加¥ 但肯定不能一直点下去 抓包看看 这看起来是一个 JWT(JSON Web Token)字符串。JWT 通常由三部分组成,通过点(.)分隔,分别是头部(Header&…...
Node.js 异步并发控制:`p-map` 和 `p-limit` 的使用与对比
在 Node.js 中,处理异步任务是开发中非常常见的需求。无论是批量处理数据、调用外部 API,还是操作文件系统,我们经常需要对多个异步任务进行管理。然而,当任务数量较多时,如果不加以控制,并发可能会导致性能…...
【c++】c++内存管理
目录 c和c的内存分布回顾C语言动态管理内存的方式malloccallocreallocfree C动态管理内存的方式new和deleteoperator new和operator delete定位new c和c的内存分布 回顾C语言动态管理内存的方式 malloc void* malloc (size_t size);malloc可以在堆上开辟指定内存的空间&#…...
EtherNet/IP转Modbus TCP:新能源风电监控与分析实用案例
EtherNet/IP转Modbus TCP:新能源风电监控与分析实用案例 一、案例背景 在某新能源汽车电池生产线上,需要将采用EtherNet/IP协议的电池检测设备与采用ProfiNet协议的生产线控制系统进行集成,以实现对电池生产过程的全面监控和数据采集。 二、…...
伪装目标检测(Camouflaged Object Detection, COD)教程
1. 引言 伪装目标检测(Camouflaged Object Detection, COD)是一项计算机视觉任务,旨在识别和分割背景中难以察觉的目标,如动物伪装、隐形物体检测等。由于伪装目标通常与背景高度相似,这项任务比传统的目标检测更具挑…...
烧烤炉出口亚马逊欧盟站CE认证EN1860安全标准
什么是欧盟CE认证: 在欧盟市场“CE”标志属强制性认证标志,不论是欧盟内部企业生产的产品,还是其他国家生产的产品,要想在欧盟市场上自由流通,就必须加贴“CE”标志,以表明产品符合欧盟《技术协调与标准化新…...
动态DNS神器nip.io使用指南:快速实现域名与IP的动态映射--告别配置本地hosts
动态DNS神器nip.io使用指南:快速实现域名与IP的动态映射--告别配置本地hosts 一、项目简介二、快速入门三、进阶配置四、典型应用场景 本文基于开源项目 v1.2.1版本撰写,适用于开发测试、CI/CD等场景 一、项目简介 nip.io 是由Exentrique Solutions开发…...
人工智能 - 机器学习、深度学习、强化学习是人工智能领域的理论基础和方法论
机器学习、深度学习、强化学习是人工智能领域的三大核心方向,各自具有独特的理论基础和方法论。以下是它们的核心理论知识总结: 一、机器学习(Machine Learning, ML) 1. 基础概念 目标:通过数据驱动的方式,让机器从经验中学习规律,完成预测、分类或决策任务。 核心范式…...
数字电路-基础逻辑门实验
基础逻辑门是数字电路设计的核心元件,它们执行的是基本的逻辑运算。通过这些基本运算,可以构建出更为复杂的逻辑功能。常见的基础逻辑门包括与门(AND)、或门(OR)、非门(NOT)、异或门…...
国产编辑器EverEdit - 如虎添翼的功能:快速选择
1 快速选择 1.1 应用场景 快速选择适用于批量选择和修改的场景,比如:变量改名。 1.2 使用方法 1.2.1 逐项快速选择 将光标放置在单词前或单词中,选择主菜单查找 -> 快速选择 -> 快速选择或使用快捷键Ctrl D 注:光标放…...
国内外网络安全政策动态(2025年1月)
▶︎ 1.国家互联网信息办公室发布《个人信息出境个人信息保护认证办法(征求意见稿)》 1月3日,国家互联网信息办公室发布《个人信息出境个人信息保护认证办法(征求意见稿)》。根据《意见稿》,个人信息出境个…...
68页PDF | 数据安全总体解决方案:从数据管理方法论到落地实践的全方位指南(附下载)
一、前言 这份报告旨在应对数字化转型过程中数据安全面临的挑战,并提供全面的管理与技术体系建设框架。报告首先分析了数字化社会的发展背景,强调了数据安全在国家安全层面的重要性,并指出数据安全风险的来源和防护措施。接着,报…...
AI大模型的文本流如何持续吐到前端,实时通信的技术 SSE(Server-Sent Events) 认知
写在前面 没接触过 SSE(Server-Sent Events),AI大模型出来之后,一直以为文本流是用 WebSocket 做的偶然看到返回到报文格式是 text/event-stream,所以简单认知,整理笔记博文内容涉及 SSE 认知,以及对应的 D…...
Electron:使用electron-react-boilerplate创建一个react + electron的项目
使用 electron-react-boilerplate git clone --depth 1 --branch main https://github.com/electron-react-boilerplate/electron-react-boilerplate.git your-project-name cd your-project-name npm install npm start 安装不成功 在根目录加上 .npmrc文件 内容为 electron_…...
Spring Boot三:Springboot自动装配原理
精心整理了最新的面试资料,有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 原理初探 pom.xml 核心依赖在父工程中 spring-boot-dependencies所有的jar包都在这里管理 我们在写或者引入一些依赖的时候,不需要指定版本 启动器 <…...
【ISO 14229-1:2023 UDS诊断全量测试用例清单系列:第十八节】
ISO 14229-1:2023 UDS诊断服务测试用例全解析(ResponseOnEvent_0x86服务) 作者:车端域控测试工程师 更新日期:2025年02月14日 关键词:UDS协议、0x86服务、事件响应、ISO 14229-1:2023、ECU测试 一、服务功能概述 0x86…...
Qt 中使用 SQLite 数据库的完整指南
SQLite 是一款轻量级、嵌入式的关系型数据库,无需独立的服务器进程,数据以文件形式存储,非常适合桌面和移动端应用的本地数据管理。Qt 通过 Qt SQL 模块提供了对 SQLite 的原生支持,开发者可以轻松实现数据库的增删改查、事务处理…...
2024 年 CSDN 博客之星年度评选:技术创作与影响力的碰撞(统计时间2025-02-17 11:06:06)
摘要:在技术的海洋里,每一位博主都像是一座独特的灯塔,用自己创作的光芒照亮他人前行的道路。2024 年 CSDN 博客之星年度评选活动,正是对这些灯塔的一次盛大检阅,让我们看到了众多优秀博主在技术创作领域的卓越表现以及…...
