UniApp实现漂亮的音乐歌词滚动播放效果

news2025/1/11 4:14:30

在现代的音乐播放应用中,歌词的展示和滚动播放已经成为了一个非常常见的功能。今天,我们将通过UniApp来实现一个漂亮的歌词滚动播放功能。我们将使用UniApp提供的组件和API来完成这个任务。

页面结构

在页面的模板部分,我们需要创建一个音频播放器和歌词展示区域。使用<scroll-view>组件来实现歌词的滚动效果。

<template>
  <view class="audio-container">
    <!-- 音频播放器 -->
    <view class="audio-player">
      <audio :src="audioSrc" @timeupdate="updateTime" @ended="audioEnded"></audio>
      <view class="controls">
        <button @click="playAudio">播放</button>
        <button @click="pauseAudio">暂停</button>
      </view>
      <view class="time">
        {{ currentTime }} / {{ duration }}
      </view>
    </view>

    <!-- 歌词展示区域 -->
    <scroll-view class="lyrics" scroll-y :scroll-top="scrollTop">
      <view v-for="(line, index) in lyrics" :key="index" :class="{ active: currentLineIndex === index }">
        {{ line.text }}
      </view>
    </scroll-view>
  </view>
</template>

脚本逻辑

在脚本部分,我们需要处理音频的播放、暂停、时间更新等事件,并根据当前播放时间更新歌词的显示和滚动位置。

<script>
export default {
  data() {
    return {
      audioSrc: 'https://example.com/audio.mp3', // 音频文件地址
      lyrics: [
        { time: 0, text: '第一行歌词' },
        { time: 5000, text: '第二行歌词' },
        { time: 10000, text: '第三行歌词' },
        // 更多歌词行...
      ],
      currentTime: '00:00', // 当前播放时间
      duration: '00:00', // 音频总时长
      currentLineIndex: 0, // 当前高亮的歌词行索引
      scrollTop: 0, // 歌词滚动位置
    };
  },
  methods: {
    playAudio() {
      const audio = document.querySelector('audio');
      audio.play();
    },
    pauseAudio() {
      const audio = document.querySelector('audio');
      audio.pause();
    },
    updateTime(event) {
      const audio = event.target;
      this.currentTime = this.formatTime(audio.currentTime);
      this.duration = this.formatTime(audio.duration);
      this.updateLyrics(audio.currentTime * 1000); // 转换为毫秒
    },
    audioEnded() {
      this.currentTime = '00:00';
      this.currentLineIndex = 0;
      this.scrollTop = 0;
    },
    updateLyrics(currentTime) {
      for (let i = 0; i < this.lyrics.length; i++) {
        if (currentTime >= this.lyrics[i].time) {
          this.currentLineIndex = i;
        } else {
          break;
        }
      }
      this.scrollLyrics();
    },
    scrollLyrics() {
      const lineHeight = 30; // 每行歌词的高度
      this.scrollTop = this.currentLineIndex * lineHeight;
    },
    formatTime(time) {
      const minutes = Math.floor(time / 60);
      const seconds = Math.floor(time % 60);
      return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
    },
  },
};
</script>

样式设计

在样式部分,我们可以设计音频播放器和歌词展示区域的样式,使其看起来更加美观。

<style scoped>
.audio-container {
  padding: 20px;
}

.audio-player {
  margin-bottom: 20px;
}

.controls {
  margin-bottom: 10px;
}

.time {
  font-size: 14px;
  color: #666;
}

.lyrics {
  height: 300px;
  overflow-y: auto;
  border: 1px solid #ccc;
  padding: 10px;
  font-size: 16px;
  line-height: 1.5;
  text-align: center;
}

.lyrics view {
  transition: color 0.3s ease;
}

.lyrics .active {
  color: #ff6600;
  font-weight: bold;
}
</style>

运行效果

通过以上步骤,你可以在UniApp中实现一个漂亮的音乐歌词滚动播放效果。运行项目后,你应该能够看到一个带有播放、暂停按钮的音频播放器,以及随着音乐播放自动滚动的歌词。

app体验地址

项目开源地址:imovie: 爱电影小程序uni-app

歌词解析

 网络上拿到的歌词,可能是类似如下格式:

{"songStatus":1,"lyricVersion":3,"lyric":"[by:有个陷阱他们早就已经沦陷]\n\n[00:09.23]\n[00:20.20]花的心藏在蕊中\n[00:23.80]空把花期都错过\n[00:27.26]\n[00:29.52]你的心忘了季节\n[00:33.12]从不轻易让人懂\n[00:37.24]\n[00:38.81]为何不牵我的手\n[00:42.49]共听日月唱首歌\n[00:46.09]\n[00:47.46]黑夜又白昼\n[00:49.72]黑夜又白昼\n[00:51.73]人生为欢有几何\n[00:55.54]\n[00:57.00]春去春会来\n[01:01.72]花谢花会再开\n[01:06.08]只要你愿意\n[01:08.39]只要你愿意\n[01:10.41]让梦划向你心海\n[01:15.65]春去春会来\n[01:20.32]花谢花会再开\n[01:24.73]只要你愿意\n[01:27.06]只要你愿意\n[01:29.02]让梦划向你心海\n[01:33.43]\n[02:12.13]花瓣泪飘落风中\n[02:15.73]虽有悲意也从容\n[02:19.33]\n[02:21.41]你的泪晶莹剔透\n[02:25.04]心中一定还有梦\n[02:29.30]\n[02:30.73]为何不牵我的手\n[02:34.38]同看海天成一色\n[02:39.36]潮起又潮落\n[02:41.68]潮起又潮落\n[02:43.68]送走人间许多愁\n[02:48.28]\n[02:48.99]春去春会来\n[02:53.61]花谢花会再开\n[02:57.98]只要你愿意\n[03:00.35]只要你愿意\n[03:02.32]让梦划向你心海\n[03:07.58]春去春会来\n[03:12.32]花谢花会再开\n[03:16.58]只要你愿意\n[03:18.95]只要你愿意\n[03:21.01]让梦划向你心海\n[03:26.23]只要你愿意\n[03:28.29]只要你愿意\n[03:30.26]让梦划向你心海\n[03:35.06]\n","code":200}

需要对其解析,解析为类似以下的格式:

lyrics: [
        { time: 0, text: '第一行歌词' },
        { time: 5000, text: '第二行歌词' },
        { time: 10000, text: '第三行歌词' },
        // 更多歌词行...
      ],

解析方法:

/**
 * 歌词解析
 * @param {lrcContent} string - 歌词内容
 * @returns {lyrics} 对象数组
 */
function parseLyric(lrcContent) {
    const lines = lrcContent.split('\n');
    const lyrics = [];

    lines.forEach(line => {
        const match = line.match(/\[(\d{2}):(\d{2})\.(\d{2,3})\]/);
        if (match) {
            const minutes = parseInt(match[1]);
            const seconds = parseInt(match[2]);
            const milliseconds = parseInt(match[3]);

            const time = minutes * 60 * 1000 + seconds * 1000 + milliseconds;

            // 提取歌词文本
            const text = line.replace(/\[\d{2}:\d{2}\.\d{2,3}\]/g, '').trim();
            lyrics.push({ time, text });
        }
    });

    return lyrics;
}

完成audio组件源码

<template>
	<view class="audio_container">
		<view class="audio-title"
			style="width: 100%; text-align: left; font-size: 36rpx;font-weight: bold;padding: 0rpx 0rpx; position: relative;">
			<uni-notice-bar single :scrollable="titleScroll" :size="titleFontSize"
				:background-color="titleBackgroundColor" :color="titleColor" :speed="titleScrollSpeed" :text="title"
				class="uni-noticebar" style="padding: 0px; margin-bottom: 0px;">
			</uni-notice-bar>
			<uni-fav v-show="isCollectBtn" :checked="isFavorited" class="favBtn"  bgColor="#dddddd" bgColorChecked="#ffaa00" @click="handleCollec"
				style="color:#848484; position: absolute;top: 0rpx;right: 0px;"></uni-fav>
		</view>
		<view class="audio-subTitle"
			:style="'font-size: '+subTitleFontSize+';font-weight: bold;padding: 0rpx 0rpx 4rpx 0rpx;position: relative;'">
			<uni-notice-bar single :scrollable="titleScroll" :size="titleFontSize"
				:background-color="titleBackgroundColor" :color="subTitleColor" :speed="titleScrollSpeed"
				:text="localSubTitle" class="uni-noticebar">
			</uni-notice-bar>
			<uni-icons v-show="isShareBtn" @click="handleShare" type="redo" size="20"
				style="color:#848484;position: absolute;top: 0rpx;right: 0px;"></uni-icons>
		</view>
		<view>
			<slider :backgroundColor='backgroundColor' :activeColor='activeColor' @change="handleSliderChange"
				:value="sliderIndex" :max="maxSliderIndex" block-color="#343434" block-size="16" />
		</view>
		<view style="padding: 0rpx 15rpx 0rpx 15rpx ; display: block; ">
			<view style="float: left; font-size: 20rpx;color:#848484;">
				{{currentTimeText}}
			</view>
			<view style="float: right;font-size: 20rpx;color:#848484;">
				{{totalTimeText}}
			</view>
		</view>
		<view style="margin-top: 70rpx;">
			<uni-grid :column="5" :showBorder="false" :square="false">
				<uni-grid-item>
					<view class="uni-grid-icon">
						<image @tap="handleFastRewind" src="../../static/images/playlist.svg"
							style="width: 48rpx;height: 48rpx;top:6rpx;">
						</image>
					</view>
				</uni-grid-item>
				<uni-grid-item>
					<view class="uni-grid-icon">
						<image @tap="handleFastRewind" src="../../static/images/get-back.svg"
							style="width: 48rpx;height: 48rpx;top:6rpx;">
						</image>
					</view>
				</uni-grid-item>
				<uni-grid-item>
					<view class="uni-grid-icon">
						<image @tap="handleChangeAudioState" v-show="!isPlaying" src="../../static/images/play.svg"
							style="width: 48rpx;height: 48rpx;top:6rpx;">
						</image>
						<image @tap="handleChangeAudioState" v-show="isPlaying" src="../../static/images/pause.svg"
							style="width: 48rpx;height: 48rpx;top:6rpx;">
						</image>
					</view>
				</uni-grid-item>
				<uni-grid-item>
					<view class="uni-grid-icon">
						<image @tap="handleFastForward" src="../../static/images/fast-forward.svg"
							style="width: 48rpx;height: 48rpx;top:6rpx;">
						</image>
					</view>
				</uni-grid-item>
				<uni-grid-item>
					<view class="uni-grid-icon">
						<image @tap="handleLoopPlay" src="../../static/images/Loop.svg"
							style="width: 48rpx;height: 48rpx; top:6rpx; ">
						</image>
					</view>
				</uni-grid-item>
			</uni-grid>
		</view>
		<view v-show="isShowLrc">
		   <scroll-view class="lyrics" scroll-y :scroll-top="scrollTop" :current="currentLineIndex" ref="lyricsContainer" >
				<block v-for="(line, index) in lyrics" :key="index">
				  <view :class="{ 'active': currentLineIndex === index }">{{ line.text }}</view>
				</block>
			</scroll-view>
		</view>
	</view>
</template>
<script>
	export default {
		name: 'my-audio',
		//audioPlay开始播放
		//audioPause停止播放
		//audioEnd音频自然播放结束事件
		//audioCanplay音频进入可以播放状态,但不保证后面可以流畅播放
		//change播放状态改变 返回值false停止播放 true开始播放
		//audioError 播放器错误
		//audioCollec 音频收藏
		emits: ['audioPlay', 'audioPause', 'audioEnd', 'audioCanplay', 'change', 'audioError','audioCollec'],
		props: {
			//标题文字
			title: {
				type: String,
				default: '空'
			},
			//标题默认字体大小
			titleFontSize: {
				type: Number,
				default: 35
			},
			//标题文字颜色
			titleColor: {
				type: String,
				default: '#303030'
			},
			//标题背景色
			titleBackgroundColor: {
				type: String,
				default: 'white'
			},
			//标题是否滚动
			titleScroll: {
				type: Boolean,
				default: false
			},
			//标题滚动速度
			titleScrollSpeed: {
				type: Number,
				default: 100
			},

			subTitle: {
				type: String,
				default: '空'
			},
			subTitleColor: {
				type: String,
				default: '#6C7996'
			},
			subTitleFontSize: {
				type: String,
				default: "30rpx"
			},
			//是否自动播放
			autoplay: {
				type: Boolean,
				default: false
			},
			//滑块左侧已选择部分的线条颜色
			activeColor: {
				type: String,
				default: '#7C7C7C'
			},
			//滑块右侧背景条的颜色
			backgroundColor: {
				type: String,
				default: '#E5E5E5'
			},

			//音频地址
			src: {
				type: [String, Array],
				default: ''
			},

			//是否倒计时
			isCountDown: {
				type: Boolean,
				default: false
			},

			//音乐封面
			audioCover: {
				type: String,
				default: ''
			},
			//是否显示收藏按钮
			isCollectBtn: {
				type: Boolean,
				default: false
			},
			//状态是否是已收藏
			isFavorited: {
				type: Boolean,
				default: false
			},
			//是否显示分享按钮
			isShareBtn: {
				type: Boolean,
				default: false
			},
			
			//是否显示歌词
			isShowLrc: {
				type: Boolean,
				default: false
			},
			
			//歌词信息
			lyrics: {
				type: [Array],
				default: []
			},
		},
		data() {
			return {
				totalTimeText: '00:00', //视频总长度文字
				currentTimeText: '00:00:00', //视频已播放长度文字

				isPlaying: false, //播放状态

				sliderIndex: 0, //滑块当前值
				maxSliderIndex: 100, //滑块最大值

				IsReadyPlay: false, //是否已经准备好可以播放了

				isLoop: false, //是否循环播放

				speedValue: [0.5, 0.8, 1.0, 1.25, 1.5, 2.0],
				speedValueIndex: 2,
				playSpeed: '1.0', //播放倍速 可取值:0.5/0.8/1.0/1.25/1.5/2.0

				currentLineIndex: 0,
				localSubTitle:this.subTitle,
				shortLrc:'',
				scrollTop: 0, // 初始滚动位置
				stringObject: (data) => {
					return typeof(data)
				},
				innerAudioContext: uni.createInnerAudioContext()
			}
		},
		watch: {
		    subTitle(newVal) {
		      this.localSubTitle = newVal;
			  }
		},
		async mounted() {
			this.innerAudioContext.src = typeof(this.src) == 'string' ? this.src : this.src[0];
			if (this.autoplay) {
				if (!this.src) return console.error('src cannot be empty,The target value is string or array')

				// #ifdef H5
				var ua = window.navigator.userAgent.toLowerCase();
				if (ua.match(/MicroMessenger/i) == 'micromessenger') {
					const jweixin = require('../../utils/jweixin');

					jweixin.config({});
					jweixin.ready(() => {
						WeixinJSBridge.invoke('getNetworkType', {}, (e) => {
							this.innerAudioContext.play();

						})
					})
				}
				// #endif

				// #ifndef H5
				this.innerAudioContext.autoplay = true;
				// #endif
			}

			//音频播放事件
			this.innerAudioContext.onPlay(() => {
				this.isPlaying = true;
				this.$emit('audioPlay')

				this.$emit('change', {
					state: true
				});

				setTimeout(() => {
					this.maxSliderIndex = parseFloat(this.innerAudioContext.duration).toFixed(2);

				}, 100)
			});

			//音频暂停事件
			this.innerAudioContext.onPause(() => {
				this.$emit('audioPause');
				this.$emit('change', {
					state: false
				});
			});

			//音频自然播放结束事件
			this.innerAudioContext.onEnded(() => {
				this.isPlaying = !this.isPlaying;
				this.$emit('audioEnd');

				if (this.isLoop) {
					this.changePlayProgress(0);
					this.innerAudioContext.play();
				}
			});

			//音频进入可以播放状态,但不保证后面可以流畅播放
			this.innerAudioContext.onCanplay((event) => {

				this.IsReadyPlay = true;

				this.$emit('audioCanplay');
				
				let duration = this.innerAudioContext.duration;

				//console.log('总时长', duration)

				//将当前音频长度秒转换为00:00:00格式
				this.totalTimeText = this.getFormateTime(duration);
				this.maxSliderIndex = parseFloat(duration).toFixed(2);

				//console.log(this.getFormateTime(duration))
				
				//console.log('总时长1', this.totalTimeText)

				//防止视频无法正确获取时长
				setTimeout(() => {
					duration = this.innerAudioContext.duration;

					//将当前音频长度秒转换为00:00:00格式
					this.totalTimeText = this.getFormateTime(duration);
					this.maxSliderIndex = parseFloat(duration).toFixed(2);
					
					//console.log('总时长2', this.totalTimeText)
				}, 300)
				
			});

			//音频播放错误事件
			this.innerAudioContext.onTimeUpdate((res) => {
				this.sliderIndex = parseFloat(this.innerAudioContext.currentTime).toFixed(2);
				this.currentTimeText = this.getFormateTime(this.innerAudioContext.currentTime);
				//更新歌词
				const currentTime = this.innerAudioContext.currentTime * 1000; // 转换为毫秒
				this.updateLyrics(currentTime);
			});

			//音频播放错误事件
			this.innerAudioContext.onError((res) => {
				console.log(res.errMsg);
				console.log(res.errCode);
				this.$emit('change', {
					state: false
				});
				this.audioPause();

				this.$emit('audioError', res);
			});

		},
		methods: {
			//销毁innerAudioContext()实例
			audioDestroy() {
				console.log("audioDestroy")
				if (this.innerAudioContext) {
					if (this.isPlaying && !this.innerAudioContext.paused) {
						this.audioPause();
					}
					this.innerAudioContext.destroy();
					this.isPlaying = false;
				}
			},
			//点击变更播放状态
			handleChangeAudioState() {
				if(this.src ===''){
					uni.showToast({
									title: '无播放资源',
									icon: 'none',
									duration: 1000
								});
					return;
				}
				if (this.isPlaying && !this.innerAudioContext.paused) {
					this.audioPause();
				} else {
					this.audioPlay();
				}
			},
			//开始播放
			audioPlay() {
				this.$nextTick(() => {
					this.innerAudioContext.src = this.src;
					setTimeout(() => {
						this.innerAudioContext.play();
						this.isPlaying = true;
					}, 100); // 100毫秒
				});
				
			},
			//暂停播放
			audioPause() {
				this.innerAudioContext.pause();
				this.isPlaying = false;
			},
			//变更滑块位置
			handleSliderChange(e) {
				this.changePlayProgress(e.detail ? e.detail.value : e)
			},
			//更改播放倍速
			handleChageSpeed() {
				//获取播放倍速列表长度
				let speedCount = this.speedValue.length;
				//如果当前是最大倍速,从-1开始
				if (this.speedValueIndex == (speedCount - 1)) {
					this.speedValueIndex = -1;
				}
				//最新倍速序号
				this.speedValueIndex += 1;
				//获取最新倍速文字
				this.playSpeed = this.speedValue[this.speedValueIndex].toFixed(1);
				//暂停播放
				this.audioPause();
				//变更播放倍速
				this.innerAudioContext.playbackRate(this.speedValue[this.speedValueIndex]);
				//开始播放
				this.audioPlay();
			},
			//快退15秒
			handleFastRewind() {
				if (this.IsReadyPlay) {
					let value = parseInt(this.sliderIndex) - 15;
					this.changePlayProgress(value >= 0 ? value : 0);
				}
			},
			//快进15秒
			handleFastForward() {
				if (this.IsReadyPlay) {
					let value = parseInt(this.sliderIndex) + 15;
					this.changePlayProgress(value <= this.innerAudioContext.duration ? value : this.innerAudioContext
						.duration);
				}
			},
			//开启循环播放
			handleLoopPlay() {
				this.isLoop = !this.isLoop;
				if (this.isLoop) {
					uni.showToast({
						title: '已开启循环播放',
						duration: 1000
					});
				} else {
					uni.showToast({
						title: '取消循环播放',
						duration: 1000
					});
				}
			},
			//更改播放进度
			changePlayProgress(value) {
				this.innerAudioContext.seek(value);
				this.sliderIndex = value;
				this.currentTimeText = this.getFormateTime(value);
			},
			//秒转换为00:00:00
			getFormateTime(time) {
				let ms = time * 1000; // 1485000毫秒
				let date = new Date(ms);

				// 注意这里是使用的getUTCHours()方法,转换成UTC(协调世界时)时间的小时
				let hour = date.getUTCHours();
				// let hour = date.getHours(); 如果直接使用getHours()方法,则得到的时分秒格式会多出来8个小时(在国内开发基本都是使用的是东八区时间),getHours()方法会把当前的时区给加上。
				let minute = date.getMinutes();
				let second = date.getSeconds();

				let formatTime =
					`${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${second.toString().padStart(2, '0')}`;

				return formatTime;
			},
			handleCollec() {
				this.$emit('audioCollec');
			},
			handleShare() {
				this.$emit('audioShare');
			},
			updateLyrics(currentTime) {
			    for (let i = 0; i < this.lyrics.length; i++) {
			        if (currentTime >= this.lyrics[i].time) {
			          this.currentLineIndex = i;
					  this.shortLrc = this.lyrics[i].text;
					  this.localSubTitle = this.subTitle + ' : '+this.shortLrc
			        } else {
			          break;
			        }
			    }
				this.scrollLyrics();
			},
			scrollLyrics() {
			    const lineHeight = 20; // 每行歌词的高度
			    this.scrollTop = this.currentLineIndex * lineHeight;
			},
		},
		onLoad() {
			console.log("onLoad")
		},
		onUnload() {
			console.log("onUnload")
			this.audioDestroy()
		},
		onHide() {
			console.log("onHide")
			this.audioDestroy()
		},
		beforeDestroy() {
			console.log("beforeDestroy")
			this.audioDestroy()
		}
	}
</script>

<style lang="scss" scoped>
	.audio_container {
		box-shadow: 0 0 10rpx #c3c3c3;
		padding: 30rpx 20rpx 30rpx 20rpx;

		.audio-title {
			font-size: 28rpx;
		}

		.uni-noticebar {
			padding: 0px;
			padding-right: 50rpx;
			margin-bottom: 0px;
			display: inline-block;
		}


		.audio-subTitle {
			width: 100%;
			text-align: left;
			font-size: 40rpx;
			color: blue;
		}

		.speed-text {
			position: absolute;
			top: 0rpx;
			left: 30rpx;
			right: 0;
			color: #475266;
			font-size: 16rpx;
			font-weight: 600;
		}

		.uni-grid-icon {
			text-align: center;
		}
		
		.lyrics {
		  margin-top: 20px;
		  height: 660rpx; /* 设置歌词容器的高度 */
		  // overflow: hidden; /* 隐藏溢出的歌词 */
		  overflow-y: auto; /* 允许垂直滚动 */
		  position: relative;
		  font-size: 32rpx;
		  line-height: 1.8;
		  text-align: center;
		}
		.lyrics view {
		  transition: color 1.2s ease; /* 添加平滑颜色变化效果 */
		}
		
		.lyrics .active {
		  color: #00aa00;
		  font-size: 45rpx;
		  font-weight: bold;
		}

	}
</style>

总结

通过使用UniApp的组件和API,我们可以轻松实现音乐歌词的滚动播放效果。关键在于监听音频的播放时间,并根据时间更新歌词的显示和滚动位置。

这里面有个悬而未决的问题,就是这个滚动显示,有时候会滚动到最上方或最下方,导致在视野区域看不到。以下的处理,虽然简单, 但也粗暴。原因就出在这里:

scrollLyrics() {
		const lineHeight = 20; // 每行歌词的高度
		this.scrollTop = this.currentLineIndex * lineHeight;
},

如何让歌词能够根据进度居中显示?有知道的欢迎留言,感谢!

其他资源

vue实现歌词滚动_vue 实现一个歌词滚动效果-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实现简单聊天

管道通信 目的&#xff1a; 实现 xxz 与 zy 之间的通讯聊天 功能&#xff1a; 同一网络下进行的模拟通讯实现 缺点&#xff1a;没有涉及到网络的通信&#xff0c;只是简单的进程之间模拟的通信 采用到的技术点&#xff1a; 1 有名管道 优点&#xff1a; 在文件系统中作为一…

c++修炼之路之特殊类设计与类型转换

目录 一&#xff1a;特殊类设计 1.设计一个不能被拷贝的类 2.设计一个只能在堆上创建对象的类 3.设计一个只能在栈上创建对象的类 4.设计一个不能被继承的类 5.设计一个只能创建一个对象的类(单例模式) 二&#xff1a;c的类型转换 1.自定义类型&#xff0c;内置类型…

计算机的错误计算(八十五)

摘要 计算机的错误计算&#xff08;八十一&#xff09;至&#xff08;八十四&#xff09;介绍了双曲正弦、余弦、正割以及余割函数的计算精度问题。本节说明导致这些计算错误的主要原因。 首先&#xff0c;双曲正弦、余弦、正割以及余割函数主要包括 与 的计算&#xff0c;以…

逐行讲解Transformer的代码实现和原理讲解:nn.Linear线性层原理

视频详细讲解&#xff1a;LLM模型&#xff1a;代码讲解Transformer运行原理(1)_哔哩哔哩_bilibili 1 概述 经过Transformer的12个块处理完之后&#xff0c;4批文本数据得到了一个矩阵[4, 8, 16]&#xff0c;也就是每批数据都训练出了一个结果&#xff0c;在训练阶段&#xff…

Netty系列-3 ChannelFuture和ChannelPromise介绍

背景 Netty源码中大量使用了Future和Promise&#xff0c;学习ChannelFuture和ChannelFuture有助于理解Netty的设计思路。 本文的重点内容在于梳理清楚这些类的关系以及结合源码实现介绍这些类的作用&#xff0c;其中核心逻辑在于DefaultPromise和DefaultChannelPromise&#x…

GBase8sV8.8安装指南

目录 一、下载 Gbase 安装包二、安装预置条件1.确保安装包和平台适配2.安装依赖包&#xff1a;jdk(1.6版本以上)、unzip、libaio、libgcc、libstdc、ncurses、pam&#xff0c;如果缺失请提前安装 三、上传包并解压四、安装五、登录并创建数据库六、启动停止数据库七、常见问题八…

虚拟机ubuntu与主机共享文件夹

现在主机&#xff08;windows&#xff09;上新建一个共享文件夹 打开虚拟机 按下面操作打开共享文件夹 进入虚拟机的系统 cd /mnt/hgfs 如果报错 可以按下面的解决 挂载一下 sudo mount -t fuse.vmhgfs-fuse .host:/ /mnt/hgfs -o allow_other 如果显示不存在这个文…

session机制

场景&#xff1a;当众多用户访问网站&#xff0c;发出HTTP请求&#xff0c;那么网站是如何判断哪个HTTP请求对应的是哪个用户 &#xff1f; 作用&#xff1a;用于服务端区分用户。 当用户使用客户端登录时&#xff0c;服务端会进行验证&#xff0c;验证通过后会为这次登录创建…

剖析Cookie的工作原理及其安全风险

Cookie的工作原理主要涉及到HTTP协议中的状态管理。HTTP协议本身是无状态的&#xff0c;这意味着每次请求都是独立的&#xff0c;服务器不会保留之前的请求信息。为了在无状态的HTTP协议上实现有状态的会话&#xff0c;引入了Cookie机制。 1. Cookie定义 Cookie&#xff0c;也…

EMC测试

传导干扰测试&#xff1a; 现场实录CE传导骚扰电压测试&#xff0c;硬件环境&#xff1a; R&S EPL1000 EMI测量接收机&#xff08;支持时域测试&#xff09; R&S ENV216人工电源网络 R&S ELEKTRA 测试软件 黑色底板&#xff0c;不写丝印&#xff0c;0402封装平行排…

Tomcat服务详解

一、部署Tomcat服务器 JDK安装官方网址&#xff1a;https://www.oracle.com/cn/java Tomcat安装官方网址&#xff1a;Apache Tomcat - Welcome! 安装JDK 1.获取安装包 wget https://download.oracle.com/otn/java/jdk/8u411-b09/43d62d619be4e416215729597d70b8ac/jdk-8u41…

【工程测试技术】第13章 流体参量测量

目录 第13章 流体参量测量 13.1压力的测量 13.1.1 弹性式压力敏感元件 1. 波登管 2. 膜片和膜盒 3. 波纹管 13.1.2 常用压力传感器 1. 应变式压力传感器 2. 压阻式压力传感器 3. 压电式压力传感器 4. 电容式压力传感器 5. 谐振式压力传感器 6. 位移式压力传感器 (1)…

整型数组按个位值排序

题目描述 给定一个非空数组(列表)&#xff0c;其元素数据类型为整型&#xff0c;请按照数组元素十进制最低位从小到大进行排序&#xff0c;十进制最低位相同的元司 相对位置保持不变。 当数组元素为负值时&#xff0c;十进制最低位等同于去除符号位后对应十进制值最低位。 输…

吐血整理 ChatGPT 3.5/4.0 新手使用手册~ 【2024.09.04 更新】

以前我也是通过官网使用&#xff0c;但是经常被封号&#xff0c;就非常不方便&#xff0c;后来有朋友推荐国内工具&#xff0c;用了一阵之后&#xff0c;发现&#xff1a;稳定方便&#xff0c;用着也挺好的。 最新的 GPT-4o、4o mini&#xff0c;可搭配使用~ 1、 最新模型科普&…

VisualStudio环境搭建C++

Visual Studio环境搭建 说明 C程序编写中&#xff0c;经常需要链接头文件(.h/.hpp)和源文件(.c/.cpp)。这样的好处是&#xff1a;控制主文件的篇幅&#xff0c;让代码架构更加清晰。一般来说头文件里放的是类的申明&#xff0c;函数的申明&#xff0c;全局变量的定义等等。源…

Java面试题·解释题·框架部分

系列文章目录 Java面试题解释题总体概括 Java面试题解释题JavaSE部分 Java面试题解释题框架部分 文章目录 系列文章目录前言一、MyBatis1. 请你介绍MyBatis框架2. MyBatis框架的核心思想是什么&#xff1f;3. MyBatis的核心配置文件中常用的子标签有哪些&#xff1f;4. mapper…

饲料加工机器设备有哪些组成部分

在快速发展的畜牧业中&#xff0c;饲料加工作为支撑养殖业的重要环节&#xff0c;其效率与品质直接影响着养殖业的成本效益与动物健康。随着科技的进步&#xff0c;饲料加工机器设备也在不断升级&#xff0c;为养殖行业带来了变革。一、智能化粉碎机&#xff1a;细度可调&#…

Unity Adressables 使用说明(五)在运行时使用 Addressables(Use Addressables at Runtime)

一旦你将 Addressable assets 组织到 groups 并构建到 AssetBundles 中&#xff0c;就需要在运行时加载、实例化和释放它们。 Addressables 使用引用计数系统来确保 assets 只在需要时保留在内存中。 Addressables 初始化 Addressables 系统在运行时第一次加载 Addressable …

SimD:基于相似度距离的小目标检测标签分配

摘要 https://arxiv.org/pdf/2407.02394 由于物体尺寸有限且信息不足&#xff0c;小物体检测正成为计算机视觉领域最具挑战性的任务之一。标签分配策略是影响物体检测精度的关键因素。尽管已经存在一些针对小物体的有效标签分配策略&#xff0c;但大多数策略都集中在降低对边界…

怎么利用XML发送物流快递通知短信

现如今短信平台越来越普遍了&#xff0c;而短信通知也分很多种&#xff0c;例如服务通知、订单通知、交易短信通知、会议通知等。而短信平台在物流行业通知这一块作用也很大。在家时:我们平时快递到了&#xff0c;如果电话联系不到本人&#xff0c;就会放到代收点&#xff0c;然…