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

H5: 使用Web Audio API播放音乐

简介

记录关于自己使用 Web Audio API 的 AudioContext 播放音乐的知识点。

需求分析

在这里插入图片描述

1.列表展示音乐;
2.上/下一首、播放/暂停/续播;
3.播放模式切换:循环播放、单曲循环、随机播放;
4.播放状态显示:当前播放的音乐名、播放时间、总时间、进度条效果;
5.播放控制器显示在底部区域;
6.支持音量调节;
7.浏览器隐藏、显示的交互后,也能正常有效播放(播放、声音)。

注意

安卓IOS上有不同的兼容性,所以采用了 Web Audio API 的 AudioContext ,兼容性强大(但是截止写文章前,IOS17+版本不支持,没有声音)。

稍微复杂点点的逻辑就是AudioContext与手机系统的关联,可以看看 AudioContext: createMediaElementSource。

在这里插入图片描述

具体实现

test/music/musicPlayer/musics.ts
test/music/musicPlayer/useMusicPlayer.ts
test/music/index.vue

1.test/music/musicPlayer/musics.ts

interface musicItem {title: stringsrc: stringtime: stringmp3Name: string
}
const musicList: musicItem[] = [{title: 'How to Love',src: '',time: '03:39',mp3Name: 'sx_music_HowtoLove_CashCash'},{title: '空空如也',src: '',time: '03:34',mp3Name: 'sx_music_kongkongruye'},{title: '2 Soon',src: '',time: '03:19',mp3Name: 'sx_music_Soon_JonYoung'},{title: '孤勇者',src: '',time: '04:16',mp3Name: 'sx_music_guyongzhe'},{ title: '秒针', src: '', time: '02:58', mp3Name: 'sx_music_miaozhen' },{title: '热爱105˚的你',src: '',time: '03:15',mp3Name: 'sx_music_reai105dudeni'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'}
] // 音乐列表信息export { type musicItem, musicList }

2.test/music/musicPlayer/useMusicPlayer.ts

import { ref, nextTick } from 'vue'
import { musicList } from './musics'enum PlayMode {REPEAT, // 循环播放SINGLE_CYCLE, // 单曲循环RANDOM // 随机播放
}const musicPlayer = ref<HTMLAudioElement | null>()
const musicPlayingIndex = ref(-1) // 播放的音乐的下标
const musicIsPlaying = ref(false) // 是否播放中
const currentTime = ref(0) // 正在播放的音乐时间点
const musicPlayMode = ref(PlayMode.REPEAT) // 播放模式
const progressInterval = 500 // 计时器触发的频率
let defaultVolume = 1 // 音量 0-1
let timer: NodeJS.Timer | null = null // 计时器  ---此处需要在 .eslintrc.js/.cjs 文件中配置 globals: { NodeJS: true }
let source: MediaElementAudioSourceNode | null = null
let audioCtx: AudioContext | null = null
let gainNode: GainNode | null = null
let audioContextAttr: string | null = null
if ('AudioContext' in window) {audioContextAttr = 'AudioContext'
} else if ('webkitAudioContext' in window) {audioContextAttr = 'webkitAudioContext'
}const useMusicPlayer = () => {const _getMusicFile = (mp3Name: string) => {// 此处需要相对路径// vite项目// return new URL(`../../../assets/music/${mp3Name}.mp3`, import.meta.url).href// webpack项目return require(`../../../assets/music/${mp3Name}.mp3`)}/** 设置:音量百分比 0-100 变为 0-1* @param v number 0-100*/const _saveDefaultVolume = (v: number) => {let num = vif (v < 0) {num = 0} else if (v > 100) {num = 100}defaultVolume = num / 100return defaultVolume}/*** 计时器:回调-更新显示-MP3的播放时间*/const _intervalUpdatePlayTime = () => {const player = musicPlayer.valueif (!player) returncurrentTime.value = player.currentTime}/*** 计时器:清除*/const _clearTimer = () => {if (!timer) returnclearInterval(timer)timer = null}/*** 计时器:绑定&开始*/const _startTimer = () => {_clearTimer()timer = setInterval(_intervalUpdatePlayTime, progressInterval)}/*** 方法:取两个值之间的随机数*/const _random = (min = 0, max = 100) => Math.floor(Math.random() * (max - min + 1)) + min/*** 销毁:断开audio与AudioContext之间的链接*/const _destroyConnect = () => {if (source) {source.disconnect()}if (gainNode) {gainNode.disconnect()}if (audioCtx) {audioCtx.close()}source = nullgainNode = nullaudioCtx = nullmusicPlayer.value = null}/*** 音乐:初始化audio与AudioContext的绑定* 目的是为了 IOS 上能调整音量*/const _init = () => {if (!audioContextAttr) return// 先暂停已有的播放pause()// 对已创建的绑定关系进行解绑_destroyConnect()// 若在body中找得到对应的dom,则进行移除const findDom = document.getElementById('musicPlayerAudio') as HTMLAudioElementif (findDom) {findDom.remove()}// 创建audio,加入body中const dom = document.createElement('audio')dom.id = 'musicPlayerAudio'document.body.appendChild(dom)// 给audio绑定播放结束的回调函数dom.onended = onAudioEnded// 创建AudioContext、source、gainNode,进行关联(便于IOS控制音量)const UseAudioContext = (window as any)[audioContextAttr]audioCtx = new UseAudioContext()if (!audioCtx) returnsource = audioCtx.createMediaElementSource(dom)gainNode = audioCtx.createGain()source.connect(gainNode)gainNode.connect(audioCtx.destination)// 设置音量if (defaultVolume === 0) {dom.muted = true} else {dom.muted = false}gainNode.gain.value = defaultVolume// 存储dom,便于后续访问audio对应的属性musicPlayer.value = dom// 若播放控制器的状态未启动,则启动if (audioCtx && audioCtx.state === 'suspended') {audioCtx.resume()}}/*** 音乐:播放器-音量调整*/const setVolume = (volume: number) => {const v = _saveDefaultVolume(volume)const player = musicPlayer.valueif (!player) returnif (v === 0) {player.muted = true} else {player.muted = false}if (!gainNode || !gainNode.gain) returngainNode.gain.value = v}/*** 音乐:播放器-暂停*/const pause = () => {const player = musicPlayer.valueif (!musicIsPlaying.value || !player) {return}musicIsPlaying.value = falseplayer.pause()_clearTimer()}/*** 音乐:播放器-播放*/const playByLast = () => {const player = musicPlayer.valueif (!player || !player.src) returnif (audioCtx && audioCtx.state === 'suspended') {audioCtx.resume()}nextTick(() => {// play触发时,会先自动加载资源player.play().then(() => {musicIsPlaying.value = true_startTimer()})})}/*** 音乐:播放器-播放-通过下标*/const playByIndex = (index: number) => {if (index < 0 || index + 1 > musicList.length) {return}musicIsPlaying.value = false// 重新初始化,便于释放上一个播放器所占用的内存_init()const player = musicPlayer.valueif (!player) {return}// 重置当前播放了的时长currentTime.value = 0// 更新要播放的下标musicPlayingIndex.value = indexif (!musicList[index].src) {// 若资源路径不存在,则进行对应的路径引入musicList[index].src = _getMusicFile(musicList[index].mp3Name)}if (!musicList[index].src) {console.error('find music file failed')return}player.src = musicList[index].srcplayByLast()}/*** 音乐:随机播放*/const randomPlay = () => {const index = _random(0, musicList.length - 1)playByIndex(index)}/*** 音乐:播放器-下一首*/const playNext = () => {if (musicPlayMode.value === PlayMode.RANDOM) {randomPlay()} else {const index: number =musicPlayingIndex.value + 1 === musicList.length ? 0 : musicPlayingIndex.value + 1playByIndex(index)}}/*** 音乐:播放器-上一首*/const playPrev = () => {if (musicPlayMode.value === PlayMode.RANDOM) {randomPlay()} else {const index: number =musicPlayingIndex.value < 1 ? musicList.length - 1 : musicPlayingIndex.value - 1playByIndex(index)}}/*** 回调:播放结束后,下一首播放什么*/const onAudioEnded = () => {switch (musicPlayMode.value) {case PlayMode.REPEAT:playNext()breakcase PlayMode.SINGLE_CYCLE:playByIndex(musicPlayingIndex.value)breakcase PlayMode.RANDOM:randomPlay()breakdefault:break}return true}/** 自动播放音乐 */const startPlayInRoom = () => {// 用户第一次点击时,自动播放音乐const initMusicAutoPlayOnReload = () => {document.removeEventListener('click', initMusicAutoPlayOnReload, true)playByIndex(0)}document.addEventListener('click', initMusicAutoPlayOnReload, true)}return {musicList,musicPlayer,musicPlayingIndex,musicIsPlaying,currentTime,musicPlayMode,setVolume,pause,playByIndex,playByLast,playPrev,playNext,_clearTimer,startPlayInRoom}
}export { PlayMode, useMusicPlayer }

3.test/music/index.vue

<template><div class="music-box"><!-- 音乐列表 --><div class="music-list"><divv-for="(music, index) in musicList":key="index"class="music-item":class="{ 'music-item-active': musicPlayer.musicPlayingIndex.value === index }"@click.stop="switchAudio(index)"><div class="item-left"><div class="item-left-title">{{ music.title }}</div><svgv-if="musicPlayer.musicPlayingIndex.value === index && musicPlayer.musicIsPlaying.value"id="equalizer"width="13px"height="11px"viewBox="0 0 10 7"version="1.1"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="#3994f9"><rectid="bar1"transform="translate(0.500000, 6.000000) rotate(180.000000) translate(-0.500000, -6.000000) "x="0"y="5"width="1"height="2px"></rect><rectid="bar2"transform="translate(3.500000, 4.500000) rotate(180.000000) translate(-3.500000, -4.500000) "x="3"y="2"width="1"height="5"></rect><rectid="bar3"transform="translate(6.500000, 3.500000) rotate(180.000000) translate(-6.500000, -3.500000) "x="6"y="0"width="1"height="7"></rect></g></svg><svgv-else-if="musicPlayer.musicPlayingIndex.value === index"id="equalizer"width="13px"height="11px"viewBox="0 0 10 7"version="1.1"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="#3994f9"><rect x="0" y="5" width="1" height="2px"></rect><rect x="3" y="2" width="1" height="5"></rect><rect x="6" y="0" width="1" height="7"></rect></g></svg></div><div class="item-right">{{ music.time }}</div></div></div><!-- 播放控制 --><div class="music-control"><div class="control-content"><div class="control-content-left"><divclass="music-btn prev"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="prev"/><div:class="['music-btn', musicPlayer.musicIsPlaying.value ? 'pause' : 'play']"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="togglePlayer"/><divclass="music-btn next"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="next"/></div><div class="control-content-center"><div class="center-title">{{ currentMusicTitle || '-' }}</div><div ref="audioProgressWrap" class="center-progress-wrap"><div ref="audioProgress" class="center-progress-wrap-active" /></div><div class="center-time"><div class="center-time-now">{{ formatSecond(musicPlayer.currentTime.value) }}</div><div class="center-time-total">{{ currentMusicTotalTimeStr }}</div></div></div><div class="control-content-right"><divv-if="musicPlayer.musicPlayMode.value === PlayMode.REPEAT"class="music-btn playRepeat"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="nextPlayMode"/><divv-if="musicPlayer.musicPlayMode.value === PlayMode.SINGLE_CYCLE"class="music-btn singleCycle"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="nextPlayMode"/><divv-if="musicPlayer.musicPlayMode.value === PlayMode.RANDOM"class="music-btn playRandom"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="nextPlayMode"/></div></div></div></div>
</template><script setup lang="ts">import { ref, computed, watch } from 'vue'import { PlayMode, useMusicPlayer } from './musicPlayer/useMusicPlayer'import { musicList } from './musicPlayer/musics'const systemSoundMode = ref(true) // 该变量应该在store中,便于设置页面控制全局声音的开启与否const musicPlayer = useMusicPlayer()const audioProgressWrap = ref()const audioProgress = ref()/** 当前播放的音乐名 */const currentMusicTitle = computed(() =>musicPlayer.musicPlayingIndex.value + 1 > 0? musicList[musicPlayer.musicPlayingIndex.value].title: '')/** 当前播放的音乐总时间 */const currentMusicTotalTimeStr = computed(() =>musicPlayer.musicPlayingIndex.value + 1 > 0? musicList[musicPlayer.musicPlayingIndex.value].time: '00:00')/** 操作:切换播放模式 */const nextPlayMode = () => {musicPlayer.musicPlayMode.value = (musicPlayer.musicPlayMode.value + 1) % 3switch (musicPlayer.musicPlayMode.value) {case PlayMode.REPEAT:console.log('循环播放')breakcase PlayMode.RANDOM:console.log('随机播放')breakcase PlayMode.SINGLE_CYCLE:console.log('单曲循环')breakdefault:break}}/** 事件:当点击按钮时的过渡效果 */const onTouchEvent = (event: Event) => {const tg = event.currentTarget as HTMLElementif (!tg) returnif (event.type === 'touchstart') {tg.classList.add('touch')}if (event.type === 'touchend') {tg.classList.remove('touch')}}/** 格式化:秒数=>ss:mm */const formatSecond = (second: number) => {let hourStr = `${Math.floor(second / 60)}`let secondStr = `${Math.ceil(second % 60)}`if (hourStr.length === 1) {hourStr = `0${hourStr}`}if (secondStr.length === 1) {secondStr = `0${secondStr}`}return `${hourStr}:${secondStr}`}/** 操作:播放所选音乐 */const switchAudio = (index: number) => {const player = musicPlayer.musicPlayer.valueif (!systemSoundMode.value) {window.alert('所有声音已关闭')}if (player?.src && player?.src.includes(musicList[index].mp3Name)) {if (musicPlayer.musicIsPlaying.value) {return}musicPlayer.playByLast()} else {musicPlayer.playByIndex(index)}}/** 操作:上一首 */const prev = () => {if (!systemSoundMode.value) {window.alert('所有声音已关闭')}musicPlayer.playPrev()}/** 操作:下一首 */const next = () => {if (!systemSoundMode.value) {window.alert('所有声音已关闭')}musicPlayer.playNext()}/** 操作:播放/暂停 */const togglePlayer = () => {const player = musicPlayer.musicPlayer.valueif (!systemSoundMode.value) {window.alert('所有声音已关闭')}if (musicPlayer.musicIsPlaying.value && player?.src) {// 正在播放,则暂停musicPlayer.pause()} else if (!player?.src) {// 未开始播放,则播放第一首musicPlayer.playByIndex(0)} else {// 暂停了,则继续播放刚才的musicPlayer.playByLast()}}/** 监听:当前播放中的音乐的进度时间=>进度条变化 */watch(() => musicPlayer.currentTime.value,() => {const player = musicPlayer.musicPlayer.valueif (!audioProgressWrap.value || !audioProgress.value || !player) {return}const offsetLeft =(player.currentTime / player.duration) * audioProgressWrap.value.offsetWidthaudioProgress.value.style.width = `${offsetLeft}px`})
</script><style lang="less" scoped>@bottomHeight: 97px;@controlHeight: 63px;@controlBottom: 34px;.music-box {width: 100%;height: 100%;background-color: #141624;position: relative;display: flex;flex-direction: column;}.music-list {flex: 1;overflow-y: auto;scrollbar-width: none;-ms-overflow-style: none;&::-webkit-scrollbar {display: none;}.music-item:nth-of-type(1) {margin-top: 7px;}.music-item {padding: 12px 20px 19px;display: flex;align-items: center;justify-content: space-between;font-size: 14px;font-weight: 500;line-height: 120%;color: #8f9095;.item-left {display: flex;align-items: center;.item-left-title {height: 17px;margin-right: 10px;}#equalizer {position: relative;}#bar1 {animation: bar1 1.2s infinite linear;}#bar2 {animation: bar2 0.8s infinite linear;}#bar3 {animation: bar3 1s infinite linear;}#bar4 {animation: bar4 0.7s infinite linear;}@keyframes bar1 {0% {height: 2px;}50% {height: 7px;}100% {height: 2px;}}@keyframes bar2 {0% {height: 5px;}40% {height: 1px;}80% {height: 7px;}100% {height: 5px;}}@keyframes bar3 {0% {height: 7px;}50% {height: 0;}100% {height: 7px;}}@keyframes bar4 {0% {height: 2px;}50% {height: 7px;}100% {height: 2px;}}}}.music-item-active {.item-left {.item-left-title {color: #3994f9;}}.item-right {color: #3994f9;}}}.music-control {height: @bottomHeight;padding: 0 10px;background-color: #141624;.control-content {height: @controlHeight;border-radius: 7px;background-color: #1b1d2a;display: flex;align-items: center;justify-content: space-between;.control-content-left {display: flex;align-items: center;.prev,.pause,.play {margin-right: 10px;}.next {margin-right: 17px;}}.control-content-center {margin-top: 1px;flex: 1;.center-title {margin-bottom: 5px;line-height: 120%;font-size: 13px;font-weight: 500;color: #fff;}.center-progress-wrap {width: 100%;height: 2px;background-color: #3e404e;.center-progress-wrap-active {width: 0;height: 100%;background-color: #3994f9;}}.center-time {height: 50%;margin-top: 10px;display: flex;justify-content: space-between;align-items: center;.center-time-now,.center-time-total {font-size: 10px;font-weight: 400;line-height: 12px;color: #3994f9;}.center-time-total {color: #8f9095;}}}.control-content-right {padding-left: 10px;}.music-btn {width: 33px;height: 33px;&.prev {background: url('../../assets/images/music/music-prev.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-prev-touch.png');}}&.play {background: url('../../assets/images/music/music-play.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-play-touch.png');}}&.pause {background: url('../../assets/images/music/music-pause.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-pause-touch.png');}}&.next {background: url('../../assets/images/music/music-next.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-next-touch.png');}}&.playRepeat {background: url('../../assets/images/music/music-repeat.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-repeat-touch.png');}}&.singleCycle {background: url('../../assets/images/music/music-single-cycle.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-single-cycle-touch.png');}}&.playRandom {background: url('../../assets/images/music/music-random.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-random-touch.png');}}}}}
</style>

最后

觉得有用的朋友请用你的金手指点一下赞,或者评论留言一起探讨技术!

相关文章:

H5: 使用Web Audio API播放音乐

简介 记录关于自己使用 Web Audio API 的 AudioContext 播放音乐的知识点。 需求分析 1.列表展示音乐&#xff1b; 2.上/下一首、播放/暂停/续播&#xff1b; 3.播放模式切换&#xff1a;循环播放、单曲循环、随机播放&#xff1b; 4.播放状态显示&#xff1a;当前播放的音乐…...

Parasoft C/C++test:汽车网络安全ISO 21434最佳实践

为什么汽车网络安全很重要Why Automotive Cybersecurity Is Important 许多汽车公司向电子道路车辆的转变从根本上改变了整个行业&#xff0c;提高了汽车的互联性和智能性。随着电子汽车变得更加互联和智能&#xff0c;它们也越来越依赖软件来实现车辆操作&#xff0c;驱动更多…...

如何卸载干净 IDEA(图文讲解)windows和Mac教程

大家好&#xff0c;我是sun~ 很多小伙伴会问 Windows / Mac 系统上要怎么彻底卸载 IDEA 呢&#xff1f; 本文通过图片文字&#xff0c;详细讲解具体步骤&#xff1a; 如何卸载干净 IDEA&#xff08;图文讲解&#xff09; Windows1、卸载 IDEA 程序2、注册表清理3、残留清理 M…...

Docker搭建Gitlab

拉取镜像&#xff1a;docker pull gitlab/gitlab-ce创建映射目录&#xff1a; mkdir -p /usr/local/gitlab/config mkdir -p /usr/local/gitlab/data mkdir -p /usr/local/gitlab/logs运行容器&#xff1a; docker run -d -p 443:443 -p 8000:8000 -p 222:22 --name gitlab …...

STM32F4X SDIO(四) SDIO控制器

STM32F4X SDIO&#xff08;四&#xff09; SDIO控制器 STM32F4X SDIO控制器SDIO控制器框图SDIO控制器时钟适配器寄存器FIFO控制单元命令路径数据路径 SDIO寄存器SDIO控制相关寄存器SDIO电源控制寄存器 (SDIO_POWER)SDIO时钟控制寄存器 (SDIO_CLKCR)SDIO_CK相位 SDIO命令响应相关…...

【flink】Task 故障恢复详解以及各重启策略适用场景说明

文章目录 一. 重启策略种类&#xff08;Restart Strategies&#xff09;1. Fixed Delay Restart Strategy2. Failure Rate Restart Strategy3. Fallback Restart Strategy4. No Restart Strategy 二. 故障恢复策略&#xff08;Failover Strategies&#xff09;1. &#xff08;全…...

一个计算机高手的成长3

这是转在茶余的帖子。文中绝大部分技术术语我不懂&#xff0c;所以无资格评论他的技术价值。但文章强烈的逻辑说服力&#xff0c;和通篇流露的进取精神&#xff0c;使我觉得这是篇有价值的帖子&#xff0c;至少值得一读。 就像我开始从MIS转到通信一样&#xff0c;我看过大量通…...

2023应届生能力考试含解析(Java后端开发)——(1)

1.以下代码的循环次数是 ( ) public class Test {public static void main(String[] args) {int i 7;do {System.out.println(--i);--i;} while (i ! 0);System.out.println(i);} } A 0 B 1 C 7 D 无限次 这段代码会导致无限循环的原因是在 do-while 循环中&#…...

Ansible中的任务执行控制

循环 简单循环 {{item}} 迭代变量名称 loop: - value1 - value2 - ... //赋值列表{{item}} //迭代变量名称循环散列或字典列表 - name: create filehosts: host1tasks:- name: file moudleservice:name: "{{ item.name }}"state: "{{…...

利用maven的dependency插件分析工程的依赖

dependency:analyze https://maven.apache.org/plugins/maven-dependency-plugin/analyze-mojo.html 分析项目的依赖&#xff0c;确定哪些&#xff1a;用了并且声明了、用了但没有声明、没有使用但声明了。 dependency:analyze可以单独使用&#xff0c;所以它总是会执行test-…...

【广州华锐互动】VR野外求生技能学习,让你感受真实的冒险之旅!

随着科技的迅速发展&#xff0c;虚拟现实(VR)技术为人们提供了一个全新的、身临其境的探险体验。通过将用户带入一个仿真的、沉浸式的虚拟环境&#xff0c;VR互动体验让人们在安全的氛围中感受到野外探险的乐趣。本文将从视觉呈现、沉浸式体验、交互性和应用范围四个方面&#…...

k8s、调度约束

Kubernetes 是通过 List-Watch **** 的机制进行每个组件的协作&#xff0c;保持数据同步的&#xff0c;每个组件之间的设计实现了解耦 用户是通过 kubectl 根据配置文件&#xff0c;向 APIServer 发送命令&#xff0c;在 Node 节点上面建立 Pod 和 Container。 APIS…...

Redis的介绍,以及Redis的安装(本机windows版,虚拟机Linux版)和Redis常用命令的介绍

目录 一. Redis简介 二. Redis的安装 2.1 Linux版安装 2.2 windows版安装 三. Redis的常用命令 一. Redis简介 Redis是一个开源&#xff08;BSD许可&#xff09;&#xff0c;内存存储的数据结构服务器&#xff0c;可用作数据库&#xff0c;高速缓存和消息队列代理。 它…...

电子器件 MOS管的参数、选型与使用技巧

一、电路符号 MOS管分为 G&#xff08;栅极&#xff09;、S&#xff08;源极&#xff09;、D&#xff08;漏极&#xff09; 三极&#xff0c;在图中 S 极有两条线&#xff0c;D 极只有一条线。 1.1 NMOS 和 PMOS 下图中&#xff0c;左侧是 PMOS&#xff0c;右侧是 NMOS。箭头…...

EtherCAT主站SOEM -- 2 -- SOEM之ethercatbase.h/c文件解析

EtherCAT主站SOEM -- 2 -- SOEM之ethercatbase.h/c文件解析 一 ethercatbase.h/c文件功能预览&#xff1a;二 ethercatbase.h/c 文件的主要函数的作用&#xff1a;2.1 ecx_writedatagramdata&#xff1a;2.2 ecx_setupdatagram&#xff1a;2.3 ecx_adddatagram&#xff1a;2.4 …...

Spring集成高性能队列Disruptor

Disruptor简介 Disruptor&#xff08;中文翻译为“破坏者”或“颠覆者”&#xff09;是一种高性能、低延迟的并发编程框架&#xff0c;最初由LMAX Exchange开发。它的主要目标是解决在金融交易系统等需要高吞吐量和低延迟的应用中的并发问题。 Disruptor特点 无锁并发&#x…...

C++——类和对象(中)完结

赋值运算符重载 运算符重载 C 为了增强代码的可读性引入了运算符重载 &#xff0c; 运算符重载是具有特殊函数名的函数 &#xff0c;也具有其 返回值类型&#xff0c;函数名字以及参数列表&#xff0c;其返回值类型与参数列表与普通的函数类似。 函数名字为&#xff1a;关键…...

Sqoop的安装和使用

目录 一.安装 二.导入 1.全量导入 一.MySQL导入HDFS 二.MySQL导入Hive 2.增量导入 一.过滤导入hdfs/hive 二.导出 一.安装 1.下载地址&#xff1a;sqoop下载地址 2.解压 tar -zxvf ./sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C ../module/ 3.改名和配置归属权限 #改名…...

java毕业设计基于springboot+vue的村委会管理系统

项目介绍 采用JAVA语言&#xff0c;结合SpringBoot框架与Vue框架以及MYSQL数据库设计并实现的。本村委会管理系统主要包括个人中心、村民管理、村委会管理、村民信息管理、土地变更管理、农业补贴管理、党员信息管理等多个模块。它帮助村委会管理实现了信息化、网络化&#xf…...

【C++】多态 ⑪ ( 纯虚函数和抽象类 | 纯虚函数语法 | 抽象类和实现 | 代码示例 )

文章目录 一、纯虚函数和抽象类1、纯虚函数2、纯虚函数语法3、抽象类和实现 二、完整代码示例 一、纯虚函数和抽象类 1、纯虚函数 纯虚函数 : 在 C 语言中 , " 纯虚函数 " 是 特殊类型的 虚函数 , " 纯虚函数 " 在 父类 中 声明 , 但是没有实现 ; 抽象类 …...

node 第十四天 基于express的第三方中间件multer node后端处理用户上传文件

Multer 是一个 node.js 中间件&#xff0c;用于处理multipart/form-data 类型的表单数据&#xff0c;它主要用于上传文件。它是写在 busboy 之上的所以非常高效。前面我们已经知道了怎样利用express提供的静态资源处理中间件express.static()处理用户请求静态资源文件(图片, js…...

KnowledgeGPT:利用检索和存储访问知识库上增强大型语言模型10.30

利用检索和存储访问知识库上增强大型语言模型 摘要引言2 相关研究3方法3.1 任务定义3.2 知识检索3.2.1 代码实现3.2.2 实体链接3.2.3 获取实体信息3.2.4 查找实体或值3.2.5 查找关系 3.3 知识存储 4 实验4.1 实验设置4.2 流行知识库上的查询4.3 基于知识的问题回答 摘要 大型语…...

Angular material Chips Autocomplete

Chips Autocomplete 官网的例子我没法正常使用&#xff0c;无法实现搜索 我的select是个通用组件&#xff0c;现在贴代码&#xff1a; component.ts import {Component,ElementRef,forwardRef,Input,OnChanges,OnDestroy,OnInit,SimpleChanges,ViewChild, } from angular/co…...

『亚马逊云科技产品测评』活动征文|搭建基础运维环境

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 目录 1、什么是容器化部署 2、连接到控制台 3、安装docker 3.1 更新…...

双指针扫描

import os import sys# 请在此输入您的代码 sinput() Alist(s) nlen(A) t1 for i in range(n//2):if A[i]!A[n-1-i]:t0break if t1:print(Y) else:print(N)n,smap(int,input().split()) alist(map(int,input().split())) #尺取法&#xff0c;变O(n*n)为O(n) #维护一个最短的区间…...

uniapp小程序九宫格抽奖

定义好奖品下标&#xff0c;计时器开始抽奖&#xff0c;请求接口&#xff0c;出现中奖奖品之后&#xff0c;获取中奖商品对应的奖品下标&#xff0c;再次计时器判断当前移动的小标是否为中奖商品的下标&#xff0c;并且是否转到3圈&#xff08;防止转1圈就停止&#xff09;&…...

mysql树状结构查询及注意事项

一、说明 由于Mysql不像oracle一样支持树状查询&#xff0c;需要用户自行处理&#xff0c;本文记录了一种常见的通过自定义函数的方式进行mysql树状查询的方法&#xff0c;以及使用的注意事项。 二、函数 CREATE DEFINERrootlocalhost FUNCTION get_child_menus(in_pid varc…...

TimeGPT-1——第一个时间序列数据领域的大模型他来了

一直有一个问题:时间序列的基础模型能像自然语言处理那样存在吗?一个预先训练了大量时间序列数据的大型模型&#xff0c;是否有可能在未见过的数据上产生准确的预测?最近刚刚发表的一篇论文&#xff0c;Azul Garza和Max Mergenthaler-Canseco提出的TimeGPT-1&#xff0c;将ll…...

通过Google搜索广告传送的携带木马的PyCharm软件版本

导语 最近&#xff0c;一起新的恶意广告活动被发现&#xff0c;利用被入侵的网站通过Google搜索结果推广虚假版本的PyCharm软件。这个活动利用了动态搜索广告&#xff0c;将广告链接指向被黑客篡改的网页&#xff0c;用户点击链接后下载的并不是PyCharm软件&#xff0c;而是多种…...

网站文章收录因素,别人复制文章排名比你原创的好?

我经常看到有站长抱怨“网站不收录”&#xff0c;“排名不好”&#xff0c;“复制的文章为什么秒收”之类的问题。对于SEO从业者来说&#xff0c;这确实是一个打击&#xff0c;认为搜索引擎不公平。凭什么自己原创不收录&#xff0c;别人复制去了&#xff0c;秒收他的&#xff…...