当前位置: 首页 > news >正文

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.lengththis.danmuList = listthis.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, // 字体大小,单位pxwidth: 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, // 移动距离, 单位pxsystemInfo: {},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 = truethis.last = -1this.sending = falsethis.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 = -1this.sending = falsethis.bulletStatus = new Array(this.maxNum).fill(0)this.bullets.forEach(bullet => bullet.removeContent())}getIdleBulletIdx() {return this.bulletStatus.indexOf(0)}getIdleBulletNum() {let count = 0this.bulletStatus.forEach(status => {if (status === 0) count++})return count}addBullet(opt) {if (this.disabled) returnconst idx = this.getIdleBulletIdx()if (idx >= 0) {this.bulletStatus[idx] = 1this.bullets[idx].addContent(opt)}}removeBullet(bulletId) {if (this.disabled) returnthis.bulletStatus[bulletId] = 0const 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.windowWidththis.height = res.height || 300this.last = -1this.$emit('reloadDanmu')}).exec()},// 设置显示范围 range: [0,1]setRange(range) {range = range || this.rangeconst top = range[0] * this.tunnelNumconst 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.disabledtunnel.enable()enableTunnels.add(tunnelId)if (disabled || this.idleTunnels.has(tunnelId)) {idleTunnels.add(tunnelId)}} else {tunnel.disable()}})this.idleTunnels = idleTunnelsthis.enableTunnels = enableTunnelsthis.range = range},setFont(font) {this.font = font},setAlpha(alpha) {if (typeof alpha !== 'number') returnthis.alpha = alpha},setDuration(duration) {if (typeof duration !== 'number') returnthis.duration = durationthis.clear()},// 开启弹幕open() {this._isActive = true},// 关闭弹幕,清除所有数据close(cb) {this._isActive = falsethis.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) returndata.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) + 60item.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) returnconst tunnelId = tunnel.tunnelIdtunnel.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 = bIndexconst 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 = -1this.$emit('reloadDanmu')} else {this.$emit('reloadDanmu', 'addDanmu')}clearTimeout(reloadDanmuTimer)}, lastPassTime)},tunnelAnimate(tunnel) {if (tunnel.disabled || tunnel.sending) returnconst next = (tunnel.last + 1) % tunnel.maxNumconst bullet = tunnel.bullets[next]if (!bullet) returnif (bullet.content || bullet.image) {tunnel.sending = truetunnel.last = nextconst duration = this.distance * this.duration / (this.distance + bullet.width)const passDistance = bullet.width + tunnel.safeGapbullet.duration = this.duration// 等上一条通过右边界bullet.passtime = Math.ceil(passDistance * bullet.duration * 1000 / this.distance)let sendTimer = setTimeout(() => {tunnel.sending = falsethis.tunnelAnimate(tunnel)clearTimeout(sendTimer)}, bullet.passtime)}},// 从还有余量的轨道中随机挑选一个getIdleTunnel(addIndex) {if (!this.idleTunnels || this.idleTunnels.size === 0) return nullconst idleTunnels = Array.from(this.idleTunnels)let index = -1if (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} = optconst tunnel = this.tunnels[tunnelId]const bullet = tunnel && tunnel.bullets && tunnel.bullets[bulletId]if (!tunnel || !bullet) returntunnel.removeBullet(bulletId)this.idleTunnels.add(tunnelId)},tapBullet(opt) {if (!this.enableTap) returnconst {tunnelId,bulletId} = optconst 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.windowWidththis.height = res.height || 80this.init()this.open()this.addData(this.danmuList)}).exec()},onAnimationend(e) {const {tunnelid,bulletid} = e.currentTarget.datasetthis.animationend({tunnelId: tunnelid,bulletId: bulletid})},onTapBullet(e) {const {tunnelid,bulletid} = e.currentTarget.datasetthis.tapBullet({tunnelId: tunnelid,bulletId: bulletid})},// 获取字节长度,中文算2个字节getStrLen(str) {// eslint-disable-next-line no-control-regexreturn str.replace(/[^\x00-\xff]/g, 'aa').length},// 截取指定字节长度的子串substring(str, n) {if (!str) return ''const len = this.getStrLen(str)if (n >= len) return strlet l = 0let result = ''for (let i = 0; i < str.length; i++) {const ch = str.charAt(i)// eslint-disable-next-line no-control-regexl = /[^\x00-\xff]/i.test(ch) ? l + 2 : l + 1result += chif (l >= n) break}return result},getRandom(max = 10, min = 0) {return Math.floor(Math.random() * (max - min) + min)},getFontSize(font) {const reg = /(\d+)(px)/iconst 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>

相关文章:

uniapp小程序实现弹幕不重叠

uniapp小程序实现弹幕不重叠 1、在父组件中引入弹幕组件 <template><!-- 弹幕 --><barrage ref"barrage" class"barrage-content" reloadDanmu"reloadDanmu"></barrage> </template> <script>import barr…...

快速排序学习优化

首先&#xff0c;上图。 ‘’’ cpp int partSort(int *a ,int left,int right) {int keyi left; //做左侧基准while(left<right){while(left<right && a[right]>a[keyi]){right--;}while(left<right && a[left]<a[keyi]){left;}swap(a[left…...

微信流量主挑战:三天25用户!功能未完善?(新纪元4)

&#x1f389;【小程序上线第三天&#xff01;突破25用户大关&#xff01;】&#x1f389; 嘿&#xff0c;大家好&#xff01;今天是我们小程序上线的第三天&#xff0c;我们的用户量已经突破了25个&#xff01;昨天还是16个&#xff0c;今天一觉醒来竟然有25个&#xff01;这涨…...

jetson 无显示器配置WIFI

我使用的 jetpack 版本是 6.1&#xff0c;发现自带 NetworkManager 软件包&#xff0c;此软件包包含一个守护程序、一个命令行界面&#xff08;nmcli&#xff09;和一个基于 curses 的界面&#xff08;nmtui&#xff09;。 可以使用 nmcli 命令配置wifi&#xff0c;nmcli 示例…...

SpringCloudAlibaba实战入门之路由网关Gateway断言(十二)

上一节课中我们初步讲解了网关的基本概念、基本功能,并且带大家实战体验了一下网关的初步效果,这节课我们继续学习关于网关的一些更高级有用功能,比如本篇文章的断言。 一、网关主要组成部分 上图中是核心的流程图,最主要的就是Route、Predicates 和 Filters 作用于特定路…...

【ES6复习笔记】ES6的模块化(18)

模块化的概念 模块化是指将一个复杂的系统分解为多个模块&#xff0c;每个模块完成一个特定的功能&#xff0c;模块之间通过接口进行通信。模块化的目的是提高代码的可读性、可维护性和可重用性。 模块化规范产品&#xff0c; ES6 之前的模块化规范有&#xff1a; CommonJS …...

兰亭妙微:专注医疗 UI 设计,点亮数字化医疗新视界

医疗行业界面解决方案以医患使用者为中心&#xff0c;遵循行业使用习惯和表达方式&#xff0c;优化使用流程、设计简洁、人性化的操作界面&#xff0c;采用插画、三维动画、微动效的创作方法&#xff0c;让用户感受到愉悦易用美观的使用体验。蓝蓝设计与知名企业合作项目有&…...

c# 线程 AutoResetEvent 的Set()函数多次调用

本文部分内容摘自ChatGPT 在 C# 中&#xff0c;AutoResetEvent 是一种用于线程同步的机制&#xff0c;它的行为类似于一个信号量&#xff0c;主要用于在多线程环境中发出信号并控制线程的执行。AutoResetEvent 的主要特点是每当调用 Set() 方法时&#xff0c;信号会被设置&…...

汽车行业的MES系统方案(附案例资料合集)

针对汽车行业的MES系统方案&#xff0c;以下是一些关键点和实施案例&#xff1a; 核心功能&#xff1a; 实时监控&#xff1a;MES系统通过传感器和物联网技术实时监控生产线上的每一个环节&#xff0c;确保信息的及时传递。数据分析&#xff1a;系统对收集的数据进行深度分析&a…...

基于监督学习的神经网络控制算法详细介绍和例程

基于监督学习的神经网络控制算法通常用于对已有数据进行训练&#xff0c;以学习输入与输出之间的映射关系。下面我将详细介绍这种算法的原理和流程&#xff0c;并提供一个简单的例程&#xff1a; 算法原理&#xff1a; 输入&#xff1a;给定一组已知的输入信号和对应的输出控制…...

springMVC-请求响应

springmvc——一 站式web框架&#xff0c;核心是处理http请求响应。 前后端分离&#xff1a;需要序列化&#xff0c;服务端把数据序列化成字符串或者流给前端&#xff0c;前端又把json转成对象&#xff0c;前端的叫反序列化。前端把数据序列化转成字符串给服务器&#xff0c;服…...

数据交易和联邦学习的背景下的安全属性

数据交易和联邦学习的背景下的安全属性 在数据交易和联邦学习的背景下,安全属性对于保护数据隐私、确保系统可靠性和维护交易公平性至关重要。以下将分析文章中涉及的安全属性以及分析这些属性的目的。 涉及的安全属性 双向认证:文章虽未明确提及传统意义上的双向认证机制,…...

顶顶通呼叫中心中间件mod_cti模块安全增强,预防盗打风险(mod_cti基于FreeSWITCH)

文章目录 前言联系我们mod_cti版本支持安全加强说明 前言 FreeSWITCH暴露在公网最大的风险就是被不法之人盗打 出现盗打的主要原因以下几点&#xff1a; 分机密码太简单或者密码泄露了拨号方案配置不合理sofia配置错误 所以我们给顶顶通呼叫中心中间件添加了安全加强功能&am…...

Datawhale-AI冬令营二期

目录 一、番茄时钟&#xff08;1&#xff09;输入Prompt&#xff08;2&#xff09;创建 HTML 文件解析1&#xff1a;HTML结构解析2&#xff1a;计时器内容解析3&#xff1a;按钮区域解析4&#xff1a;脚本引用 &#xff08;3&#xff09;使用JavaScript实现时钟功能解析1&#…...

Python的秘密基地--[章节7] Python 并发与多线程编程

第7章&#xff1a;Python 并发与多线程编程 随着计算机硬件的发展&#xff0c;多核处理器已经成为主流。为了更好地利用多核资源&#xff0c;提高程序的运行效率&#xff0c;Python 提供了并发&#xff08;Concurrency&#xff09;和并行&#xff08;Parallelism&#xff09;编…...

每天五分钟机器学习:凸函数

一、凸函数的定义:何为“凸”? 在数学上,凸函数的概念源于几何直观——想象一个平面上的曲线,如果在这条曲线上的任意两点之间连线段总是位于曲线的下方(或恰好与曲线重合),则这条曲线所对应的函数即为凸函数。更正式地,对于定义在实数集(或某个子集)上的函数f(x),…...

Merry Christmas HTML

简单分享 Merry Christmas HTML 设计的核心代码 HTML: <body class"card"> <div class"dialog"><div class"dialog-in"><div class"dialog-msg"><div class"heading">Youve got a post card!…...

JavaScript甘特图 dhtmlx-gantt

背景 需求是在后台中&#xff0c;需要用甘特图去展示管理任务相关视图&#xff0c;并且不用依赖vue&#xff0c;兼容JavaScript原生开发。最终使用dhtmlx-gantt&#xff0c;一个半开源的库&#xff0c;基础功能免费&#xff0c;更多功能付费。 甘特图需求如图&#xff1a; 调…...

阿里云-将旧服务器数据与配置完全迁移至新服务器

文章目录 一&#xff1a;创建镜像二&#xff1a;将创建好的镜像复制到新服务器所在的目标地域&#xff08;如果新服务器与镜像在同一地域就不用进行这一操作&#xff09;三&#xff1a;将镜像配置到新服务器上四&#xff1a;导出安全组&#xff08;如果新服务器与旧服务器使用同…...

以EM算法为例介绍坐标上升(Coordinate Ascent)算法:中英双语

中文版 什么是 Coordinate Ascent 算法&#xff1f; Coordinate Ascent&#xff08;坐标上升&#xff09;是一种优化算法&#xff0c;它通过在每次迭代时优化一个变量&#xff08;或一个坐标&#xff09;&#xff0c;并保持其他变量不变&#xff0c;逐步逼近最优解。与坐标下…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...