uni-app canvas 签名

news2025/1/13 13:50:05

在这里插入图片描述
调用方法

import Signature from "@/components/signature.vue"
const base64Img = ref()
//监听getSignImg
uni.$on('getSignImg', ({ base64, path }) => {
		base64Img.value = base64
	    //console.log('签名base64, path ====>', base64, path) //拿到的图片数据
	    // 之后取消监听,防止重复监听
	    uni.$off('getSignImg')
	})
<Signature :showMark="false"  @cancle="cancle"></Signature>

signature.vue

<template>
	<view class="sign-page" v-cloak>
		<view class="dis-flex justify-end">
			<uv-icon name="close" color="#333" size="48rpx" @click="cancle"></uv-icon>
		</view>
		<view class="sign-body">
			<canvas id="signCanvas" canvas-id="signCanvas" class="sign-canvas" disable-scroll
				@touchstart.stop="signCanvasStart" @touchmove.stop="signCanvasMove"
				@touchend.stop="signCanvasEnd"></canvas>
			<!-- #ifndef APP -->
			<!--用于临时储存横屏图片的canvas容器,H5和小程序需要-->
			<canvas v-if="horizontal" id="hsignCanvas" canvas-id="hsignCanvas"
				style="position: absolute; top: -1000px; z-index: -1"
				:style="{ width: canvasHeight + 'px', height: canvasWidth + 'px' }"></canvas>
			<!-- #endif -->
		</view>
		<view class="sign-footer" :class="[horizontal ? 'horizontal-btns' : 'vertical-btns']">
			<uv-button
				customStyle="margin-top: 20rpx;width:300rpx;height:100rpx;border-radius:20rpx;border:1px solid #3894FF"
				@click="reset">
				<uv-icon name="shuaxin" color="#3894FF" size="48rpx" custom-prefix="custom-icon"></uv-icon>
				<text class="txt">重新签字</text>
			</uv-button>

			<uv-button type="primary" text="确定提交" customTextStyle="font-size:36rpx"
				customStyle="margin-top: 20rpx;width:300rpx;height:100rpx;border-radius:20rpx"
				@click="confirm"></uv-button>
		</view>
	</view>
</template>

<script>
	import {
		pathToBase64,
		base64ToPath
	} from '@/utils/signature.js'
	export default {
		name: 'sign',
		props: {
			// 背景水印图,优先级大于 bgColor
			bgImg: {
				type: String,
				default: ''
			},
			// 背景纯色底色,为空则透明
			bgColor: {
				type: String,
				default: ''
			},
			// 是否显示水印
			showMark: {
				type: Boolean,
				default: true
			},
			// 水印内容,可多行
			markText: {
				type: Array,
				default: () => {
					return [] // ['水印1', '水印2']
				}
			},
			// 水印样式
			markStyle: {
				type: Object,
				default: () => {
					return {
						fontSize: 12, // 水印字体大小
						fontFamily: 'microsoft yahei', // 水印字体
						color: '#cccccc', // 水印字体颜色
						rotate: 60, // 水印旋转角度
						step: 2.2 // 步长,部分场景下可通过调节该参数来调整水印间距,建议为1.4-2.6左右
					}
				}
			},
			// 是否横屏
			horizontal: {
				type: Boolean,
				default: false
			},
			// 画笔样式
			penStyle: {
				type: Object,
				default: () => {
					return {
						lineWidth: 3, // 画笔线宽 建议1~5
						color: '#000000' // 画笔颜色
					}
				}
			},
			// 导出图片配置
			expFile: {
				type: Object,
				default: () => {
					return {
						fileType: 'png', // png/jpg (png不可压缩质量,支持透明;jpg可压缩质量,不支持透明)
						quality: 1 // 范围 0 - 1 (仅jpg支持)
					}
				}
			}
		},
		data() {
			return {
				canvasCtx: null, // canvascanvasWidth: 0, // canvas宽度
				canvasWidth: 0, // canvas宽度
				canvasHeight: 0, // canvas高度
				x0: 0, // 初始横坐标或上一段touchmove事件中触摸点的横坐标
				y0: 0, // 初始纵坐标或上一段touchmove事件中触摸点的纵坐标
				signFlag: false // 签名旗帜
			}
		},
		mounted() {
			this.$nextTick(() => {
				this.createCanvas()
			})
		},
		methods: {
			// 创建canvas实例
			createCanvas() {
				this.canvasCtx = uni.createCanvasContext('signCanvas', this)
				this.canvasCtx.setLineCap('round') // 向线条的每个末端添加圆形线帽

				// 获取canvas宽高
				const query = uni.createSelectorQuery().in(this)
				query
					.select('.sign-body')
					.boundingClientRect((data) => {
						this.canvasWidth = data.width
						this.canvasHeight = data.height
					})
					.exec(async () => {
						await this.drawBg()
						this.drawMark(this.markText)
					})
			},
			async drawBg() {
				if (this.bgImg) {
					const img = await uni.getImageInfo({
						src: this.bgImg
					})
					this.canvasCtx.drawImage(img.path, 0, 0, this.canvasWidth, this.canvasHeight)
				} else if (this.bgColor) {
					// 绘制底色填充,否则为透明
					this.canvasCtx.setFillStyle(this.bgColor)
					this.canvasCtx.fillRect(0, 0, this.canvasWidth, this.canvasHeight)
				}
			},
			// 绘制动态水印
			drawMark(textArray) {
				if (!this.showMark) {
					this.canvasCtx.draw()
					return
				}
				// 绘制背景
				this.drawBg()

				// 水印参数
				const markStyle = Object.assign({
						fontSize: 12, // 水印字体大小
						fontFamily: 'microsoft yahei', // 水印字体
						color: '#cccccc', // 水印字体颜色
						rotate: 60, // 水印旋转角度
						step: 2 // 步长,部分场景下可通过调节该参数来调整水印间距,建议为1.4-2.6左右
					},
					this.markStyle
				)
				this.canvasCtx.font = `${markStyle.fontSize}px ${markStyle.fontFamily}`
				this.canvasCtx.fillStyle = markStyle.color
				// 文字坐标
				const maxPx = Math.max(this.canvasWidth / 2, this.canvasHeight / 2)
				const stepPx = Math.floor(maxPx / markStyle.step)
				let arrayX = [0] // 初始水印位置 canvas坐标 0 0 点
				while (arrayX[arrayX.length - 1] < maxPx / 2) {
					arrayX.push(arrayX[arrayX.length - 1] + stepPx)
				}
				arrayX.push(
					...arrayX.slice(1, arrayX.length).map((item) => {
						return -item
					})
				)

				for (let i = 0; i < arrayX.length; i++) {
					for (let j = 0; j < arrayX.length; j++) {
						this.canvasCtx.save()
						this.canvasCtx.translate(this.canvasWidth / 2, this.canvasHeight / 2) // 画布旋转原点 移到 图片中心
						this.canvasCtx.rotate(Math.PI * (markStyle.rotate / 180))
						textArray.forEach((item, index) => {
							let offsetY = markStyle.fontSize * index
							this.canvasCtx.fillText(item, arrayX[i], arrayX[j] + offsetY)
						})
						this.canvasCtx.restore()
					}
				}

				this.canvasCtx.draw()
			},
			cancle() {
				//取消按钮事件
				this.$emit('cancle')
				this.reset()
				//uni.navigateBack()
			},
			async reset() {
				this.$emit('reset')
				this.signFlag = false
				this.canvasCtx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
				await this.drawBg()
				this.drawMark(this.markText)
			},
			async confirm() {
				this.$emit('confirm')
				// 确认按钮事件
				if (!this.signFlag) {
					uni.showToast({
						title: '请签名后再点击确定',
						icon: 'none',
						duration: 2000
					})
					return
				}

				uni.showModal({
					title: '确认',
					content: '确认签名无误吗',
					showCancel: true,
					success: async ({
						confirm
					}) => {
						if (confirm) {
							let tempFile
							if (this.horizontal) {
								tempFile = await this.saveHorizontalCanvas()
							} else {
								tempFile = await this.saveCanvas()
							}
							const base64 = await pathToBase64(tempFile)
							const path = await base64ToPath(base64)
							uni.$emit('getSignImg', {
								base64,
								path
							})
							//uni.navigateBack()
						}
					}
				})
			},
			signCanvasEnd(e) {
				// 签名抬起事件
				// console.log(e, 'signCanvasEnd')
				this.x0 = 0
				this.y0 = 0
			},
			signCanvasMove(e) {
				// 签名滑动事件
				// console.log(e, 'signCanvasMove')
				// #ifdef MP-WEIXIN
				let dx = e.touches[0].clientX - this.x0
				let dy = e.touches[0].clientY - this.y0
				// #endif
				// #ifndef MP-WEIXIN
				let dx = e.touches[0].x - this.x0
				let dy = e.touches[0].y - this.y0
				// #endif
				this.canvasCtx.moveTo(this.x0, this.y0)
				this.canvasCtx.lineTo(this.x0 + dx, this.y0 + dy)
				this.canvasCtx.setLineWidth(this.penStyle?.lineWidth || 4)
				this.canvasCtx.strokeStyle = this.penStyle?.color || '#000000'
				this.canvasCtx.stroke()
				this.canvasCtx.draw(true)
				// #ifdef MP-WEIXIN
				this.x0 = e.touches[0].clientX
				this.y0 = e.touches[0].clientY
				// #endif
				// #ifndef MP-WEIXIN
				this.x0 = e.touches[0].x
				this.y0 = e.touches[0].y
				// #endif
			},
			signCanvasStart(e) {
				// 签名按下事件 app获取的e不一样区分小程序app
				// console.log('signCanvasStart', e)
				if (!this.signFlag) {
					// 导出第一次开始触碰事件
					this.$emit('firstTouchStart')
				}
				this.signFlag = true
				// #ifdef MP-WEIXIN
				this.x0 = e.touches[0].clientX
				this.y0 = e.touches[0].clientY
				// #endif
				// #ifndef MP-WEIXIN
				this.x0 = e.touches[0].x
				this.y0 = e.touches[0].y
				// #endif
			},
			// 保存竖屏图片
			async saveCanvas() {
				return await new Promise((resolve, reject) => {
					uni.canvasToTempFilePath({
							canvasId: 'signCanvas',
							fileType: this.expFile.fileType, // 只支持png和jpg
							quality: this.expFile.quality, // 范围 0 - 1
							success: (res) => {
								if (!res.tempFilePath) {
									uni.showModal({
										title: '提示',
										content: '保存签名失败',
										showCancel: false
									})
									return
								}
								resolve(res.tempFilePath)
							},
							fail: (r) => {
								console.log('图片生成失败:' + r)
								resolve(false)
							}
						},
						this
					)
				})
			},
			// 保存横屏图片
			async saveHorizontalCanvas() {
				return await new Promise((resolve, reject) => {
					uni.canvasToTempFilePath({
							canvasId: 'signCanvas',
							fileType: this.expFile.fileType, // 只支持png和jpg
							success: (res) => {
								if (!res.tempFilePath) {
									uni.showModal({
										title: '提示',
										content: '保存签名失败',
										showCancel: false
									})
									return
								}

								// #ifdef APP
								uni.compressImage({
									src: res.tempFilePath,
									quality: this.expFile.quality * 100, // 范围 0 - 100
									rotate: 270,
									success: (r) => {
										console.log('==== compressImage :', r)
										resolve(r.tempFilePath)
									}
								})
								// #endif

								// #ifndef APP
								uni.getImageInfo({
									src: res.tempFilePath,
									success: (r) => {
										// console.log('==== getImageInfo :', r)
										// 将signCanvas的内容复制到hsignCanvas中
										const hcanvasCtx = uni.createCanvasContext(
											'hsignCanvas', this)
										// 横屏宽高互换
										hcanvasCtx.translate(this.canvasHeight / 2, this
											.canvasWidth / 2)
										hcanvasCtx.rotate(Math.PI * (-90 / 180))
										hcanvasCtx.drawImage(
											r.path,
											-this.canvasWidth / 2,
											-this.canvasHeight / 2,
											this.canvasWidth,
											this.canvasHeight
										)
										hcanvasCtx.draw(false, async () => {
											const hpathRes = await uni
												.canvasToTempFilePath({
														canvasId: 'hsignCanvas',
														fileType: this.expFile
															.fileType, // 只支持png和jpg
														quality: this.expFile
															.quality // 范围 0 - 1
													},
													this
												)
											let tempFile = ''
											if (Array.isArray(hpathRes)) {
												hpathRes.some((item) => {
													if (item) {
														tempFile = item
															.tempFilePath
														return
													}
												})
											} else {
												tempFile = hpathRes
													.tempFilePath
											}
											resolve(tempFile)
										})
									}
								})
								// #endif
							},
							fail: (err) => {
								console.log('图片生成失败:' + err)
								resolve(false)
							}
						},
						this
					)
				})
			}
		}
	}
</script>

<style scoped lang="scss">
	[v-cloak] {
		display: none !important;
	}

	.sign-page {
		height: 600rpx;
		width: 710rpx;
		padding: 20rpx;
		display: flex;
		flex-direction: column;

		.sign-body {
			margin-top: 50rpx;
			width: 100%;
			flex-grow: 1;
			background: #E5E5E5;

			.sign-canvas {
				width: 100%;
				height: 100%;
			}
		}

		.sign-footer {
			width: 100%;
			height: 80rpx;
			margin: 15rpx 0;
			display: flex;
			justify-content: space-evenly;
			align-items: center;

			.txt{
				color:#3894FF;
				padding-left:10rpx;
				font-size: 36rpx;
			}
		}

		.vertical-btns {
			.btn {
				width: 120rpx;
				height: 66rpx;
			}
		}

		.horizontal-btns {
			.btn {
				width: 66rpx;
				height: 120rpx;
				writing-mode: vertical-lr;
				transform: rotate(90deg);
			}
		}
	}

	:deep(.uvicon-close) {
		font-size: 48rpx
	}
</style>

signature.js

function getLocalFilePath(path) {
    if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path.indexOf('_downloads') === 0) {
        return path
    }
    if (path.indexOf('file://') === 0) {
        return path
    }
    if (path.indexOf('/storage/emulated/0/') === 0) {
        return path
    }
    if (path.indexOf('/') === 0) {
        var localFilePath = plus.io.convertAbsoluteFileSystem(path)
        if (localFilePath !== path) {
            return localFilePath
        } else {
            path = path.substr(1)
        }
    }
    return '_www/' + path
}

function dataUrlToBase64(str) {
    var array = str.split(',')
    return array[array.length - 1]
}

var index = 0
function getNewFileId() {
    return Date.now() + String(index++)
}

function biggerThan(v1, v2) {
    var v1Array = v1.split('.')
    var v2Array = v2.split('.')
    var update = false
    for (var index = 0; index < v2Array.length; index++) {
        var diff = v1Array[index] - v2Array[index]
        if (diff !== 0) {
            update = diff > 0
            break
        }
    }
    return update
}

export function pathToBase64(path) {
    return new Promise(function(resolve, reject) {
        if (typeof window === 'object' && 'document' in window) {
            if (typeof FileReader === 'function') {
                var xhr = new XMLHttpRequest()
                xhr.open('GET', path, true)
                xhr.responseType = 'blob'
                xhr.onload = function() {
                    if (this.status === 200) {
                        let fileReader = new FileReader()
                        fileReader.onload = function(e) {
                            resolve(e.target.result)
                        }
                        fileReader.onerror = reject
                        fileReader.readAsDataURL(this.response)
                    }
                }
                xhr.onerror = reject
                xhr.send()
                return
            }
            var canvas = document.createElement('canvas')
            var c2x = canvas.getContext('2d')
            var img = new Image
            img.onload = function() {
                canvas.width = img.width
                canvas.height = img.height
                c2x.drawImage(img, 0, 0)
                resolve(canvas.toDataURL())
                canvas.height = canvas.width = 0
            }
            img.onerror = reject
            img.src = path
            return
        }
        if (typeof plus === 'object') {
            plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), function(entry) {
                entry.file(function(file) {
                    var fileReader = new plus.io.FileReader()
                    fileReader.onload = function(data) {
                        resolve(data.target.result)
                    }
                    fileReader.onerror = function(error) {
                        reject(error)
                    }
                    fileReader.readAsDataURL(file)
                }, function(error) {
                    reject(error)
                })
            }, function(error) {
                reject(error)
            })
            return
        }
        if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
            wx.getFileSystemManager().readFile({
                filePath: path,
                encoding: 'base64',
                success: function(res) {
                    resolve('data:image/png;base64,' + res.data)
                },
                fail: function(error) {
                    reject(error)
                }
            })
            return
        }
        reject(new Error('not support'))
    })
}

export function base64ToPath(base64) {
    return new Promise(function(resolve, reject) {
        if (typeof window === 'object' && 'document' in window) {
            base64 = base64.split(',')
            var type = base64[0].match(/:(.*?);/)[1]
            var str = atob(base64[1])
            var n = str.length
            var array = new Uint8Array(n)
            while (n--) {
                array[n] = str.charCodeAt(n)
            }
            return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([array], { type: type })))
        }
        var extName = base64.split(',')[0].match(/data\:\S+\/(\S+);/)
        if (extName) {
            extName = extName[1]
        } else {
            reject(new Error('base64 error'))
        }
        var fileName = getNewFileId() + '.' + extName
        if (typeof plus === 'object') {
            var basePath = '_doc'
            var dirPath = 'uniapp_temp'
            var filePath = basePath + '/' + dirPath + '/' + fileName
            if (!biggerThan(plus.os.name === 'Android' ? '1.9.9.80627' : '1.9.9.80472', plus.runtime.innerVersion)) {
                plus.io.resolveLocalFileSystemURL(basePath, function(entry) {
                    entry.getDirectory(dirPath, {
                        create: true,
                        exclusive: false,
                    }, function(entry) {
                        entry.getFile(fileName, {
                            create: true,
                            exclusive: false,
                        }, function(entry) {
                            entry.createWriter(function(writer) {
                                writer.onwrite = function() {
                                    resolve(filePath)
                                }
                                writer.onerror = reject
                                writer.seek(0)
                                writer.writeAsBinary(dataUrlToBase64(base64))
                            }, reject)
                        }, reject)
                    }, reject)
                }, reject)
                return
            }
            var bitmap = new plus.nativeObj.Bitmap(fileName)
            bitmap.loadBase64Data(base64, function() {
                bitmap.save(filePath, {}, function() {
                    bitmap.clear()
                    resolve(filePath)
                }, function(error) {
                    bitmap.clear()
                    reject(error)
                })
            }, function(error) {
                bitmap.clear()
                reject(error)
            })
            return
        }
        if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
            var filePath = wx.env.USER_DATA_PATH + '/' + fileName
            wx.getFileSystemManager().writeFile({
                filePath: filePath,
                data: dataUrlToBase64(base64),
                encoding: 'base64',
                success: function() {
                    resolve(filePath)
                },
                fail: function(error) {
                    reject(error)
                }
            })
            return
        }
        reject(new Error('not support'))
    })
}

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

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

相关文章

Java程序员必须掌握的数据结构:HashMap

HashMap底层原理实现是每个Java Boy必须掌握的基本技能&#xff0c;HashMap也是业务开发每天都需要遇到的好伙伴。如此基础且核心的底层数据结构&#xff0c;JDK也给其赋予了线程安全的功能类&#xff0c;我们来看看~ &#x1f331;以【面试官面试】形式覆盖Java程序员所需掌握…

使用Kimi快速完成高质量学术论文全流程攻略!

点击下方▼▼▼▼链接直达AIPaperPass &#xff01; AIPaperPass - AI论文写作指导平台 目录 01.论文选题(重要指数:★★★★★) 02.摘要(重要指数:★★★★) 03.关键词(重要指数:★★★★) 04.引言(重要指数:★★★★) 05.正文(重要指数:★★★★★) 06.结论(重要指数…

能源监控可视化大屏的价值,不要说没啥用了,容易暴露格局

能源监控可视化大屏具有以下几个方面的价值&#xff1a; 实时监控&#xff1a; 能源监控可视化大屏可以实时展示能源系统的运行状态&#xff0c;包括电力、水、气等能源的消耗、供应情况&#xff0c;以及设备运行状态等。通过实时监控&#xff0c;可以及时发现异常情况和故障…

翻译《The Old New Thing》 - What does SHGFI_USEFILEATTRIBUTES mean?

What does SHGFI_USEFILEATTRIBUTES mean? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20040601-00/?p39073 Raymond Chen 2004年06月01日 在使用 SHGetFileInfo 函数时&#xff0c;你可以设置一个名为 SHGFI_USEFILEATTRIBUTES 的标志…

目标检测——3D玩具数据集

在数字化时代&#xff0c;计算机视觉技术取得了长足的进展&#xff0c;其中基于形状的3D物体识别技术更是引起了广泛关注。该技术不仅有助于提升计算机对现实世界物体的感知能力&#xff0c;还在多个领域展现出了广阔的应用前景。本文将探讨基于形状的3D物体识别实验的重要性意…

WMS之添加View

目录 前言一、addview示例二、addview流程2.1 流程图2.2 流程分析2.2.1 Actitity的启动流程创建PhoneWindow和DecorView2.2.2.WindowManagerImpl 添加view2.2.3 ViewRootImpl.setView 三、总结 前言 WMS 功能繁杂&#xff0c;通过添加View流程进一步分析WMS 通过本文了解掌握…

RPC分布式通信框架

在实际开发中单机服务器存在诸多问题&#xff1a; 1.受限于硬件资源无法提高并发量 2.任意模块的修改都将导致整个项目代码重新编译部署 3.在系统中&#xff0c;有些模块属于CPU密集型&#xff0c;有些属于I/O密集型&#xff0c;各模块对于硬件资源的需求不一样 什么是分布式&a…

程序员转技术管理要做哪些努力?

对许多开发者而言&#xff0c;深耕技术&#xff0c;然后成为技术专家或许是职业发展的唯一答案。但如果你赞同「软件开发只是我众多职业目标中的一个」&#xff0c;也许你可以试试「技术管理之路」。 我原来觉得和计算机打交道比跟人打交道轻松得多&#xff0c;所以我成了一名…

每日OJ题_DFS回溯剪枝①_力扣46. 全排列(回溯算法简介)

目录 回溯算法简介 力扣46. 全排列 解析代码 回溯算法简介 回溯算法是一种经典的递归算法&#xff0c;通常⽤于解决组合问题、排列问题和搜索问题等。 回溯算法的基本思想&#xff1a;从一个初始状态开始&#xff0c;按照⼀定的规则向前搜索&#xff0c;当搜索到某个状态无…

【UnityShader入门精要学习笔记】第十一章 Shader动画

本系列为作者学习UnityShader入门精要而作的笔记&#xff0c;内容将包括&#xff1a; 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更&#xff0c;有始无终 我的GitHub仓库 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 UnityShad…

Vuforia AR篇(三)— AR模型出场效果

目录 前言一、AR模型出场二、AR出场特效三、添加过渡效果四、效果 前言 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习机器学习&#xff0c;本文就介绍了机器学习的基础内容。 一、AR模型出场 创建ARCamer…

上位机图像处理和嵌入式模块部署(树莓派4b之mcu固件升级)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在一个系统当中&#xff0c;可能不止需要树莓派4b一个设备&#xff0c;有的时候还需要搭载一个mcu&#xff0c;做一些运动控制的事情。比如说&…

【ESP32入门实战】初识ESP32

【ESP32入门实战】初识ESP32 文章目录 【ESP32入门实战】初识ESP32&#x1f468;‍&#x1f3eb;前言【写作缘由】&#x1f9d1;‍&#x1f393;ESP32介绍&#x1f469;‍&#x1f4bb;ESP32-WROOM-32&#x1f469;‍&#x1f4bb;ESP32的组成部分 &#x1f468;‍&#x1f3eb…

Android—统一依赖版本管理

依赖版本管理有多种方式 config.gradle 用于Groovy DSL&#xff0c;新建一个 config.gradle 文件&#xff0c;然后将项目中所有依赖写在里面&#xff0c;更新只需修改 config.gradle文件内容&#xff0c;作用于所有module buildSrc 可用于Kotlin DSL或Groovy DSL&#xff0c;…

48-70V降12V/33V 5A高效同步降压DC-DC——AH1007

AH1007是一款高效率、高压外置MOSFET管降压芯片TEL&#xff1a;186*4884*3702*&#xff0c;芯片典型输入是8V~100V,输出 电压可调&#xff0c;AH1007最大输出电流可支持6A以上&#xff0c;需要注意板子的散热和温升。 AH1007典型开关频率为150KHz。轻载时会自动降低开关频率以…

如何把MP3音频转AAC?超级简单的音频格式转换方法在这里

在数字化时代&#xff0c;音乐文件的格式多种多样&#xff0c;每种格式都有其独特的特点和优势。其中&#xff0c;MP3和AAC是两种非常常见的音频格式。MP3由于其较小的文件大小和良好的音质&#xff0c;在过去几十年中一直备受欢迎。然而&#xff0c;随着技术的进步和音频编码算…

掼蛋游戏中的坏习惯

掼蛋是一款需要团队合作和策略思考的游戏&#xff0c;已经成为很多人的日常休闲娱乐方式。然而在日常掼蛋游戏中&#xff0c;有些玩家可能会做出一些不良举动&#xff0c;影响游戏的进行。我们列举了一些常见的坏习惯&#xff0c;希望玩家能够注意并且避免。 1、随意退出 有些玩…

SpringCloud之Feign集成Ribbon

Feign定义【可跳过】 Spring Cloud Feign是一个声明式的伪Http客户端&#xff0c;它使得写Http客户端变得更简单。其英文表意为“假装&#xff0c;伪装&#xff0c;变形”&#xff0c;是一个http请求调用的轻量级框架&#xff0c;可以以Java接口注解的方式调用Http请求&#x…

Capture CIS设计小诀窍系列--Capture CIS配置-数据库搭建及ODBC配置

背景介绍&#xff1a;在原理图设计过程中&#xff0c;如果物料信息出现问题&#xff0c;导致BOM错误或者原理图符号、封装不对应&#xff0c;可能会导致项目延期甚至生产事故&#xff0c;严重影响产品设计效率。而Capture CIS原理图设计工具提供的CIS(Component Information Sy…

排队算法的matlab仿真,带GUI界面

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 M/M/1 单服务台单通道排队模型 4.2 M/M/k 多服务台排队模型 4.3 M/G/1 和 G/M/1 模型 5.完整程序 1.程序功能描述 排队算法的matlab仿真,带GUI界面。分别仿真单队列单服务台&#xff…