微信小程序使用canvas制作海报并保存到本地相册(超级详细)

案例图 在这里插入图片描述

分析案例图都有哪些元素 1.渐变背景 2.圆形头像 3.文字 4.文字超出换行 5.图片居中 6.文字居中 7.单位适配 8.弹窗保存图片。因为一个个绘制图形太麻烦所以这里都采用了方法封装。
canvas api介绍 最后有全部代码,复制即用。
data数据

        data() {
          return {
				myObj: {
					headImg: 'https://img.cncentre.cn/bf29eabe47edba2e6ae7249d76759247.png',
					name: '张三', //微信昵称
					introduce: '我叫张三今年18岁',
					introduction: '计算UI设计稿和你手机的屏幕宽度比例(例如UI设计稿是750宽度 你手机是3',
					bgImg: 'https://img.cncentre.cn/bf29eabe47edba2e6ae7249d76759247.png', //背景图
					rwmImg: 'https://img.cncentre.cn/bf29eabe47edba2e6ae7249d76759247.png',
					smText: '二维码介绍' //个性签名
				},
				canvasWidth: 375, //画布宽度
				canvasHeight: 800, //画布高度
				ratio: 0, //计算UI设计稿和你手机的屏幕宽度比例(例如UI设计稿是750宽度 你手机是350宽度 比例就是2  那么你画布画图时候 所有的尺寸大小、宽高、位置、定位左右上下都需要除以 / 比例2 )
				widths: '',
				heights: '',
				imgs:'' //最后生成的图片
          }
        }

1.单位适配

		onLoad() {
			let that = this
			uni.getSystemInfo({
				success: res => {
					console.log(res);
					// res.screenWidth 设备宽度
					that.canvasWidth = res.screenWidth + 'px'
					that.widths = res.screenWidth
					that.ratio = 750 / res.screenWidth
					that.canvasHeight = (that.widths / 375) * 800 + 'px'
					that.heights = (that.widths / 375) * 800
				}
			})
			uni.showLoading({
				title: '海报生成中...'
			});
			that.downImgUrl()
		},

拿到当前设备宽度用来做整个canvas的单位适配。这里根据要求高度是不变的,因为高度适配的话不同设备下最后生成的canvas 图片会被压缩

2.渐变背景

				let _this = this
				// 生成画布
				const ctx = uni.createCanvasContext('myCanvas')
				// 绘制背景
				const bcg = ctx.createLinearGradient(0, 0, 0, _this.heights)
				bcg.addColorStop(0.4, '#D9EBE6')
				bcg.addColorStop(1, '#fff')
				_this.ctxRectangle(ctx, 0, 0, (_this.widths), (_this.heights), 0, bcg)

			//画一个矩形也就是整个海报的背景
			ctxRectangle(ctx, x, y, width, height, r, gnt) {
				ctx.beginPath() //开始绘制
				ctx.save() //保存状态
				ctx.moveTo(x + r, y)
				ctx.lineTo(x + width - r, y)
				ctx.arc(x + width - r, y + r, r, Math.PI * 1.5, Math.PI * 2)
				ctx.lineTo(x + width, y + height - r)
				ctx.arc(x + width - r, y + height - r, r, 0, Math.PI * 0.5)
				ctx.lineTo(x + r, y + height)
				ctx.arc(x + r, y + height - r, r, Math.PI * 0.5, Math.PI)
				ctx.lineTo(x, y + r)
				ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5)
				ctx.fillStyle = gnt
				ctx.fill() //对当前路径中的内容进行填充
				ctx.closePath()
			},

3.圆形头像

             //绘制头像
		    _this.ctxCircular(ctx, _this.myObj.headImg, (40 / _this.ratio), (100 / _this.ratio), (160 / _this.ratio), (160 / _this.ratio), 80 / _this.ratio, 1)
			//画一个带圆角矩形
			//ctx 创建的canvas img填充的图片路径 x轴距离 y轴距离 width宽度 height高度 r圆角大小 shadow是否增加阴影
			ctxCircular(ctx, img, x, y, width, height, r, shadow) {
				ctx.beginPath() //开始绘制
				ctx.save() //保存(canvas)状态
				ctx.moveTo(x + r, y)
				ctx.lineTo(x + width - r, y)
				ctx.arc(x + width - r, y + r, r, Math.PI * 1.5, Math.PI * 2)
				ctx.lineTo(x + width, y + height - r)
				ctx.arc(x + width - r, y + height - r, r, 0, Math.PI * 0.5)
				ctx.lineTo(x + r, y + height)
				ctx.arc(x + r, y + height - r, r, Math.PI * 0.5, Math.PI)
				ctx.lineTo(x, y + r)
				ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5)
				if (shadow == 1) {
					ctx.shadowBlur = 20; // 阴影模糊程度
					ctx.shadowColor = '#fff'; // 阴影颜色
				}
				ctx.fill() //对当前路径中的内容进行填充
				ctx.clip() //从原始画布中剪切任意形状和尺寸
				ctx.closePath() //关闭一个路径
				ctx.drawImage(img, x, y, width, height);
				ctx.restore() //恢复(canvas)状态
			},	

增加了阴影的效果在这里插入图片描述

4.绘制文字

				//名字
				_this.ctxText(ctx,'normal bold 18px Arial,PingFang SC','left','#00663A',_this.myObj.name, 220 / _this.ratio, 128 / _this.ratio)
				//文字方法
				// textFont 字体样式大小 
				ctxText(ctx, textFont, textAlign, textFillStyle, textName, x, y) {
				ctx.beginPath()
				ctx.save() //保存状态
				//字体样式大小
				ctx.font = textFont,
				//文字对齐方式
				ctx.textAlign = textAlign
				//字体颜色
				ctx.fillStyle = textFillStyle
				//填充字体  x轴 y轴
				ctx.fillText(textName, x, y)
			},

5.文字超出换行

             //介绍
			_this.ctxTextWrap(ctx, _this.myObj.introduction, 220 / _this.ratio, 170 / _this.ratio, 460 / _this.ratio)
			//文字超出换行方法
			ctxTextWrap(ctx, text, x, y, w) {
				//自动换行介绍
				var temp = ""
				var row = []
				let gxqm = ''
				if (text) {
					gxqm = text
				} else {
					gxqm = '未设置个性签名'
				}
				let gexingqianming = gxqm.split("")
				for (var a = 0; a < gexingqianming.length; a++) {
					if (ctx.measureText(temp).width < w) {} else {
						row.push(temp)
						temp = ""
					}
					temp += gexingqianming[a]
				}
				row.push(temp)
				ctx.font = "13px arail"
				ctx.textAlign = 'left';
				ctx.fillStyle = "#000000"
				for (var b = 0; b < row.length; b++) {
					ctx.fillText(row[b], x, y + (b + 1) * 20)
				}
			},

6.图片居中

这里是中间的背景图比较简单就没有封装方法

	            // 背景图
				ctx.drawImage(_this.myObj.bgImg, //图像资源
					(48 / _this.ratio),//图像的左上角在目标canvas上 X 轴的位置
					(290 / _this.ratio),//图像的左上角在目标canvas上 Y 轴的位置
					(654 / _this.ratio),//在目标画布上绘制图像的宽度
					(1064 / _this.ratio)//在目标画布上绘制图像的高度
				)

7.文字居中

                // 文字居中
				_this.ctxText(ctx,
					'13px Arial,PingFang SC',
					'center',
					'#242424',
					_this.myObj.smText, 375 / _this.ratio, 1562 / _this.ratio)
			//封装方法 textAlign传入 center就可以
			ctxText(ctx, textFont, textAlign, textFillStyle, textName, x, y) {
				ctx.beginPath()
				ctx.save() //保存状态
				//字体
				ctx.font = textFont,
				//字体样式
				ctx.textAlign = textAlign
				//字体颜色
				ctx.fillStyle = textFillStyle
				//填充字体
				ctx.fillText(textName, x, y)
			},

8.渲染画布,保存图片

8.1 渲染画布

				// 渲染画布
				ctx.draw(false, (() => {
					setTimeout(() => {
						uni.canvasToTempFilePath({
							canvasId: 'myCanvas',
							destWidth: _this.canvasWidth * 2, //展示图片尺寸=画布尺寸1*像素比2
							destHeight: _this.canvasHeight * 2,
							quality: 1,
							fileType: 'jpg',
							success: (res) => {
								uni.hideLoading()
								console.log('通过画布绘制出的图片--保存的就是这个图', res.tempFilePath)
								_this.imgs = res.tempFilePath
								//点击保存方法 打开弹窗
								_this.$refs.popup.open()
							},
							fail: function(error) {
								uni.hideLoading()
								uni.showToast({
									icon: 'none',
									position: 'bottom',
									title: "绘制图片失败", // res.tempFilePath
								})
							}
						}, _this)

					}, 100)
				})())

到这里绘图就结束了最后借助 uni.canvasToTempFilePath()把当前画布指定区域的内容导出生成指定大小的图片,并返回文件路径,也就是我们 data定义的imgs

8.2 点击保存图片

			saveImage() {
				uni.saveImageToPhotosAlbum({
					filePath: this.imgs,
					success: function() {
						uni.showToast({
							icon: 'none',
							position: 'bottom',
							title: "已保存到系统相册",
						})
					},
					fail: function(error) {
						uni.showModal({
							title: '提示',
							content: '若点击不授权,将无法使用保存图片功能',
							cancelText: '不授权',
							cancelColor: '#999',
							confirmText: '授权',
							confirmColor: '#f94218',
							success(res) {
								console.log(res)
								if (res.confirm) {
									// 选择弹框内授权
									uni.openSetting({
										success(res) {
											console.log(res.authSetting)
										}
									})
								} else if (res.cancel) {
									// 选择弹框内 不授权
									console.log('用户点击不授权')
								}
							}
						})
					}
				})
			},

8.3 长按保存图片

如果要实现这个功能需要用到image带的longtap方法,也就是 长按事件。还需要一个值来隐藏显示image这里用的data里面的 isshow,然后监听imgs是否为空打开弹窗。

	<view class="percard">
		<canvas v-show='isshow' canvas-id="myCanvas" :style="{ width: canvasWidth, height: canvasHeight }"></canvas>
		<image v-show='!isshow' @longtap="saveImage()" :src="imgs" mode=""
			:style="{ width: canvasWidth, height: canvasHeight }"></image>
		<uni-popup ref="popup" type="center">
			<view class="pop1">
				<view class="tit">
					//点击下面按钮下载到相册
					请长按下载到相册
				</view>
				<view class="btns" @click="closer()">知道了</view>
				<view class="btns" @click="goindex()">回到首页</view>
			</view>
		</uni-popup>
	</view>
	watch: {
		imgs(newlue) {
			if (newlue) {
				this.isshow = false
				this.$refs.popup.open()
			}
		}
	},
	//关闭弹窗
	closer() {
		this.$refs.popup.close()
	},

全部代码

需要注意点canvas的绘制时不能直接使用网络路径图片,需要使用 uni.getImageInfo 返回图片本地路径再使用。本页面使用了uni-popup组件,自己的项目记得引入。

<template>
	<view class="percard">
		<canvas v-show='isshow' canvas-id="myCanvas" :style="{ width: canvasWidth, height: canvasHeight }"></canvas>
		<image v-show='!isshow' @longtap="saveImage()" :src="imgs" mode=""
			:style="{ width: canvasWidth, height: canvasHeight }"></image>
		<uni-popup ref="popup" type="center">
			<view class="pop1">
				<view class="tit">
					<!-- 点击下面按钮下载到相册 -->
				    请长按下载图片
				</view>
				<!-- <view class="btns" @click="saveImage()">点击保存</view> -->
				<view class="btns" @click="closer()">知道了</view>
				<view class="btns" @click="goindex()">回到首页</view>
			</view>
		</uni-popup>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				isshow: true,
				myObj: {
					headImg: 'https://img.cncentre.cn/bf29eabe47edba2e6ae7249d76759247.png',
					name: '张三', //微信昵称
					introduce: '我叫张三今年18岁',
					introduction: '计算UI设计稿和你手机的屏幕宽度比例(例如UI设计稿是750宽度 你手机是3',
					bgImg: 'https://img.cncentre.cn/bf29eabe47edba2e6ae7249d76759247.png', //背景图
					rwmImg: 'https://img.cncentre.cn/bf29eabe47edba2e6ae7249d76759247.png',
					smText: '二维码介绍' //个性签名
				},
				canvasWidth: 375, //画布宽度
				canvasHeight: 800, //画布高度
				ratio: 0, //计算UI设计稿和你手机的屏幕宽度比例(例如UI设计稿是750宽度 你手机是350宽度 比例就是2  那么你画布画图时候 所有的尺寸大小、宽高、位置、定位左右上下都需要除以 / 比例2 )
				widths: '',
				heights: '',
				imgs: ''

			}
		},
		watch: {
			imgs(newlue) {
				if (newlue) {
					this.isshow = false
					this.$refs.popup.open()
				}
			}
		},
		onLoad() {
			let that = this
			uni.getSystemInfo({
				success: res => {
					console.log(res);
					// res.screenWidth 设备宽度
					that.canvasWidth = res.screenWidth + 'px'
					that.widths = res.screenWidth
					that.ratio = 750 / res.screenWidth
					that.canvasHeight = (that.widths / 375) * 800 + 'px'
					that.heights = (that.widths / 375) * 800
				}
			})
			uni.showLoading({
				title: '海报生成中...'
			});
			that.downImgUrl()
		},
		methods: {
			downImgUrl() {
				let that = this
				uni.getImageInfo({
					src: that.myObj.headImg,
					success: function(res) {
						that.myObj.headImg = res.path
						uni.getImageInfo({
							src: that.myObj.bgImg,
							success: function(res) {
								that.myObj.bgImg = res.path
								uni.getImageInfo({
									src: that.myObj.rwmImg,
									success: function(res) {
										that.myObj.rwmImg = res.path
										that.drawPageImg()
									}
								});
							}
						});
					}
				});

			},
			closer() {
				this.$refs.popup.close()
			},
			goindex() {
				uni.reLaunch({
					url: '/pages/index/index'
				})
			},
			saveImage() {
				uni.saveImageToPhotosAlbum({
					filePath: this.imgs,
					success: function() {
						uni.showToast({
							icon: 'none',
							position: 'bottom',
							title: "已保存到系统相册",
						})
					},
					fail: function(error) {
						uni.showModal({
							title: '提示',
							content: '若点击不授权,将无法使用保存图片功能',
							cancelText: '不授权',
							cancelColor: '#999',
							confirmText: '授权',
							confirmColor: '#f94218',
							success(res) {
								console.log(res)
								if (res.confirm) {
									// 选择弹框内授权
									uni.openSetting({
										success(res) {
											console.log(res.authSetting)
										}
									})
								} else if (res.cancel) {
									// 选择弹框内 不授权
									console.log('用户点击不授权')
								}
							}
						})
					}
				})
			},
			//画一个带圆角矩形
			ctxCircular(ctx, img, x, y, width, height, r, shadow) {
				ctx.beginPath() //开始绘制
				ctx.save() //保存(canvas)状态
				ctx.moveTo(x + r, y)
				ctx.lineTo(x + width - r, y)
				ctx.arc(x + width - r, y + r, r, Math.PI * 1.5, Math.PI * 2)
				ctx.lineTo(x + width, y + height - r)
				ctx.arc(x + width - r, y + height - r, r, 0, Math.PI * 0.5)
				ctx.lineTo(x + r, y + height)
				ctx.arc(x + r, y + height - r, r, Math.PI * 0.5, Math.PI)
				ctx.lineTo(x, y + r)
				ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5)
				if (shadow == 1) {
					ctx.shadowBlur = 20; // 模糊效果程度的
					ctx.shadowColor = 'red'; // 阴影颜色
				}
				ctx.fill() //对当前路径中的内容进行填充
				ctx.clip() //从原始画布中剪切任意形状和尺寸
				ctx.closePath() //关闭一个路径
				ctx.drawImage(img, x, y, width, height);
				ctx.restore() //恢复(canvas)状态
			},
			//画一个矩形也就是整个海报的背景
			ctxRectangle(ctx, x, y, width, height, r, gnt) {
				ctx.beginPath()
				ctx.save() //保存状态
				ctx.moveTo(x + r, y)
				ctx.lineTo(x + width - r, y)
				ctx.arc(x + width - r, y + r, r, Math.PI * 1.5, Math.PI * 2)
				ctx.lineTo(x + width, y + height - r)
				ctx.arc(x + width - r, y + height - r, r, 0, Math.PI * 0.5)
				ctx.lineTo(x + r, y + height)
				ctx.arc(x + r, y + height - r, r, Math.PI * 0.5, Math.PI)
				ctx.lineTo(x, y + r)
				ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5)
				ctx.fillStyle = gnt
				ctx.fill() //对当前路径中的内容进行填充
				ctx.closePath() //关闭一个路径
			},
			ctxText(ctx, textFont, textAlign, textFillStyle, textName, x, y) {
				ctx.beginPath()
				ctx.save() //保存状态
				//字体
				ctx.font = textFont,
				//字体样式
				ctx.textAlign = textAlign
				//字体颜色
				ctx.fillStyle = textFillStyle
				//填充字体
				ctx.fillText(textName, x, y)
			},
			ctxTextWrap(ctx, text, x, y, w) {
				//自动换行介绍
				var temp = ""
				var row = []
				let gxqm = ''
				if (text) {
					gxqm = text
				} else {
					gxqm = '未设置个性签名'
				}
				let gexingqianming = gxqm.split("")
				for (var a = 0; a < gexingqianming.length; a++) {
					if (ctx.measureText(temp).width < w) {} else {
						row.push(temp)
						temp = ""
					}
					temp += gexingqianming[a]
				}
				row.push(temp)
				ctx.font = "13px arail"
				ctx.textAlign = 'left';
				ctx.fillStyle = "#000000"
				for (var b = 0; b < row.length; b++) {
					ctx.fillText(row[b], x, y + (b + 1) * 20)
				}
			},
			// 使用画布绘制页面
			drawPageImg() {
				let _this = this
				// 生成画布
				const ctx = uni.createCanvasContext('myCanvas')
				// 绘制背景
				const bcg = ctx.createLinearGradient(0, 0, 0, _this.heights)
				bcg.addColorStop(0.4, '#D9EBE6')
				bcg.addColorStop(1, '#fff')
				_this.ctxRectangle(ctx, 0, 0, (_this.widths), (_this.heights), 0, bcg)
				//名字
				_this.ctxText(ctx,
					'normal bold 18px Arial,PingFang SC',
					'left',
					'#00663A',
					_this.myObj.name, 220 / _this.ratio, 128 / _this.ratio)
				//名称
				_this.ctxText(ctx,
					'13px Arial,PingFang SC',
					'left',
					'#242424',
					_this.myObj.introduce, 220 / _this.ratio, 170 / _this.ratio)
				//介绍
				_this.ctxTextWrap(ctx, _this.myObj.introduction, 220 / _this.ratio, 170 / _this.ratio, 460 / _this.ratio)
				// // 背景图
				ctx.drawImage(_this.myObj.bgImg, //图像资源
					(48 / _this.ratio),//图像的左上角在目标canvas上 X 轴的位置
					(290 / _this.ratio),//图像的左上角在目标canvas上 Y 轴的位置
					(654 / _this.ratio),//在目标画布上绘制图像的宽度
					(1064 / _this.ratio)//在目标画布上绘制图像的高度
				)
				_this.ctxText(ctx,
					'13px Arial,PingFang SC',
					'center',
					'#242424',
					_this.myObj.smText, 375 / _this.ratio, 1562 / _this.ratio)
				// // 绘制头像
				_this.ctxCircular(ctx, _this.myObj.headImg, (40 / _this.ratio), (100 / _this.ratio), (160 / _this.ratio), (
					160 / _this.ratio), 80 / _this.ratio)

				//矩形二维码
				_this.ctxCircular(ctx, _this.myObj.rwmImg, (305 / _this.ratio), (1382 / _this.ratio), (140 / _this.ratio),
					(140 / _this.ratio), 6)
				// 渲染画布
				ctx.draw(false, (() => {
					setTimeout(() => {
						uni.canvasToTempFilePath({
							canvasId: 'myCanvas',
							destWidth: _this.canvasWidth * 2, //展示图片尺寸=画布尺寸1*像素比2
							destHeight: _this.canvasHeight * 2,
							quality: 1,
							fileType: 'jpg',
							success: (res) => {
								uni.hideLoading()
								console.log('通过画布绘制出的图片--保存的就是这个图', res.tempFilePath)
								_this.imgs = res.tempFilePath
								// _this.$refs.popup.open()
							},
							fail: function(error) {
								uni.hideLoading()
								uni.showToast({
									icon: 'none',
									position: 'bottom',
									title: "绘制图片失败", // res.tempFilePath
								})
							}
						}, _this)

					}, 100)
				})())
			},

		}
	}
</script>
<style lang="scss">
	.pop1 {
		background-color: #fff;
		width: 520rpx;
		padding: 68rpx 120rpx;
		box-sizing: border-box;
		border-radius: 20rpx;
		background: linear-gradient(#E6F5EB 45%, #FEFFFE 100%);

		.tit {
			font-size: 32rpx;
			color: #000000;
			margin: 20px 0px;
		}

		.btns {
			font-size: 32rpx;
			color: #fff;
			background-color: #00663A;
			border-radius: 14rpx;
			padding: 14rpx 30rpx;
			margin-top: 40rpx;
			text-align: center;
		}
	}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

并发程序设计--D1进程的创建和回收

进程和程序内容区别 进程包含的内容&#xff1a; BSS段&#xff1a;存放程序中未初始化的全局变量 数据段&#xff1a;已初始化的全局变量 代码段&#xff1a;程序执行代码 堆&#xff08;heap&#xff09;&#xff1a;malloc等函数分配内存 栈(stack)&#xff1a;局部变量…

升压斩波电路的simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 一、升压斩波电路概述 二、升压斩波电路的基本工作原理 5.完整工程文件 1.课题概述 升压斩波电路的simulink建模与仿真&#xff0c;通过双闭环结构实现电池&#xff0c;点击的控制。 2.系统仿真结果 …

复试情报准备

英语自我介绍&#xff0c;介绍完老师会根据你的回答用英语问你问题&#xff0c;比如介绍一下你的本科学校&#xff0c;或者家乡什么的。计网过一遍&#xff0c;会问两道题。接下来是重点&#xff0c;我当时是根据我成绩单&#xff0c;问了我本科学过的科目&#xff0c;比如pyth…

PPT中加入页码

PPT中加入页码 文章目录 简单版本样式更改 简单版本 PPT中插入页码&#xff0c;基础的就是在“插入”选项卡中单机“幻灯片编号”即可 样式更改 然而&#xff0c;就像我们做幻灯片不满足于白底黑字一样&#xff0c;页码也总不能是默认的样式。 比如&#xff0c;在页码下面…

Spring Cloud Gateway 网关整合 Knife4j 4.3 实现微服务接口文档聚合

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

QT小技巧 - 使用QMovie进行gif切帧

简介 使用QMovie 将 gif 进行切帧&#xff0c; magick 进行合并代码 QString gifPath "E:\\workspace\\qt\\gif2imgs\\203526qre64haq3ccoobqi.gif"; // 你的图片QMovie movie(gifPath); movie.setCacheMode(QMovie::CacheNone);qDebug() << movie.frameCou…

31. Ajax

简介 AJAX 是 Asynchronous JavaScript And XML 的简称。直译为&#xff0c;异步的JS和XML。AJAX的实际意义是&#xff0c;不发生页面跳转、异步载入内容并改写页面内容的技术。AJAX也可以简单的理解为通过JS向服务器发送请求。 AJAX这门技术很早就被发明&#xff0c;但是直到…

基于AR+地图导航的景区智慧导览设计

随着科技的飞速发展&#xff0c;智慧旅游已经成为现代旅游业的一个重要趋势。在这个背景下&#xff0c;景区智慧导览作为智慧旅游的核心组成部分&#xff0c;正逐渐受到越来越多游客的青睐。本文将深入探讨地图导航软件在景区智慧导览中的应用&#xff0c;并分析其为游客和景区…

C++中的存储类及其实例

文章目录 0. 语法1. 自动存储类自动存储类对象的属性自动存储类的例子 2. 外部存储类extern存储类对象的属性extern存储类的例子 3. 静态存储类静态存储类的属性静态存储类的例子 4. 寄存器存储类寄存器存储类对象的属性寄存器存储类例子 5. 可变&#xff08;mutable&#xff0…

iMazing2024免费版iOS移动设备管理软件

以自己的方式管理iPhone&#xff0c;让备受信赖的软件为您传输和保存音乐、消息、文件和数据。安全备份任何 iPhone、iPad 或 iPod touch。iMazing 功能强大、易于使用&#xff0c;称得上是 Mac 和 PC 上最好的 iOS 设备管理器。 正在为iTunes繁琐的操作发愁&#xff1f;设备数…

86% 的网络攻击是通过加密渠道进行

自 2022 年以来&#xff0c;HTTPS 威胁增长了 24%&#xff0c;凸显了针对加密通道的网络犯罪策略的复杂性。 制造业连续第二年成为最常受到攻击的行业&#xff0c;教育和政府组织的攻击同比增幅最高。此外&#xff0c;包括恶意 Web 内容和恶意软件负载在内的恶意软件继续主导其…

数字化转型是什么?有哪些应用?_光点科技

数字化转型是什么&#xff1f; 数字化转型是指企业或组织通过采用数字技术来改变其业务模式和运营方式&#xff0c;以适应新兴市场趋势、提高效率、增强客户体验和增加竞争优势的过程。它不仅涉及技术的变革&#xff0c;还包括企业文化、组织结构和业务流程的全面调整。数字化…

zookeeper基本使用

目录 环境搭建 单机版搭建 集群版搭建 基本语法使用 可视化客户端 数据结构 节点分类 1. 持久节点 2. 临时节点 3. 有序节点 4. 容器节点 5. TTL节点 节点状态 监听机制 watch监听 永久性watch 应用场景 1. 实现分布式锁 2. 乐观锁更新数据 应用场景总结 选…

docker安装MySQL8.0

1、从docker仓库中拉去mysql 8.0 docker pull mysql:8.0 2、查看是否拉取成功 docker images mysql:8.0 3、安装运行mysql8.0容器 docker run --name mysql8 -v /my/mysql/config:/etc/mysql/conf.d -v /my/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306…

《Spring Cloud学习笔记:Nacos配置管理 OpenFeign LoadBalancer Getway》

基于Feign的声明式远程调用&#xff08;代码更优雅&#xff09;&#xff0c;用它来去代替我们之前的RestTemplate方式的远程调用 1. Nacos配置管理 Nacos除了可以做注册中心&#xff0c;同样也可以做配置管理来使用。 利用Nacos实现统一配置管理以及配置的热更新&#xff1a;…

几种串口扩展电路

一、IIC串口扩展电路 LCT200 是一款可以通过 I2C 接口通讯&#xff0c;拓展 2 路独立串口的通讯芯片&#xff0c;同时也支持通过 2 路串口读写 I2C 接口的数据。LCT200 的封装为 TSSOP-20。 主要功能&#xff1a;⚫ 通过对 I2C 接口读写实现拓展 2 路独立串口功能 ⚫ 通过读写…

SpringBoot Event,事件驱动轻松实现业务解耦

什么是事件驱动 Spring 官方文档AWS Event Driven 简单来说事件驱动是一种行为型设计模式&#xff0c;通过建立一对多的依赖关系&#xff0c;使得当一个对象的状态发生变化时&#xff0c;所有依赖它的对象都能自动接收通知并更新。即将自身耦合的行为进行拆分&#xff0c;使拆…

AI安全综述

1、引言 AI安全这个话题&#xff0c;通常会引伸出来图像识别领域的对抗样本攻击。下面这张把“熊猫”变“猴子”的攻击样例应该都不陌生&#xff0c;包括很多照片/视频过人脸的演示也很多。 对抗样本的研究领域已经具备了一定的成熟性&#xff0c;有一系列的理论来论述对抗样本…

【JavaEE初阶二】 Thread类及常见方法

1. 线程的创建 主要来简单学习一下以下几种方法&#xff1a; 1.1 继承 Thread 类 具体代码见前面的一章节&#xff0c;主体的步骤有以下几部分&#xff1b; 1、继承 Thread 来创建一个自定义线程类MyThread class MyThread2 extends Thread{//重写run方法Overridepublic void …

VScode远程连接服务器,Pycharm专业版下载及远程连接(深度学习远程篇)

Visual Code、PyCharm专业版&#xff0c;本地和远程交互。 远程连接需要用到SSH协议的技术&#xff0c;常用的代码编辑器vscode 和 pycharm都有此类功能。社区版的pycharm是免费的&#xff0c;但是社区版不支持ssh连接服务器&#xff0c;只有专业版才可以&#xff0c;需要破解…