uniapp小程序实现弹幕不重叠

uniapp小程序实现弹幕不重叠

1、在父组件中引入弹幕组件

<template>
	<!-- 弹幕 -->
	<barrage ref="barrage" class="barrage-content" @reloadDanmu="reloadDanmu"></barrage>
</template>
<script>
	import barrage from './components/barrage.vue'
	import {
		getBarrageListApi
	} from '@/api/voteApi.js'
	
	export default {
		components: {
			barrage
		},
		data() {
			return {
				danmuList: [], // 弹幕列表
				danmuContion: { // 弹幕查询条件
					page: 1,
					size: 200
				}
		},
		onLoad(){
			this.getBarrageList()
		},
		methods: {
			async getBarrageList(isInit) {
				try {
					let res = await getBarrageListApi(this.danmuContion)
					let resData = (res && res.data) || {}
					let list = Array.isArray(resData.records) ? resData.records : []
					list.map((item) => {
						item.color = '#fff'
						item.timestampt = new Date().getTime()
						item.image = {
							head: {
								src: item.avatarUrl,
								width: 44,
								height: 44
							}, // 弹幕头部添加图片
							gap: 8 // 图片与文本间隔
						}
						item.content = `{${item.nickname}} 已为《${item.voteName}》投下宝贵的一票`
					})
					let danmuLength = this.danmuList.length
					this.danmuList = list
					this.addBarrage(isInit || danmuLength === 0)
				} catch (e) {
					uni.showToast({
						title: (e && e.message) || '查询弹幕列表失败',
						icon: 'none',
						during: 2000
					})
				}
			},
			addBarrage(isInit) {
				if (!isInit || !this.danmuList.length) {
					return
				}
				const barrageComp = this.$refs && this.$refs.barrage || {}
				barrageComp.getBarrageInstance({
					duration: 15, // 弹幕动画时长 (移动 1500px 所需时长)
					lineHeight: 2.4, // 弹幕行高
					padding: [0, 0, 0, 0], // 弹幕区四周留白
					alpha: 1, // 全局透明度
					font: '10px PingFang SC', // 全局字体
					range: [0, 1], // 弹幕显示的垂直范围,支持两个值。[0,1]表示弹幕整个随机分布,
					tunnelShow: false, // 显示轨道线
					tunnelMaxNum: 200, // 隧道最大缓冲长度
					maxLength: 5000, // 弹幕最大字节长度,汉字算双字节
					safeGap: 20, // 发送时的安全间隔
					enableTap: false, // 点击弹幕停止动画高亮显示
					danmuList: this.danmuList
				})
			},
			async reloadDanmu(type) {
				const barrageComp = this.$refs && this.$refs.barrage || {}
				if(type === 'addDanmu') {
					await this.getBarrageList(false)
					barrageComp.open()
					barrageComp.addData(this.danmuList)
					return
				}
				await this.getBarrageList(true)
			}, 
	}
</script>

<style lang="less" scoped>
	.barrage-conten {
		width: 100%;
		height: 156rpx;
		position: absolute;
		top: 192rpx;
		box-sizing: border-box;
	}
</style>

2、弹幕组件

 <template>
	<view class="barrage-area" :style="{'opacity': alpha, 'font-size': fontSize*2 + 'rpx', 'padding': padding}">
		<block v-for="(tunnel, tunnelId) in tunnels" :key="tunnelId">
			<view class="barrage-tunnel"
				:style="{'height': tunnel.height*2 + 'rpx', 'border-top-width': (tunnelShow ? 1 : 0) + 'px'}">
				<view class="tunnel-tips" :style="{'display': !tunnelShow ? 'none' : 'block'}">轨道{{tunnelId}}</view>
				<block v-for="(bullet, bulletId) in tunnel.bullets" :key="bullet.timestampt + bulletId">
					<view :data-tunnelid="{tunnelId}" :data-bulletid="{bulletId}"
						:class="['bullet-item', bullet.duration > 0 ? 'bullet-move' : '', bullet.paused ? 'paused' : '']"
						:style="{'color': bullet.paused ? '#fff' : bullet.color, 'line-height': tunnel.height*2 + 'rpx', 'animation-duration': bullet.duration + 's', 'animation-play-state': bullet.paused ? 'paused' : 'running'}"
						@animationend="onAnimationend" @tap="onTapBullet">
						<image class="bullet-item_img" v-if="bullet.image && bullet.image.head"
							:style="{'width': bullet.image.head.width + 'rpx', 'height': bullet.image.head.height + 'rpx'}"
							mode="aspectFill" :src="bullet.image.head.src"></image>
						<view class="bullet-item_text"
							:style="{'margin':'0 ' + (bullet.image && bullet.image.gap || 0) + 'rpx', opacity: 1}">
							<text>{{bullet.content}}</text>
						</view>
					</view>
				</block>
			</view>
		</block>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				fontSize: 10, // 字体大小,单位px
				width: 375, // 弹幕区域宽度
				height: 80, // 弹幕区域高度
				duration: 15, // 弹幕动画时长
				lineHeight: 3, // 弹幕行高
				padding: [0, 0, 0, 0], // 弹幕区四周留白
				alpha: 1, // 全局透明度
				font: '10px PingFang SC', // 全局字体
				range: [0, 1], // 弹幕显示的垂直范围,支持两个值。[0,1]表示弹幕整个随机分布,
				tunnelShow: false, // 显示轨道线
				tunnelMaxNum: 200, // 轨道最大缓冲长度
				maxLength: 5000, // 弹幕最大字节长度,汉字算双字节
				safeGap: 20, // 发送时的安全间隔
				enableTap: false, // 点击弹幕停止动画高亮显示
				tunnelHeight: 0,
				tunnelNum: 0,
				tunnels: [],
				idleTunnels: null,
				enableTunnels: {},
				distance: 1500, // 移动距离, 单位px
				systemInfo: {},
				danmuList: []
			};
		},
		methods: {
			init() {
				this.fontSize = this.getFontSize(this.font)
				this.idleTunnels = new Set()
				this.enableTunnels = new Set()
				this.tunnels = []
				this.availableHeight = (this.height - this.padding[0] - this.padding[2])
				this.tunnelHeight = this.fontSize * this.lineHeight
				// 轨道行数 = 弹幕区域高度/(单个弹幕高度+下边距)
				this.tunnelNum = Math.floor(this.availableHeight / (this.tunnelHeight + 15))
				// tunnel(轨道)
				class Tunnel {
					constructor(opt = {}) {
						const defaultTunnelOpt = {
							tunnelId: 0,
							height: 0, // 轨道高度
							width: 0, // 轨道宽度
							safeGap: 4, // 相邻弹幕安全间隔
							maxNum: 10, // 缓冲队列长度
							bullets: [], // 弹幕
							last: -1, // 上一条发送的弹幕序号
							bulletStatus: [], // 0 空闲,1 占用中
							disabled: false, // 禁用中
							sending: false, // 弹幕正在发送
						}
						Object.assign(this, defaultTunnelOpt, opt)
						this.bulletStatus = new Array(this.maxNum).fill(0)
						class Bullet {
							constructor(opt = {}) {
								this.bulletId = opt.bulletId
							}

							/**
							 * image 结构
							 * {
							 *   head: {src, width, height},
							 *   gap: 4 // 图片与文本间隔
							 * }
							 */
							addContent(opt = {}) {
								const defaultBulletOpt = {
									duration: 0, // 动画时长
									passtime: 0, // 弹幕穿越右边界耗时
									content: '', // 文本
									color: '#000000', // 默认黑色
									width: 0, // 弹幕宽度
									height: 0, // 弹幕高度
									image: {}, // 图片
									paused: false // 是否暂停
								}
								Object.assign(this, defaultBulletOpt, opt)
							}

							removeContent() {
								this.addContent({})
							}
						}
						for (let i = 0; i < this.maxNum; i++) {
							this.bullets.push(new Bullet({
								bulletId: i,
							}))
						}
					}

					disable() {
						this.disabled = true
						this.last = -1
						this.sending = false
						this.bulletStatus = new Array(this.maxNum).fill(1)
						this.bullets.forEach(bullet => bullet.removeContent())
					}

					enable() {
						if (this.disabled) {
							this.bulletStatus = new Array(this.maxNum).fill(0)
						}
						this.disabled = false
					}

					clear() {
						this.last = -1
						this.sending = false
						this.bulletStatus = new Array(this.maxNum).fill(0)
						this.bullets.forEach(bullet => bullet.removeContent())
					}

					getIdleBulletIdx() {
						return this.bulletStatus.indexOf(0)
					}

					getIdleBulletNum() {
						let count = 0
						this.bulletStatus.forEach(status => {
							if (status === 0) count++
						})
						return count
					}

					addBullet(opt) {
						if (this.disabled) return
						const idx = this.getIdleBulletIdx()
						if (idx >= 0) {
							this.bulletStatus[idx] = 1
							this.bullets[idx].addContent(opt)
						}
					}

					removeBullet(bulletId) {
						if (this.disabled) return
						this.bulletStatus[bulletId] = 0
						const bullet = this.bullets[bulletId]
						bullet.removeContent()
					}
				}
				for (let i = 0; i < this.tunnelNum; i++) {
					this.idleTunnels.add(i) // 空闲的轨道id集合
					this.enableTunnels.add(i) // 可用的轨道id集合
					this.tunnels.push(new Tunnel({ // 轨道集合
						width: this.width,
						height: this.tunnelHeight,
						safeGap: this.safeGap,
						maxNum: this.tunnelMaxNum,
						tunnelId: i,
					}))
				}
				// 筛选符合范围的轨道
				this.setRange()
			},
			resize() {
				const query = uni.createSelectorQuery().in(this)
				query.select('.barrage-area').boundingClientRect((res) => {
					res = res || {}
					let systemInfo = uni.getSystemInfoSync()
					this.systemInfo = systemInfo || {}
					this.width = res.width || systemInfo.windowWidth
					this.height = res.height || 300
					this.last = -1
					this.$emit('reloadDanmu')
				}).exec()
			},
			// 设置显示范围 range: [0,1]
			setRange(range) {
				range = range || this.range
				const top = range[0] * this.tunnelNum
				const bottom = range[1] * this.tunnelNum
				// 释放符合要求的轨道
				// 找到目前空闲的轨道
				const idleTunnels = new Set()
				const enableTunnels = new Set()
				this.tunnels.forEach((tunnel, tunnelId) => {
					if (tunnelId >= top && tunnelId < bottom) {
						const disabled = tunnel.disabled
						tunnel.enable()
						enableTunnels.add(tunnelId)

						if (disabled || this.idleTunnels.has(tunnelId)) {
							idleTunnels.add(tunnelId)
						}
					} else {
						tunnel.disable()
					}
				})
				this.idleTunnels = idleTunnels
				this.enableTunnels = enableTunnels
				this.range = range
			},
			setFont(font) {
				this.font = font
			},
			setAlpha(alpha) {
				if (typeof alpha !== 'number') return
				this.alpha = alpha
			},
			setDuration(duration) {
				if (typeof duration !== 'number') return
				this.duration = duration
				this.clear()
			},
			// 开启弹幕
			open() {
				this._isActive = true
			},
			// 关闭弹幕,清除所有数据
			close(cb) {
				this._isActive = false
				this.clear(cb)
			},
			clear(cb) {
				this.tunnels.forEach(tunnel => tunnel.clear())
				this.idleTunnels = new Set(this.enableTunnels)
				if (typeof cb === 'function') {
					cb()
				}
			},
			// 添加一批弹幕,轨道满时会被丢弃
			addData(data = []) {
				if (!this._isActive || !data || !data.length) return
				data.forEach((item, index) => {
					item.timestampt = new Date().getTime()
					item.content = item.content || ''
					item.content = this.substring(item.content, this.maxLength)
					if (!item.width) {
						// 一个弹幕总长度=头像(包含边框)+文本+内边距+外边距
						item.width = (44 + 4) + item.content.length * this.fontSize * 2 + (8 + 20) + 60
						item.width = Math.ceil(((this.systemInfo.windowWidth || 375) / 375) * (item.width / 2))
					}
					this.addBullet2Tunnel(item, index)
				})

				// 更新弹幕
				this.updateBullets()
			},
			// 添加至轨道
			addBullet2Tunnel(opt = {}, index) {
				const tunnel = this.getIdleTunnel(index)
				if (tunnel === null) return

				const tunnelId = tunnel.tunnelId
				tunnel.addBullet(opt)
				if (tunnel.getIdleBulletNum() === 0) {
					this.idleTunnels.delete(tunnelId)
				}
			},
			updateBullets() {
				if (!this.tunnels || !this.tunnels.length) {
					return
				}
				this.tunnels.map((a) => {
					a.batchTime = 0 // 通过一批弹幕花费(即一次addData添加的所有弹幕)的时间
					a.lastBulletIndex = a.lastBulletIndex >= 0 ? a.lastBulletIndex : -1 // 轨道最后通过的弹幕下标
					a.bullets && a.bullets.map((b, bIndex) => {
						if ((a.lastBulletIndex === -1 || bIndex > a.lastBulletIndex) && b.content) {
							a.lastBulletIndex = bIndex
							const duration = this.distance * this.duration / (this.distance + b.width)
							const passDistance = b.width + a.safeGap
							// 等上一条通过右边界
							b.passtime1 = Math.ceil(passDistance * this.duration * 1000 / this.distance)
							a.batchTime += b.passtime1
						}
					})
					this.tunnelAnimate(a)
				})
				let list = JSON.parse(JSON.stringify(this.tunnels))
				list.sort((a, b) => {
					return b.batchTime - a.batchTime
				})
				let lastBullet = list[0].bullets[list[0].lastBulletIndex]
				// 最后一条弹幕通过屏幕的时间
				let lastPassTime = list[0].batchTime + Math.ceil((this.width) * this.duration * 1000 / this.distance)
				console.log('最后一条弹幕通过屏幕的时间:', lastPassTime)
				let reloadDanmuTimer = setTimeout(() => {
					// 轨道已满,重置轨道并重新加载弹幕
					if (!this.idleTunnels || this.idleTunnels.size === 0) {
						this.last = -1
						this.$emit('reloadDanmu')
					} else {
						this.$emit('reloadDanmu', 'addDanmu')
					}
					clearTimeout(reloadDanmuTimer)
				}, lastPassTime)
			},
			tunnelAnimate(tunnel) {
				if (tunnel.disabled || tunnel.sending) return

				const next = (tunnel.last + 1) % tunnel.maxNum
				const bullet = tunnel.bullets[next]

				if (!bullet) return

				if (bullet.content || bullet.image) {
					tunnel.sending = true
					tunnel.last = next
					const duration = this.distance * this.duration / (this.distance + bullet.width)
					const passDistance = bullet.width + tunnel.safeGap
					bullet.duration = this.duration
					// 等上一条通过右边界
					bullet.passtime = Math.ceil(passDistance * bullet.duration * 1000 / this.distance)
					let sendTimer = setTimeout(() => {
						tunnel.sending = false
						this.tunnelAnimate(tunnel)
						clearTimeout(sendTimer)
					}, bullet.passtime)
				}
			},
			// 从还有余量的轨道中随机挑选一个
			getIdleTunnel(addIndex) {
				if (!this.idleTunnels || this.idleTunnels.size === 0) return null
				const idleTunnels = Array.from(this.idleTunnels)
				let index = -1
				if (this.tunnelNum == 2 && (addIndex || addIndex === 0)) { // 只有两个轨道的情况下,优先手动分发轨道
					index = addIndex % 2 === 0 ? 0 : 1
				}
				if (index === -1 || (!idleTunnels[index] && idleTunnels[index] !== 0)) { // 随机选轨道
					index = this.getRandom(idleTunnels.length)
				}
				return this.tunnels[idleTunnels[index]]
			},
			animationend(opt) {
				const {
					tunnelId,
					bulletId
				} = opt
				const tunnel = this.tunnels[tunnelId]
				const bullet = tunnel && tunnel.bullets && tunnel.bullets[bulletId]

				if (!tunnel || !bullet) return

				tunnel.removeBullet(bulletId)
				this.idleTunnels.add(tunnelId)
			},
			tapBullet(opt) {
				if (!this.enableTap) return

				const {
					tunnelId,
					bulletId
				} = opt
				const tunnel = this.tunnels[tunnelId]
				const bullet = tunnel.bullets[bulletId]
				bullet.paused = !bullet.paused
			},
			// 初始化弹幕组件数据
			getBarrageInstance(opt) {
				for (let key in opt) {
					this[key] = opt[key]
				}
				const query = uni.createSelectorQuery().in(this)
				query.select('.barrage-area').boundingClientRect((res) => {
					res = res || {}
					let systemInfo = uni.getSystemInfoSync()
					this.systemInfo = systemInfo || {}
					this.width = res.width || systemInfo.windowWidth
					this.height = res.height || 80
					this.init()
					this.open()
					this.addData(this.danmuList)
				}).exec()
			},
			onAnimationend(e) {
				const {
					tunnelid,
					bulletid
				} = e.currentTarget.dataset
				this.animationend({
					tunnelId: tunnelid,
					bulletId: bulletid
				})
			},
			onTapBullet(e) {
				const {
					tunnelid,
					bulletid
				} = e.currentTarget.dataset
				this.tapBullet({
					tunnelId: tunnelid,
					bulletId: bulletid
				})
			},
			// 获取字节长度,中文算2个字节
			getStrLen(str) {
				// eslint-disable-next-line no-control-regex
				return str.replace(/[^\x00-\xff]/g, 'aa').length
			},
			// 截取指定字节长度的子串
			substring(str, n) {
				if (!str) return ''

				const len = this.getStrLen(str)
				if (n >= len) return str

				let l = 0
				let result = ''
				for (let i = 0; i < str.length; i++) {
					const ch = str.charAt(i)
					// eslint-disable-next-line no-control-regex
					l = /[^\x00-\xff]/i.test(ch) ? l + 2 : l + 1
					result += ch
					if (l >= n) break
				}
				return result
			},
			getRandom(max = 10, min = 0) {
				return Math.floor(Math.random() * (max - min) + min)
			},
			getFontSize(font) {
				const reg = /(\d+)(px)/i
				const match = font.match(reg)
				return (match && match[1]) || 10
			},
		}
	}
</script>

<style scoped>
	.barrage-area {
		position: relative;
		box-sizing: border-box;
		width: 100%;
		height: 100%;
		z-index: 2;
		pointer-events: auto;
		overflow-x: hidden;
	}

	.barrage-tunnel {
		box-sizing: border-box;
		position: relative;
		display: flex;
		align-items: center;
		border-top: 1px solid #CCB24D;
		width: 100%;
		margin-bottom: 30rpx;
	}

	.tunnel-tips {
		display: inline-block;
		margin-left: 60px;
	}

	.bullet-item {
		position: absolute;
		display: flex;
		align-items: center;
		top: 0;
		left: 100%;
		white-space: nowrap;
		background: rgba(0, 0, 0, 0.3);
		border-radius: 80rpx;
		padding: 0 20rpx 0 0;
	}

	.bullet-item.paused {
		background: #000;
		opacity: 0.6;
		padding: 0 10px;
		z-index: 2;
	}

	.bullet-item_img {
		max-height: 100%;
		border-radius: 50%;
		border: 2px solid #FFFFFF;
	}

	.bullet-item_text {
		display: inline-block;
		margin: 0;
	}

	.bullet-move {
		animation: 0s linear slidein
	}

	@keyframes slidein {
		0% {
			transform: translate3d(0, 0, 0)
		}

		100% {
			transform: translate3d(-1500px, 0, 0)
		}
	}
</style>

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

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

相关文章

linux 7.6安装mysql 8.0步骤如下

linux 7.6安装mysql 8.0步骤如下&#xff1a; 注意&#xff1a;在导入密钥的时候这个不行&#xff0c;可更换为 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023

GEE云计算、多源遥感、高光谱遥感技术蓝碳储量估算;红树林植被指数计算及提取

大气温室气体浓度不断增加&#xff0c;导致气候变暖加剧&#xff0c;随之会引发一系列气象、生态和环境灾害。如何降低温室气体浓度和应对气候变化已成为全球关注的焦点。海洋是地球上最大的“碳库”,“蓝碳”即海洋活动以及海洋生物&#xff08;特别是红树林、盐沼和海草&…

头部固定的响应式jQuery表格插件

jquery.fixme是一款头部固定的响应式jQuery表格插件。该表格通过jQuery来构造固定的表格头效果&#xff0c;在页面向下滚动时&#xff0c;表格头固定在页面的顶部。 在线预览 下载 使用方法 HTML结构 该表格的HTML结构使用标准的HTML表格的结构&#xff1a; <table clas…

系统思考—冰山模型

“卓越不是因机遇而生&#xff0c;而是智慧的选择与用心的承诺。”—— 亚里士多德 卓越&#xff0c;从来不是一次性行为&#xff0c;而是一种习惯。正如我们在日常辅导中常提醒自己&#xff1a;行为的背后&#xff0c;隐藏着选择的逻辑&#xff0c;而选择的根源&#xff0c;源…

基于aspose.words组件的word bytes转pdf bytes,去除水印和解决linux中文乱码问题

详情见 https://preferdoor.top/archives/ji-yu-aspose.wordszu-jian-de-word-byteszhuan-pdf-bytes

文档大师:打造一站式 Word 报告解决方案1

前言 在政府、医院、银行、财务以及销售等领域&#xff0c;常常需要创建各种报告文件来展开工作汇报&#xff0c;譬如季度销售报告、年度总结报告、体检报告和保险合同等。在没有报表工具支持之前&#xff0c;这类报告主要通过 Word 制作&#xff0c;费时费力且难以维护&#…

不安全物联网的轻量级加密:综述

Abstract 本文综述了针对物联网&#xff08;IoT&#xff09;的轻量级加密解决方案。这项综述全面覆盖了从轻量级加密方案到不同类型分组密码的比较等多个方面。同时&#xff0c;还对硬件与软件解决方案之间的比较进行了讨论&#xff0c;并分析了当前最受信赖且研究最深入的分组…

实现某海外大型车企(T)Cabin Wi-Fi 需求的概述 - 4

大家好&#xff0c;我是Q&#xff0c;邮箱&#xff1a;1042484520qq.com。 今天我们在上几讲的基础上再扩展下 Cabin Wi-Fi 的功能需求&#xff0c;讲讲如何使能 5G TCU Wi-Fi STA Bridge 模式。 参考&#xff1a; 实现某海外大型车企&#xff08;T&#xff09;Cabin Wi-Fi 需求…

当视觉提示调优遇到无源领域自适应语义分割

首先将源预训练骨干与冻结参数分为多个阶段&#xff0c;并提出了一个轻量级的提示适配器&#xff0c;用于渐进地将信息性知识编码为提示&#xff0c;并增强相邻骨干阶段之间目标特征的泛化。 同时&#xff0c;设计了一种具有多尺度一致性损失的新型自适应伪标签校正策略&#x…

40.2 预聚合和prometheus-record使用

本节重点介绍 : downsample降采样可以降低查询数据量 prometheus原生不支持downsample 实时查询/聚合 VS 预查询/聚合的优缺点 实时查询/聚合条件随意组合&#xff0c;性能差预查询/聚合 性能好&#xff0c;聚合条件需要提前定义 prometheus的预查询/聚合配置举例 downsample…

Docker安装GPUStack详细教程

目录 前置条件 安装Nvidia Container Tooikit 前置条件 DockerNvidia Container Toolkit 安装Nvidia Container Tooikit 1.安装必要的包 # 更新系统 sudo apt update && sudo apt upgrade -y# 安装 CUDA 相关包 sudo apt-get install -y cuda-drivers nvidia-cuda-…

Intent--组件通信

组件通信1 获取子活动的返回值 创建Activity时实现自动注册&#xff01;【Activity必须要注册才能使用】 默认 LinearLayout 布局&#xff0c;注意 xml 中约束布局的使用&#xff1b; 若需要更改 线性布局 只需要将标签更改为 LinearLayout 即可&#xff0c;记得 设置线性布局…

overleaf中文生僻字显示不正确,显示双线F

我是不想换全文字体的&#xff0c;只是一个生僻字显示不出来&#xff0c;就想要像word一样&#xff0c;把这个生僻字用包含这个生僻字的字体来显示就好了。 解决步骤&#xff1a; 1、使用如下宏包&#xff1a; \usepackage{xeCJK} %声明宏包&#xff0c;主要用于支持在XeTeX…

info There appears to be trouble with your network connection. Retrying

这个错误信息表明你在使用包管理器安装项目依赖时遇到了网络连接问题。 可能的解决方法&#xff1a; 检查当前node.js版本是否过低。 建议使用当前长期支持版本 yarn的淘宝镜像&#xff1a;yarn的淘宝镜像-CSDN博客 nvm常用命令:NVM常用命令-CSDN博客 下载 | Node.js 中文…

使用 HTML 和 CSS 实现绚丽的节日烟花效果

文章目录 1. 效果预览2. 核心技术栈3. 核心代码解读3.1 HTML结构3.2 霓虹文字的CSS样式3.2.1 核心样式代码3.2.2 动画效果 3.3 JavaScript 的烟花效果实现3.3.1 烟花上升3.3.2 粒子爆炸 4. 用户交互5. 运行步骤总结 1. 效果预览 打开后输入文本的展示内容 用户点击页面后播放…

金仓数据库之巡查KCP-客户端验证

巡查 查看KES 服务器的时区 show timezone;查看KES 服务器的时间 select now;查看KES 无故障运行时长 select now-sys_postmaster_start_time as uptime;查看数据库占用的磁盘空间 查看数据库占用的磁盘空间 select sys_database_size(current_database)/1024/1024; sele…

二分和离散化

为什么把二分和离散化放一起&#xff1a;因为离散化其实是一种二分整数的过程。 二分 相信大家都接触过二分查找&#xff08;折半查找&#xff09;&#xff0c;这就是二分的思想。 二分通过每次舍弃一半并不存在答案的区间&#xff0c;进而快速锁定要求的答案&#xff08;二…

OpenCV-Python实战(9)——滤波降噪

一、均值滤波器 cv2.blur() img cv2.blur(src*,ksize*,anchor*,borderType*)img&#xff1a;目标图像。 src&#xff1a;原始图像。 ksize&#xff1a;滤波核大小&#xff0c;&#xff08;width&#xff0c;height&#xff09;。 anchor&#xff1a;滤波核锚点&#xff0c…

Java MySQL 连接

Java MySQL 连接 本章节我们为大家介绍 Java 如何使用 使用 JDBC 连接 MySQL 数据库。 Java 连接 MySQL 需要驱动包&#xff0c;最新版下载地址为&#xff1a;http://dev.mysql.com/downloads/connector/j/&#xff0c;解压后得到 jar 库文件&#xff0c;然后在对应的项目中导…

【二叉树遍历 Java版】二叉树的前中后序遍历and层次遍历

二叉树的前中后序遍历and层次遍历 深度优先遍历题目链接递归前序遍历中序遍历后序遍历 迭代前序遍历后序遍历中序遍历 统一迭代前序遍历后序遍历中序遍历 广度优先遍历102. 二叉树的层序遍历107. 二叉树的层序遍历 II637. 二叉树的层平均值199. 二叉树的右视图 深度优先遍历 深…