uniapp使用Canvas给图片加水印把临时文件上传到服务器

生成的临时路径是没有完整的路径没办法上传到服务器

16:37:40.993 添加水印后的路径,  _doc/uniapp_temp_1710923708347/canvas/17109238597881.png 
16:37:41.041 添加水印后的完整路径,  file://storage/emulated/0/Android/data/com.jingruan.zjd/apps/__UNI__BE4B000/doc/uniapp_temp_1710923708347/canvas/17109238597881.png

使用以下代码得到完整的路径

let path = 'file:/' + plus.io.convertLocalFileSystemURL(tempFilePath);


 

完整代码如下 使用的插件市场的hpy-watermark组件   一共2个

效果是

其他页面调用方式

<!-- 
增加水印上传villageReviewForm.preciseAddr 是通过高德获取的定位地址 
-->
<hpy-watermark ref="uploadImage" :address="villageReviewForm.preciseAddr" @waterMark="waterMark"></hpy-watermark>	

高德获取定位

		uni.getLocation({
					type: 'gcj02',
					geocode: true,
					isHighAccuracy: true,
					success: res => { 
						const {
							province,
							city,
							district,
							street,
							streetNum,
							poiName
						} = res.address;
						this.villageReviewForm.preciseAddr =
							`${district}${street}${streetNum}${poiName}${res.longitude},${res.latitude}`;
						console.log("经纬度地点",this.villageReviewForm.preciseAddr)
						// 数据渲染
						this.$forceUpdate();
					}
				});

获取上传数据结果

const fileList = this.$refs.uploadImage.fileList

组件样式

hpy-watermark.vue

<template>
	<view>
	<view class="watermark-content">
		<canvas canvas-id="watermarkCanvas" id="watermarkCanvas" :style="{width:canvasWidth + 'px', height:canvasHeight + 'px'}"></canvas>
	</view>
	<upload-image   v-model="fileList" style="margin-left: 15rpx"   :image-styles="imageStyles" :files-list="filesList"   :delIcon="delIcon"   @choose="chooseImage" @delFile="delFile">
		<slot>
			<view class="is-add">
				<view class="icon-add"></view>
				<view class="icon-add rotate"></view>
			</view>
		</slot>
	</upload-image>
	</view>
</template>

<script>
	import {
		fileServerIp
	} from "@/common/utils/config.js"
	import Session from "@/common/Session";
	import uploadImage from './upload-image.vue'
	export default {
		components: {
			uploadImage, 
		},
		name:'hpy-watermark',
		props:{
			address:{
				type:String,
				default:''
			},
			delIcon: {
				type: Boolean,
				default: true
			},
		 
			listStyles: {
				type: Object,
				default () {
					return {
						// 是否显示边框
						border: true,
						// 是否显示分隔线
						dividline: true,
						// 线条样式
						borderStyle: {}
					}
				}
			},
			imageStyles: {
				type: Object,
				default () {
					return {
						width: 'auto',
						height: 'auto'
					}
				}
			},
			/**
			 * 文字文字位置(默认:左下角)可选值:左上角:topLeft、右上角:topRight、左下角:bottomLeft、右下角:bottomRight
			 */
			markAlign:{
				type:String,
				default:function(){
					return 'bottomLeft'
				}
			},
			/**
			 * 设置文本的水平对齐方式,默认:start,文本在指定的位置开始。
			 * end	文本在指定的位置结束。
			 * center 文本的中心被放置在指定的位置。
			 * left	文本左对齐。
			 * right	文本右对齐。
			 */
			textAlign:{
				type:String,
				default:function(){
					return 'start';
				}
			},
			/**
			 * 设置文本的垂直对齐方式,默认:alphabetic文本基线是普通的字母基线。
			 * top	文本基线是 em 方框的顶端。
			 * hanging	文本基线是悬挂基线。
			 * middle	文本基线是 em 方框的正中。
			 * ideographic	文本基线是表意基线。
			 * bottom	文本基线是 em 方框的底端。
			 */
			textBaseline:{
				type:String,
				default:function(){
					return 'alphabetic';
				}
			},
			/**
			 * 文字大小
			 */
			fontSize:{
				type:[Number, String],
				default:30
			},
			/**
			 * 文字颜色
			 */
			fontColor:{
				type:String,
				default:function(){
					return 'red'
				}
			},
			/**
			 * 阴影颜色
			 */
			shadowColor:{
				type:String,
				default:function(){
					return 'rgba(0, 0, 0, 1.0)';
				}
			},
			/**
			 * 阴影边框大小
			 */
			shadowWidth:{
				type:[Number, String],
				default:2
			},
			/**
			 * 图片的质量,取值范围为 (0, 1],不在范围内时当作1处理
			 */
			quality:{
				type:[Number, String],
				default:1
			},
			/**
			 * 目标文件的类型,只支持 'jpg' 或 'png'。默认为 'png'
			 */
			fileType:{
				type:String,
				default:function(){
					return 'png'
				}
			}
		},
		data() {
			return {
				fileList: [],
				files: [],
				filesList:[],
				canvasWidth:0,
				canvasHeight:0
			};
		},
		watch: { 
			fileList: {
				handler(newVal, oldVal) { 
					this.filesList=newVal;
				},
				immediate: true
			},
		 
		},
		methods: {
			
			// 选择图片
				chooseImage() {
					if(this.isEmpty(this.address)){
						uni.showToast({
							icon:'none',
							title:'请打开定位或者重新获取'
						});
						return;
					}
					uni.chooseImage({
						count: this.limit,              // 限制的图片数量
						sizeType: ['compressed'],       // original 原图,compressed 压缩图,默认二者都有 
						sourceType: [ 'camera'],// album 从相册选图,camera 使用相机,默认二者都有
						success: (res) => {
							var imgPathList = res.tempFilePaths;
							if(imgPathList.length > 0){
								this.addImages(imgPathList);
							}
						},
						fail: (err) => {
							console.log('chooseImage fail', err)
							if("chooseImage:fail cancel" == err.errMsg){
								uni.showToast({
									icon:'none',
									title:'取消了选择'
								});
							}else{
						
							}
						}
					});
				},
				// 添加图片
				addImages(filePaths){
					
					if(filePaths.length > 0){
						var fillTexts = ["地址:"+this.address];
						fillTexts.push("时间:" + this.getNowTime());
						// 添加水印
						this.addWaterMark({
							filePaths,
							fillTexts
						});
					}
				},
				/**
				 * 水印添加回调,在H5平台下,filePath 为 base64
				 */
				waterMark(filePath){
					this.imageList.push(filePath);
				},
				/**
				 * 获取当前时间
				 */
				getNowTime(){
					var date = new Date(),
					year = date.getFullYear(),
					month = date.getMonth() + 1,
					day = date.getDate(),
					hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours(),
					minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes(),
					second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
					month >= 1 && month <= 9 ? (month = "0" + month) : "";
					day >= 0 && day <= 9 ? (day = "0" + day) : "";
					return (year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);
				},
			 
			/**
			 * 删除文件
			 * @param {Object} index
			 */
			delFile(index) {
				this.$emit('delete', {
					tempFile: this.filesList[index],
					tempFilePath: this.filesList[index].url
				})
				this.filesList.splice(index, 1) 
			 	
				 
			},
			/**
			 * 增加水印
			 * @param {Object} {filePaths:['图片地址1', '图片地址2'], fillTexts:['水印1', '水印2']}
			 */
			async addWaterMark({ filePaths = [], fillTexts = [] }) {
				
				try{
					for (const filePath of filePaths) {
						await this.drawImage(filePath, fillTexts.reverse());
					}
				}catch(e){
					// TODO handle the exception
				}finally{
					// uni.hideLoading();
				}
			},
			/**
			 * 绘制单个图片
			 */
			async drawImage(filePath, fillTexts){
				uni.showLoading({title:'图片处理中···'});
				const ctx = uni.createCanvasContext('watermarkCanvas', this);
				return new Promise(resolve => {
					uni.getImageInfo({
						src: filePath,
						success: (image) => {
							this.canvasWidth = image.width;
							this.canvasHeight = image.height;
							ctx.clearRect(0, 0, image.width, image.height);
							setTimeout(()=>{
								ctx.drawImage(image.path, 0, 0, image.width, image.height);
								ctx.setFontSize(this.fontSize);
								ctx.setFillStyle(this.fontColor);
								// 设置阴影
								let shadowWidth = Number(this.shadowWidth + "");
								if(shadowWidth > 0){
									ctx.shadowColor = this.shadowColor;
									ctx.shadowOffsetX = shadowWidth;
									ctx.shadowOffsetY = shadowWidth;
								}
								// 设置水平对齐方式
								ctx.textAlign = this.textAlign;
								// 设置垂直对齐方式
								ctx.textBaseline = this.textBaseline;
								const maxText = fillTexts.reduce((text, val) => {
									return text.length >= val.length ? text : val;
								});
								fillTexts.forEach((mark, index) => {
									if(this.markAlign == "bottomRight"){
										ctx.fillText(mark, image.width - (ctx.measureText(maxText).width+60), image.height - (index*60+60));
									}else if(this.markAlign == "topLeft"){
										ctx.fillText(mark, 20, (index*60+60));
									}else if(this.markAlign == "topRight"){
										ctx.fillText(mark, image.width - (ctx.measureText(maxText).width+60), (index*60+60));
									}else{
										ctx.fillText(mark, 20, image.height - (index*60+60));
									}
								});
								ctx.draw(false, (() => {
									setTimeout(()=>{
										uni.canvasToTempFilePath({
											canvasId: 'watermarkCanvas',
											fileType:this.fileType,
											quality:Number(this.quality + "" || "1"),
											success: (res) => { 
												console.log("添加水印后的路径",res.tempFilePath )
												this.saveUploadImage(res.tempFilePath )
											},
											fail:(err) => {
												uni.hideLoading();
												console.log(err)
											},
											complete: () => {
												resolve();
											}
										}, this);
									}, 300);
								})());
							}, 200);
						},
						fail: (e) => {
							resolve();
						}
					});
				});
			},
			saveUploadImage(tempFilePath){
				uni.showLoading({title:'图片上传中···'});
				// #ifdef APP-PLUS
				var p = plus.io.convertLocalFileSystemURL(tempFilePath);
				this.url = 'file:/' + p
				console.log("添加水印后的完整路径",this.url )
				// #endif
				uni.uploadFile({
					url: fileServerIp + 'common/upload',
					name: "file",
					// #ifdef H5
					filePath: tempFilePath,
					// #endif
					// #ifdef APP-PLUS
					filePath: this.url,
					// #endif
					header: {
						Authorization: "Bearer " + Session.getValue('token')
					},
					success: uploadFileRes => {
						uni.hideLoading();
						const {
							data
						} = JSON.parse(uploadFileRes.data)
						this.filesList.push({
							url: data.url,
							name: data.fileName,
							extname: 'png'
						}) 
							this.$emit('waterMark',{
							url: data.url,
							name: data.fileName,
							extname: 'png'
						});
					},
					fail: error => {
						uni.hideLoading();
						uni.showToast({
							title: '上传失败!',
							icon: 'error',
							duration: 2000
						});
					}
				})
			}
		}
	}
</script>

<style scoped>
	.watermark-content{width: 0;height: 0;overflow: hidden;}
	.uni-file-picker__container {
		/* #ifndef APP-NVUE */
		display: flex;
		box-sizing: border-box;
		/* #endif */
		flex-wrap: wrap;
		margin: -5px;
	}
	.rotate {
		position: absolute;
		transform: rotate(90deg);
	}
	
	.icon-add {
		width: 50px;
		height: 5px;
		background-color: #f1f1f1;
		border-radius: 2px;
	}
</style>

upload-image.vue

<template>
	<view class="uni-file-picker__container">
		<view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle">
			<view class="file-picker__box-content" :style="borderStyle">
				<image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image>
				<view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)">
					<view class="icon-del"></view>
					<view class="icon-del rotate"></view>
				</view>
				<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
					<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4"
					 :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
				</view>
				<view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
					点击重试
				</view>
			</view>
		</view>
		<view v-if="filesList.length < limit && !readonly" class="file-picker__box" :style="boxStyle">
			<view class="file-picker__box-content is-add" :style="borderStyle" @click="choose">
				<slot>
					<view class="icon-add"></view>
					<view class="icon-add rotate"></view>
				</slot>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		name: "uploadImage",
		emits:['uploadFiles','choose','delFile'],
		props: {
			filesList: {
				type: Array,
				default () {
					return []
				}
			},
			disabled:{
				type: Boolean,
				default: false
			},
			disablePreview: {
				type: Boolean,
				default: false
			},
			limit: {
				type: [Number, String],
				default: 9
			},
			imageStyles: {
				type: Object,
				default () {
					return {
						width: 'auto',
						height: 'auto',
						border: {}
					}
				}
			},
			delIcon: {
				type: Boolean,
				default: true
			},
			readonly:{
				type:Boolean,
				default:false
			}
		},
		computed: {
			styles() {
				let styles = {
					width: 'auto',
					height: 'auto',
					border: {}
				}
				return Object.assign(styles, this.imageStyles)
			},
			boxStyle() {
				const {
					width = 'auto',
						height = 'auto'
				} = this.styles
				let obj = {}
				if (height === 'auto') {
					if (width !== 'auto') {
						obj.height = this.value2px(width)
						obj['padding-top'] = 0
					} else {
						obj.height = 0
					}
				} else {
					obj.height = this.value2px(height)
					obj['padding-top'] = 0
				}

				if (width === 'auto') {
					if (height !== 'auto') {
						obj.width = this.value2px(height)
					} else {
						obj.width = '33.3%'
					}
				} else {
					obj.width = this.value2px(width)
				}

				let classles = ''
				for(let i in obj){
					classles+= `${i}:${obj[i]};`
				}
				return classles
			},
			borderStyle() {
				let {
					border
				} = this.styles
				let obj = {}
				const widthDefaultValue = 1
				const radiusDefaultValue = 3
				if (typeof border === 'boolean') {
					obj.border = border ? '1px #eee solid' : 'none'
				} else {
					let width = (border && border.width) || widthDefaultValue
					width = this.value2px(width)
					let radius = (border && border.radius) || radiusDefaultValue
					radius = this.value2px(radius)
					obj = {
						'border-width': width,
						'border-style': (border && border.style) || 'solid',
						'border-color': (border && border.color) || '#eee',
						'border-radius': radius
					}
				}
				let classles = ''
				for(let i in obj){
					classles+= `${i}:${obj[i]};`
				}
				return classles
			}
		},
		methods: {
			uploadFiles(item, index) {
				this.$emit("uploadFiles", item)
			},
			choose() {
				this.$emit("choose")
			},
			delFile(index) {
				this.$emit('delFile', index)
			},
			prviewImage(img, index) {
				let urls = []
				if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){
					this.$emit("choose")
				}
				if(this.disablePreview) return
				this.filesList.forEach(i => {
					urls.push(i.url)
				})

				uni.previewImage({
					urls: urls,
					current: index
				});
			},
			value2px(value) {
				if (typeof value === 'number') {
					value += 'px'
				} else {
					if (value.indexOf('%') === -1) {
						value = value.indexOf('px') !== -1 ? value : value + 'px'
					}
				}
				return value
			}
		}
	}
</script>

<style lang="scss">
	.uni-file-picker__container {
		/* #ifndef APP-NVUE */
		display: flex;
		box-sizing: border-box;
		/* #endif */
		flex-wrap: wrap;
		margin: -5px;
	}

	.file-picker__box {
		position: relative;
		// flex: 0 0 33.3%;
		width: 33.3%;
		height: 0;
		padding-top: 33.33%;
		/* #ifndef APP-NVUE */
		box-sizing: border-box;
		/* #endif */
	}

	.file-picker__box-content {
		position: absolute;
		top: 0;
		right: 0;
		bottom: 0;
		left: 0;
		margin: 5px;
		border: 1px #eee solid;
		border-radius: 5px;
		overflow: hidden;
	}

	.file-picker__progress {
		position: absolute;
		bottom: 0;
		left: 0;
		right: 0;
		/* border: 1px red solid; */
		z-index: 2;
	}

	.file-picker__progress-item {
		width: 100%;
	}

	.file-picker__mask {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		justify-content: center;
		align-items: center;
		position: absolute;
		right: 0;
		top: 0;
		bottom: 0;
		left: 0;
		color: #fff;
		font-size: 12px;
		background-color: rgba(0, 0, 0, 0.4);
	}

	.file-image {
		width: 100%;
		height: 100%;
	}

	.is-add {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		align-items: center;
		justify-content: center;
	}

	.icon-add {
		width: 50px;
		height: 5px;
		background-color: #f1f1f1;
		border-radius: 2px;
	}

	.rotate {
		position: absolute;
		transform: rotate(90deg);
	}

	.icon-del-box {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		align-items: center;
		justify-content: center;
		position: absolute;
		top: 3px;
		right: 3px;
		height: 26px;
		width: 26px;
		border-radius: 50%;
		background-color: rgba(0, 0, 0, 0.5);
		z-index: 2;
		transform: rotate(-45deg);
	}

	.icon-del {
		width: 15px;
		height: 2px;
		background-color: #fff;
		border-radius: 2px;
	}
</style>

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

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

相关文章

C语言字符函数与字符串函数:编织文字的舞会之梦(上)

欢迎来到白刘的领域 Miracle_86.-CSDN博客 系列专栏 C语言知识 先赞后看&#xff0c;已成习惯 创作不易&#xff0c;多多支持&#xff01; 在编程的过程中&#xff0c;我们经常要处理字符以及字符串&#xff0c;为了方便操作这些字符和字符串&#xff0c;C语言标准库中提供…

PyTorch 深度学习(GPT 重译)(一)

第一部分&#xff1a;PyTorch 核心 欢迎来到本书的第一部分。在这里&#xff0c;我们将与 PyTorch 迈出第一步&#xff0c;获得理解其结构和解决 PyTorch 项目机制所需的基本技能。 在第一章中&#xff0c;我们将首次接触 PyTorch&#xff0c;了解它是什么&#xff0c;解决了…

Amuse .NET application for stable diffusion

Amuse github地址&#xff1a;https://github.com/tianleiwu/Amuse .NET application for stable diffusion, Leveraging OnnxStack, Amuse seamlessly integrates many StableDiffusion capabilities all within the .NET eco-system Welcome to Amuse! Amuse is a profes…

跨越时空的纽带:探索Facebook如何连接人与人

引言 Facebook作为全球最大的社交媒体平台之一&#xff0c;已经成为了人们日常生活中不可或缺的一部分。它不仅仅是一个社交网络&#xff0c;更是连接人与人、人与世界的纽带。在这篇文章中&#xff0c;我们将深入探讨Facebook如何跨越时空&#xff0c;连接人与人之间的关系&a…

机器学习-06-回归算法

总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍机器学习中回归算法&#xff0c;包括线性回归&#xff0c;岭回归&#xff0c;逻辑回归等部分。 参考 fit_transform,fit,transform区别和作用详解&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&am…

spring boot学习第十四篇:使用AOP编程

一、基本介绍 1&#xff0c;什么是 AOP &#xff08;1&#xff09;AOP 为 Aspect Oriented Programming 的缩写&#xff0c;意为&#xff1a;面向切面编程&#xff0c;通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。 &#xff08;2&#xff09;利用 AOP…

Cronos zkEVM 基于 Covalent 数据可用性 API,推动其 Layer2 DeFi 生态更好地发展

在一项旨在显著改善 DeFi 生态的战略举措中&#xff0c;Cronos 与 Covalent Network&#xff08;CQT&#xff09;携手合作&#xff0c;以期待 Cronos zkEVM 的推出。这一整合&#xff0c;预计将进一步降低以太坊生态系统的交易成本、提升交易速度&#xff0c;并带来更好的交易体…

ES的集群节点发现故障排除指南(1)

本文是ES官方文档关于集群节点发现与互联互通的问题排查指南内容。 集群节点发现是首要任务 集群互连&#xff0c;重中之重&#xff01; 在大多数情况下&#xff0c;发现和选举过程会迅速完成&#xff0c;并且主节点会长时间保持当选状态。 如果集群没有稳定的主节点&#xf…

四、Elasticsearch 进阶

自定义目录 4.1 核心概念4.1.1 索引&#xff08;Index&#xff09;4.1.2 类型&#xff08;Type&#xff09;4.1.3 文档&#xff08;Document&#xff09;4.1.3 字段&#xff08;Field&#xff09;4.1.5 映射&#xff08;Mapping&#xff09;4.1.6 分片&#xff08;Shards&#…

C语言指针与地址基础学习(取地址运算)

C语言指针与地址基础学习&#xff08;取地址运算&#xff09; 取地址运算&#xff1a;&运算符取得变量的地址代码示例一运算符& 取地址运算&#xff1a;&运算符取得变量的地址 代码示例一 #include<stdio.h> int main() {int a;a 6;printf("sizeof(i…

区块链革命:探索 Web3 的全球影响

引言 自比特币的诞生以来&#xff0c;区块链技术已经成为全球范围内备受瞩目的创新之一。其去中心化、不可篡改、透明的特性不仅使其成为数字货币领域的核心技术&#xff0c;还在金融、供应链管理、智能合约等领域展现出了巨大的应用潜力。随着区块链技术的不断发展&#xff0…

Jackson 2.x 系列【3】解析器 JsonParser

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-seata-demo 文章目录 1. 前言2. 解析原理3. 案例演示3.1 创建 JsonParser3.2 解析3.3 读取3.4 测试 1. 前…

【Qt】使用Qt实现Web服务器(三):QtWebApp中HttpRequest和HttpResponse

1、HttpRequest 1.1 示例 1)在Demo1的Dump HTTP request示例 在浏览器中输入http://127.0.0.1:8080点击Dump HTTP request 2)切换到页面:http://127.0.0.1:8080/dump 该页面显示请求和响应的内容: Request: Method: GET Path: /dump Version: HTTP/1.1 Headers: accep…

【C语言】【牛客】BC136 KiKi判断上三角矩阵

文章目录 题目 BC136 KiKi判断上三角矩阵思路代码呈现 题目 BC136 KiKi判断上三角矩阵 链接: link 思路 这题很简单但是再牛客中属于中等题 我们通过读题发现 2<n<10 &#xff0c;所以我们首先创建一个变量 n 以及一个 10*10 个元素数组 然后题目是判断该矩阵是否是…

Android 系统开发工具大全

写给应用开发的 Android Framework 教程——玩转AOSP篇之 Android 系统开发工具推荐 下面推荐的是我常用的工具&#xff0c;如果你有好用的开发工具欢迎在评论区留言讨论交流。 1. SSH 服务与 Tabby Terminal SSH 服务使得我们在其他平台上通过 SSH 客户端程序即可访问到我们…

时序预测 | Matlab实现BiTCN-BiLSTM双向时间卷积神经网络结合双向长短期记忆神经网络时间序列预测

时序预测 | Matlab实现BiTCN-BiLSTM双向时间卷积神经网络结合双向长短期记忆神经网络时间序列预测 目录 时序预测 | Matlab实现BiTCN-BiLSTM双向时间卷积神经网络结合双向长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现BiTCN…

Monaco Editor系列(一)启动项目与入门示例解析

前言&#xff1a;作为一名程序员&#xff0c;我们工作中的每一天都在与代码编辑器打交道&#xff0c;相信各位前端程序员对 VS Code 一定都不陌生&#xff0c;VS Code 可以为我们提供代码高亮、代码对比等等功能&#xff0c;让我们在开发的时候&#xff0c;不需要对着暗淡无光的…

Redis模拟小例子

我们模拟游戏中的一个角色&#xff0c;这个角色被动技能就是受到攻击的时候&#xff0c;会有十分之三的概率爆出金币&#xff0c;而在一个回合之中&#xff0c;爆出的金币个数有限制&#xff0c;限制为两个&#xff0c;假设攻击是按照一定的频率进行的&#xff0c;而一个回合的…

海外云手机如何帮助亚马逊引流?

随着全球化的推进&#xff0c;出海企业和B2B外贸企业越来越注重海外市场的开拓&#xff0c;这已成为企业争夺市场份额的重要策略。本文将重点探讨海外云手机在优化亚马逊店铺引流方面的作用和优势。 海外云手机是一种在云端运行的虚拟手机&#xff0c;能够在单一芯片上多开几个…

20---复位电路设计

视频链接 复位电路设计01_哔哩哔哩_bilibili 复位电路设计 1、复位介绍 复位电路又叫初始化电路&#xff0c;它的作用是将芯片的工作状态回到初始状态&#xff01; 复位电路在硬件设计中至关重要&#xff0c;在实际调试的过程中&#xff0c;与复位相关的点必核查&#xff…